@caoruhua/open-claude-remote 0.3.4 → 0.3.7
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/README.md +28 -32
- package/dist/backend/src/api/hook-routes.d.ts.map +1 -1
- package/dist/backend/src/api/hook-routes.js +35 -0
- package/dist/backend/src/api/hook-routes.js.map +1 -1
- package/dist/backend/src/config.js +5 -5
- package/dist/backend/src/config.js.map +1 -1
- package/dist/backend/src/daemon/daemon-guard.d.ts +3 -2
- package/dist/backend/src/daemon/daemon-guard.d.ts.map +1 -1
- package/dist/backend/src/daemon/daemon-guard.js +15 -22
- package/dist/backend/src/daemon/daemon-guard.js.map +1 -1
- package/dist/backend/src/index.d.ts.map +1 -1
- package/dist/backend/src/index.js +2 -1
- package/dist/backend/src/index.js.map +1 -1
- package/dist/backend/src/instance/instance-manager.d.ts +4 -0
- package/dist/backend/src/instance/instance-manager.d.ts.map +1 -1
- package/dist/backend/src/instance/instance-manager.js +6 -0
- package/dist/backend/src/instance/instance-manager.js.map +1 -1
- package/dist/backend/src/registry/shared-token.d.ts.map +1 -1
- package/dist/backend/src/registry/shared-token.js +7 -1
- package/dist/backend/src/registry/shared-token.js.map +1 -1
- package/dist/backend/src/utils/network.d.ts +6 -0
- package/dist/backend/src/utils/network.d.ts.map +1 -1
- package/dist/backend/src/utils/network.js +17 -0
- package/dist/backend/src/utils/network.js.map +1 -1
- package/frontend-dist/assets/{index-FfUR0j1j.js → index-Biw9Vigh.js} +24 -24
- package/frontend-dist/assets/{index-DlG_4fGj.css → index-DUvMeUw6.css} +1 -1
- package/frontend-dist/index.html +2 -2
- package/package.json +1 -1
- package/dist/backend/src/registry/instance-registry.d.ts +0 -32
- package/dist/backend/src/registry/instance-registry.d.ts.map +0 -1
- package/dist/backend/src/registry/instance-registry.js +0 -115
- package/dist/backend/src/registry/instance-registry.js.map +0 -1
- package/dist/backend/src/registry/instance-spawner.d.ts +0 -33
- package/dist/backend/src/registry/instance-spawner.d.ts.map +0 -1
- package/dist/backend/src/registry/instance-spawner.js +0 -103
- package/dist/backend/src/registry/instance-spawner.js.map +0 -1
- package/dist/backend/src/registry/port-finder.d.ts +0 -8
- package/dist/backend/src/registry/port-finder.d.ts.map +0 -1
- package/dist/backend/src/registry/port-finder.js +0 -35
- package/dist/backend/src/registry/port-finder.js.map +0 -1
- package/dist/backend/src/session/session-controller.d.ts +0 -73
- package/dist/backend/src/session/session-controller.d.ts.map +0 -1
- package/dist/backend/src/session/session-controller.js +0 -356
- package/dist/backend/src/session/session-controller.js.map +0 -1
|
@@ -29,4 +29,4 @@
|
|
|
29
29
|
* The original design remains. The terminal itself
|
|
30
30
|
* has been extended to include xterm CSI codes, among
|
|
31
31
|
* other features.
|
|
32
|
-
*/.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}.scroll-to-bottom-btn{position:fixed;right:16px;bottom:calc(var(--inputbar-height, 56px) + var(--safe-bottom, env(safe-area-inset-bottom, 0px)) + 16px);width:48px;height:48px;min-width:44px;min-height:44px;border-radius:50%;background:var(--accent-primary, #3b82f6);color:#fff;border:none;box-shadow:0 4px 12px #0000004d;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:1000;font-size:20px;touch-action:manipulation;transition:transform .2s ease,box-shadow .2s ease,opacity .2s ease;pointer-events:auto;-webkit-tap-highlight-color:transparent}.scroll-to-bottom-btn:hover{background:var(--accent-hover, #2563eb);box-shadow:0 6px 16px #0006}.scroll-to-bottom-btn:active{transform:scale(.95)}.scroll-to-bottom-btn:focus-visible{outline:2px solid var(--accent-primary, #3b82f6);outline-offset:2px}.scroll-to-bottom-btn.visible{animation:fadeIn .2s ease-out forwards}.scroll-to-bottom-btn.hidden{animation:fadeOut .2s ease-out forwards}@keyframes fadeIn{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes fadeOut{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.8)}}@media(prefers-reduced-motion:reduce){.scroll-to-bottom-btn{animation:none;transition:none}.scroll-to-bottom-btn.visible{animation:none;opacity:1}.scroll-to-bottom-btn.hidden{animation:none;opacity:0}}:root{color-scheme:dark;--bg-primary: #0d1117;--bg-secondary: #161b22;--bg-tertiary: #21262d;--bg-overlay: rgba(0, 0, 0, .6);--text-primary: #e6edf3;--text-secondary: #8b949e;--text-muted: #484f58;--border-color: #30363d;--status-idle: #3fb950;--status-running: #58a6ff;--status-waiting: #d29922;--status-error: #f85149;--status-disconnected: #8b949e;--approve-bg: #238636;--approve-hover: #2ea043;--reject-bg: #da3633;--reject-hover: #f85149;--safe-top: env(safe-area-inset-top, 0px);--safe-bottom: env(safe-area-inset-bottom, 0px);--safe-left: env(safe-area-inset-left, 0px);--safe-right: env(safe-area-inset-right, 0px);--statusbar-height: 44px;--inputbar-height: 56px;--min-touch-target: 44px;--keybar-height: 48px;--breakpoint-tablet: 768px;--breakpoint-desktop: 1024px;--app-height: 100dvh;--content-inline-padding: 12px;--content-inline-padding-tablet: 16px;--bottom-interactive-inset: var(--safe-bottom);--font-mono: "JetBrains Mono", "Menlo", "Monaco", "Courier New", monospace;--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html,body,#root{height:var(--app-height);width:100%;overflow:hidden;background:var(--bg-primary);color:var(--text-primary);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;overscroll-behavior:none}@supports not (height: 100dvh){:root{--app-height: 100vh}}body{padding-left:var(--safe-left);padding-right:var(--safe-right)}@media(min-width:768px){:root{--content-inline-padding: var(--content-inline-padding-tablet)}}button,input,label,nav{-webkit-user-select:none;user-select:none}input{font-family:inherit;font-size:16px}button{cursor:pointer;border:none;background:none;font-family:inherit;font-size:inherit;color:inherit}.app-toast{position:fixed;left:50%;bottom:calc(16px + var(--safe-bottom));transform:translate(-50%);z-index:2000;padding:8px 12px;border-radius:8px;background:#111827eb;color:#fff;font-size:13px;line-height:1.4;white-space:nowrap}.app-toast-top{top:calc(var(--
|
|
32
|
+
*/.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}.scroll-to-bottom-btn{position:fixed;right:16px;bottom:calc(var(--inputbar-height, 56px) + var(--safe-bottom, env(safe-area-inset-bottom, 0px)) + 16px);width:48px;height:48px;min-width:44px;min-height:44px;border-radius:50%;background:var(--accent-primary, #3b82f6);color:#fff;border:none;box-shadow:0 4px 12px #0000004d;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:1000;font-size:20px;touch-action:manipulation;transition:transform .2s ease,box-shadow .2s ease,opacity .2s ease;pointer-events:auto;-webkit-tap-highlight-color:transparent}.scroll-to-bottom-btn:hover{background:var(--accent-hover, #2563eb);box-shadow:0 6px 16px #0006}.scroll-to-bottom-btn:active{transform:scale(.95)}.scroll-to-bottom-btn:focus-visible{outline:2px solid var(--accent-primary, #3b82f6);outline-offset:2px}.scroll-to-bottom-btn.visible{animation:fadeIn .2s ease-out forwards}.scroll-to-bottom-btn.hidden{animation:fadeOut .2s ease-out forwards}@keyframes fadeIn{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes fadeOut{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.8)}}@media(prefers-reduced-motion:reduce){.scroll-to-bottom-btn{animation:none;transition:none}.scroll-to-bottom-btn.visible{animation:none;opacity:1}.scroll-to-bottom-btn.hidden{animation:none;opacity:0}}:root{color-scheme:dark;--bg-primary: #0d1117;--bg-secondary: #161b22;--bg-tertiary: #21262d;--bg-overlay: rgba(0, 0, 0, .6);--text-primary: #e6edf3;--text-secondary: #8b949e;--text-muted: #484f58;--border-color: #30363d;--status-idle: #3fb950;--status-running: #58a6ff;--status-waiting: #d29922;--status-error: #f85149;--status-disconnected: #8b949e;--approve-bg: #238636;--approve-hover: #2ea043;--reject-bg: #da3633;--reject-hover: #f85149;--safe-top: env(safe-area-inset-top, 0px);--safe-bottom: env(safe-area-inset-bottom, 0px);--safe-left: env(safe-area-inset-left, 0px);--safe-right: env(safe-area-inset-right, 0px);--statusbar-height: 44px;--inputbar-height: 56px;--min-touch-target: 44px;--keybar-height: 48px;--breakpoint-tablet: 768px;--breakpoint-desktop: 1024px;--app-height: 100dvh;--content-inline-padding: 12px;--content-inline-padding-tablet: 16px;--bottom-interactive-inset: var(--safe-bottom);--font-mono: "JetBrains Mono", "Menlo", "Monaco", "Courier New", monospace;--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html,body,#root{height:var(--app-height);width:100%;overflow:hidden;background:var(--bg-primary);color:var(--text-primary);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;overscroll-behavior:none}@supports not (height: 100dvh){:root{--app-height: 100vh}}body{padding-left:var(--safe-left);padding-right:var(--safe-right)}@media(min-width:768px){:root{--content-inline-padding: var(--content-inline-padding-tablet)}}button,input,label,nav{-webkit-user-select:none;user-select:none}input{font-family:inherit;font-size:16px}button{cursor:pointer;border:none;background:none;font-family:inherit;font-size:inherit;color:inherit}.app-toast{position:fixed;left:50%;bottom:calc(16px + var(--safe-bottom));transform:translate(-50%);z-index:2000;padding:8px 12px;border-radius:8px;background:#111827eb;color:#fff;font-size:13px;line-height:1.4;white-space:nowrap}.app-toast-top{top:calc(var(--safe-top) + 8px);bottom:auto}.focus-ring:focus-visible{outline:none;box-shadow:0 0 0 2px var(--bg-primary),0 0 0 4px #58a6ff}.perm-btn{touch-action:manipulation;-webkit-tap-highlight-color:transparent;transition:opacity .1s,background .1s}.perm-btn:hover{opacity:.85}.perm-btn:active{opacity:.7}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}input:focus-visible,textarea:focus-visible{outline:none;box-shadow:0 0 0 2px var(--bg-primary),0 0 0 4px #58a6ff}button{touch-action:manipulation;-webkit-tap-highlight-color:transparent}.xterm-helper-textarea{pointer-events:none;-webkit-user-select:none;user-select:none}.xterm{height:100%;width:100%}@keyframes fadeInOut{0%{opacity:0;transform:translate(-50%) translateY(10px)}10%{opacity:1;transform:translate(-50%) translateY(0)}90%{opacity:1;transform:translate(-50%) translateY(0)}to{opacity:0;transform:translate(-50%) translateY(-10px)}}
|
package/frontend-dist/index.html
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
7
7
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
8
8
|
<title>Claude Code Remote</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-Biw9Vigh.js"></script>
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DUvMeUw6.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { InstanceInfo } from '../../../shared/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* 实例注册表管理器。
|
|
4
|
-
* 操作 ~/.claude-remote/instances.json 实现多实例发现。
|
|
5
|
-
* 所有公共方法通过 mkdir-based 文件锁保护 read-modify-write 操作。
|
|
6
|
-
*/
|
|
7
|
-
export declare class InstanceRegistryManager {
|
|
8
|
-
private readonly registryPath;
|
|
9
|
-
private readonly lockPath;
|
|
10
|
-
private readonly baseDir;
|
|
11
|
-
constructor(baseDir: string);
|
|
12
|
-
/**
|
|
13
|
-
* 注册实例信息(如已存在同 ID 则替换)。
|
|
14
|
-
*/
|
|
15
|
-
register(info: InstanceInfo): void;
|
|
16
|
-
/**
|
|
17
|
-
* 注销实例。
|
|
18
|
-
*/
|
|
19
|
-
unregister(instanceId: string): void;
|
|
20
|
-
/**
|
|
21
|
-
* 列出所有存活实例,自动清理僵尸进程。
|
|
22
|
-
* 使用异步文件锁,避免在 API 请求路径上阻塞事件循环。
|
|
23
|
-
*/
|
|
24
|
-
list(): Promise<InstanceInfo[]>;
|
|
25
|
-
/**
|
|
26
|
-
* 更新实例的 host 字段(用于 IP 变化时更新)。
|
|
27
|
-
*/
|
|
28
|
-
updateHost(instanceId: string, newHost: string): void;
|
|
29
|
-
private readRegistry;
|
|
30
|
-
private writeRegistry;
|
|
31
|
-
}
|
|
32
|
-
//# sourceMappingURL=instance-registry.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"instance-registry.d.ts","sourceRoot":"","sources":["../../../../backend/src/registry/instance-registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,SAAS,CAAC;AAgB9D;;;;GAIG;AACH,qBAAa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,EAAE,MAAM;IAM3B;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAUlC;;OAEG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAYpC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAgBrC;;OAEG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAYrD,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,aAAa;CAStB"}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, renameSync, mkdirSync, existsSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { REGISTRY_FILENAME } from '../../../shared/index.js';
|
|
4
|
-
import { logger } from '../logger/logger.js';
|
|
5
|
-
import { withFileLock, withFileLockAsync } from '../utils/file-lock.js';
|
|
6
|
-
/**
|
|
7
|
-
* 检查进程是否存活。
|
|
8
|
-
*/
|
|
9
|
-
function isProcessAlive(pid) {
|
|
10
|
-
try {
|
|
11
|
-
process.kill(pid, 0);
|
|
12
|
-
return true;
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* 实例注册表管理器。
|
|
20
|
-
* 操作 ~/.claude-remote/instances.json 实现多实例发现。
|
|
21
|
-
* 所有公共方法通过 mkdir-based 文件锁保护 read-modify-write 操作。
|
|
22
|
-
*/
|
|
23
|
-
export class InstanceRegistryManager {
|
|
24
|
-
registryPath;
|
|
25
|
-
lockPath;
|
|
26
|
-
baseDir;
|
|
27
|
-
constructor(baseDir) {
|
|
28
|
-
this.baseDir = baseDir;
|
|
29
|
-
this.registryPath = join(baseDir, REGISTRY_FILENAME);
|
|
30
|
-
this.lockPath = this.registryPath + '.lock';
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* 注册实例信息(如已存在同 ID 则替换)。
|
|
34
|
-
*/
|
|
35
|
-
register(info) {
|
|
36
|
-
withFileLock(this.lockPath, () => {
|
|
37
|
-
const data = this.readRegistry();
|
|
38
|
-
data.instances = data.instances.filter(i => i.instanceId !== info.instanceId);
|
|
39
|
-
data.instances.push(info);
|
|
40
|
-
this.writeRegistry(data);
|
|
41
|
-
logger.info({ instanceId: info.instanceId, name: info.name, port: info.port }, 'Instance registered');
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* 注销实例。
|
|
46
|
-
*/
|
|
47
|
-
unregister(instanceId) {
|
|
48
|
-
withFileLock(this.lockPath, () => {
|
|
49
|
-
const data = this.readRegistry();
|
|
50
|
-
const before = data.instances.length;
|
|
51
|
-
data.instances = data.instances.filter(i => i.instanceId !== instanceId);
|
|
52
|
-
if (data.instances.length < before) {
|
|
53
|
-
this.writeRegistry(data);
|
|
54
|
-
logger.info({ instanceId }, 'Instance unregistered');
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* 列出所有存活实例,自动清理僵尸进程。
|
|
60
|
-
* 使用异步文件锁,避免在 API 请求路径上阻塞事件循环。
|
|
61
|
-
*/
|
|
62
|
-
async list() {
|
|
63
|
-
return withFileLockAsync(this.lockPath, async () => {
|
|
64
|
-
const data = this.readRegistry();
|
|
65
|
-
const alive = data.instances.filter(i => isProcessAlive(i.pid));
|
|
66
|
-
if (alive.length < data.instances.length) {
|
|
67
|
-
const removed = data.instances.length - alive.length;
|
|
68
|
-
logger.info({ removed }, 'Cleaned up zombie instances');
|
|
69
|
-
data.instances = alive;
|
|
70
|
-
this.writeRegistry(data);
|
|
71
|
-
}
|
|
72
|
-
return alive;
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* 更新实例的 host 字段(用于 IP 变化时更新)。
|
|
77
|
-
*/
|
|
78
|
-
updateHost(instanceId, newHost) {
|
|
79
|
-
withFileLock(this.lockPath, () => {
|
|
80
|
-
const data = this.readRegistry();
|
|
81
|
-
const instance = data.instances.find(i => i.instanceId === instanceId);
|
|
82
|
-
if (instance && instance.host !== newHost) {
|
|
83
|
-
instance.host = newHost;
|
|
84
|
-
this.writeRegistry(data);
|
|
85
|
-
logger.info({ instanceId, newHost }, 'Instance host updated');
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
readRegistry() {
|
|
90
|
-
if (!existsSync(this.registryPath)) {
|
|
91
|
-
return { version: 1, instances: [] };
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
const content = readFileSync(this.registryPath, 'utf-8');
|
|
95
|
-
const data = JSON.parse(content);
|
|
96
|
-
if (!data.instances || !Array.isArray(data.instances)) {
|
|
97
|
-
return { version: 1, instances: [] };
|
|
98
|
-
}
|
|
99
|
-
return data;
|
|
100
|
-
}
|
|
101
|
-
catch (err) {
|
|
102
|
-
logger.warn({ err, path: this.registryPath }, 'Failed to read registry, starting fresh');
|
|
103
|
-
return { version: 1, instances: [] };
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
writeRegistry(data) {
|
|
107
|
-
if (!existsSync(this.baseDir)) {
|
|
108
|
-
mkdirSync(this.baseDir, { recursive: true, mode: 0o700 });
|
|
109
|
-
}
|
|
110
|
-
const tmpPath = `${this.registryPath}.tmp.${process.pid}`;
|
|
111
|
-
writeFileSync(tmpPath, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
112
|
-
renameSync(tmpPath, this.registryPath);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
//# sourceMappingURL=instance-registry.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"instance-registry.js","sourceRoot":"","sources":["../../../../backend/src/registry/instance-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
export interface SpawnOptions {
|
|
2
|
-
/** 工作目录 */
|
|
3
|
-
cwd: string;
|
|
4
|
-
/** 实例名称 */
|
|
5
|
-
name?: string;
|
|
6
|
-
/** Claude 额外参数 */
|
|
7
|
-
claudeArgs?: string[];
|
|
8
|
-
/** 是否为 headless 模式(无终端) */
|
|
9
|
-
headless?: boolean;
|
|
10
|
-
/** 指定端口(可选,默认自动分配) */
|
|
11
|
-
port?: number;
|
|
12
|
-
}
|
|
13
|
-
export interface SpawnResult {
|
|
14
|
-
/** 进程 PID */
|
|
15
|
-
pid: number;
|
|
16
|
-
/** 工作目录 */
|
|
17
|
-
cwd: string;
|
|
18
|
-
/** 实例名称 */
|
|
19
|
-
name: string;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* 实例创建服务。
|
|
23
|
-
* 通过 spawn 子进程启动新的 claude-remote 实例。
|
|
24
|
-
*/
|
|
25
|
-
export declare class InstanceSpawner {
|
|
26
|
-
private readonly entryScript;
|
|
27
|
-
constructor();
|
|
28
|
-
/**
|
|
29
|
-
* 创建新实例。
|
|
30
|
-
*/
|
|
31
|
-
spawn(options: SpawnOptions): Promise<SpawnResult>;
|
|
32
|
-
}
|
|
33
|
-
//# sourceMappingURL=instance-spawner.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"instance-spawner.d.ts","sourceRoot":"","sources":["../../../../backend/src/registry/instance-spawner.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,YAAY;IAC3B,WAAW;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;;IAyBrC;;OAEG;IACG,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC;CAkFzD"}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
3
|
-
import { resolve, dirname } from 'node:path';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { logger } from '../logger/logger.js';
|
|
6
|
-
/**
|
|
7
|
-
* 实例创建服务。
|
|
8
|
-
* 通过 spawn 子进程启动新的 claude-remote 实例。
|
|
9
|
-
*/
|
|
10
|
-
export class InstanceSpawner {
|
|
11
|
-
entryScript;
|
|
12
|
-
constructor() {
|
|
13
|
-
// 获取当前模块所在目录
|
|
14
|
-
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
15
|
-
// TypeScript 编译输出结构:
|
|
16
|
-
// 源码:backend/src/registry/instance-spawner.ts
|
|
17
|
-
// 编译:dist/backend/src/registry/instance-spawner.js
|
|
18
|
-
//
|
|
19
|
-
// 入口脚本路径计算:
|
|
20
|
-
// 生产模式:import.meta.url 指向 dist/backend/src/registry/,向上找 cli.js
|
|
21
|
-
// 开发模式:import.meta.url 指向 backend/src/registry/,需要找编译后的 dist/backend/src/cli.js
|
|
22
|
-
const prodEntryScript = resolve(currentDir, '../cli.js');
|
|
23
|
-
const devEntryScript = resolve(currentDir, '../../../dist/backend/src/cli.js');
|
|
24
|
-
if (existsSync(prodEntryScript)) {
|
|
25
|
-
this.entryScript = prodEntryScript;
|
|
26
|
-
}
|
|
27
|
-
else if (existsSync(devEntryScript)) {
|
|
28
|
-
this.entryScript = devEntryScript;
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
throw new Error(`Entry script not found. Tried: ${prodEntryScript}, ${devEntryScript}`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* 创建新实例。
|
|
36
|
-
*/
|
|
37
|
-
async spawn(options) {
|
|
38
|
-
const { cwd, name, claudeArgs = [], headless = true, port, } = options;
|
|
39
|
-
logger.info({
|
|
40
|
-
cwd,
|
|
41
|
-
name,
|
|
42
|
-
headless,
|
|
43
|
-
port,
|
|
44
|
-
}, 'InstanceSpawner: spawning new instance');
|
|
45
|
-
// 构建子进程参数
|
|
46
|
-
const args = [];
|
|
47
|
-
// 禁用 CLI 模式输出,改用环境变量控制
|
|
48
|
-
// headless 模式下传递 --no-terminal 标志
|
|
49
|
-
if (headless) {
|
|
50
|
-
args.push('--no-terminal');
|
|
51
|
-
}
|
|
52
|
-
if (name) {
|
|
53
|
-
args.push('--name', name);
|
|
54
|
-
}
|
|
55
|
-
if (port) {
|
|
56
|
-
args.push('--port', String(port));
|
|
57
|
-
}
|
|
58
|
-
// 添加 Claude 参数分隔符
|
|
59
|
-
if (claudeArgs.length > 0) {
|
|
60
|
-
args.push('--', ...claudeArgs);
|
|
61
|
-
}
|
|
62
|
-
return new Promise((resolve, reject) => {
|
|
63
|
-
const child = spawn(process.execPath, [this.entryScript, ...args], {
|
|
64
|
-
cwd,
|
|
65
|
-
detached: true, // 与父进程解绑,允许父进程退出后继续运行
|
|
66
|
-
stdio: 'ignore', // headless 模式忽略 stdio
|
|
67
|
-
// 继承父进程的环境变量,并添加自定义变量
|
|
68
|
-
env: {
|
|
69
|
-
...process.env,
|
|
70
|
-
// 设置 headless 标志供 index.ts 使用
|
|
71
|
-
NO_TERMINAL: headless ? 'true' : 'false',
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
const childPid = child.pid;
|
|
75
|
-
child.on('error', (err) => {
|
|
76
|
-
logger.error({ err, cwd, name }, 'InstanceSpawner: failed to spawn');
|
|
77
|
-
reject(err);
|
|
78
|
-
});
|
|
79
|
-
// 子进程启动后立即 unref,让父进程可以独立退出
|
|
80
|
-
child.unref();
|
|
81
|
-
// 给子进程一点启动时间,然后检查是否存活
|
|
82
|
-
setTimeout(() => {
|
|
83
|
-
try {
|
|
84
|
-
process.kill(childPid, 0); // 检查进程是否存在
|
|
85
|
-
logger.info({
|
|
86
|
-
pid: childPid,
|
|
87
|
-
cwd,
|
|
88
|
-
name,
|
|
89
|
-
}, 'InstanceSpawner: instance spawned successfully');
|
|
90
|
-
resolve({
|
|
91
|
-
pid: childPid,
|
|
92
|
-
cwd,
|
|
93
|
-
name: name ?? cwd.split('/').pop() ?? 'unknown',
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
reject(new Error('Spawned process exited immediately'));
|
|
98
|
-
}
|
|
99
|
-
}, 500);
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
//# sourceMappingURL=instance-spawner.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"instance-spawner.js","sourceRoot":"","sources":["../../../../backend/src/registry/instance-spawner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAwB7C;;;GAGG;AACH,MAAM,OAAO,eAAe;IACT,WAAW,CAAS;IAErC;QACE,aAAa;QACb,MAAM,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAE3D,qBAAqB;QACrB,8CAA8C;QAC9C,mDAAmD;QACnD,EAAE;QACF,YAAY;QACZ,gEAAgE;QAChE,gFAAgF;QAChF,MAAM,eAAe,GAAG,OAAO,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QACzD,MAAM,cAAc,GAAG,OAAO,CAAC,UAAU,EAAE,kCAAkC,CAAC,CAAC;QAE/E,IAAI,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;YAChC,IAAI,CAAC,WAAW,GAAG,eAAe,CAAC;QACrC,CAAC;aAAM,IAAI,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,WAAW,GAAG,cAAc,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,kCAAkC,eAAe,KAAK,cAAc,EAAE,CAAC,CAAC;QAC1F,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK,CAAC,OAAqB;QAC/B,MAAM,EACJ,GAAG,EACH,IAAI,EACJ,UAAU,GAAG,EAAE,EACf,QAAQ,GAAG,IAAI,EACf,IAAI,GACL,GAAG,OAAO,CAAC;QAEZ,MAAM,CAAC,IAAI,CAAC;YACV,GAAG;YACH,IAAI;YACJ,QAAQ;YACR,IAAI;SACL,EAAE,wCAAwC,CAAC,CAAC;QAE7C,UAAU;QACV,MAAM,IAAI,GAAa,EAAE,CAAC;QAE1B,uBAAuB;QACvB,kCAAkC;QAClC,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC7B,CAAC;QAED,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC;QAED,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,CAAC;QAED,kBAAkB;QAClB,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,UAAU,CAAC,CAAC;QACjC,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,EAAE;gBACjE,GAAG;gBACH,QAAQ,EAAE,IAAI,EAAG,sBAAsB;gBACvC,KAAK,EAAE,QAAQ,EAAE,sBAAsB;gBACvC,sBAAsB;gBACtB,GAAG,EAAE;oBACH,GAAG,OAAO,CAAC,GAAG;oBACd,8BAA8B;oBAC9B,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;iBACzC;aACF,CAAC,CAAC;YAEH,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC;YAE3B,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACxB,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,kCAAkC,CAAC,CAAC;gBACrE,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC,CAAC,CAAC;YAEH,4BAA4B;YAC5B,KAAK,CAAC,KAAK,EAAE,CAAC;YAEd,sBAAsB;YACtB,UAAU,CAAC,GAAG,EAAE;gBACd,IAAI,CAAC;oBACH,OAAO,CAAC,IAAI,CAAC,QAAS,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW;oBACvC,MAAM,CAAC,IAAI,CAAC;wBACV,GAAG,EAAE,QAAQ;wBACb,GAAG;wBACH,IAAI;qBACL,EAAE,gDAAgD,CAAC,CAAC;oBAErD,OAAO,CAAC;wBACN,GAAG,EAAE,QAAS;wBACd,GAAG;wBACH,IAAI,EAAE,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,SAAS;qBAChD,CAAC,CAAC;gBACL,CAAC;gBAAC,MAAM,CAAC;oBACP,MAAM,CAAC,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC,CAAC;gBAC1D,CAAC;YACH,CAAC,EAAE,GAAG,CAAC,CAAC;QACV,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 从 preferredPort 开始递增查找可用端口。
|
|
3
|
-
* @param preferredPort 首选端口
|
|
4
|
-
* @param host 绑定地址
|
|
5
|
-
* @param maxAttempts 最大尝试次数(默认 100)
|
|
6
|
-
*/
|
|
7
|
-
export declare function findAvailablePort(preferredPort: number, host: string, maxAttempts?: number): Promise<number>;
|
|
8
|
-
//# sourceMappingURL=port-finder.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"port-finder.d.ts","sourceRoot":"","sources":["../../../../backend/src/registry/port-finder.ts"],"names":[],"mappings":"AAgBA;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,EACZ,WAAW,GAAE,MAAY,GACxB,OAAO,CAAC,MAAM,CAAC,CAcjB"}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { createServer } from 'node:net';
|
|
2
|
-
import { logger } from '../logger/logger.js';
|
|
3
|
-
/**
|
|
4
|
-
* 检测指定端口是否可用。
|
|
5
|
-
*/
|
|
6
|
-
function isPortAvailable(port, host) {
|
|
7
|
-
return new Promise((resolve) => {
|
|
8
|
-
const server = createServer();
|
|
9
|
-
server.once('error', () => resolve(false));
|
|
10
|
-
server.listen(port, host, () => {
|
|
11
|
-
server.close(() => resolve(true));
|
|
12
|
-
});
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* 从 preferredPort 开始递增查找可用端口。
|
|
17
|
-
* @param preferredPort 首选端口
|
|
18
|
-
* @param host 绑定地址
|
|
19
|
-
* @param maxAttempts 最大尝试次数(默认 100)
|
|
20
|
-
*/
|
|
21
|
-
export async function findAvailablePort(preferredPort, host, maxAttempts = 100) {
|
|
22
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
23
|
-
const port = preferredPort + i;
|
|
24
|
-
if (port > 65535)
|
|
25
|
-
break;
|
|
26
|
-
if (await isPortAvailable(port, host)) {
|
|
27
|
-
if (i > 0) {
|
|
28
|
-
logger.info({ preferredPort, assignedPort: port }, 'Preferred port occupied, using alternative');
|
|
29
|
-
}
|
|
30
|
-
return port;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
throw new Error(`No available port found starting from ${preferredPort} after ${maxAttempts} attempts`);
|
|
34
|
-
}
|
|
35
|
-
//# sourceMappingURL=port-finder.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"port-finder.js","sourceRoot":"","sources":["../../../../backend/src/registry/port-finder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C;;GAEG;AACH,SAAS,eAAe,CAAC,IAAY,EAAE,IAAY;IACjD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;YAC7B,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,aAAqB,EACrB,IAAY,EACZ,cAAsB,GAAG;IAEzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,aAAa,GAAG,CAAC,CAAC;QAC/B,IAAI,IAAI,GAAG,KAAK;YAAE,MAAM;QAExB,IAAI,MAAM,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACV,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,4CAA4C,CAAC,CAAC;YACnG,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,yCAAyC,aAAa,UAAU,WAAW,WAAW,CAAC,CAAC;AAC1G,CAAC"}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import type { SessionStatus } from '../../../shared/index.js';
|
|
2
|
-
import { PtyManager } from '../pty/pty-manager.js';
|
|
3
|
-
import { WsServer } from '../ws/ws-server.js';
|
|
4
|
-
import { HookReceiver } from '../hooks/hook-receiver.js';
|
|
5
|
-
import type { PushService } from '../push/push-service.js';
|
|
6
|
-
import type { TerminalRelay } from '../terminal/terminal-relay.js';
|
|
7
|
-
import type { NotificationManager } from '../notification/notification-manager.js';
|
|
8
|
-
import type { NotificationServiceFactory } from '../notification/notification-service-factory.js';
|
|
9
|
-
/**
|
|
10
|
-
* Core coordinator: wires PTY ↔ WebSocket ↔ Terminal ↔ Hooks.
|
|
11
|
-
*/
|
|
12
|
-
export declare class SessionController {
|
|
13
|
-
private ptyManager;
|
|
14
|
-
private wsServer;
|
|
15
|
-
private hookReceiver;
|
|
16
|
-
private terminalRelay?;
|
|
17
|
-
private _status;
|
|
18
|
-
private buffer;
|
|
19
|
-
private pushService;
|
|
20
|
-
private notificationServiceFactory;
|
|
21
|
-
private notificationManager;
|
|
22
|
-
private instanceUrl;
|
|
23
|
-
private wsPendingChunks;
|
|
24
|
-
private wsPendingBytes;
|
|
25
|
-
private wsFlushTimer;
|
|
26
|
-
private ptyInputBytesTotal;
|
|
27
|
-
private wsFlushCount;
|
|
28
|
-
private wsFlushBytesTotal;
|
|
29
|
-
private wsMaxPendingBytes;
|
|
30
|
-
private wsBackpressureEvents;
|
|
31
|
-
constructor(ptyManager: PtyManager, wsServer: WsServer, hookReceiver: HookReceiver, maxBufferLines: number, terminalRelay?: TerminalRelay | undefined);
|
|
32
|
-
get status(): SessionStatus;
|
|
33
|
-
get connectedClients(): number;
|
|
34
|
-
/**
|
|
35
|
-
* Inject PushService for hook-triggered notifications.
|
|
36
|
-
*/
|
|
37
|
-
setPushService(pushService: PushService): void;
|
|
38
|
-
/**
|
|
39
|
-
* Inject NotificationServiceFactory for lazily creating notification services.
|
|
40
|
-
*/
|
|
41
|
-
setNotificationServiceFactory(factory: NotificationServiceFactory): void;
|
|
42
|
-
/**
|
|
43
|
-
* Inject NotificationManager for dynamic enabled status checking.
|
|
44
|
-
*/
|
|
45
|
-
setNotificationManager(manager: NotificationManager): void;
|
|
46
|
-
/**
|
|
47
|
-
* Set instance URL for inclusion in notifications.
|
|
48
|
-
*/
|
|
49
|
-
setInstanceUrl(url: string): void;
|
|
50
|
-
/**
|
|
51
|
-
* Wire PTY output → buffer + WS broadcast + process.stdout
|
|
52
|
-
*/
|
|
53
|
-
private setupPtyHandlers;
|
|
54
|
-
private enqueueWsOutput;
|
|
55
|
-
private flushPendingWsOutput;
|
|
56
|
-
/**
|
|
57
|
-
* Wire WS messages → PTY input
|
|
58
|
-
*/
|
|
59
|
-
private setupWsHandlers;
|
|
60
|
-
/**
|
|
61
|
-
* Wire Hook notifications → status update + external notifications
|
|
62
|
-
*/
|
|
63
|
-
private setupHookHandlers;
|
|
64
|
-
/**
|
|
65
|
-
* 根据渠道类型发送通知
|
|
66
|
-
*/
|
|
67
|
-
private sendNotificationByChannel;
|
|
68
|
-
/**
|
|
69
|
-
* Update session status.
|
|
70
|
-
*/
|
|
71
|
-
setStatus(status: SessionStatus): void;
|
|
72
|
-
}
|
|
73
|
-
//# sourceMappingURL=session-controller.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"session-controller.d.ts","sourceRoot":"","sources":["../../../../backend/src/session/session-controller.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,EAAE,QAAQ,EAAmB,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAQzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AACnF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,iDAAiD,CAAC;AAMlG;;GAEG;AACH,qBAAa,iBAAiB;IAmB1B,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,YAAY;IAEpB,OAAO,CAAC,aAAa,CAAC;IAtBxB,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,0BAA0B,CAA2C;IAC7E,OAAO,CAAC,mBAAmB,CAAoC;IAC/D,OAAO,CAAC,WAAW,CAAuB;IAE1C,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,YAAY,CAA+B;IAEnD,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,oBAAoB,CAAK;gBAGvB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,YAAY,EAClC,cAAc,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,aAAa,YAAA;IAQvC,IAAI,MAAM,IAAI,aAAa,CAE1B;IAED,IAAI,gBAAgB,IAAI,MAAM,CAE7B;IAED;;OAEG;IACH,cAAc,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAI9C;;OAEG;IACH,6BAA6B,CAAC,OAAO,EAAE,0BAA0B,GAAG,IAAI;IAIxE;;OAEG;IACH,sBAAsB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI;IAI1D;;OAEG;IACH,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAKjC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAqDxB,OAAO,CAAC,eAAe;IAwBvB,OAAO,CAAC,oBAAoB;IAiC5B;;OAEG;IACH,OAAO,CAAC,eAAe;IAkFvB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAmCzB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IA0EjC;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;CAIvC"}
|