@caoruhua/open-claude-remote 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +449 -0
  3. package/dist/api/auth-routes.d.ts +4 -0
  4. package/dist/api/auth-routes.d.ts.map +1 -0
  5. package/dist/api/auth-routes.js +7 -0
  6. package/dist/api/auth-routes.js.map +1 -0
  7. package/dist/api/config-routes.d.ts +4 -0
  8. package/dist/api/config-routes.d.ts.map +1 -0
  9. package/dist/api/config-routes.js +180 -0
  10. package/dist/api/config-routes.js.map +1 -0
  11. package/dist/api/health-routes.d.ts +3 -0
  12. package/dist/api/health-routes.d.ts.map +1 -0
  13. package/dist/api/health-routes.js +9 -0
  14. package/dist/api/health-routes.js.map +1 -0
  15. package/dist/api/hook-routes.d.ts +4 -0
  16. package/dist/api/hook-routes.d.ts.map +1 -0
  17. package/dist/api/hook-routes.js +32 -0
  18. package/dist/api/hook-routes.js.map +1 -0
  19. package/dist/api/instance-routes.d.ts +20 -0
  20. package/dist/api/instance-routes.d.ts.map +1 -0
  21. package/dist/api/instance-routes.js +128 -0
  22. package/dist/api/instance-routes.js.map +1 -0
  23. package/dist/api/push-routes.d.ts +5 -0
  24. package/dist/api/push-routes.d.ts.map +1 -0
  25. package/dist/api/push-routes.js +45 -0
  26. package/dist/api/push-routes.js.map +1 -0
  27. package/dist/api/router.d.ts +19 -0
  28. package/dist/api/router.d.ts.map +1 -0
  29. package/dist/api/router.js +37 -0
  30. package/dist/api/router.js.map +1 -0
  31. package/dist/api/status-routes.d.ts +5 -0
  32. package/dist/api/status-routes.d.ts.map +1 -0
  33. package/dist/api/status-routes.js +17 -0
  34. package/dist/api/status-routes.js.map +1 -0
  35. package/dist/attach.d.ts +9 -0
  36. package/dist/attach.d.ts.map +1 -0
  37. package/dist/attach.js +155 -0
  38. package/dist/attach.js.map +1 -0
  39. package/dist/auth/auth-middleware.d.ts +52 -0
  40. package/dist/auth/auth-middleware.d.ts.map +1 -0
  41. package/dist/auth/auth-middleware.js +124 -0
  42. package/dist/auth/auth-middleware.js.map +1 -0
  43. package/dist/auth/rate-limiter.d.ts +37 -0
  44. package/dist/auth/rate-limiter.d.ts.map +1 -0
  45. package/dist/auth/rate-limiter.js +81 -0
  46. package/dist/auth/rate-limiter.js.map +1 -0
  47. package/dist/auth/token-generator.d.ts +9 -0
  48. package/dist/auth/token-generator.d.ts.map +1 -0
  49. package/dist/auth/token-generator.js +15 -0
  50. package/dist/auth/token-generator.js.map +1 -0
  51. package/dist/cli-utils.d.ts +19 -0
  52. package/dist/cli-utils.d.ts.map +1 -0
  53. package/dist/cli-utils.js +132 -0
  54. package/dist/cli-utils.js.map +1 -0
  55. package/dist/cli.d.ts +21 -0
  56. package/dist/cli.d.ts.map +1 -0
  57. package/dist/cli.js +58 -0
  58. package/dist/cli.js.map +1 -0
  59. package/dist/config.d.ts +146 -0
  60. package/dist/config.d.ts.map +1 -0
  61. package/dist/config.js +329 -0
  62. package/dist/config.js.map +1 -0
  63. package/dist/hooks/hook-receiver.d.ts +39 -0
  64. package/dist/hooks/hook-receiver.d.ts.map +1 -0
  65. package/dist/hooks/hook-receiver.js +46 -0
  66. package/dist/hooks/hook-receiver.js.map +1 -0
  67. package/dist/index.d.ts +3 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +353 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/logger/logger.d.ts +8 -0
  72. package/dist/logger/logger.d.ts.map +1 -0
  73. package/dist/logger/logger.js +90 -0
  74. package/dist/logger/logger.js.map +1 -0
  75. package/dist/notification/dingtalk-service.d.ts +24 -0
  76. package/dist/notification/dingtalk-service.d.ts.map +1 -0
  77. package/dist/notification/dingtalk-service.js +94 -0
  78. package/dist/notification/dingtalk-service.js.map +1 -0
  79. package/dist/pty/output-buffer.d.ts +25 -0
  80. package/dist/pty/output-buffer.d.ts.map +1 -0
  81. package/dist/pty/output-buffer.js +58 -0
  82. package/dist/pty/output-buffer.js.map +1 -0
  83. package/dist/pty/pty-manager.d.ts +45 -0
  84. package/dist/pty/pty-manager.d.ts.map +1 -0
  85. package/dist/pty/pty-manager.js +108 -0
  86. package/dist/pty/pty-manager.js.map +1 -0
  87. package/dist/pty/types.d.ts +11 -0
  88. package/dist/pty/types.d.ts.map +1 -0
  89. package/dist/pty/types.js +2 -0
  90. package/dist/pty/types.js.map +1 -0
  91. package/dist/pty/virtual-pty.d.ts +37 -0
  92. package/dist/pty/virtual-pty.d.ts.map +1 -0
  93. package/dist/pty/virtual-pty.js +161 -0
  94. package/dist/pty/virtual-pty.js.map +1 -0
  95. package/dist/push/push-service.d.ts +87 -0
  96. package/dist/push/push-service.d.ts.map +1 -0
  97. package/dist/push/push-service.js +301 -0
  98. package/dist/push/push-service.js.map +1 -0
  99. package/dist/registry/instance-registry.d.ts +32 -0
  100. package/dist/registry/instance-registry.d.ts.map +1 -0
  101. package/dist/registry/instance-registry.js +115 -0
  102. package/dist/registry/instance-registry.js.map +1 -0
  103. package/dist/registry/instance-spawner.d.ts +33 -0
  104. package/dist/registry/instance-spawner.d.ts.map +1 -0
  105. package/dist/registry/instance-spawner.js +91 -0
  106. package/dist/registry/instance-spawner.js.map +1 -0
  107. package/dist/registry/port-finder.d.ts +8 -0
  108. package/dist/registry/port-finder.d.ts.map +1 -0
  109. package/dist/registry/port-finder.js +35 -0
  110. package/dist/registry/port-finder.js.map +1 -0
  111. package/dist/registry/shared-token.d.ts +11 -0
  112. package/dist/registry/shared-token.d.ts.map +1 -0
  113. package/dist/registry/shared-token.js +82 -0
  114. package/dist/registry/shared-token.js.map +1 -0
  115. package/dist/registry/stop-instances.d.ts +27 -0
  116. package/dist/registry/stop-instances.d.ts.map +1 -0
  117. package/dist/registry/stop-instances.js +212 -0
  118. package/dist/registry/stop-instances.js.map +1 -0
  119. package/dist/session/session-controller.d.ts +58 -0
  120. package/dist/session/session-controller.d.ts.map +1 -0
  121. package/dist/session/session-controller.js +273 -0
  122. package/dist/session/session-controller.js.map +1 -0
  123. package/dist/terminal/terminal-relay.d.ts +29 -0
  124. package/dist/terminal/terminal-relay.d.ts.map +1 -0
  125. package/dist/terminal/terminal-relay.js +106 -0
  126. package/dist/terminal/terminal-relay.js.map +1 -0
  127. package/dist/utils/ansi-filter.d.ts +41 -0
  128. package/dist/utils/ansi-filter.d.ts.map +1 -0
  129. package/dist/utils/ansi-filter.js +147 -0
  130. package/dist/utils/ansi-filter.js.map +1 -0
  131. package/dist/utils/file-lock.d.ts +23 -0
  132. package/dist/utils/file-lock.d.ts.map +1 -0
  133. package/dist/utils/file-lock.js +125 -0
  134. package/dist/utils/file-lock.js.map +1 -0
  135. package/dist/utils/ip-monitor.d.ts +39 -0
  136. package/dist/utils/ip-monitor.d.ts.map +1 -0
  137. package/dist/utils/ip-monitor.js +114 -0
  138. package/dist/utils/ip-monitor.js.map +1 -0
  139. package/dist/utils/network.d.ts +19 -0
  140. package/dist/utils/network.d.ts.map +1 -0
  141. package/dist/utils/network.js +54 -0
  142. package/dist/utils/network.js.map +1 -0
  143. package/dist/utils/pid-file.d.ts +10 -0
  144. package/dist/utils/pid-file.d.ts.map +1 -0
  145. package/dist/utils/pid-file.js +30 -0
  146. package/dist/utils/pid-file.js.map +1 -0
  147. package/dist/utils/qrcode-banner.d.ts +6 -0
  148. package/dist/utils/qrcode-banner.d.ts.map +1 -0
  149. package/dist/utils/qrcode-banner.js +16 -0
  150. package/dist/utils/qrcode-banner.js.map +1 -0
  151. package/dist/ws/ws-handler.d.ts +10 -0
  152. package/dist/ws/ws-handler.d.ts.map +1 -0
  153. package/dist/ws/ws-handler.js +36 -0
  154. package/dist/ws/ws-handler.js.map +1 -0
  155. package/dist/ws/ws-server.d.ts +78 -0
  156. package/dist/ws/ws-server.d.ts.map +1 -0
  157. package/dist/ws/ws-server.js +223 -0
  158. package/dist/ws/ws-server.js.map +1 -0
  159. package/frontend-dist/assets/index-BKudo1Dw.css +32 -0
  160. package/frontend-dist/assets/index-BqqB1hYe.js +141 -0
  161. package/frontend-dist/index.html +15 -0
  162. package/frontend-dist/sw.js +46 -0
  163. package/package.json +79 -0
  164. package/shared-dist/constants.d.ts +10 -0
  165. package/shared-dist/constants.d.ts.map +1 -0
  166. package/shared-dist/constants.js +10 -0
  167. package/shared-dist/constants.js.map +1 -0
  168. package/shared-dist/defaults.d.ts +30 -0
  169. package/shared-dist/defaults.d.ts.map +1 -0
  170. package/shared-dist/defaults.js +31 -0
  171. package/shared-dist/defaults.js.map +1 -0
  172. package/shared-dist/index.d.ts +5 -0
  173. package/shared-dist/index.d.ts.map +1 -0
  174. package/shared-dist/index.js +5 -0
  175. package/shared-dist/index.js.map +1 -0
  176. package/shared-dist/instance.d.ts +25 -0
  177. package/shared-dist/instance.d.ts.map +1 -0
  178. package/shared-dist/instance.js +7 -0
  179. package/shared-dist/instance.js.map +1 -0
  180. package/shared-dist/question-utils.d.ts +15 -0
  181. package/shared-dist/question-utils.d.ts.map +1 -0
  182. package/shared-dist/question-utils.js +24 -0
  183. package/shared-dist/question-utils.js.map +1 -0
  184. package/shared-dist/ws-protocol.d.ts +60 -0
  185. package/shared-dist/ws-protocol.d.ts.map +1 -0
  186. package/shared-dist/ws-protocol.js +5 -0
  187. package/shared-dist/ws-protocol.js.map +1 -0
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Ring buffer for PTY output. Stores raw ANSI strings, enforces max line count.
3
+ * Provides monotonically increasing sequence numbers for reconnection sync.
4
+ */
5
+ export class OutputBuffer {
6
+ lines = [];
7
+ partial = ''; // incomplete line (no trailing \n yet)
8
+ seq = 0;
9
+ maxLines;
10
+ constructor(maxLines = 10000) {
11
+ this.maxLines = maxLines;
12
+ }
13
+ get sequenceNumber() {
14
+ return this.seq;
15
+ }
16
+ /**
17
+ * Append raw PTY output data. May contain multiple lines or partial lines.
18
+ */
19
+ append(data) {
20
+ this.seq++;
21
+ // Combine with any pending partial line
22
+ const combined = this.partial + data;
23
+ const parts = combined.split('\n');
24
+ // Last element is either empty (data ended with \n) or a partial line
25
+ this.partial = parts.pop() ?? '';
26
+ // All other elements are complete lines
27
+ for (const line of parts) {
28
+ this.lines.push(line);
29
+ }
30
+ // Trim when exceeding max by 10%, to amortize allocation cost
31
+ const trimThreshold = Math.floor(this.maxLines * 1.1);
32
+ if (this.lines.length > trimThreshold) {
33
+ this.lines = this.lines.slice(this.lines.length - this.maxLines);
34
+ }
35
+ }
36
+ /**
37
+ * Get full buffered content as a single string (for history_sync).
38
+ */
39
+ getFullContent() {
40
+ const joined = this.lines.join('\n');
41
+ if (this.lines.length === 0) {
42
+ return this.partial;
43
+ }
44
+ if (this.partial) {
45
+ return joined + '\n' + this.partial;
46
+ }
47
+ // Lines were split from \n, so the original data had \n between/after them
48
+ return joined + '\n';
49
+ }
50
+ /**
51
+ * Clear the buffer.
52
+ */
53
+ clear() {
54
+ this.lines = [];
55
+ this.partial = '';
56
+ }
57
+ }
58
+ //# sourceMappingURL=output-buffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-buffer.js","sourceRoot":"","sources":["../../src/pty/output-buffer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,YAAY;IACf,KAAK,GAAa,EAAE,CAAC;IACrB,OAAO,GAAW,EAAE,CAAC,CAAC,uCAAuC;IAC7D,GAAG,GAAW,CAAC,CAAC;IACP,QAAQ,CAAS;IAElC,YAAY,WAAmB,KAAK;QAClC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,GAAG,CAAC;IAClB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,IAAY;QACjB,IAAI,CAAC,GAAG,EAAE,CAAC;QAEX,wCAAwC;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACrC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAEnC,sEAAsE;QACtE,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;QAEjC,wCAAwC;QACxC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC;QAED,8DAA8D;QAC9D,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC;QACtD,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,aAAa,EAAE,CAAC;YACtC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED;;OAEG;IACH,cAAc;QACZ,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,OAAO,CAAC;QACtB,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC;QACtC,CAAC;QACD,2EAA2E;QAC3E,OAAO,MAAM,GAAG,IAAI,CAAC;IACvB,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;IACpB,CAAC;CACF"}
@@ -0,0 +1,45 @@
1
+ import { EventEmitter } from 'node:events';
2
+ export interface PtyManagerOptions {
3
+ command: string;
4
+ args?: string[];
5
+ cwd?: string;
6
+ cols?: number;
7
+ rows?: number;
8
+ env?: Record<string, string>;
9
+ }
10
+ export interface PtyManagerEvents {
11
+ data: (data: string) => void;
12
+ exit: (exitCode: number, signal?: number) => void;
13
+ error: (err: Error) => void;
14
+ resize: (cols: number, rows: number) => void;
15
+ }
16
+ /**
17
+ * Manages a PTY process (Claude Code CLI).
18
+ * Emits: 'data', 'exit', 'error', 'resize'
19
+ */
20
+ export declare class PtyManager extends EventEmitter {
21
+ private process;
22
+ private _exited;
23
+ private _cols;
24
+ private _rows;
25
+ get exited(): boolean;
26
+ get cols(): number;
27
+ get rows(): number;
28
+ /**
29
+ * Spawn the PTY process.
30
+ */
31
+ spawn(options: PtyManagerOptions): void;
32
+ /**
33
+ * Write data to the PTY stdin.
34
+ */
35
+ write(data: string): void;
36
+ /**
37
+ * Resize the PTY.
38
+ */
39
+ resize(cols: number, rows: number): void;
40
+ /**
41
+ * Kill the PTY process.
42
+ */
43
+ destroy(): void;
44
+ }
45
+ //# sourceMappingURL=pty-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-manager.d.ts","sourceRoot":"","sources":["../../src/pty/pty-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,KAAK,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;IAC5B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAED;;;GAGG;AACH,qBAAa,UAAW,SAAQ,YAAY;IAC1C,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,KAAK,CAAc;IAE3B,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAyCvC;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQzB;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAmBxC;;OAEG;IACH,OAAO,IAAI,IAAI;CAUhB"}
@@ -0,0 +1,108 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import * as pty from 'node-pty';
3
+ import { logger } from '../logger/logger.js';
4
+ /**
5
+ * Manages a PTY process (Claude Code CLI).
6
+ * Emits: 'data', 'exit', 'error', 'resize'
7
+ */
8
+ export class PtyManager extends EventEmitter {
9
+ process = null;
10
+ _exited = false;
11
+ _cols = 80;
12
+ _rows = 24;
13
+ get exited() {
14
+ return this._exited;
15
+ }
16
+ get cols() {
17
+ return this._cols;
18
+ }
19
+ get rows() {
20
+ return this._rows;
21
+ }
22
+ /**
23
+ * Spawn the PTY process.
24
+ */
25
+ spawn(options) {
26
+ if (this.process) {
27
+ throw new Error('PTY process already running');
28
+ }
29
+ const cols = options.cols ?? process.stdout.columns ?? 80;
30
+ const rows = options.rows ?? process.stdout.rows ?? 24;
31
+ // Store initial size
32
+ this._cols = cols;
33
+ this._rows = rows;
34
+ logger.info({ command: options.command, args: options.args, cwd: options.cwd, cols, rows }, 'Spawning PTY process');
35
+ try {
36
+ this.process = pty.spawn(options.command, options.args ?? [], {
37
+ name: 'xterm-256color',
38
+ cols,
39
+ rows,
40
+ cwd: options.cwd ?? process.cwd(),
41
+ env: { ...process.env, ...options.env },
42
+ });
43
+ this.process.onData((data) => {
44
+ this.emit('data', data);
45
+ });
46
+ this.process.onExit(({ exitCode, signal }) => {
47
+ this._exited = true;
48
+ logger.info({ exitCode, signal }, 'PTY process exited');
49
+ this.emit('exit', exitCode, signal);
50
+ this.process = null;
51
+ });
52
+ logger.info({ pid: this.process.pid }, 'PTY process spawned');
53
+ }
54
+ catch (err) {
55
+ logger.error({ err }, 'Failed to spawn PTY process');
56
+ this.emit('error', err instanceof Error ? err : new Error(String(err)));
57
+ }
58
+ }
59
+ /**
60
+ * Write data to the PTY stdin.
61
+ */
62
+ write(data) {
63
+ if (!this.process) {
64
+ logger.warn('Attempted to write to PTY but no process is running');
65
+ return;
66
+ }
67
+ this.process.write(data);
68
+ }
69
+ /**
70
+ * Resize the PTY.
71
+ */
72
+ resize(cols, rows) {
73
+ if (!this.process)
74
+ return;
75
+ // 避免重复 resize,防止无限循环
76
+ if (cols === this._cols && rows === this._rows) {
77
+ logger.info({ cols, rows }, 'PTY resize skipped (same size)');
78
+ return;
79
+ }
80
+ logger.info({ cols, rows, prevCols: this._cols, prevRows: this._rows }, 'PTY resize executing');
81
+ try {
82
+ this.process.resize(cols, rows);
83
+ this._cols = cols;
84
+ this._rows = rows;
85
+ logger.info({ cols, rows }, 'PTY resized successfully');
86
+ this.emit('resize', cols, rows);
87
+ }
88
+ catch (err) {
89
+ logger.error({ err }, 'Failed to resize PTY');
90
+ }
91
+ }
92
+ /**
93
+ * Kill the PTY process.
94
+ */
95
+ destroy() {
96
+ if (!this.process)
97
+ return;
98
+ try {
99
+ this.process.kill();
100
+ logger.info('PTY process killed');
101
+ }
102
+ catch (err) {
103
+ logger.error({ err }, 'Failed to kill PTY process');
104
+ }
105
+ this.process = null;
106
+ }
107
+ }
108
+ //# sourceMappingURL=pty-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-manager.js","sourceRoot":"","sources":["../../src/pty/pty-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * PTY 管理器接口
3
+ * 支持本地 PtyManager 和远程 VirtualPtyManager
4
+ */
5
+ export interface IPtyManager {
6
+ write(data: string): void;
7
+ resize(cols: number, rows: number): void;
8
+ cols: number;
9
+ rows: number;
10
+ }
11
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/pty/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/pty/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,37 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import type { IPtyManager } from './types.js';
3
+ /**
4
+ * VirtualPtyManager 模拟 PtyManager 接口,
5
+ * 但将写入操作通过 WebSocket 转发到远程实例。
6
+ * 用于 attach 命令,让本地终端接管远程实例。
7
+ */
8
+ export declare class VirtualPtyManager extends EventEmitter implements IPtyManager {
9
+ private ws;
10
+ private _cols;
11
+ private _rows;
12
+ private _connected;
13
+ get cols(): number;
14
+ get rows(): number;
15
+ get connected(): boolean;
16
+ /**
17
+ * 连接到远程实例的 WebSocket。
18
+ */
19
+ connect(url: string, token: string): Promise<void>;
20
+ /**
21
+ * 写入数据(发送到远程实例)。
22
+ */
23
+ write(data: string): void;
24
+ /**
25
+ * 调整终端大小。
26
+ */
27
+ resize(cols: number, rows: number): void;
28
+ /**
29
+ * 发送 resize 消息到服务端(内部方法)。
30
+ */
31
+ private sendResize;
32
+ /**
33
+ * 断开连接。
34
+ */
35
+ destroy(): void;
36
+ }
37
+ //# sourceMappingURL=virtual-pty.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"virtual-pty.d.ts","sourceRoot":"","sources":["../../src/pty/virtual-pty.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,YAAa,YAAW,WAAW;IACxE,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,UAAU,CAAkB;IAEpC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;OAEG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqGlD;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAazB;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQxC;;OAEG;IACH,OAAO,CAAC,UAAU;IAYlB;;OAEG;IACH,OAAO,IAAI,IAAI;CAQhB"}
@@ -0,0 +1,161 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { WebSocket } from 'ws';
3
+ import { logger } from '../logger/logger.js';
4
+ /**
5
+ * VirtualPtyManager 模拟 PtyManager 接口,
6
+ * 但将写入操作通过 WebSocket 转发到远程实例。
7
+ * 用于 attach 命令,让本地终端接管远程实例。
8
+ */
9
+ export class VirtualPtyManager extends EventEmitter {
10
+ ws = null;
11
+ _cols = 80;
12
+ _rows = 24;
13
+ _connected = false;
14
+ get cols() {
15
+ return this._cols;
16
+ }
17
+ get rows() {
18
+ return this._rows;
19
+ }
20
+ get connected() {
21
+ return this._connected;
22
+ }
23
+ /**
24
+ * 连接到远程实例的 WebSocket。
25
+ */
26
+ connect(url, token) {
27
+ return new Promise((resolve, reject) => {
28
+ const wsUrl = `${url}?token=${encodeURIComponent(token)}`;
29
+ logger.info({ url }, 'VirtualPtyManager: connecting to remote instance');
30
+ this.ws = new WebSocket(wsUrl);
31
+ this.ws.on('open', () => {
32
+ logger.info('VirtualPtyManager: connected');
33
+ this._connected = true;
34
+ resolve();
35
+ });
36
+ this.ws.on('message', (rawData) => {
37
+ try {
38
+ const msg = JSON.parse(rawData.toString());
39
+ switch (msg.type) {
40
+ case 'terminal_output':
41
+ // 转发输出到本地终端
42
+ this.emit('data', msg.data);
43
+ break;
44
+ case 'history_sync':
45
+ // 历史数据也是输出
46
+ this.emit('data', msg.data);
47
+ // history_sync 包含服务端当前 PTY 尺寸
48
+ // 作为从控端,attach 应该跟随服务端(webapp 主控端)的尺寸
49
+ if (msg.cols && msg.rows && (msg.cols !== this._cols || msg.rows !== this._rows)) {
50
+ const oldCols = this._cols;
51
+ const oldRows = this._rows;
52
+ this._cols = msg.cols;
53
+ this._rows = msg.rows;
54
+ logger.info({ serverCols: msg.cols, serverRows: msg.rows, localCols: oldCols, localRows: oldRows }, 'VirtualPtyManager: server size differs, updating local to match');
55
+ // 通知本地终端调整尺寸
56
+ this.emit('resize', msg.cols, msg.rows);
57
+ }
58
+ break;
59
+ case 'terminal_resize':
60
+ // 服务端 PTY 尺寸变化通知
61
+ // 作为从控端,attach 应该跟随 webapp(主控端)的尺寸
62
+ if (msg.cols && msg.rows) {
63
+ const sizeChanged = msg.cols !== this._cols || msg.rows !== this._rows;
64
+ this._cols = msg.cols;
65
+ this._rows = msg.rows;
66
+ if (sizeChanged) {
67
+ // 通知本地终端调整尺寸
68
+ this.emit('resize', msg.cols, msg.rows);
69
+ }
70
+ // emit server_resize 事件,让 attach.ts 用真实终端尺寸响应
71
+ // 这在 WebApp 断开后特别重要:让 PTY 恢复到 attach 终端的尺寸
72
+ this.emit('server_resize', msg.cols, msg.rows);
73
+ logger.info({ cols: msg.cols, rows: msg.rows }, 'VirtualPtyManager: synced size from server');
74
+ }
75
+ break;
76
+ case 'status_update':
77
+ logger.info({ status: msg.status }, 'VirtualPtyManager: status update');
78
+ break;
79
+ case 'ip_changed':
80
+ logger.info({ newUrl: msg.newUrl }, 'VirtualPtyManager: IP changed');
81
+ break;
82
+ default:
83
+ // 忽略其他消息类型
84
+ break;
85
+ }
86
+ }
87
+ catch (err) {
88
+ logger.warn({ err }, 'VirtualPtyManager: failed to parse server message');
89
+ }
90
+ });
91
+ this.ws.on('close', () => {
92
+ logger.info('VirtualPtyManager: connection closed');
93
+ this._connected = false;
94
+ this.emit('exit', 0);
95
+ });
96
+ this.ws.on('error', (err) => {
97
+ logger.error({ err }, 'VirtualPtyManager: WebSocket error');
98
+ const wasConnected = this._connected;
99
+ this._connected = false;
100
+ if (!wasConnected) {
101
+ // 连接阶段的错误
102
+ reject(err);
103
+ }
104
+ else {
105
+ // 已连接后的错误
106
+ this.emit('error', err);
107
+ }
108
+ });
109
+ });
110
+ }
111
+ /**
112
+ * 写入数据(发送到远程实例)。
113
+ */
114
+ write(data) {
115
+ if (!this.ws || !this._connected) {
116
+ logger.warn('VirtualPtyManager: attempted to write but not connected');
117
+ return;
118
+ }
119
+ const msg = {
120
+ type: 'user_input',
121
+ data,
122
+ };
123
+ this.ws.send(JSON.stringify(msg));
124
+ }
125
+ /**
126
+ * 调整终端大小。
127
+ */
128
+ resize(cols, rows) {
129
+ if (!this.ws || !this._connected)
130
+ return;
131
+ this._cols = cols;
132
+ this._rows = rows;
133
+ this.sendResize();
134
+ }
135
+ /**
136
+ * 发送 resize 消息到服务端(内部方法)。
137
+ */
138
+ sendResize() {
139
+ if (!this.ws || !this._connected)
140
+ return;
141
+ const msg = {
142
+ type: 'resize',
143
+ cols: this._cols,
144
+ rows: this._rows,
145
+ };
146
+ this.ws.send(JSON.stringify(msg));
147
+ logger.debug({ cols: this._cols, rows: this._rows }, 'VirtualPtyManager: resized');
148
+ }
149
+ /**
150
+ * 断开连接。
151
+ */
152
+ destroy() {
153
+ if (this.ws) {
154
+ this.ws.close();
155
+ this.ws = null;
156
+ }
157
+ this._connected = false;
158
+ logger.info('VirtualPtyManager: destroyed');
159
+ }
160
+ }
161
+ //# sourceMappingURL=virtual-pty.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"virtual-pty.js","sourceRoot":"","sources":["../../src/pty/virtual-pty.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ export interface PushNotificationPayload {
2
+ title: string;
3
+ body: string;
4
+ tag?: string;
5
+ renotify?: boolean;
6
+ }
7
+ export interface PushSubscriptionData {
8
+ endpoint: string;
9
+ keys: {
10
+ p256dh: string;
11
+ auth: string;
12
+ };
13
+ }
14
+ /**
15
+ * Web Push notification service.
16
+ * Manages VAPID keys, subscriptions, and notification delivery.
17
+ *
18
+ * 多实例支持:订阅数据通过文件持久化并跨实例共享。
19
+ * subscribe/unsubscribe 使用最终一致性语义——内存立即更新,
20
+ * 文件持久化异步执行(不阻塞调用方)。如果进程在持久化完成前崩溃,
21
+ * 该订阅需客户端重新注册。
22
+ */
23
+ export declare class PushService {
24
+ private subscriptions;
25
+ private vapidPublicKey;
26
+ private vapidPrivateKey;
27
+ private vapidSubject;
28
+ private vapidInitPromise;
29
+ private readonly vapidKeysFile;
30
+ private readonly vapidLockPath;
31
+ private readonly subscriptionsFile;
32
+ private readonly subscriptionsLockPath;
33
+ private readonly dataDir;
34
+ constructor(baseDir: string);
35
+ /**
36
+ * 确保数据目录存在
37
+ */
38
+ private ensureDataDir;
39
+ /**
40
+ * 从文件加载 VAPID 密钥
41
+ */
42
+ private loadVapidKeys;
43
+ /**
44
+ * 保存 VAPID 密钥到文件
45
+ */
46
+ private saveVapidKeys;
47
+ /**
48
+ * 从文件加载订阅数据
49
+ */
50
+ private loadSubscriptions;
51
+ /**
52
+ * 从文件读取所有订阅(包含其他实例写入的)。
53
+ * 通过文件锁保护,防止读到半写的 JSON。
54
+ */
55
+ private readSubscriptionsFromFile;
56
+ /**
57
+ * 锁定读-合并-写订阅变更。
58
+ * type='add': 将 sub 合并到文件中(按 endpoint 去重)
59
+ * type='remove': 从文件中移除一个或多个 endpoints
60
+ */
61
+ private persistSubscriptionChange;
62
+ /**
63
+ * 初始化 VAPID 密钥和订阅数据。
64
+ * VAPID 密钥生成通过文件锁保护,防止多实例并发生成不同密钥对。
65
+ */
66
+ private initVapidAndSubscriptions;
67
+ getVapidPublicKey(): string;
68
+ waitForVapidPublicKey(timeoutMs?: number): Promise<string | null>;
69
+ /**
70
+ * 添加推送订阅。
71
+ * 内存立即更新,文件持久化异步执行(最终一致性)。
72
+ */
73
+ subscribe(subscription: PushSubscriptionData): void;
74
+ /**
75
+ * 移除推送订阅。
76
+ * 内存立即更新,文件持久化异步执行(最终一致性)。
77
+ */
78
+ unsubscribe(endpoint: string): boolean;
79
+ get subscriberCount(): number;
80
+ /**
81
+ * 验证订阅数据是否有效。
82
+ * p256dh 是 65 字节的 ECDH 公钥,Base64 URL-safe 编码后约 87 字符。
83
+ */
84
+ private isValidSubscription;
85
+ notifyAll(payload: PushNotificationPayload): Promise<void>;
86
+ }
87
+ //# sourceMappingURL=push-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push-service.d.ts","sourceRoot":"","sources":["../../src/push/push-service.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAOD;;;;;;;;GAQG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,aAAa,CAA2C;IAChE,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,gBAAgB,CAAgB;IAExC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAS;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,EAAE,MAAM;IA0B3B;;OAEG;YACW,aAAa;IAS3B;;OAEG;YACW,aAAa;IAa3B;;OAEG;YACW,aAAa;IAK3B;;OAEG;YACW,iBAAiB;IAc/B;;;OAGG;YACW,yBAAyB;IAWvC;;;;OAIG;YACW,yBAAyB;IAwCvC;;;OAGG;YACW,yBAAyB;IA+BvC,iBAAiB,IAAI,MAAM;IAIrB,qBAAqB,CAAC,SAAS,GAAE,MAAa,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAmB7E;;;OAGG;IACH,SAAS,CAAC,YAAY,EAAE,oBAAoB,GAAG,IAAI;IAQnD;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAWtC,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAKrB,SAAS,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;CAwEjE"}