aws-runtime-bridge 1.9.153 → 1.9.168
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +766 -0
- package/dist/adapter/AcodeSdkAdapter.d.ts +57 -6
- package/dist/adapter/AcodeSdkAdapter.js +4 -8
- package/dist/adapter/ClaudeSdkAdapter.d.ts +2 -0
- package/dist/adapter/ClaudeSdkAdapter.js +3 -3
- package/dist/adapter/CodexSdkAdapter.d.ts +2 -0
- package/dist/adapter/CodexSdkAdapter.js +3 -3
- package/dist/adapter/OpencodeSdkAdapter.d.ts +2 -0
- package/dist/adapter/OpencodeSdkAdapter.js +3 -3
- package/dist/adapter/prompts.d.ts +1 -1
- package/dist/adapter/prompts.js +1 -1
- package/dist/adapter/types.d.ts +41 -20
- package/dist/adapter/types.js +1 -1
- package/dist/browser/frame-encode.d.ts +25 -0
- package/dist/browser/frame-encode.js +2 -0
- package/dist/browser/screencast-differ.d.ts +2 -3
- package/dist/browser/screencast-differ.js +1 -1
- package/dist/browser/screencast-manager.js +1 -1
- package/dist/browser/types.d.ts +5 -1
- package/dist/browser/types.js +1 -1
- package/dist/desktop/desktop-mcp-client.js +2 -2
- package/dist/routes/acode-todos.js +1 -1
- package/dist/routes/background-tasks.js +3 -3
- package/dist/routes/capabilities.js +1 -1
- package/dist/routes/dashboard.d.ts +2 -0
- package/dist/routes/dashboard.js +4 -4
- package/dist/routes/git.js +22 -22
- package/dist/routes/instance.js +1 -1
- package/dist/routes/launch.js +10 -10
- package/dist/routes/ltr.js +1 -1
- package/dist/routes/mcp-extra.js +2 -2
- package/dist/routes/mcp.d.ts +4 -0
- package/dist/routes/mcp.js +1 -1
- package/dist/routes/plugins.js +1 -1
- package/dist/routes/pty.d.ts +11 -3
- package/dist/routes/pty.js +3 -3
- package/dist/routes/runtime-binding.js +1 -1
- package/dist/routes/sessions.d.ts +18 -74
- package/dist/routes/sessions.js +1 -1
- package/dist/routes/skills.js +1 -1
- package/dist/routes/sub-agents.js +8 -8
- package/dist/routes/terminal.d.ts +24 -2
- package/dist/routes/terminal.js +34 -36
- package/dist/services/agent-process-manager.js +1 -1
- package/dist/services/agent-pty-manager.js +4 -4
- package/dist/services/aws-client-agent-mcp.js +2 -2
- package/dist/services/capability-config.d.ts +8 -0
- package/dist/services/capability-config.js +2 -2
- package/dist/services/desktop-mcp-manager.d.ts +3 -1
- package/dist/services/desktop-mcp-manager.js +1 -1
- package/dist/services/instance-ai-config-service.js +1 -1
- package/dist/services/instance-features.d.ts +26 -2
- package/dist/services/instance-features.js +1 -1
- package/dist/services/instance-init-service.js +1 -1
- package/dist/services/launch-manager.d.ts +9 -1
- package/dist/services/launch-manager.js +6 -6
- package/dist/services/launch-metrics.d.ts +1 -1
- package/dist/services/launch-metrics.js +4 -3
- package/dist/services/mcp-deploy.d.ts +4 -0
- package/dist/services/mcp-env-injector.d.ts +12 -0
- package/dist/services/mcp-env-injector.js +2 -0
- package/dist/services/mcp-launch-binding-queue.d.ts +8 -0
- package/dist/services/mcp-launch-binding-queue.js +1 -1
- package/dist/services/orphan-monitor.js +1 -1
- package/dist/services/panel-env.d.ts +6 -2
- package/dist/services/panel-env.js +1 -1
- package/dist/services/plugin-manager.js +9 -9
- package/dist/services/process-detector.d.ts +23 -3
- package/dist/services/process-detector.js +13 -13
- package/dist/services/runtime-resources-snapshot.d.ts +3 -0
- package/dist/services/runtime-resources-snapshot.js +3 -0
- package/dist/services/session-output.d.ts +13 -0
- package/dist/services/session-output.js +4 -4
- package/dist/services/tunnel-client.js +1 -1
- package/dist/services/workspace-files.js +1 -1
- package/dist/services/workspace-watch.js +1 -1
- package/dist/types.d.ts +4 -0
- package/dist/utils/env-safety.d.ts +15 -0
- package/dist/utils/env-safety.js +2 -0
- package/dist/utils/lifecycle-log.d.ts +2 -0
- package/dist/utils/lifecycle-log.js +3 -0
- package/dist/utils/mcp-utils.js +1 -1
- package/package/acode/dist/ask-user-question-tool.js +1 -1
- package/package/acode/dist/await-complete-tool.js +1 -1
- package/package/acode/dist/background-command-manager.js +4 -4
- package/package/acode/dist/background-command-tools.js +1 -1
- package/package/acode/dist/built-in-file-tools.d.ts +31 -2
- package/package/acode/dist/built-in-file-tools.js +2 -2
- package/package/acode/dist/built-in-symbols-tool.js +1 -1
- package/package/acode/dist/builtins/shellCommandPreparation.d.ts +2 -0
- package/package/acode/dist/builtins/shellCommandPreparation.js +2 -2
- package/package/acode/dist/compaction.d.ts +10 -0
- package/package/acode/dist/compaction.js +6 -6
- package/package/acode/dist/context-overflow.d.ts +19 -0
- package/package/acode/dist/context-overflow.js +2 -0
- package/package/acode/dist/grammars/grammars/tree-sitter-lua.wasm +0 -0
- package/package/acode/dist/index.d.ts +4 -1
- package/package/acode/dist/index.js +1 -1
- package/package/acode/dist/mcpToolNames.d.ts +4 -0
- package/package/acode/dist/mcpToolNames.js +1 -1
- package/package/acode/dist/parsers/ast-cache.d.ts +12 -1
- package/package/acode/dist/parsers/ast-cache.js +1 -1
- package/package/acode/dist/parsers/language-queries.js +1 -1
- package/package/acode/dist/parsers/symbol-extractor.js +2 -2
- package/package/acode/dist/parsers/tree-sitter-loader.d.ts +1 -1
- package/package/acode/dist/parsers/tree-sitter-loader.js +1 -1
- package/package/acode/dist/plugin-types.d.ts +5 -0
- package/package/acode/dist/plugin-types.js +1 -0
- package/package/acode/dist/read-file-state.d.ts +48 -0
- package/package/acode/dist/read-file-state.js +2 -0
- package/package/acode/dist/runtime-environment.d.ts +2 -0
- package/package/acode/dist/runtime-environment.js +3 -2
- package/package/acode/dist/runtime.d.ts +124 -10
- package/package/acode/dist/runtime.js +27 -23
- package/package/acode/dist/skill-tool.js +3 -3
- package/package/acode/dist/sub-agent-manager.js +1 -1
- package/package/acode/dist/sub-agent-tools.js +5 -5
- package/package/acode/dist/todo-tool.js +3 -3
- package/package/acode/dist/tool-allocation.js +1 -1
- package/package/acode/dist/tool-concurrency/index.d.ts +5 -0
- package/package/acode/dist/tool-concurrency/index.js +2 -0
- package/package/acode/dist/tool-concurrency/observability.d.ts +46 -0
- package/package/acode/dist/tool-concurrency/observability.js +3 -0
- package/package/acode/dist/tool-concurrency/resource-lock.d.ts +25 -0
- package/package/acode/dist/tool-concurrency/resource-lock.js +2 -0
- package/package/acode/dist/tool-concurrency/scheduler.d.ts +30 -0
- package/package/acode/dist/tool-concurrency/scheduler.js +2 -0
- package/package/acode/dist/tool-concurrency/types.d.ts +80 -0
- package/package/acode/dist/tool-concurrency/types.js +2 -0
- package/package/acode/dist/tool-group-manager.d.ts +128 -0
- package/package/acode/dist/tool-group-manager.js +18 -0
- package/package/acode/dist/types.d.ts +20 -2
- package/package/acode/package.json +3 -3
- package/package/aws-client-agent-mcp/README.md +1 -0
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +20 -32
- package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.d.ts +10 -0
- package/package/aws-client-agent-mcp/dist/http-client.js +1 -1
- package/package/aws-client-agent-mcp/dist/logger.d.ts +2 -1
- package/package/aws-client-agent-mcp/dist/logger.js +2 -2
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +65 -10
- package/package/aws-client-agent-mcp/dist/mcp-server.js +30 -30
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +42 -7
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +196 -21
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/memory-store.js +3 -3
- package/package/aws-client-agent-mcp/dist/message-buffer.js +1 -1
- package/package/aws-client-agent-mcp/dist/office/office-equation.d.ts +5 -0
- package/package/aws-client-agent-mcp/dist/office/office-equation.js +2 -0
- package/package/aws-client-agent-mcp/dist/office/office-image.d.ts +12 -0
- package/package/aws-client-agent-mcp/dist/office/office-image.js +2 -0
- package/package/aws-client-agent-mcp/dist/office/office-workspace.d.ts +40 -0
- package/package/aws-client-agent-mcp/dist/office/office-workspace.js +2 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.js +1 -1
- package/package/aws-client-agent-mcp/dist/tool-groups.d.ts +4 -0
- package/package/aws-client-agent-mcp/dist/tool-groups.js +2 -2
- package/package/aws-client-agent-mcp/dist/types.d.ts +56 -3
- package/package/aws-client-agent-mcp/dist/websocket-client.js +1 -1
- package/package/aws-client-agent-mcp/package.json +2 -1
- package/package.json +1 -1
- package/package/acode/dist/capability-prompt.d.ts +0 -16
- package/package/acode/dist/capability-prompt.js +0 -3
- package/package/acode/dist/plugin-tools.d.ts +0 -14
- package/package/acode/dist/plugin-tools.js +0 -9
package/dist/routes/launch.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import b from"node:fs/promises";import w from"node:path";import{Router as M}from"express";import{validateToken as l}from"../middleware/auth.js";import{getLaunchManager as c,LaunchAlreadyRunningError as
|
|
2
|
-
`,"utf-8"),await b.rename(s,n),d.info(`[launch/file] \u542F\u52A8\u9879\u5FEB\u7167\u5DF2\u5199\u5165: ${n}`),t.json({ok:!0,path:n})}catch(r){const e=r;d.error("[launch/file] Error:",e),t.status(500).json({error:e.message})}}),
|
|
1
|
+
import b from"node:fs/promises";import w from"node:path";import{Router as M}from"express";import{validateToken as l}from"../middleware/auth.js";import{getLaunchManager as c,LaunchAlreadyRunningError as N}from"../services/launch-manager.js";import{METRICS_HISTORY_CAPACITY as P}from"../services/launch-metrics.js";import{getLaunchStreamHub as j}from"../services/launch-streams.js";import{createLogger as $}from"../utils/logger.js";import{noteDashboardActivity as k}from"./dashboard.js";const d=$("launch"),h=M(),v=3e5,I=20;h.put("/launch-configs/file",l,async(a,t)=>{try{const r=a.body||{},e=typeof r.workspacePath=="string"?r.workspacePath.trim():"";if(!e||!w.isAbsolute(e)){t.status(400).json({error:"workspacePath must be a non-empty absolute path"});return}if(!r.content||typeof r.content!="object"||Array.isArray(r.content)){t.status(400).json({error:"content must be an object"});return}const n=w.join(e,".agentswork","launcher","launcher-config.json");await b.mkdir(w.dirname(n),{recursive:!0});const s=`${n}.${process.pid}.tmp`;await b.writeFile(s,JSON.stringify(r.content,null,2)+`
|
|
2
|
+
`,"utf-8"),await b.rename(s,n),d.info(`[launch/file] \u542F\u52A8\u9879\u5FEB\u7167\u5DF2\u5199\u5165: ${n}`),t.json({ok:!0,path:n})}catch(r){const e=r;d.error("[launch/file] Error:",e),t.status(500).json({error:e.message})}}),h.get("/launch-configs/file",l,async(a,t)=>{try{const r=typeof a.query.workspacePath=="string"?a.query.workspacePath.trim():"";if(!r||!w.isAbsolute(r)){t.status(400).json({error:"workspacePath must be a non-empty absolute path"});return}const e=w.join(r,".agentswork","launcher","launcher-config.json");try{const n=await b.readFile(e,"utf-8");t.json({ok:!0,found:!0,path:e,content:JSON.parse(n)})}catch(n){if(n.code==="ENOENT"){t.json({ok:!0,found:!1,path:e});return}throw n}}catch(r){const e=r;d.error("[launch/file:read] Error:",e),t.status(500).json({error:e.message})}}),h.get("/launch-configs/runs",l,async(a,t)=>{try{const r=a.query.includePorts,e=!(r==="0"||r==="false"),n=await c().listRuns({withPorts:e});t.json({ok:!0,runs:n})}catch(r){const e=r;d.error("[launch/runs] Error:",e),t.status(500).json({error:e.message})}}),h.post("/launch-configs/:id/start",l,async(a,t)=>{const r=a.params.id;try{const e=a.body||{},n=Number(e.waitTimeoutMs),s=Number.isFinite(n)&&n>0?Math.min(n,v):0;let o;try{o=await c().start({configId:r,command:e.command,cwd:e.cwd,env:e.env,jdkPath:e.jdkPath,workspacePath:e.workspacePath,readinessPattern:e.readinessPattern,startedBy:e.startedBy})}catch(i){if(i instanceof N){t.json({ok:!0,skipped:!0,message:"\u5F53\u524D\u5DF2\u5904\u4E8E\u542F\u52A8/\u6B63\u5728\u542F\u52A8\u72B6\u6001\uFF0C\u8DF3\u8FC7",run:i.existing});return}throw i}if(s>0){const i=await c().waitStartupResult(r,s);t.json({ok:!0,run:o,wait:i});return}t.json({ok:!0,run:o})}catch(e){const n=e;d.warn(`[launch/start] configId=${r}, error=${n.message}`),t.status(400).json({error:n.message})}}),h.post("/launch-configs/:id/restart",l,async(a,t)=>{const r=a.params.id;try{const e=a.body||{},n=Number(e.waitTimeoutMs),s=Number.isFinite(n)&&n>0?Math.min(n,v):0,o=await c().restart({configId:r,command:e.command,cwd:e.cwd,env:e.env,jdkPath:e.jdkPath,workspacePath:e.workspacePath,readinessPattern:e.readinessPattern,startedBy:e.startedBy});if(s>0){const i=await c().waitStartupResult(r,s);t.json({ok:!0,run:o,wait:i});return}t.json({ok:!0,run:o})}catch(e){const n=e;d.warn(`[launch/restart] configId=${r}, error=${n.message}`),t.status(400).json({error:n.message})}}),h.post("/launch-configs/start-batch",l,async(a,t)=>{try{const r=a.body||{};if(!Array.isArray(r.items)||r.items.length===0){t.status(400).json({error:"items must be a non-empty array"});return}if(r.items.length>I){t.status(400).json({error:`items exceeds limit ${I}`});return}const e=Number(r.waitTimeoutMs),n=Number.isFinite(e)&&e>0?Math.min(e,v):0,s=await c().startBatch(r.items,n);t.json({ok:!0,...s})}catch(r){const e=r;d.warn(`[launch/start-batch] error=${e.message}`),t.status(400).json({error:e.message})}}),h.post("/launch-configs/stop-all",l,async(a,t)=>{try{const r=await c().stopAll();t.json({ok:!0,...r})}catch(r){const e=r;d.warn(`[launch/stop-all] error: ${e.message}`),t.status(500).json({error:e.message})}}),h.post("/launch-configs/:id/stop",l,async(a,t)=>{const r=a.params.id;try{const e=a.body?.actor,n=typeof e=="string"&&e.trim()?e.trim():void 0,s=await c().stop(r,"manual",n);t.json({ok:s.success,...s})}catch(e){const n=e;d.warn(`[launch/stop] configId=${r}, error=${n.message}`),t.status(400).json({error:n.message})}}),h.get("/launch-configs/:id/logs",l,async(a,t)=>{const r=a.params.id,e=Number(a.query.since)||0;try{const n=await c().getLogs(r,e);if(!n){t.status(404).json({error:"launch run not found"});return}t.json({ok:!0,...n})}catch(n){const s=n;d.error(`[launch/logs] configId=${r}, error:`,s),t.status(500).json({error:s.message})}}),h.get("/launch-configs/:id/metrics",l,async(a,t)=>{const r=a.params.id,e=Number(a.query.limit),n=Number.isFinite(e)&&e>0?Math.min(Math.floor(e),P):P;try{const s=c().getRun(r);if(!s){t.status(404).json({error:"launch run not found"});return}const o=c().getMetrics(r,n);t.json({ok:!0,configId:r,state:s.state,metrics:o})}catch(s){const o=s;d.error(`[launch/metrics] configId=${r}, error:`,o),t.status(500).json({error:o.message})}}),h.get("/launch-configs/:id/logs/stream",l,async(a,t)=>{k();const r=a.params.id;if(!c().getRun(r)){t.status(404).json({error:"launch run not found"});return}const n=Number(a.headers["last-event-id"]),s=Number.isFinite(n)&&n>=0?n:-1;t.on("error",()=>{});let o=null,i=null;try{t.writeHead(200,{"Content-Type":"text/event-stream; charset=utf-8","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"}),t.write(`: connected
|
|
3
3
|
|
|
4
|
-
`);const g=f=>{t.destroyed||t.write(f)};let
|
|
4
|
+
`);const g=f=>{t.destroyed||t.write(f)};let m=-1,y=!1;if(o=j().subscribe(r,"logs",f=>{if(t.destroyed)return;g(f),f.startsWith("event: run_state")&&(y=!0);const p=/^id: (\d+)\n/.exec(f);p&&(m=Math.max(m,Number(p[1])))}),a.on("close",()=>{o?.(),i&&clearInterval(i);try{t.end()}catch{}}),s>=-1){const f=await c().getLogs(r,Math.max(s,m));if(f)for(const p of f.entries)p.index<=m||g(`id: ${p.index}
|
|
5
5
|
data: ${JSON.stringify(p.data)}
|
|
6
6
|
|
|
7
7
|
`)}const u=c().getRun(r);if(!y&&u&&u.state!=="running"){const f={configId:r,state:u.state};u.state==="exited"&&(f.exitCode=u.exitCode??null),g(`event: run_state
|
|
@@ -9,22 +9,22 @@ data: ${JSON.stringify(f)}
|
|
|
9
9
|
|
|
10
10
|
`)}i=setInterval(()=>{g(`: heartbeat
|
|
11
11
|
|
|
12
|
-
`)},15e3)}catch(g){const
|
|
12
|
+
`)},15e3)}catch(g){const m=g;if(d.error(`[launch/logs/stream] configId=${r}, error:`,m),o?.(),i&&clearInterval(i),t.headersSent){t.end();return}t.status(500).json({error:m.message})}}),h.get("/launch-configs/:id/metrics/stream",l,(a,t)=>{k();const r=a.params.id;if(!c().getRun(r)){t.status(404).json({error:"launch run not found"});return}const n=Number(a.headers["last-event-id"]),s=Number.isFinite(n)&&n>=0?n:-1;t.on("error",()=>{}),t.writeHead(200,{"Content-Type":"text/event-stream; charset=utf-8","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"}),t.write(`: connected
|
|
13
13
|
|
|
14
14
|
`);const o=u=>{t.destroyed||t.write(u)};if(s>=0)for(const u of c().getMetricsSince(r,s))o(`id: ${u.index}
|
|
15
15
|
data: ${JSON.stringify(u.sample)}
|
|
16
16
|
|
|
17
|
-
`);let i=!1,g=null,
|
|
17
|
+
`);let i=!1,g=null,m=null;g=j().subscribe(r,"metrics",u=>{t.destroyed||(o(u),u.startsWith("event: run_state")&&(i=!0))}),a.on("close",()=>{g?.(),m&&clearInterval(m);try{t.end()}catch{}});const y=c().getRun(r);if(!i&&y&&y.state!=="running"){const u={configId:r,state:y.state};y.state==="exited"&&(u.exitCode=y.exitCode??null),o(`event: run_state
|
|
18
18
|
data: ${JSON.stringify(u)}
|
|
19
19
|
|
|
20
|
-
`)}
|
|
20
|
+
`)}m=setInterval(()=>{o(`: heartbeat
|
|
21
21
|
|
|
22
|
-
`)},15e3)}),
|
|
22
|
+
`)},15e3)}),h.get("/launch-configs/status/stream",l,async(a,t)=>{k(),t.on("error",()=>{});let r=null,e=null;try{t.writeHead(200,{"Content-Type":"text/event-stream; charset=utf-8","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"}),t.write(`: connected
|
|
23
23
|
|
|
24
|
-
`);const n=o=>{t.destroyed||t.write(o)};r=j().subscribeStatus(o=>{t.destroyed||n(o)}),a.on("close",()=>{r?.(),e&&clearInterval(e);try{t.end()}catch{}});const s=await c().listRuns();t.destroyed||n(`event: snapshot
|
|
24
|
+
`);const n=o=>{t.destroyed||t.write(o)};r=j().subscribeStatus(o=>{t.destroyed||n(o)}),a.on("close",()=>{r?.(),e&&clearInterval(e);try{t.end()}catch{}});const s=await c().listRuns({withPorts:!1});t.destroyed||n(`event: snapshot
|
|
25
25
|
data: ${JSON.stringify({runs:s})}
|
|
26
26
|
|
|
27
|
-
`),e=setInterval(()=>{n(`: heartbeat
|
|
27
|
+
`),c().publishRunningPorts(),e=setInterval(()=>{n(`: heartbeat
|
|
28
28
|
|
|
29
|
-
`)},15e3)}catch(n){const s=n;if(d.error("[launch/status/stream] error:",s),r?.(),e&&clearInterval(e),t.headersSent){t.end();return}t.status(500).json({error:s.message})}});export{I as MAX_BATCH_START_ITEMS,
|
|
29
|
+
`)},15e3)}catch(n){const s=n;if(d.error("[launch/status/stream] error:",s),r?.(),e&&clearInterval(e),t.headersSent){t.end();return}t.status(500).json({error:s.message})}});export{I as MAX_BATCH_START_ITEMS,v as MAX_START_WAIT_MS,h as launchRouter};
|
|
30
30
|
|
package/dist/routes/ltr.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{randomUUID as
|
|
1
|
+
import{randomUUID as u}from"node:crypto";import{promises as i}from"node:fs";import p from"node:os";import n from"node:path";import{Router as w}from"express";import{validateToken as f}from"../middleware/auth.js";import{createLogger as y}from"../utils/logger.js";const a=y("ltr"),d=w(),c=n.join(p.tmpdir(),"aw-temp","LTR"),h=7,l=512*1024;function g(o=new Date){const e=o.getFullYear(),r=String(o.getMonth()+1).padStart(2,"0"),t=String(o.getDate()).padStart(2,"0");return n.join(c,`${e}${r}${t}`)}async function T(){const o=Date.now()-h*24*60*60*1e3;try{const e=await i.readdir(c,{withFileTypes:!0});for(const r of e){if(!r.isDirectory())continue;const t=n.join(c,r.name);try{(await i.stat(t)).mtimeMs<o&&(await i.rm(t,{recursive:!0,force:!0}),a.info(`[ltr] removed expired temp dir: ${t}`))}catch{}}}catch(e){const r=e;r.code!=="ENOENT"&&a.warn("[ltr] cleanup expired dirs failed:",r)}}async function j(o){const e=String(o??"");if(!e.trim())throw new Error("ltr content is empty");const r=Buffer.byteLength(e,"utf8");if(r>l)throw new Error(`ltr content too large: ${r} bytes, max ${l} bytes`);const t=g();await i.mkdir(t,{recursive:!0});const s=n.join(t,`${u()}.txt`);return await i.writeFile(s,e,"utf8"),T(),s}d.post("/upload",f,async(o,e)=>{try{const{content:r}=o.body||{},t=await j(String(r??""));e.json({path:t})}catch(r){const t=r;a.error("[ltr] failed to write temp text file:",t),e.status(400).json({error:t.message})}}),d.post("/read",f,async(o,e)=>{try{const{path:r}=o.body||{};if(!r||typeof r!="string"||!r.trim()){e.status(400).json({error:"filePath is required"});return}const t=n.resolve(String(r).trim()),s=n.resolve(c);if(!t.startsWith(s+n.sep)&&t!==s){a.warn("[ltr] read attempted outside LTR root:",t),e.status(403).json({error:"access denied: file is not within LTR temp directory"});return}if(!(await i.stat(t)).isFile()){e.status(400).json({error:"path is not a file"});return}const m=await i.readFile(t,{encoding:"utf8",flag:"r"});e.json({content:m})}catch(r){const t=r;if(t.code==="ENOENT"){e.status(404).json({error:"LTR file not found"});return}a.error("[ltr] failed to read temp text file:",t),e.status(500).json({error:t.message})}});export{c as LTR_ROOT,d as ltrRouter};
|
|
2
2
|
|
package/dist/routes/mcp-extra.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{readFileSync as I}from"node:fs";import{Router as j}from"express";import{validateToken as o}from"../middleware/auth.js";import{getAgentMcpExtraFilePath as
|
|
2
|
-
`)}function i(n){return(
|
|
1
|
+
import{readFileSync as I}from"node:fs";import{Router as j}from"express";import{validateToken as o}from"../middleware/auth.js";import{getAgentMcpExtraFilePath as f}from"../services/instance-ai-config-service.js";import{isBuiltinMcpName as x}from"../services/mcp-env-injector.js";import{atomicWriteFileSync as y}from"../utils/file-utils.js";import{createLogger as k}from"../utils/logger.js";const u=k("mcp-extra"),s=j(),v=/^[a-zA-Z0-9_.-]+$/;function d(n){return n!=="__proto__"&&v.test(n)}function c(n){try{const t=JSON.parse(I(f(n),"utf-8"));return t.servers&&typeof t.servers=="object"?t.servers:{}}catch{return{}}}function p(n,t){const r=f(n);y(r,`${JSON.stringify({version:1,servers:t},null,2)}
|
|
2
|
+
`)}function i(n){return(t,r)=>{try{n(t,r)}catch(e){u.error("[mcp-extra] %s %s \u5931\u8D25: %s",t.method,t.path,e instanceof Error?e.message:String(e)),r.headersSent||r.status(500).json({ok:!1,error:e instanceof Error?e.message:String(e)})}}}function g(n,t){const r=String(n.params.agentId??"").trim();if(!r){t.status(400).json({ok:!1,error:"agentId is required"});return}t.json({ok:!0,agentId:r,servers:c(r)})}function l(n,t){const r=String(n.params.agentId??"").trim(),e=String(n.params.name??"").trim(),a=n.body??{};if(!r||!e){t.status(400).json({ok:!1,error:"agentId and name are required"});return}if(!d(e)){t.status(400).json({ok:!1,error:`name \u975E\u6CD5\uFF08\u4EC5\u5141\u8BB8\u5B57\u6BCD\u6570\u5B57 _ . -\uFF09: ${JSON.stringify(e)}`});return}if(typeof a!="object"||Array.isArray(a)){t.status(400).json({ok:!1,error:"body must be an MCP spec object"});return}if(x(e)){t.status(400).json({ok:!1,error:`\u300C${e}\u300D\u662F\u5185\u7F6E\u901A\u8BAF MCP\uFF0C\u4E0D\u53EF\u4F5C\u4E3A\u79C1\u6709 MCP \u8986\u76D6`});return}const m=c(r);m[e]=a,p(r,m),u.info("[mcp-extra] agent=%s \u79C1\u6709 MCP \u5DF2\u4FDD\u5B58: %s",r,e),t.json({ok:!0,agentId:r,name:e,spec:a})}function S(n,t){const r=String(n.params.agentId??"").trim(),e=String(n.params.name??"").trim();if(!r||!e){t.status(400).json({ok:!1,error:"agentId and name are required"});return}if(!d(e)){t.status(400).json({ok:!1,error:`name \u975E\u6CD5\uFF08\u4EC5\u5141\u8BB8\u5B57\u6BCD\u6570\u5B57 _ . -\uFF09: ${JSON.stringify(e)}`});return}const a=c(r),m=e in a;delete a[e],p(r,a),t.json({ok:!0,agentId:r,name:e,existed:m})}s.get("/mcp-extra/:agentId",o,i(g)),s.put("/mcp-extra/:agentId/:name",o,i(l)),s.delete("/mcp-extra/:agentId/:name",o,i(S)),s.post("/mcp-extra/:agentId",o,i(g)),s.post("/mcp-extra/:agentId/:name/save",o,i(l)),s.post("/mcp-extra/:agentId/:name/delete",o,i(S));export{s as mcpExtraRouter};
|
|
3
3
|
|
package/dist/routes/mcp.d.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
|
|
2
|
+
import { type McpDeploySpec } from "../services/mcp-deploy.js";
|
|
2
3
|
export declare const mcpRouter: import("express-serve-static-core").Router;
|
|
4
|
+
export declare function normalizeSpec(raw: unknown): McpDeploySpec | null;
|
|
5
|
+
|
|
6
|
+
export declare function stripBuiltinMcpDisable(map: Record<string, unknown>): Record<string, unknown>;
|
package/dist/routes/mcp.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Router as
|
|
1
|
+
import{Router as y}from"express";import{validateToken as i}from"../middleware/auth.js";import{getEffective as f,setDefault as g,setInherited as b,setOverride as j}from"../services/capability-config.js";import{mcpDeployManager as p}from"../services/mcp-deploy.js";import{isBuiltinMcpName as l}from"../services/mcp-env-injector.js";import{createLogger as k}from"../utils/logger.js";const S=k("mcp"),a=y();function c(n){return(e,o)=>{n(e,o).catch(t=>{S.error("[mcp] %s %s \u5931\u8D25: %s",e.method,e.path,t.message),o.headersSent||o.status(500).json({ok:!1,error:t.message})})}}function h(n){if(!n||typeof n!="object")return null;const e=n,t={type:String(e.type||e.transport||"stdio").trim().toLowerCase()||"stdio"};e.command!==null&&e.command!==void 0&&String(e.command).trim()&&(t.command=String(e.command).trim()),e.url!==null&&e.url!==void 0&&String(e.url).trim()&&(t.url=String(e.url).trim()),Array.isArray(e.args)&&(t.args=e.args.map(s=>String(s))),e.env&&typeof e.env=="object"&&(t.env=e.env),e.headers&&typeof e.headers=="object"&&(t.headers=e.headers),e.collapsed===!0&&(t.collapsed=!0);const r=e.description===null||e.description===void 0?"":String(e.description).trim();return r&&(t.description=r),t}function v(n){const e={};for(const[o,t]of Object.entries(n))l(o)&&t===!1||(e[o]=t);return e}a.post("/mcp/deployed/list",i,c(async(n,e)=>{const t=p.listDeployed().map(r=>({...r,enabled:f("","mcp",r.name)}));e.json({ok:!0,deployedMcp:t})})),a.get("/mcp/deployed",i,c(async(n,e)=>{const t=p.listDeployed().map(r=>({...r,enabled:f("","mcp",r.name)}));e.json({ok:!0,deployedMcp:t})})),a.post("/mcp/deployed/default",i,c(async(n,e)=>{const o=String((n.body??{})?.name??"").trim();if(!o){e.status(400).json({ok:!1,error:"name is required"});return}const t=String((n.body??{})?.agentId??"").trim(),r=(n.body??{})?.enabled,s=r==null?null:!!r;if(l(o)&&s===!1){e.status(400).json({ok:!1,error:`\u300C${o}\u300D\u662F\u5185\u7F6E\u901A\u8BAF MCP\uFF0C\u6052\u4E3A\u9ED8\u8BA4\u542F\u7528\uFF0C\u4E0D\u53EF\u5173\u95ED`});return}t?j(t,"mcp",o,s):g("mcp",o,s),e.json({ok:!0,name:o,agentId:t,enabled:s})})),a.post("/mcp/deployed/inherited",i,c(async(n,e)=>{const o=String((n.body??{})?.agentId??"").trim();if(!o){e.status(400).json({ok:!1,error:"agentId is required"});return}const t=(n.body??{})?.map,r=t&&typeof t=="object"&&!Array.isArray(t)?v(t):t??null;b(o,"mcp",r),e.json({ok:!0,agentId:o,count:r&&typeof r=="object"?Object.keys(r).length:0})})),a.post("/mcp/deployed/save",i,c(async(n,e)=>{const{name:o,spec:t,defaultEnabled:r,sha256:s}=n.body??{},d=String(o??"").trim();if(!d){e.status(400).json({ok:!1,error:"name is required"});return}const m=h(t);if(!m){e.status(400).json({ok:!1,error:"spec \u975E\u6CD5"});return}if(l(d)){e.status(400).json({ok:!1,error:`\u300C${d}\u300D\u662F\u5185\u7F6E\u901A\u8BAF MCP\uFF0C\u7531\u5B9E\u4F8B\u521D\u59CB\u5316\u81EA\u52A8\u90E8\u7F72\uFF0C\u4E0D\u53EF\u8986\u76D6`});return}const u=p.saveMcp(d,m,r!==!1,s?String(s):void 0);e.status(u.ok?200:400).json(u)})),a.post("/mcp/deployed/remove",i,c(async(n,e)=>{const o=String((n.body??{})?.name??"").trim(),t=p.removeMcp(o);e.status(t.ok?200:400).json(t)})),a.post("/mcp/cc-switch/list",i,c(async(n,e)=>{e.json({ok:!0,mcpServers:[],state:{skills:[],mcpServers:[]}})}));export{a as mcpRouter,h as normalizeSpec,v as stripBuiltinMcpDisable};
|
|
2
2
|
|
package/dist/routes/plugins.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Router as b}from"express";import{raw as I}from"express";import{validateToken as u,validateTokenOrLoopback as f}from"../middleware/auth.js";import{getEffective as k,setDefault as j,setOverride as
|
|
1
|
+
import{Router as b}from"express";import{raw as I}from"express";import{validateToken as u,validateTokenOrLoopback as f}from"../middleware/auth.js";import{getEffective as k,setDefault as j,setInherited as S,setOverride as w}from"../services/capability-config.js";import{isValidPluginName as c,pluginManager as g}from"../services/plugin-manager.js";import{createLogger as h}from"../utils/logger.js";const N=h("plugins"),r=b();function i(s){return(n,t)=>{s(n,t).catch(e=>{N.error("[plugins] %s %s \u5931\u8D25: %s",n.method,n.path,e.message),t.headersSent||t.status(500).json({ok:!1,error:e.message})})}}r.get("/plugins/deployed",u,i(async(s,n)=>{const e=(await g.listDeployed()).map(a=>({...a,enabled:k("","plugins",a.pluginName)}));n.json({ok:!0,deployedPlugins:e})})),r.post("/plugins/deployed/default",u,i(async(s,n)=>{const t=String((s.body??{})?.pluginName??"").trim();if(!c(t)){n.status(400).json({ok:!1,error:"pluginName \u975E\u6CD5\uFF08\u987B\u4E3A kebab-case\uFF0C1~64 \u5B57\u7B26\uFF09"});return}const e=String((s.body??{})?.agentId??"").trim(),a=(s.body??{})?.enabled,o=a==null?null:!!a;e?w(e,"plugins",t,o):j("plugins",t,o),n.json({ok:!0,pluginName:t,agentId:e,enabled:o})})),r.post("/plugins/deployed/inherited",u,i(async(s,n)=>{const t=String((s.body??{})?.agentId??"").trim();if(!t){n.status(400).json({ok:!1,error:"agentId is required"});return}const e=(s.body??{})?.map;S(t,"plugins",e??null),n.json({ok:!0,agentId:t,count:Object.keys(e??{}).length})})),r.post("/plugins/deployed/remove",u,i(async(s,n)=>{const t=String((s.body??{})?.pluginName??"").trim();if(!c(t)){n.status(400).json({ok:!1,error:"pluginName \u975E\u6CD5\uFF08\u987B\u4E3A kebab-case\uFF0C1~64 \u5B57\u7B26\uFF09"});return}const e=await g.removeDeployedPlugin(t);n.status(e.ok?200:400).json(e)})),r.get("/plugins/:agentId",u,i(async(s,n)=>{const t=String(s.params.agentId??"").trim();if(!t){n.status(400).json({ok:!1,error:"agentId is required"});return}const e=await g.list(t);n.json({ok:!0,agentId:t,plugins:e})})),r.post("/plugins/:agentId/install",u,i(async(s,n)=>{const t=String(s.params.agentId??"").trim(),e=s.body??{},a=typeof e.source=="string"?e.source:"",o=e.config&&typeof e.config=="object"&&!Array.isArray(e.config)?Object.fromEntries(Object.entries(e.config).filter(l=>typeof l[1]=="string")):void 0;if(!t){n.status(400).json({ok:!1,error:"agentId is required"});return}const p={action:"install",source:a,...o?{config:o}:{}},d=await g.manage(t,p);n.status(d.ok?200:400).json(d)}));function m(s){return[u,i(async(n,t)=>{const e=String(n.params.agentId??"").trim(),a=String(n.params.name??"").trim();if(!e||e==="deployed"||!c(a)){t.status(400).json({ok:!1,error:"agentId \u4E14 name \u987B\u4E3A\u5408\u6CD5\u63D2\u4EF6\u540D\uFF08kebab-case\uFF0C1~64 \u5B57\u7B26\uFF09"});return}const o=await g.manage(e,{action:s,name:a});t.status(o.ok?200:400).json(o)})]}r.post("/plugins/:agentId/:name/reload",...m("reload")),r.post("/plugins/:agentId/:name/enable",...m("enable")),r.post("/plugins/:agentId/:name/disable",...m("disable")),r.delete("/plugins/:agentId/:name",...m("uninstall")),r.get("/plugins/:agentId/:name/logs",u,i(async(s,n)=>{const t=String(s.params.agentId??"").trim(),e=String(s.params.name??"").trim();if(!t||!c(e)){n.status(400).json({ok:!1,error:"agentId \u4E14 name \u987B\u4E3A\u5408\u6CD5\u63D2\u4EF6\u540D\uFF08kebab-case\uFF0C1~64 \u5B57\u7B26\uFF09"});return}const a=await g.getLogs(t,e);n.status(a.ok?200:404).json(a)})),r.get("/plugins/:agentId/commands",u,i(async(s,n)=>{const t=String(s.params.agentId??"").trim();if(!t){n.status(400).json({ok:!1,error:"agentId is required"});return}const e=await g.getPluginCommands(t);n.json({ok:!0,agentId:t,commands:e.map(a=>({name:a.name,description:a.description??"",argumentHint:a.argumentHint??"",sourcePath:a.sourcePath,plugin:a.plugin}))})})),r.get("/plugin-events/:agentId/events",f,i(async(s,n)=>{const t=String(s.params.agentId??"").trim();if(!t){n.status(400).json({ok:!1,error:"agentId is required"});return}const e=Number(s.query.maxPerKey),a=Number.isFinite(e)&&e>0?e:void 0,o=await g.drainEvents(t,{maxPerKey:a});n.json({ok:!0,agentId:t,...o})})),r.post("/plugin-events/:agentId/callback/:plugin",f,i(async(s,n)=>{const t=String(s.params.agentId??"").trim(),e=String(s.params.plugin??"").trim(),a=s.body??{};if(!t||!e){n.status(400).json({ok:!1,error:"agentId and plugin are required"});return}const o=await g.enqueueExternalEvent(t,e,a);n.status(o.ok?200:400).json(o)})),r.post("/plugins/:agentId/upload",u,I({type:()=>!0,limit:"100mb"}),i(async(s,n)=>{const t=String(s.params.agentId??"").trim();if(!t){n.status(400).json({ok:!1,error:"agentId is required"});return}const e=s.body;if(!Buffer.isBuffer(e)||e.length===0){n.status(400).json({ok:!1,error:"\u7F3A\u5C11 zip \u5185\u5BB9\uFF08raw body, Content-Type: application/zip\uFF09"});return}const a=String(s.query.filename??"plugin.zip");if(!a.toLowerCase().endsWith(".zip")){n.status(400).json({ok:!1,error:"\u4EC5\u652F\u6301 .zip \u63D2\u4EF6\u5305"});return}let o;const p=String(s.query.config??"").trim();if(p)try{const l=JSON.parse(p);if(l&&typeof l=="object"&&!Array.isArray(l))o=Object.fromEntries(Object.entries(l).filter(y=>typeof y[1]=="string"));else{n.status(400).json({ok:!1,error:"config \u5FC5\u987B\u662F JSON \u5BF9\u8C61"});return}}catch{n.status(400).json({ok:!1,error:"config \u4E0D\u662F\u5408\u6CD5\u7684 JSON \u5BF9\u8C61"});return}const d=await g.installFromZip(t,e,a,o);n.status(d.ok?200:400).json(d)}));export{r as pluginsRouter};
|
|
2
2
|
|
package/dist/routes/pty.d.ts
CHANGED
|
@@ -22,10 +22,17 @@ export interface PtySessionSummary {
|
|
|
22
22
|
tenantId?: string;
|
|
23
23
|
projectId?: string;
|
|
24
24
|
}
|
|
25
|
-
interface PtySessionEntry extends PtySessionSummary {
|
|
25
|
+
export interface PtySessionEntry extends PtySessionSummary {
|
|
26
26
|
ptyProcess: pty.IPty;
|
|
27
27
|
sockets: Set<WebSocket>;
|
|
28
|
-
|
|
28
|
+
|
|
29
|
+
outputChunks: string[];
|
|
30
|
+
|
|
31
|
+
outputChars: number;
|
|
32
|
+
|
|
33
|
+
pendingOutput: string[];
|
|
34
|
+
|
|
35
|
+
outputFlushTimer: NodeJS.Timeout | null;
|
|
29
36
|
idleTimer: NodeJS.Timeout | null;
|
|
30
37
|
persistent: boolean;
|
|
31
38
|
}
|
|
@@ -47,6 +54,8 @@ export declare function isPathInsideRoot(candidatePath: string, rootPath: string
|
|
|
47
54
|
|
|
48
55
|
export declare function resolvePtyWorkspacePath(workspacePath: unknown, env?: NodeJS.ProcessEnv): string;
|
|
49
56
|
|
|
57
|
+
export declare function readPtyOutputBuffer(session: PtySessionEntry): string;
|
|
58
|
+
|
|
50
59
|
export declare function createPtySession(input: {
|
|
51
60
|
workspacePath: unknown;
|
|
52
61
|
title?: unknown;
|
|
@@ -76,4 +85,3 @@ export declare const ptyRouter: Router;
|
|
|
76
85
|
export declare function attachPtyWebSocketServer(server: {
|
|
77
86
|
on: (event: "upgrade", listener: (request: IncomingMessage, socket: Socket, head: Buffer) => void) => unknown;
|
|
78
87
|
}): void;
|
|
79
|
-
export {};
|
package/dist/routes/pty.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import U from"node:crypto";import C from"node:fs";import u from"node:path";import{Router as D}from"express";import*as N from"node-pty";import{v4 as M}from"uuid";import z,{WebSocketServer as j}from"ws";import{isRuntimeTokenValid as _,validateToken as w}from"../middleware/auth.js";import{isUpgradeAllowed as F}from"../services/access-control.js";import{createLogger as R}from"../utils/logger.js";const p=R("pty"),W=80,B=24,x=2e5,H=16,E=30*60*1e3,O=5*60*1e3,b=60*1e3,v=8,X=new Set(["false","halt","nologin","reboot","shutdown","sync","true"]),Y=new Set(["sh","dash","ash"]),l=new Map,h=new Map;function J(t=process.env){const e=String(t.AWS_PTY_IDLE_TTL_MS||"").trim();if(!e)return E;const r=Number(e);return!Number.isFinite(r)||r<1e3?E:r}function K(t=process.platform,e=process.env,r=C.existsSync){if(t==="win32")return{shell:String(e.ComSpec||e.COMSPEC||"").trim()||"powershell.exe",args:[]};const n=String(e.SHELL||"").trim();return{shell:[...Y.has(u.basename(n).toLowerCase())?[]:[n],"/bin/bash","/usr/bin/bash",n,"/bin/sh"].find(i=>V(i,r))||"/bin/sh",args:[]}}function V(t,e){const r=t.trim();if(!r)return!1;const n=u.basename(r).toLowerCase();return X.has(n)?!1:!u.isAbsolute(r)||e(r)}function Z(t,e=process.platform,r=process.env){const n=String(t||"auto").trim();if(!n||n==="auto")return K(e,r);const a=new Set(["bash","sh","zsh","fish","pwsh","pwsh.exe","powershell","powershell.exe","cmd","cmd.exe"]),s=(e==="win32"?u.win32.basename(n):u.basename(n)).toLowerCase();if(!a.has(s))throw new Error(`shell is not allowed: ${n}`);return{shell:/^[A-Za-z]:[\\/]/.test(n)||n.startsWith("\\")||n.startsWith("/")?n:e==="win32"?{cmd:"cmd.exe",powershell:"powershell.exe",pwsh:"pwsh.exe"}[s]??n:n,args:[]}}function q(t,e){const r=u.relative(u.resolve(e),u.resolve(t));return r===""||!r.startsWith("..")&&!u.isAbsolute(r)}function G(t=process.env){return String(t.AWS_PTY_ALLOWED_ROOTS||"").split(u.delimiter).map(e=>e.trim()).filter(Boolean).map(e=>u.resolve(e))}function Q(t,e=process.env){const r=String(t||"").trim();if(!r)throw new Error("workspacePath is required");if(!u.isAbsolute(r))throw new Error("workspacePath must be absolute");const n=u.resolve(r);if(!C.statSync(n).isDirectory())throw new Error("workspacePath must be an existing directory");const s=G(e);if(s.length>0&&!s.some(i=>q(n,i)))throw new Error("workspacePath is outside allowed roots");return n}function T(t,e){const r=Number(t);return!Number.isInteger(r)||r<=0?e:Math.min(r,500)}function tt(t){return{...t,TERM:t.TERM||"xterm-256color",COLORTERM:t.COLORTERM||"truecolor"}}function et(t,e){for(t.outputChunks.push(e),t.outputChars+=e.length;t.outputChars>x&&t.outputChunks.length>0;){const r=t.outputChars-x,n=t.outputChunks[0];n.length<=r?(t.outputChunks.shift(),t.outputChars-=n.length):(t.outputChunks[0]=n.slice(r),t.outputChars-=r)}}function rt(t){return t.outputChunks.join("")}function nt(t,e){t.pendingOutput.push(e),!t.outputFlushTimer&&(t.outputFlushTimer=setTimeout(()=>{t.outputFlushTimer=null,g(t)},H))}function g(t){if(t.outputFlushTimer&&(clearTimeout(t.outputFlushTimer),t.outputFlushTimer=null),t.pendingOutput.length===0)return;const e=t.pendingOutput.join("");t.pendingOutput.length=0,P(t,{type:"output",data:e})}function k(t){return{id:t.id,title:t.title,workspacePath:t.workspacePath,shell:t.shell,cols:t.cols,rows:t.rows,status:t.status,createdAt:t.createdAt,lastActiveAt:t.lastActiveAt,exitCode:t.exitCode,signal:t.signal,attachedClients:t.sockets.size,ownerUserId:t.ownerUserId,tenantId:t.tenantId,projectId:t.projectId}}function I(t,e){t.readyState===z.OPEN&&t.send(JSON.stringify(e))}function P(t,e){for(const r of t.sockets)I(r,e)}function A(t){t.idleTimer&&clearTimeout(t.idleTimer),t.idleTimer=setTimeout(()=>{t.sockets.size===0&&S(t.id,"idle-timeout")},J())}function $(t){t.idleTimer&&(clearTimeout(t.idleTimer),t.idleTimer=null)}function ot(t){if(l.size>=ct())throw new Error("maximum PTY sessions reached");const e=Q(t.workspacePath),r=Z(t.shell),n=T(t.cols,W),a=T(t.rows,B),s=`pty_${M()}`,i=new Date().toISOString(),d=String(t.title||"").trim()||`Terminal ${l.size+1}`,c=N.spawn(r.shell,r.args,{name:"xterm-256color",cols:n,rows:a,cwd:e,env:tt(process.env)}),o={id:s,title:d,workspacePath:e,shell:r.shell,cols:n,rows:a,status:"running",createdAt:i,lastActiveAt:i,attachedClients:0,ptyProcess:c,sockets:new Set,outputChunks:[],outputChars:0,pendingOutput:[],outputFlushTimer:null,idleTimer:null,persistent:t.persistent===!0,ownerUserId:String(t.ownerUserId||"").trim()||void 0,tenantId:String(t.tenantId||"").trim()||void 0,projectId:String(t.projectId||"").trim()||void 0};return c.onData(f=>{o.lastActiveAt=new Date().toISOString(),et(o,f),nt(o,f)}),c.onExit(({exitCode:f,signal:m})=>{o.status="exited",o.exitCode=f,o.signal=m,o.lastActiveAt=new Date().toISOString(),g(o),P(o,{type:"exit",exitCode:f,signal:m}),o.sockets.size===0&&!o.persistent?A(o):o.persistent&&(o.idleTimer&&clearTimeout(o.idleTimer),o.idleTimer=setTimeout(()=>{S(o.id,"exited-timeout")},at()))}),l.set(s,o),o.persistent||A(o),st(o,t.preCommand),p.info(`created pty session id=${s}, cwd=${e}, shell=${r.shell}`),k(o)}function st(t,e){const r=typeof e=="string"?e:"";if(!r.trim())return;const n=`${r.replace(/\n/g,"\r").replace(/\r+$/g,"")}\r`;try{t.ptyProcess.write(n)}catch(a){const s=a;p.warn(`failed to inject pty pre-command sessionId=${t.id}: ${s.message}`)}}function it(t=process.env){const e=String(t.AWS_PTY_CONNECT_TOKEN_TTL_MS||"").trim();if(!e)return b;const r=Number(e);return!Number.isFinite(r)||r<5e3||r>10*60*1e3?b:r}function at(t=process.env){const e=String(t.AWS_PTY_EXITED_TTL_MS||"").trim();if(!e)return O;const r=Number(e);return!Number.isFinite(r)||r<1e3?O:r}function ct(t=process.env){const e=String(t.AWS_PTY_MAX_SESSIONS||"").trim();if(!e)return v;const r=Number(e);return!Number.isInteger(r)||r<1||r>100?v:r}function ut(t){if(!t||typeof t!="object")return{workspacePath:void 0};const e=t;return{workspacePath:e.workspacePath,title:e.title,shell:e.shell,cols:e.cols,rows:e.rows,persistent:e.persistent,preCommand:e.preCommand,ownerUserId:e.ownerUserId,tenantId:e.tenantId,projectId:e.projectId}}function lt(){return Array.from(l.values()).map(k)}function pt(t){const e=l.get(t);return e?k(e):void 0}function dt(t){if(!l.has(t))return;const e=`ptyws_${U.randomBytes(32).toString("base64url")}`,r=Date.now()+it();return h.set(e,{sessionId:t,expiresAt:r}),{token:e,expiresAt:new Date(r).toISOString()}}function ft(t,e){const r=h.get(e);return r?r.expiresAt<Date.now()?(h.delete(e),p.warn(`[pty-ws] consumePtyConnectToken: token \u5DF2\u8FC7\u671F, sessionId=${t}, expiredAt=${new Date(r.expiresAt).toISOString()}`),!1):r.sessionId!==t?(p.warn(`[pty-ws] consumePtyConnectToken: sessionId \u4E0D\u5339\u914D, expected=${r.sessionId}, actual=${t}`),!1):(h.delete(e),p.info(`[pty-ws] consumePtyConnectToken: \u6210\u529F, sessionId=${t}`),!0):(p.warn(`[pty-ws] consumePtyConnectToken: token \u4E0D\u5B58\u5728, sessionId=${t}, ptyConnectTokens.size=${h.size}`),!1)}function mt(t){for(const[e,r]of h.entries())r.sessionId===t&&h.delete(e)}function S(t,e="closed"){const r=l.get(t);if(!r)return!1;$(r),g(r),mt(t),P(r,{type:"status",status:"closing",reason:e});for(const n of r.sockets)n.close(1e3,e);return r.sockets.clear(),r.status==="running"&&r.ptyProcess.kill(),l.delete(t),p.info(`closed pty session id=${t}, reason=${e}`),!0}function xt(t="shutdown"){for(const e of Array.from(l.keys()))S(e,t)}const y=D();y.get("/sessions",w,(t,e)=>{e.json({ok:!0,sessions:lt()})}),y.post("/sessions",w,(t,e)=>{try{const r=ot(ut(t.body));e.status(201).json({ok:!0,session:r})}catch(r){const n=r;e.status(400).json({error:n.message})}}),y.get("/sessions/:sessionId",w,(t,e)=>{const r=pt(t.params.sessionId);if(!r){e.status(404).json({error:"pty session not found"});return}e.json({ok:!0,session:r})}),y.delete("/sessions/:sessionId",w,(t,e)=>{if(!S(t.params.sessionId,"explicit-close")){e.status(404).json({error:"pty session not found"});return}e.json({ok:!0})}),y.post("/sessions/:sessionId/connect-token",w,(t,e)=>{const r=dt(t.params.sessionId);if(!r){e.status(404).json({error:"pty session not found"});return}e.json({ok:!0,...r})});function ht(t){const e=t.toString();try{return JSON.parse(e)}catch{return{type:"input",data:e}}}function wt(t,e){$(t),g(t),t.sockets.add(e),t.lastActiveAt=new Date().toISOString(),I(e,{type:"status",status:t.status,session:k(t)});const r=rt(t);r&&I(e,{type:"output",data:r,replay:!0}),e.on("message",n=>{const a=ht(n);if(!a||typeof a!="object")return;const s=a,i=String(s.type||"");if(t.lastActiveAt=new Date().toISOString(),i==="input"){t.ptyProcess.write(String(s.data||""));return}if(i==="resize"){const d=T(s.cols,t.cols),c=T(s.rows,t.rows);t.cols=d,t.rows=c,t.ptyProcess.resize(d,c);return}if(i==="close"){S(t.id,"client-close");return}i==="ping"&&I(e,{type:"pong"})}),e.on("close",()=>{t.sockets.delete(e),t.lastActiveAt=new Date().toISOString(),t.sockets.size===0&&t.status==="running"&&!t.persistent&&A(t)})}function St(t,e){const r=e.searchParams.get("token")||"";if(r.trim())return r.trim();const n=t.headers["x-runtime-token"];return Array.isArray(n)?n[0]?.trim()||"":typeof n=="string"?n.trim():String(t.headers.authorization||"").match(/^Bearer\s+(.+)$/i)?.[1]?.trim()||""}function Et(t){const e=new j({noServer:!0});t.on("upgrade",(r,n,a)=>{if(!F(r,n))return;const s=r.headers.host||"localhost",i=new URL(r.url||"/",`http://${s}`),d=i.pathname.match(/^\/pty\/sessions\/([^/]+)\/stream$/);if(!d)return;const c=St(r,i),o=decodeURIComponent(d[1]||""),f=c?c.length>12?c.substring(0,12)+"...":c:"empty";if(p.info(`[pty-ws] upgrade \u8BF7\u6C42: sessionId=${o}, token=${f}, pathname=${i.pathname}`),!_(c)&&!ft(o,c)){p.warn(`[pty-ws] token \u6821\u9A8C\u5931\u8D25: sessionId=${o}, token=${f}, runtimeTokenValid=${_(c)}, ptySessions.size=${l.size}`),n.write(`HTTP/1.1 401 Unauthorized\r
|
|
2
2
|
\r
|
|
3
|
-
`),n.destroy();return}const m=
|
|
3
|
+
`),n.destroy();return}const m=l.get(o);if(!m){p.warn(`[pty-ws] session \u4E0D\u5B58\u5728: sessionId=${o}, ptySessions.size=${l.size}, keys=${Array.from(l.keys()).join(",")}`),n.write(`HTTP/1.1 404 Not Found\r
|
|
4
4
|
\r
|
|
5
|
-
`),n.destroy();return}p.info(`[pty-ws] handleUpgrade: sessionId=${
|
|
5
|
+
`),n.destroy();return}p.info(`[pty-ws] handleUpgrade: sessionId=${o}, status=${m.status}, persistent=${m.persistent}, sockets=${m.sockets.size}`),e.handleUpgrade(r,n,a,L=>{wt(m,L)})})}export{Et as attachPtyWebSocketServer,xt as closeAllPtySessions,S as closePtySession,dt as createPtyConnectToken,ot as createPtySession,pt as getPtySessionSummary,q as isPathInsideRoot,lt as listPtySessions,y as ptyRouter,l as ptySessions,rt as readPtyOutputBuffer,K as resolveDefaultShell,ct as resolveMaxPtySessions,at as resolvePtyExitedTtlMs,J as resolvePtyIdleTtlMs,Q as resolvePtyWorkspacePath,Z as resolveRequestedShell};
|
|
6
6
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
1
|
+
import f from"axios";import{Router as j}from"express";import{validateToken as p}from"../middleware/auth.js";import{getConfiguredConnectionKeys as h}from"../services/auto-register.js";import{claimMcpLaunchBinding as S,getLatestAgentToken as R,getLatestAgentTokenEntry as B,getMcpLaunchQueueSize as b,updateLatestAgentToken as I}from"../services/mcp-launch-binding-queue.js";import{clearRuntimeBinding as w,getRuntimeAccessToken as l,getRuntimeBindingPublicState as m,getRuntimePairingCode as $,hasRuntimeBinding as v,saveRuntimeBinding as T,validateRuntimeBindingToken as A,validateRuntimePairingCode as U}from"../services/runtime-binding.js";import{createLogger as q}from"../utils/logger.js";const a=q("runtime-binding-route"),c=j();function C(n){return{success:!1,error:"MCP runtime token must be claimed from launch binding; bridge-scoped token refresh is disabled"}}function P(n){const e=n.header("X-Runtime-Token");return e?e.trim():(n.header("Authorization")||"").match(/^Bearer\s+(.+)$/i)?.[1]?.trim()||""}c.get("/binding/status",(n,e)=>{const t=m();e.json({ok:!0,...t,pairingRequired:!t.paired})}),c.post("/binding/pair",(n,e)=>{const{accessToken:t,instanceId:i,userId:o,schedulerBaseUrl:r,pairingCode:s}=n.body||{},u=v(),d=P(n);if(u){if(!A(d)){e.status(401).json({error:"unauthorized",message:"Bridge is already paired. Provide the current runtime token to rotate binding."});return}}else if(!U(s)){e.status(401).json({error:"invalid_pairing_code",message:"Invalid pairing code. Read the current pairing code from the bridge console and try again."});return}try{T({accessToken:String(t||""),instanceId:i?String(i):void 0,userId:o?String(o):void 0,schedulerBaseUrl:r?String(r):void 0}),a.info(`[runtime-bridge] paired with instanceId=${i||"unknown"}`),e.json({ok:!0,...m()})}catch(g){const y=g;e.status(400).json({error:y.message||"pair runtime bridge failed"})}});function k(n){const e=n.ip||n.socket.remoteAddress||"";return e==="127.0.0.1"||e==="::1"||e==="::ffff:127.0.0.1"}c.post("/binding/request-token",async(n,e)=>{if(!k(n)){e.status(403).json({ok:!1,error:"loopback request required"});return}const t=C(n.body||{});if(!t.success||!t.runtimeAccessToken){e.status(400).json({ok:!1,error:t.error||"request runtime token failed"});return}e.json({ok:!0,runtimeAccessToken:t.runtimeAccessToken,updated:t.updated})}),c.post("/binding/claim-agent",(n,e)=>{if(!k(n)){e.status(403).json({ok:!1,error:"loopback request required"});return}const{agentId:t,workspacePath:i,serverUrl:o}=n.body||{};if(!i||!String(i).trim()){e.status(400).json({ok:!1,error:"workspacePath is required"});return}if(!o||!String(o).trim()){e.status(400).json({ok:!1,error:"serverUrl is required"});return}const r=S({agentId:t,workspacePath:i,serverUrl:o});if(!r){e.status(404).json({ok:!1,error:"launch binding not found for workspacePath and serverUrl",queueSize:b(i)});return}x(r.agentId,r.schedulerBaseUrl).catch(()=>{}),e.json({ok:!0,agentId:r.agentId,workspacePath:r.workspacePath,runtimeAccessToken:r.runtimeAccessToken,userId:r.userId,schedulerBaseUrl:r.schedulerBaseUrl,serverUrl:r.serverUrl,mcpHttpUrl:r.mcpHttpUrl,bindingId:r.id,remainingQueueSize:b(i)})}),c.post("/binding/refresh-token",async(n,e)=>{if(!k(n)){e.status(403).json({ok:!1,error:"loopback request required"});return}const{agentId:t,currentToken:i,forceRefresh:o}=n.body||{},r=String(t||"").trim();if(!r){e.status(400).json({ok:!1,error:"agentId is required"});return}const s=R(r);if(!s){e.status(404).json({ok:!1,error:"no cached token for agent"});return}const u=String(i||"").trim();if(!o&&u&&s.runtimeAccessToken===u){const g=await z(r);if(g){e.json({ok:!0,runtimeAccessToken:g});return}e.status(409).json({ok:!1,error:"token_unchanged",message:"Bridge cached token is the same as current; token may be revoked server-side"});return}e.json({ok:!0,runtimeAccessToken:s.runtimeAccessToken})});async function z(n){const e=B(n),t=e?.schedulerBaseUrl;if(!t)return a.warn(`[runtime-binding] \u65E0\u6CD5\u91CD\u7B7E\u53D1\uFF1A\u7F3A\u5C11 schedulerBaseUrl, agentId=${n}`),null;const i=l(t)||h()[0];if(!i)return a.warn(`[runtime-binding] \u65E0\u6CD5\u91CD\u7B7E\u53D1\uFF1A\u65E0\u53EF\u7528\u51ED\u8BC1, agentId=${n}`),null;const o=`${t.replace(/\/+$/,"")}/api/runtime/bridge/reissue-session-token`;try{const r=await f.post(o,{agentId:n},{headers:{"X-Runtime-Token":i,"Content-Type":"application/json"},timeout:5e3}),s=String(r.data?.runtimeAccessToken||"").trim();return!s||s===e?.runtimeAccessToken?(a.warn(`[runtime-binding] \u91CD\u7B7E\u53D1\u672A\u8FD4\u56DE\u65B0 token, agentId=${n}`),null):(I(n,s,t),a.warn(`[runtime-binding] 409 \u81EA\u6108\u6210\u529F\uFF1A\u5DF2\u5411\u8C03\u5EA6\u4E2D\u5FC3\u91CD\u65B0\u7533\u9886 MCP session token, agentId=${n}`),s)}catch(r){const s=r instanceof Error?r.message:String(r);return a.warn(`[runtime-binding] 409 \u81EA\u6108\u91CD\u7B7E\u53D1\u5931\u8D25, agentId=${n}, error=${s}`),null}}c.post("/binding/issue",p,(n,e)=>{const{accessToken:t,instanceId:i,userId:o,schedulerBaseUrl:r}=n.body||{},s=String(t||"").trim();if(!s){e.status(400).json({error:"accessToken is required"});return}try{const d=l()!==s;d?(T({accessToken:s,instanceId:i?String(i):void 0,userId:o?String(o):void 0,schedulerBaseUrl:r?String(r):void 0}),a.info(`[runtime-bridge] runtime token redispatched and updated: instanceId=${i||"unknown"}`)):a.info(`[runtime-bridge] runtime token redispatched but unchanged: instanceId=${i||"unknown"}`),e.json({ok:!0,updated:d,...m()})}catch(u){const d=u;e.status(400).json({error:d.message||"issue runtime token failed"})}}),c.post("/binding/unpair",p,(n,e)=>{w(),e.json({ok:!0,...m()})});function Q(){const n=m();if(n.paired){a.info(`[runtime-bridge] runtime binding loaded: instanceId=${n.instanceId||"unknown"}`);return}a.info("[runtime-bridge] bridge is unpaired; sensitive APIs will reject requests until paired"),a.info(`[runtime-bridge] pairing code: ${$()}`)}async function x(n,e){if(!e){a.debug(`[runtime-binding] notifySchedulerBindingClaimed skipped: no schedulerBaseUrl for agentId=${n}`);return}const t=l(e)||h()[0];if(!t){a.warn(`[runtime-binding] notifySchedulerBindingClaimed skipped: no connection key for agentId=${n}`);return}const i=`${e.replace(/\/+$/,"")}/api/runtime/bridge/binding-claimed`;try{await f.post(i,{agentId:n},{headers:{"X-Runtime-Token":t,"Content-Type":"application/json"},timeout:5e3}),a.debug(`[runtime-binding] Notified scheduler: agentId=${n}, url=${i}`)}catch(o){const r=o instanceof Error?o.message:String(o);a.warn(`[runtime-binding] Failed to notify scheduler binding claimed: agentId=${n}, error=${r}`)}}export{Q as logRuntimeBindingStartupState,C as resolveRuntimeTokenRequest,c as runtimeBindingRouter};
|
|
2
2
|
|
|
@@ -1,91 +1,35 @@
|
|
|
1
1
|
|
|
2
2
|
export declare const sessionsRouter: import("express-serve-static-core").Router;
|
|
3
|
-
|
|
4
|
-
export declare function handleCapabilityToolsRequest(input: {
|
|
3
|
+
export interface HandleCapabilityToolsRequestInput {
|
|
5
4
|
agentId: string;
|
|
6
5
|
capability: string;
|
|
7
|
-
action:
|
|
8
|
-
|
|
9
|
-
resolveSession?: () => {
|
|
6
|
+
action: string | null | undefined;
|
|
7
|
+
resolveSession: () => {
|
|
10
8
|
agentId: string;
|
|
11
9
|
providerId: string;
|
|
12
10
|
} | undefined;
|
|
13
|
-
resolveAdapter?: (providerId: string) =>
|
|
14
|
-
|
|
15
|
-
sessions: number;
|
|
16
|
-
tools: number;
|
|
17
|
-
manifests?: Array<{
|
|
18
|
-
name: string;
|
|
19
|
-
description?: string;
|
|
20
|
-
inputSchema?: unknown;
|
|
21
|
-
}>;
|
|
22
|
-
}>;
|
|
23
|
-
};
|
|
24
|
-
}): Promise<{
|
|
25
|
-
status: 400;
|
|
26
|
-
body: {
|
|
27
|
-
ok: false;
|
|
28
|
-
error: string;
|
|
29
|
-
};
|
|
30
|
-
} | {
|
|
31
|
-
status: 200;
|
|
32
|
-
body: {
|
|
33
|
-
ok: true;
|
|
34
|
-
changed: {
|
|
35
|
-
sessions: number;
|
|
36
|
-
tools: number;
|
|
37
|
-
};
|
|
38
|
-
manifests?: Array<{
|
|
39
|
-
name: string;
|
|
40
|
-
description?: string;
|
|
41
|
-
inputSchema?: unknown;
|
|
42
|
-
}>;
|
|
43
|
-
message?: string;
|
|
44
|
-
};
|
|
45
|
-
} | {
|
|
46
|
-
status: 500;
|
|
47
|
-
body: {
|
|
48
|
-
ok: false;
|
|
49
|
-
error: string;
|
|
50
|
-
};
|
|
51
|
-
}>;
|
|
11
|
+
resolveAdapter?: (providerId: string) => unknown;
|
|
12
|
+
}
|
|
52
13
|
|
|
53
|
-
export declare function
|
|
14
|
+
export declare function handleCapabilityToolsRequest(input: HandleCapabilityToolsRequestInput): Promise<{
|
|
15
|
+
status: number;
|
|
16
|
+
body: Record<string, unknown>;
|
|
17
|
+
}>;
|
|
18
|
+
export interface HandleCapabilityToolCallRequestInput {
|
|
54
19
|
agentId: string;
|
|
55
20
|
capability: string;
|
|
56
21
|
tool: string;
|
|
57
|
-
args
|
|
58
|
-
|
|
59
|
-
resolveSession?: () => {
|
|
22
|
+
args?: Record<string, unknown> | null;
|
|
23
|
+
resolveSession: () => {
|
|
60
24
|
agentId: string;
|
|
61
25
|
providerId: string;
|
|
62
26
|
} | undefined;
|
|
63
|
-
resolveAdapter?: (providerId: string) =>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
};
|
|
70
|
-
}): Promise<{
|
|
71
|
-
status: 400;
|
|
72
|
-
body: {
|
|
73
|
-
ok: false;
|
|
74
|
-
error: string;
|
|
75
|
-
};
|
|
76
|
-
} | {
|
|
77
|
-
status: 200;
|
|
78
|
-
body: {
|
|
79
|
-
ok: boolean;
|
|
80
|
-
result?: unknown;
|
|
81
|
-
error?: string;
|
|
82
|
-
};
|
|
83
|
-
} | {
|
|
84
|
-
status: 500;
|
|
85
|
-
body: {
|
|
86
|
-
ok: false;
|
|
87
|
-
error: string;
|
|
88
|
-
};
|
|
27
|
+
resolveAdapter?: (providerId: string) => unknown;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export declare function handleCapabilityToolCallRequest(input: HandleCapabilityToolCallRequestInput): Promise<{
|
|
31
|
+
status: number;
|
|
32
|
+
body: Record<string, unknown>;
|
|
89
33
|
}>;
|
|
90
34
|
|
|
91
35
|
export declare function resolveActiveSdkSessionSnapshotStatus(adapterStatus?: string, currentRuntimeStatus?: string, currentRuntimeActionType?: string): string;
|
package/dist/routes/sessions.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{execSync as T}from"node:child_process";import{Router as R}from"express";import{adapterRegistry as y}from"../adapter/index.js";import{validateToken as I,validateTokenOrLoopback as P}from"../middleware/auth.js";import{isProcessRunning as b,findOrphanProcessByAgentId as A,findOrphanProcessByWorkspace as C}from"../services/process-detector.js";import{findPersistedSessionByAgentId as S,loadPersistedSessions as F,removePersistedSession as w}from"../services/terminal-persistence.js";import{createLogger as K}from"../utils/logger.js";import{sdkSessions as m,cleanupSdkSessionByAgentId as E}from"./terminal.js";const u=K("sessions"),h=R();h.post("/sessions/:agentId/notify-force-message",P,(i,t)=>{const e=String(i.params.agentId??"").trim();if(!e){t.status(400).json({ok:!1,error:"agentId is required"});return}const n=Array.from(m.entries()).find(([,c])=>c.agentId===e);if(!n){t.json({ok:!0,notified:!1,message:"No active SDK session for this agentId"});return}const[r,s]=n,o=y.get(s.providerId||"claude-code");if(typeof o.notifyForceMessage=="function"){const c=o.notifyForceMessage(r);t.json({ok:!0,notified:c})}else t.json({ok:!0,notified:!1,message:"Adapter does not support force message notification"})});async function x(i){const t=String(i.agentId??"").trim(),e=String(i.capability??"").trim(),n=i.action==="expand"||i.action==="collapse"?i.action:void 0;if(!t)return{status:400,body:{ok:!1,error:"agentId is required"}};if(e!=="browser"&&e!=="desktop")return{status:400,body:{ok:!1,error:'capability must be "browser" or "desktop"'}};if(n!=="expand"&&n!=="collapse")return{status:400,body:{ok:!1,error:'action must be "expand" or "collapse"'}};const s=(i.resolveSession??(()=>{const a=Array.from(m.entries()).find(([,d])=>d.agentId===t);return a?{agentId:a[1].agentId,providerId:a[1].providerId}:void 0}))();if(!s)return{status:200,body:{ok:!0,changed:{sessions:0,tools:0},message:"No active SDK session for this agentId"}};const o=i.resolveAdapter??(a=>y.get(a));let c;try{c=o(s.providerId||"claude-code")}catch(a){return{status:500,body:{ok:!1,error:a?.message||"adapter resolution failed"}}}if(typeof c.setBuiltInCapabilityTools!="function")return{status:200,body:{ok:!0,changed:{sessions:0,tools:0},message:"Adapter does not support built-in capability tool switching"}};try{const a=await c.setBuiltInCapabilityTools(t,e,n),{manifests:d,...l}=a;return{status:200,body:{ok:!0,changed:l,...d&&d.length>0?{manifests:d}:{}}}}catch(a){return u.error("[sessions] capability-tools \u6267\u884C\u5931\u8D25",{agentId:t,capability:e,action:n,error:a?.message||String(a)}),{status:500,body:{ok:!1,error:a?.message||"failed"}}}}async function L(i){const t=String(i.agentId??"").trim(),e=String(i.capability??"").trim(),n=String(i.tool??"").trim(),r=i.args&&typeof i.args=="object"&&!Array.isArray(i.args)?i.args:{};if(!t)return{status:400,body:{ok:!1,error:"agentId is required"}};if(e!=="browser"&&e!=="desktop")return{status:400,body:{ok:!1,error:'capability must be "browser" or "desktop"'}};if(!n)return{status:400,body:{ok:!1,error:"tool is required"}};const o=(i.resolveSession??(()=>{const d=Array.from(m.entries()).find(([,l])=>l.agentId===t);return d?{agentId:d[1].agentId,providerId:d[1].providerId}:void 0}))();if(!o)return{status:200,body:{ok:!1,error:"No active SDK session for this agentId"}};const c=i.resolveAdapter??(d=>y.get(d));let a;try{a=c(o.providerId||"claude-code")}catch(d){return{status:500,body:{ok:!1,error:d?.message||"adapter resolution failed"}}}if(typeof a.callBuiltInCapabilityTool!="function")return{status:200,body:{ok:!1,error:"Adapter does not support built-in capability tool dispatch"}};try{return{status:200,body:await a.callBuiltInCapabilityTool(t,e,n,r)}}catch(d){return u.error("[sessions] capability-tools/call \u6267\u884C\u5931\u8D25",{agentId:t,capability:e,tool:n,error:d?.message||String(d)}),{status:500,body:{ok:!1,error:d?.message||"failed"}}}}h.post("/sessions/:agentId/capability-tools",P,async(i,t)=>{const e=await x({agentId:String(i.params.agentId??""),capability:String(i.body?.capability??""),action:i.body?.action});t.status(e.status).json(e.body)}),h.post("/sessions/:agentId/capability-tools/call",P,async(i,t)=>{const e=await L({agentId:String(i.params.agentId??""),capability:String(i.body?.capability??""),tool:String(i.body?.tool??""),args:i.body?.arguments});t.status(e.status).json(e.body)});function $(i,t,e){const n=String(i||"").trim();if(n){const s=String(t||"").trim(),o=String(e||"").trim().toLowerCase();return n.toLowerCase()==="tool_using"&&s.toLowerCase()==="waiting_input"&&o==="idle"?s:n}return String(t||"").trim()||"running"}h.get("/sessions",I,async(i,t)=>{try{const n=Array.from(m.entries()).map(([a,d])=>{const l=y.get(d.providerId||"claude-code"),p=$(l.getSessionStatus(a),d.currentRuntimeStatus,d.currentRuntimeActionType);return{sessionId:a,agentId:d.agentId,workspacePath:d.workspacePath,status:p,runtimeStatus:p,runtimeActionType:d.currentRuntimeActionType,runtimeActionLabel:d.currentRuntimeActionLabel,runtimeActionDetail:d.currentRuntimeActionDetail,mode:"sdk",startedAt:null}}),r=await F(),s=new Set(n.map(a=>a.sessionId)),o=r.filter(a=>!s.has(a.sessionId)),c={ok:!0,active:n,persisted:o};t.json(c)}catch(e){const n=e;t.status(500).json({error:n.message})}}),h.post("/sessions/recover",I,async(i,t)=>{const{agentId:e,workspacePath:n,command:r}=i.body;if(!e){t.status(400).json({error:"agentId is required"});return}try{const s=Array.from(m.entries()).find(([,g])=>g.agentId===e);if(s){const g=y.get(s[1].providerId||"claude-code"),k=$(g.getSessionStatus(s[0]),s[1].currentRuntimeStatus,s[1].currentRuntimeActionType);t.json({ok:!0,sessionId:s[0],agentId:e,workspacePath:s[1].workspacePath,command:s[1].config.command,status:k,runtimeStatus:k,runtimeActionType:s[1].currentRuntimeActionType,runtimeActionLabel:s[1].currentRuntimeActionLabel,runtimeActionDetail:s[1].currentRuntimeActionDetail,mode:"sdk",recovered:!1,message:"SDK session already active"});return}const o=await S(e);let c=!1,a;if(o?.pid&&b(o.pid))c=!0,a=o.pid,u.info(`[Recover] Detected orphan process for agentId=${e}, pid=${o.pid}`);else if(o?.mode==="sdk"||!o?.pid){const g=await A(e);g&&(c=!0,a=g.pid,u.info(`[Recover] Found orphan process by agentId search: agentId=${e}, pid=${g.pid}`))}const d=n||o?.workspacePath,l=r||o?.command||"acode",p=o?.mode||"sdk";if(!d){t.status(400).json({error:"workspacePath is required (not found in persisted session)"});return}if(c&&a){u.info(`[Recover] Returning orphan process info for agentId=${e}`),t.json({ok:!0,sessionId:o?.sessionId||"",agentId:e,workspacePath:d,command:l,status:"orphan_detected",runtimeStatus:o?.runtimeStatus,runtimeActionType:o?.runtimeActionType,runtimeActionLabel:o?.runtimeActionLabel,runtimeActionDetail:o?.runtimeActionDetail,recovered:!1,orphan:{pid:a,message:"Found orphan process still running. Manual intervention may be required."},mode:p});return}await w(o?.sessionId||"");const f={ok:!1,sessionId:o?.sessionId||"",agentId:e,workspacePath:d,command:l,status:"stopped",recovered:!1,message:"No active SDK session found; start a new SDK runtime session instead."};t.json(f)}catch(s){const o=s;u.error("Error:",o),t.status(500).json({error:o.message})}}),h.post("/search-orphan",I,async(i,t)=>{const{agentId:e,workspacePath:n}=i.body;if(!e){t.status(400).json({error:"agentId is required"});return}try{const r=Array.from(m.entries()).find(([,p])=>p.agentId===e);let s=!1,o,c;if(r){const[p,f]=r,g=y.get(f.providerId||"claude-code");o=g.getSessionPid?.(p),c=f.config?.command;const k=o?b(o):!1,v=g.getSessionStatus(p),D=v==="completed"||v==="terminated"||v==="error";if(k)s=!0;else if(o&&!k)u.info(`[search-orphan] Cleaning up stale SDK session (process dead): agentId=${e}, sessionId=${p}, pid=${o}`),m.delete(p),await w(p);else if(D){u.info(`[search-orphan] Cleaning up stale in-process SDK session (status=${v}): agentId=${e}, sessionId=${p}`);try{await g.terminateSession(p)}catch(j){u.warn(`[search-orphan] Failed to terminate stale in-process session: ${j}`)}m.delete(p),await w(p)}else{u.info(`[search-orphan] Terminating unreachable in-process SDK session: agentId=${e}, sessionId=${p}, status=${v||"unknown"}`);try{await g.abortCurrentTurn(p).catch(()=>{}),await g.terminateSession(p)}catch(j){u.warn(`[search-orphan] Failed to terminate in-process session: ${j}`)}m.delete(p),await w(p)}}const a=await A(e);if(a){u.info(`[search-orphan] Found orphan process for agentId=${e}, pid=${a.pid}`),t.json({ok:!0,agentId:e,hasActiveSession:s,found:!0,orphan:{pid:a.pid,command:a.command,cwd:a.cwd,startedAt:a.startedAt,message:"Found orphan process. Click terminate to stop it."}});return}if(s&&o){u.info(`[search-orphan] Active session process still running, reporting as terminable orphan: agentId=${e}, pid=${o}`),t.json({ok:!0,agentId:e,hasActiveSession:!0,found:!0,orphan:{pid:o,command:c||"(active SDK session)",message:"Found active SDK session process. Click terminate to stop it."}});return}const d=await S(e);if(d?.pid&&b(d.pid)){u.info(`[search-orphan] Found orphan by persisted PID: agentId=${e}, pid=${d.pid}`),t.json({ok:!0,agentId:e,hasActiveSession:s,found:!0,orphan:{pid:d.pid,command:d.command||"(persisted session)",message:"Found orphan process by persisted PID. Click terminate to stop it."}});return}const l=n||d?.workspacePath;if(l){const p=await C(l);if(p){u.info(`[search-orphan] Found orphan by workspace: agentId=${e}, pid=${p.pid}, workspace=${l}`),t.json({ok:!0,agentId:e,hasActiveSession:s,found:!0,orphan:{pid:p.pid,command:p.command,message:"Found orphan process by workspace path. Click terminate to stop it."}});return}}u.info(`[search-orphan] No orphan process found for agentId=${e}, hasActiveSession=${!!s}`),t.json({ok:!0,agentId:e,hasActiveSession:!!s,found:!1,orphan:null,message:s?"Agent has active session in runtime-bridge":"No orphan process and no active session found"})}catch(r){const s=r;u.error("[search-orphan] Error:",s),t.status(500).json({error:s.message})}}),h.post("/terminate-orphan",I,async(i,t)=>{const{agentId:e,pid:n}=i.body;if(!e||!n){t.status(400).json({error:"agentId and pid are required"});return}try{const r=Array.from(m.entries()).find(([,c])=>c.agentId===e);if(r){const[c,a]=r,d=y.get(a.providerId||"claude-code");try{await d.terminateSession(c)}catch(l){u.warn(`[terminate-orphan] Failed to terminate SDK session for agentId=${e}:`,l)}m.delete(c),await w(c),u.info(`[terminate-orphan] Cleaned up SDK session for agentId=${e}, sessionId=${c}`)}if(!b(n)){t.json({ok:!0,agentId:e,pid:n,terminated:!1,message:"Process already terminated"});return}const s=process.platform;let o=!1;if(s==="win32")try{T(`taskkill /PID ${n} /T /F`,{encoding:"utf8",timeout:5e3}),o=!0}catch(c){u.warn(`[terminate-orphan] taskkill failed for pid=${n}:`,c)}else try{process.kill(n,"SIGTERM"),o=!0}catch(c){u.warn(`[terminate-orphan] kill failed for pid=${n}:`,c)}o?(u.info(`[terminate-orphan] Terminated orphan process for agentId=${e}, pid=${n}`),t.json({ok:!0,agentId:e,pid:n,terminated:!0,message:"Orphan process terminated successfully"})):t.json({ok:!0,agentId:e,pid:n,terminated:!1,message:"Failed to terminate process"})}catch(r){const s=r;u.error("[terminate-orphan] Error:",s),t.status(500).json({error:s.message})}}),h.get("/sessions/by-agent/:agentId",I,async(i,t)=>{const e=i.params.agentId,n=Array.isArray(e)?e[0]:e;if(!n){t.status(400).json({error:"agentId is required"});return}try{const r=Array.from(m.entries()).find(([,o])=>o.agentId===n);if(r){const o=y.get(r[1].providerId||"claude-code"),c=o.getSessionPid?.(r[0]),a=$(o.getSessionStatus(r[0]),r[1].currentRuntimeStatus,r[1].currentRuntimeActionType);t.json({ok:!0,sessionId:r[0],agentId:n,workspacePath:r[1].workspacePath,status:a,runtimeStatus:a,isActive:!0,mode:"sdk",pid:c,isManaged:!0,providerId:r[1].providerId});return}const s=await S(n);if(s){const{isProcessRunning:o}=await import("../services/process-detector.js"),c=s.pid?o(s.pid):!1;t.json({ok:!0,sessionId:s.sessionId,agentId:n,workspacePath:s.workspacePath,command:s.command,status:c?"orphan":"stopped",runtimeStatus:s.runtimeStatus,runtimeActionType:s.runtimeActionType,runtimeActionLabel:s.runtimeActionLabel,runtimeActionDetail:s.runtimeActionDetail,startedAt:s.startedAt,isActive:!1,mode:s.mode,pid:s.pid,isManaged:!1,isProcessAlive:c,isOrphan:c});return}t.json({ok:!0,agentId:n,status:"none",isActive:!1,isManaged:!1})}catch(r){const s=r;u.error("[sessions/by-agent] Error:",s),t.status(500).json({error:s.message})}}),h.post("/takeover-session",I,async(i,t)=>{const{agentId:e}=i.body;if(!e){t.status(400).json({error:"agentId is required"});return}try{const n=Array.from(m.entries()).find(([,o])=>o.agentId===e);if(n){t.json({ok:!0,agentId:e,sessionId:n[0],mode:"sdk",status:"running",isActive:!0,recovered:!1,message:"SDK session already active"});return}const r=await S(e);if(!r){t.status(404).json({error:"No persisted session found for this agentId",agentId:e});return}let s=!1;if(r.mode==="sdk"&&(s=!!await A(e),!s&&r.pid&&(s=b(r.pid))),!s){u.info(`[takeover-session] Process not alive for agentId=${e}, mode=${r.mode}`),t.json({ok:!1,agentId:e,status:"process_not_alive",isActive:!1,message:"Agent process is no longer running"});return}if(u.info(`[takeover-session] Taking over session for agentId=${e}, mode=${r.mode}, pid=${r.pid}`),r.mode==="sdk"){const o=await A(e);o?(u.info(`[takeover-session] Found SDK process for agentId=${e}, pid=${o.pid}`),t.json({ok:!0,agentId:e,sessionId:r.sessionId,mode:"sdk",status:"running",isActive:!0,recovered:!0,pid:o.pid||r.pid,message:"SDK session process found, can be taken over by aws-mcp-server"})):t.json({ok:!1,agentId:e,status:"process_not_alive",isActive:!1,message:"SDK process not found"})}else t.status(410).json({error:"Session takeover is no longer supported; start a new SDK runtime session.",agentId:e,mode:"sdk"})}catch(n){const r=n;u.error("[takeover-session] Error:",r),t.status(500).json({error:r.message})}}),h.post("/terminate-tree",I,async(i,t)=>{const{agentId:e,sessionId:n}=i.body;if(!e){t.status(400).json({error:"agentId is required"});return}try{let r=n;if(!r){const f=await S(e);if(!f){t.status(404).json({error:"Session not found for this agentId"});return}r=f.sessionId}const s=m.get(r);let o,c;if(s)o=y.get(s.providerId||"claude-code").getSessionPid?.(r),c="sdk";else{const f=await S(e);if(!f?.pid){t.status(404).json({error:"Session PID not found"});return}o=f.pid,c=f.mode||"sdk"}const{terminateProcessTree:a,waitForProcessExit:d}=await import("../services/process-detector.js");if(!o){t.status(404).json({error:"PID not found"});return}const l=await a(o),p=await d(o,5e3);E(e),await import("../services/terminal-persistence.js").then(f=>f.removePersistedSession(r)),u.info(`[terminate-tree] Terminated process tree for agentId=${e}, pid=${o}, mode=${c}, terminated=${l.terminated}, failed=${l.failed}, exited=${p}`),t.json({ok:!0,agentId:e,pid:o,mode:c,terminated:l.terminated,failed:l.failed,exited:p,message:p?`Successfully terminated ${l.terminated} processes`:`Terminated ${l.terminated} processes, but process ${o} still running`})}catch(r){const s=r;u.error("[terminate-tree] Error:",s),t.status(500).json({error:s.message})}}),h.get("/health/:agentId",I,async(i,t)=>{const e=i.params.agentId,n=Array.isArray(e)?e[0]:e;if(!n){t.status(400).json({error:"agentId is required"});return}try{const r=await S(n);if(!r){t.status(404).json({error:"Session not found"});return}const s=m.get(r.sessionId),o=!!s,{checkProcessHealth:c}=await import("../services/process-detector.js"),a={sessionId:r.sessionId,mode:r.mode,workspacePath:r.workspacePath,isManaged:o,isActive:!!s};if(r.pid){const d=await c(r.pid,n);Object.assign(a,d)}else Object.assign(a,{pid:0,alive:!1,cpu:0,memoryMB:0,status:"dead",lastUpdate:new Date});t.json({agentId:n,...a})}catch(r){const s=r;u.error("[health] Error:",s),t.status(500).json({error:s.message})}});export{L as handleCapabilityToolCallRequest,x as handleCapabilityToolsRequest,$ as resolveActiveSdkSessionSnapshotStatus,h as sessionsRouter};
|
|
1
|
+
import{execSync as R}from"node:child_process";import{Router as C}from"express";import{adapterRegistry as y}from"../adapter/index.js";import{validateToken as I,validateTokenOrLoopback as P}from"../middleware/auth.js";import{isProcessRunning as w,findOrphanProcessByAgentId as b,findOrphanProcessByWorkspace as F}from"../services/process-detector.js";import{findPersistedSessionByAgentId as S,loadPersistedSessions as E,removePersistedSession as A}from"../services/terminal-persistence.js";import{createLogger as K}from"../utils/logger.js";import{sdkSessions as m,cleanupSdkSessionByAgentId as L}from"./terminal.js";const d=K("sessions"),h=C();h.post("/sessions/:agentId/notify-force-message",P,(i,r)=>{const e=String(i.params.agentId??"").trim();if(!e){r.status(400).json({ok:!1,error:"agentId is required"});return}const n=Array.from(m.entries()).find(([,a])=>a.agentId===e);if(!n){r.json({ok:!0,notified:!1,message:"No active SDK session for this agentId"});return}const[o,s]=n,t=y.get(s.providerId||"claude-code");if(typeof t.notifyForceMessage=="function"){const a=t.notifyForceMessage(o);r.json({ok:!0,notified:a})}else r.json({ok:!0,notified:!1,message:"Adapter does not support force message notification"})}),h.post("/sessions/:agentId/capability-tools",P,async(i,r)=>{const e=String(i.params.agentId??"").trim(),n=()=>{const s=Array.from(m.entries()).find(([,t])=>t.agentId===e);return s?{agentId:e,providerId:s[1].providerId||""}:void 0},o=await x({agentId:e,capability:String(i.body?.capability??""),action:i.body?.action,resolveSession:n,resolveAdapter:s=>y.get(s||"claude-code")});r.status(o.status).json(o.body)}),h.post("/sessions/:agentId/capability-tools/call",P,async(i,r)=>{const e=String(i.params.agentId??"").trim(),n=()=>{const s=Array.from(m.entries()).find(([,t])=>t.agentId===e);return s?{agentId:e,providerId:s[1].providerId||""}:void 0},o=await B({agentId:e,capability:String(i.body?.capability??""),tool:String(i.body?.tool??""),args:i.body?.args??{},resolveSession:n,resolveAdapter:s=>y.get(s||"claude-code")});r.status(o.status).json(o.body)});const D=new Set(["browser","desktop"]);async function x(i){const r=String(i.agentId??"").trim();if(!r)return{status:400,body:{ok:!1,error:"agentId is required"}};const e=String(i.capability??"").trim();if(!D.has(e))return{status:400,body:{ok:!1,error:'capability must be "browser" or "desktop"'}};const n=i.action;if(n!=="expand"&&n!=="collapse")return{status:400,body:{ok:!1,error:`action must be "expand" or "collapse", got: ${String(n)}`}};const o=i.resolveSession();if(!o)return{status:200,body:{ok:!0,changed:{sessions:0,tools:0}}};const s=i.resolveAdapter?.(o.providerId);if(!s||typeof s.setBuiltInCapabilityTools!="function")return{status:200,body:{ok:!0,changed:{sessions:0,tools:0},message:`Adapter for provider "${o.providerId}" does not support setBuiltInCapabilityTools`}};try{const t=await s.setBuiltInCapabilityTools(r,e,n),a={ok:!0,changed:{sessions:t.sessions,tools:t.tools}};return n==="expand"&&Array.isArray(t.manifests)&&(a.manifests=t.manifests),{status:200,body:a}}catch(t){const a=t instanceof Error?t.message:String(t);return d.error(`capability-tools ${n} failed for agent ${r}: ${a}`),{status:500,body:{ok:!1,error:a}}}}async function B(i){const r=String(i.agentId??"").trim();if(!r)return{status:400,body:{ok:!1,error:"agentId is required"}};const e=String(i.capability??"").trim();if(!D.has(e))return{status:400,body:{ok:!1,error:'capability must be "browser" or "desktop"'}};const n=String(i.tool??"").trim();if(!n)return{status:400,body:{ok:!1,error:"tool is required"}};const o=i.resolveSession();if(!o)return{status:200,body:{ok:!1,error:`No active SDK session for agentId "${r}"`}};const s=i.resolveAdapter?.(o.providerId);if(!s||typeof s.callBuiltInCapabilityTool!="function")return{status:200,body:{ok:!1,error:`Adapter for provider "${o.providerId}" does not support callBuiltInCapabilityTool`}};try{return{status:200,body:await s.callBuiltInCapabilityTool(r,e,n,i.args??{})}}catch(t){const a=t instanceof Error?t.message:String(t);return d.error(`capability-tools/call ${n} failed for agent ${r}: ${a}`),{status:500,body:{ok:!1,error:a}}}}function j(i,r,e){const n=String(i||"").trim();if(n){const s=String(r||"").trim(),t=String(e||"").trim().toLowerCase();return n.toLowerCase()==="tool_using"&&s.toLowerCase()==="waiting_input"&&t==="idle"?s:n}return String(r||"").trim()||"running"}h.get("/sessions",I,async(i,r)=>{try{const n=Array.from(m.entries()).map(([p,u])=>{const l=y.get(u.providerId||"claude-code"),c=j(l.getSessionStatus(p),u.currentRuntimeStatus,u.currentRuntimeActionType);return{sessionId:p,agentId:u.agentId,workspacePath:u.workspacePath,status:c,runtimeStatus:c,runtimeActionType:u.currentRuntimeActionType,runtimeActionLabel:u.currentRuntimeActionLabel,runtimeActionDetail:u.currentRuntimeActionDetail,mode:"sdk",startedAt:null}}),o=await E(),s=new Set(n.map(p=>p.sessionId)),t=o.filter(p=>!s.has(p.sessionId)),a={ok:!0,active:n,persisted:t};r.json(a)}catch(e){const n=e;r.status(500).json({error:n.message})}}),h.post("/sessions/recover",I,async(i,r)=>{const{agentId:e,workspacePath:n,command:o}=i.body;if(!e){r.status(400).json({error:"agentId is required"});return}try{const s=Array.from(m.entries()).find(([,g])=>g.agentId===e);if(s){const g=y.get(s[1].providerId||"claude-code"),k=j(g.getSessionStatus(s[0]),s[1].currentRuntimeStatus,s[1].currentRuntimeActionType);r.json({ok:!0,sessionId:s[0],agentId:e,workspacePath:s[1].workspacePath,command:s[1].config.command,status:k,runtimeStatus:k,runtimeActionType:s[1].currentRuntimeActionType,runtimeActionLabel:s[1].currentRuntimeActionLabel,runtimeActionDetail:s[1].currentRuntimeActionDetail,mode:"sdk",recovered:!1,message:"SDK session already active"});return}const t=await S(e);let a=!1,p;if(t?.pid&&await w(t.pid))a=!0,p=t.pid,d.info(`[Recover] Detected orphan process for agentId=${e}, pid=${t.pid}`);else if(t?.mode==="sdk"||!t?.pid){const g=await b(e);g&&(a=!0,p=g.pid,d.info(`[Recover] Found orphan process by agentId search: agentId=${e}, pid=${g.pid}`))}const u=n||t?.workspacePath,l=o||t?.command||"acode",c=t?.mode||"sdk";if(!u){r.status(400).json({error:"workspacePath is required (not found in persisted session)"});return}if(a&&p){d.info(`[Recover] Returning orphan process info for agentId=${e}`),r.json({ok:!0,sessionId:t?.sessionId||"",agentId:e,workspacePath:u,command:l,status:"orphan_detected",runtimeStatus:t?.runtimeStatus,runtimeActionType:t?.runtimeActionType,runtimeActionLabel:t?.runtimeActionLabel,runtimeActionDetail:t?.runtimeActionDetail,recovered:!1,orphan:{pid:p,message:"Found orphan process still running. Manual intervention may be required."},mode:c});return}await A(t?.sessionId||"");const f={ok:!1,sessionId:t?.sessionId||"",agentId:e,workspacePath:u,command:l,status:"stopped",recovered:!1,message:"No active SDK session found; start a new SDK runtime session instead."};r.json(f)}catch(s){const t=s;d.error("Error:",t),r.status(500).json({error:t.message})}}),h.post("/search-orphan",I,async(i,r)=>{const{agentId:e,workspacePath:n}=i.body;if(!e){r.status(400).json({error:"agentId is required"});return}try{const o=Array.from(m.entries()).find(([,c])=>c.agentId===e);let s=!1,t,a;if(o){const[c,f]=o,g=y.get(f.providerId||"claude-code");t=g.getSessionPid?.(c),a=f.config?.command;const k=t?await w(t):!1,v=g.getSessionStatus(c),T=v==="completed"||v==="terminated"||v==="error";if(k)s=!0;else if(t&&!k)d.info(`[search-orphan] Cleaning up stale SDK session (process dead): agentId=${e}, sessionId=${c}, pid=${t}`),m.delete(c),await A(c);else if(T){d.info(`[search-orphan] Cleaning up stale in-process SDK session (status=${v}): agentId=${e}, sessionId=${c}`);try{await g.terminateSession(c)}catch($){d.warn(`[search-orphan] Failed to terminate stale in-process session: ${$}`)}m.delete(c),await A(c)}else{d.info(`[search-orphan] Terminating unreachable in-process SDK session: agentId=${e}, sessionId=${c}, status=${v||"unknown"}`);try{await g.abortCurrentTurn(c).catch(()=>{}),await g.terminateSession(c)}catch($){d.warn(`[search-orphan] Failed to terminate in-process session: ${$}`)}m.delete(c),await A(c)}}const p=await b(e);if(p){d.info(`[search-orphan] Found orphan process for agentId=${e}, pid=${p.pid}`),r.json({ok:!0,agentId:e,hasActiveSession:s,found:!0,orphan:{pid:p.pid,command:p.command,cwd:p.cwd,startedAt:p.startedAt,message:"Found orphan process. Click terminate to stop it."}});return}if(s&&t){d.info(`[search-orphan] Active session process still running, reporting as terminable orphan: agentId=${e}, pid=${t}`),r.json({ok:!0,agentId:e,hasActiveSession:!0,found:!0,orphan:{pid:t,command:a||"(active SDK session)",message:"Found active SDK session process. Click terminate to stop it."}});return}const u=await S(e);if(u?.pid&&await w(u.pid)){d.info(`[search-orphan] Found orphan by persisted PID: agentId=${e}, pid=${u.pid}`),r.json({ok:!0,agentId:e,hasActiveSession:s,found:!0,orphan:{pid:u.pid,command:u.command||"(persisted session)",message:"Found orphan process by persisted PID. Click terminate to stop it."}});return}const l=n||u?.workspacePath;if(l){const c=await F(l);if(c){d.info(`[search-orphan] Found orphan by workspace: agentId=${e}, pid=${c.pid}, workspace=${l}`),r.json({ok:!0,agentId:e,hasActiveSession:s,found:!0,orphan:{pid:c.pid,command:c.command,message:"Found orphan process by workspace path. Click terminate to stop it."}});return}}d.info(`[search-orphan] No orphan process found for agentId=${e}, hasActiveSession=${!!s}`),r.json({ok:!0,agentId:e,hasActiveSession:!!s,found:!1,orphan:null,message:s?"Agent has active session in runtime-bridge":"No orphan process and no active session found"})}catch(o){const s=o;d.error("[search-orphan] Error:",s),r.status(500).json({error:s.message})}}),h.post("/terminate-orphan",I,async(i,r)=>{const{agentId:e,pid:n}=i.body;if(!e||!n){r.status(400).json({error:"agentId and pid are required"});return}try{const o=Array.from(m.entries()).find(([,a])=>a.agentId===e);if(o){const[a,p]=o,u=y.get(p.providerId||"claude-code");try{await u.terminateSession(a)}catch(l){d.warn(`[terminate-orphan] Failed to terminate SDK session for agentId=${e}:`,l)}m.delete(a),await A(a),d.info(`[terminate-orphan] Cleaned up SDK session for agentId=${e}, sessionId=${a}`)}if(!await w(n)){r.json({ok:!0,agentId:e,pid:n,terminated:!1,message:"Process already terminated"});return}const s=process.platform;let t=!1;if(s==="win32")try{R(`taskkill /PID ${n} /T /F`,{encoding:"utf8",timeout:5e3}),t=!0}catch(a){d.warn(`[terminate-orphan] taskkill failed for pid=${n}:`,a)}else try{process.kill(n,"SIGTERM"),t=!0}catch(a){d.warn(`[terminate-orphan] kill failed for pid=${n}:`,a)}t?(d.info(`[terminate-orphan] Terminated orphan process for agentId=${e}, pid=${n}`),r.json({ok:!0,agentId:e,pid:n,terminated:!0,message:"Orphan process terminated successfully"})):r.json({ok:!0,agentId:e,pid:n,terminated:!1,message:"Failed to terminate process"})}catch(o){const s=o;d.error("[terminate-orphan] Error:",s),r.status(500).json({error:s.message})}}),h.get("/sessions/by-agent/:agentId",I,async(i,r)=>{const e=i.params.agentId,n=Array.isArray(e)?e[0]:e;if(!n){r.status(400).json({error:"agentId is required"});return}try{const o=Array.from(m.entries()).find(([,t])=>t.agentId===n);if(o){const t=y.get(o[1].providerId||"claude-code"),a=t.getSessionPid?.(o[0]),p=j(t.getSessionStatus(o[0]),o[1].currentRuntimeStatus,o[1].currentRuntimeActionType);r.json({ok:!0,sessionId:o[0],agentId:n,workspacePath:o[1].workspacePath,status:p,runtimeStatus:p,isActive:!0,mode:"sdk",pid:a,isManaged:!0,providerId:o[1].providerId});return}const s=await S(n);if(s){const{isProcessRunning:t}=await import("../services/process-detector.js"),a=s.pid?await t(s.pid):!1;r.json({ok:!0,sessionId:s.sessionId,agentId:n,workspacePath:s.workspacePath,command:s.command,status:a?"orphan":"stopped",runtimeStatus:s.runtimeStatus,runtimeActionType:s.runtimeActionType,runtimeActionLabel:s.runtimeActionLabel,runtimeActionDetail:s.runtimeActionDetail,startedAt:s.startedAt,isActive:!1,mode:s.mode,pid:s.pid,isManaged:!1,isProcessAlive:a,isOrphan:a});return}r.json({ok:!0,agentId:n,status:"none",isActive:!1,isManaged:!1})}catch(o){const s=o;d.error("[sessions/by-agent] Error:",s),r.status(500).json({error:s.message})}}),h.post("/takeover-session",I,async(i,r)=>{const{agentId:e}=i.body;if(!e){r.status(400).json({error:"agentId is required"});return}try{const n=Array.from(m.entries()).find(([,t])=>t.agentId===e);if(n){r.json({ok:!0,agentId:e,sessionId:n[0],mode:"sdk",status:"running",isActive:!0,recovered:!1,message:"SDK session already active"});return}const o=await S(e);if(!o){r.status(404).json({error:"No persisted session found for this agentId",agentId:e});return}let s=!1;if(o.mode==="sdk"&&(s=!!await b(e),!s&&o.pid&&(s=await w(o.pid))),!s){d.info(`[takeover-session] Process not alive for agentId=${e}, mode=${o.mode}`),r.json({ok:!1,agentId:e,status:"process_not_alive",isActive:!1,message:"Agent process is no longer running"});return}if(d.info(`[takeover-session] Taking over session for agentId=${e}, mode=${o.mode}, pid=${o.pid}`),o.mode==="sdk"){const t=await b(e);t?(d.info(`[takeover-session] Found SDK process for agentId=${e}, pid=${t.pid}`),r.json({ok:!0,agentId:e,sessionId:o.sessionId,mode:"sdk",status:"running",isActive:!0,recovered:!0,pid:t.pid||o.pid,message:"SDK session process found, can be taken over by aws-mcp-server"})):r.json({ok:!1,agentId:e,status:"process_not_alive",isActive:!1,message:"SDK process not found"})}else r.status(410).json({error:"Session takeover is no longer supported; start a new SDK runtime session.",agentId:e,mode:"sdk"})}catch(n){const o=n;d.error("[takeover-session] Error:",o),r.status(500).json({error:o.message})}}),h.post("/terminate-tree",I,async(i,r)=>{const{agentId:e,sessionId:n}=i.body;if(!e){r.status(400).json({error:"agentId is required"});return}try{let o=n;if(!o){const f=await S(e);if(!f){r.status(404).json({error:"Session not found for this agentId"});return}o=f.sessionId}const s=m.get(o);let t,a;if(s)t=y.get(s.providerId||"claude-code").getSessionPid?.(o),a="sdk";else{const f=await S(e);if(!f?.pid){r.status(404).json({error:"Session PID not found"});return}t=f.pid,a=f.mode||"sdk"}const{terminateProcessTree:p,waitForProcessExit:u}=await import("../services/process-detector.js");if(!t){r.status(404).json({error:"PID not found"});return}const l=await p(t),c=await u(t,5e3);L(e),await import("../services/terminal-persistence.js").then(f=>f.removePersistedSession(o)),d.info(`[terminate-tree] Terminated process tree for agentId=${e}, pid=${t}, mode=${a}, terminated=${l.terminated}, failed=${l.failed}, exited=${c}`),r.json({ok:!0,agentId:e,pid:t,mode:a,terminated:l.terminated,failed:l.failed,exited:c,message:c?`Successfully terminated ${l.terminated} processes`:`Terminated ${l.terminated} processes, but process ${t} still running`})}catch(o){const s=o;d.error("[terminate-tree] Error:",s),r.status(500).json({error:s.message})}}),h.get("/health/:agentId",I,async(i,r)=>{const e=i.params.agentId,n=Array.isArray(e)?e[0]:e;if(!n){r.status(400).json({error:"agentId is required"});return}try{const o=await S(n);if(!o){r.status(404).json({error:"Session not found"});return}const s=m.get(o.sessionId),t=!!s,{checkProcessHealth:a}=await import("../services/process-detector.js"),p={sessionId:o.sessionId,mode:o.mode,workspacePath:o.workspacePath,isManaged:t,isActive:!!s};if(o.pid){const u=await a(o.pid,n);Object.assign(p,u)}else Object.assign(p,{pid:0,alive:!1,cpu:0,memoryMB:0,status:"dead",lastUpdate:new Date});r.json({agentId:n,...p})}catch(o){const s=o;d.error("[health] Error:",s),r.status(500).json({error:s.message})}});export{B as handleCapabilityToolCallRequest,x as handleCapabilityToolsRequest,j as resolveActiveSdkSessionSnapshotStatus,h as sessionsRouter};
|
|
2
2
|
|
package/dist/routes/skills.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Router as
|
|
1
|
+
import{Router as f}from"express";import{raw as m}from"express";import{validateToken as i}from"../middleware/auth.js";import{getEffective as p,setDefault as y,setInherited as g,setOverride as b}from"../services/capability-config.js";import{skillManager as d,isValidSkillName as c}from"../services/skill-manager.js";import{createLogger as h}from"../utils/logger.js";const S=h("skills"),a=f();function n(t){return(e,s)=>{t(e,s).catch(l=>{S.error("[skills] %s %s \u5931\u8D25: %s",e.method,e.path,l.message),s.headersSent||s.status(500).json({ok:!1,error:l.message})})}}a.post("/skills/deployed/list",i,n(async(t,e)=>{const l=d.listDeployed().map(o=>({...o,enabled:p("","skills",o.skillName)}));e.json({ok:!0,deployedSkills:l})})),a.get("/skills/deployed",i,n(async(t,e)=>{const l=d.listDeployed().map(o=>({...o,enabled:p("","skills",o.skillName)}));e.json({ok:!0,deployedSkills:l})})),a.post("/skills/deployed/default",i,n(async(t,e)=>{const s=String((t.body??{})?.skillName??"").trim();if(!c(s)){e.status(400).json({ok:!1,error:"skillName \u975E\u6CD5"});return}const l=String((t.body??{})?.agentId??"").trim(),o=(t.body??{})?.enabled,r=o==null?null:!!o;l?b(l,"skills",s,r):y("skills",s,r),e.json({ok:!0,skillName:s,agentId:l,enabled:r})})),a.post("/skills/deployed/inherited",i,n(async(t,e)=>{const s=String((t.body??{})?.agentId??"").trim();if(!s){e.status(400).json({ok:!1,error:"agentId is required"});return}const l=(t.body??{})?.map;g(s,"skills",l??null),e.json({ok:!0,agentId:s,count:Object.keys(l??{}).length})})),a.post("/skills/deployed/upload",i,m({type:"application/zip",limit:"200mb"}),n(async(t,e)=>{const s=t.body??{},l=Buffer.isBuffer(s)?s:Buffer.from(s??"");if(!l||l.length===0){e.status(400).json({ok:!1,error:"zip \u5185\u5BB9\u4E3A\u7A7A"});return}const o=String(t.query.filename??t.headers["x-filename"]??"skill.zip"),r=String(t.query.sha256??"").trim()||void 0,u=t.query.defaultEnabled!=="false",k=await d.saveSharedSkill(l,String(o),u,r);e.status(k.ok?200:400).json(k)})),a.post("/skills/deployed/remove",i,n(async(t,e)=>{const s=String((t.body??{})?.skillName??"").trim();if(!c(s)){e.status(400).json({ok:!1,error:"skillName \u975E\u6CD5"});return}const l=await d.removeDeployedSkill(s);e.status(l.ok?200:400).json(l)})),a.post("/skills/cc-switch/list",i,n(async(t,e)=>{e.json({ok:!0,skills:[],state:{skills:[],mcpServers:[]}})}));export{a as skillsRouter};
|
|
2
2
|
|