diff --git a/agent/routes/workspace/keep.ts b/agent/routes/workspace/keep.ts index da64529..cafa14f 100644 --- a/agent/routes/workspace/keep.ts +++ b/agent/routes/workspace/keep.ts @@ -1,7 +1,7 @@ import { createSkill, tool } from '@kevisual/router'; import { app, cnb } from '../../app.ts'; import { nanoid } from 'nanoid'; - +import dayjs from 'dayjs'; import { createKeepAlive } from '../../../src/keep.ts'; type AliveInfo = { @@ -92,12 +92,19 @@ app.route({ tags: [], } }).define(async (ctx) => { - const list = Array.from(keepAliveMap.entries()).map(([id, info]) => ({ - id, - wsUrl: (info as any).KeepAlive?.wsUrl, - startTime: info.startTime, - updatedTime: info.updatedTime - })); + const list = Array.from(keepAliveMap.entries()).map(([id, info]) => { + const now = Date.now(); + const duration = Math.floor((now - info.startTime) / 60000); // 分钟 + return { + id, + wsUrl: (info as any).KeepAlive?.wsUrl, + startTime: info.startTime, + startTimeStr: dayjs(info.startTime).format('YYYY-MM-DD HH:mm'), + updatedTime: info.updatedTime, + updatedTimeStr: dayjs(info.updatedTime).format('YYYY-MM-DD HH:mm'), + duration, + } + }); ctx.body = { list }; }).addTo(app); @@ -153,3 +160,55 @@ app.route({ ctx.body = { content: `没有找到对应的工作空间保持存活任务` }; } }).addTo(app); + +app.route({ + path: 'cnb', + key: 'reset-keep-workspace-alive', + description: '对存活的工作空间,startTime进行重置', + middleware: ['admin-auth'], + metadata: { + tags: [], + } +}).define(async (ctx) => { + const now = Date.now(); + for (const info of keepAliveMap.values()) { + info.startTime = now; + } + ctx.body = { content: `已重置所有存活工作空间的开始时间` }; +}).addTo(app); + +app.route({ + path: 'cnb', + key: 'clear-keep-workspace-alive', + description: '对存活的工作空间,超过5小时的进行清理', + middleware: ['admin-auth'], + metadata: { + tags: [], + } +}).define(async (ctx) => { + const res = clearKeepAlive(); + ctx.body = { + content: `已清理所有存活工作空间中超过5小时的任务` + (res.length ? `,清理项:${res.map(i => i.wsUrl).join(', ')}` : ''), + list: res + }; +}).addTo(app); + +const clearKeepAlive = () => { + const now = Date.now(); + let clearedArr: { id: string; wsUrl: string }[] = []; + for (const [id, info] of keepAliveMap.entries()) { + if (now - info.startTime > FIVE_HOURS) { + console.log(`工作空间 ${(info as any).KeepAlive?.wsUrl} 超过5小时,自动停止`); + info.KeepAlive?.disconnect?.(); + keepAliveMap.delete(id); + clearedArr.push({ id, wsUrl: (info as any).KeepAlive?.wsUrl }); + } + } + return clearedArr; +} + +// 每5小时自动清理超时的keepAlive任务 +const FIVE_HOURS = 5 * 60 * 60 * 1000; +setInterval(() => { + clearKeepAlive(); +}, FIVE_HOURS); \ No newline at end of file diff --git a/package.json b/package.json index 6b962f2..b64be1d 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@types/bun": "^1.3.8", "@types/node": "^25.1.0", "@types/ws": "^8.18.1", + "dayjs": "^1.11.19", "dotenv": "^17.2.3" }, "publishConfig": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 85c3cae..d238a62 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,9 +39,6 @@ importers: '@kevisual/ai': specifier: ^0.0.24 version: 0.0.24 - '@kevisual/cnb': - specifier: ^0.0.10 - version: 0.0.10(dotenv@17.2.3)(typescript@5.9.3) '@kevisual/context': specifier: ^0.0.4 version: 0.0.4 @@ -60,6 +57,9 @@ importers: '@types/ws': specifier: ^8.18.1 version: 8.18.1 + dayjs: + specifier: ^1.11.19 + version: 1.11.19 dotenv: specifier: ^17.2.3 version: 17.2.3 @@ -80,9 +80,6 @@ packages: '@kevisual/ai@0.0.24': resolution: {integrity: sha512-7jvZk1/L//VIClK7usuNgN4ZA9Etgbooka1Sj5quE/0UywR+NNnwqXVZ89Y1fBhI1TkhauDsdJBAtcQ7r/vbVw==} - '@kevisual/cnb@0.0.10': - resolution: {integrity: sha512-zwdJOj64FAkbUIGy+3CC+uQMUptWGf1uuQZXy0ZLfBfdFO2BcoHuzvomWhYxF0eexOBsLrz1F9jNWS0heBrp2Q==} - '@kevisual/context@0.0.4': resolution: {integrity: sha512-HJeLeZQLU+7tCluSfOyvkgKLs0HjCZrdJlZgEgKRSa8XTwZfMAUt6J7qZTbrZAHBlPtX68EPu/PI8JMCeu3WAQ==} @@ -102,9 +99,6 @@ packages: '@kevisual/query@0.0.38': resolution: {integrity: sha512-bfvbSodsZyMfwY+1T2SvDeOCKsT/AaIxlVe0+B1R/fNhlg2MDq2CP0L9HKiFkEm+OXrvXcYDMKPUituVUM5J6Q==} - '@kevisual/router@0.0.63': - resolution: {integrity: sha512-rM/FELiNtTJkjb00sdQ2f8NpWHzfOwrtgQJhsX9Np9KdzAQ5dP6pI5nAHlWvU2QyrC1VH5IibUmsBIeMMV3nWg==} - '@kevisual/router@0.0.64': resolution: {integrity: sha512-EYz1MZxrltgySUL0Y+/MtZf2FEmqC5U8GmFAqvHNjgtS5FJdHpxRjo6zab4+0wSUlVyCxCpZXFY5vHB/g+nQBw==} @@ -339,6 +333,9 @@ packages: crossws@0.3.5: resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + dayjs@1.11.19: + resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -575,19 +572,6 @@ snapshots: '@kevisual/permission': 0.0.3 '@kevisual/query': 0.0.38 - '@kevisual/cnb@0.0.10(dotenv@17.2.3)(typescript@5.9.3)': - dependencies: - '@kevisual/query': 0.0.38 - '@kevisual/router': 0.0.63(typescript@5.9.3) - '@kevisual/use-config': 1.0.28(dotenv@17.2.3) - es-toolkit: 1.44.0 - nanoid: 5.1.6 - ws: '@kevisual/ws@8.19.0' - zod: 4.3.6 - transitivePeerDependencies: - - dotenv - - typescript - '@kevisual/context@0.0.4': {} '@kevisual/dts@0.0.3(typescript@5.9.3)': @@ -613,13 +597,6 @@ snapshots: dependencies: tslib: 2.8.1 - '@kevisual/router@0.0.63(typescript@5.9.3)': - dependencies: - '@kevisual/dts': 0.0.3(typescript@5.9.3) - hono: 4.11.7 - transitivePeerDependencies: - - typescript - '@kevisual/router@0.0.64(typescript@5.9.3)': dependencies: '@kevisual/dts': 0.0.3(typescript@5.9.3) @@ -794,6 +771,8 @@ snapshots: dependencies: uncrypto: 0.1.3 + dayjs@1.11.19: {} + deepmerge@4.3.1: {} defu@6.1.4: {}