@cjwddz/mirror 0.0.1

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 (54) hide show
  1. package/README.md +125 -0
  2. package/dist/cli/host-impl.d.ts +37 -0
  3. package/dist/cli/host-impl.d.ts.map +1 -0
  4. package/dist/cli/host-impl.js +264 -0
  5. package/dist/cli/host-impl.js.map +1 -0
  6. package/dist/cli/host.d.ts +10 -0
  7. package/dist/cli/host.d.ts.map +1 -0
  8. package/dist/cli/host.js +33 -0
  9. package/dist/cli/host.js.map +1 -0
  10. package/dist/cli/index.d.ts +6 -0
  11. package/dist/cli/index.d.ts.map +1 -0
  12. package/dist/cli/index.js +24 -0
  13. package/dist/cli/index.js.map +1 -0
  14. package/dist/cli/link.d.ts +5 -0
  15. package/dist/cli/link.d.ts.map +1 -0
  16. package/dist/cli/link.js +222 -0
  17. package/dist/cli/link.js.map +1 -0
  18. package/dist/core/file-sync.d.ts +10 -0
  19. package/dist/core/file-sync.d.ts.map +1 -0
  20. package/dist/core/file-sync.js +18 -0
  21. package/dist/core/file-sync.js.map +1 -0
  22. package/dist/core/process-manager.d.ts +19 -0
  23. package/dist/core/process-manager.d.ts.map +1 -0
  24. package/dist/core/process-manager.js +76 -0
  25. package/dist/core/process-manager.js.map +1 -0
  26. package/dist/core/protocol.d.ts +60 -0
  27. package/dist/core/protocol.d.ts.map +1 -0
  28. package/dist/core/protocol.js +10 -0
  29. package/dist/core/protocol.js.map +1 -0
  30. package/dist/core/state-machine.d.ts +18 -0
  31. package/dist/core/state-machine.d.ts.map +1 -0
  32. package/dist/core/state-machine.js +39 -0
  33. package/dist/core/state-machine.js.map +1 -0
  34. package/dist/core/workspace.d.ts +31 -0
  35. package/dist/core/workspace.d.ts.map +1 -0
  36. package/dist/core/workspace.js +78 -0
  37. package/dist/core/workspace.js.map +1 -0
  38. package/dist/index.d.ts +11 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +11 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/test/index.test.d.ts +5 -0
  43. package/dist/test/index.test.d.ts.map +1 -0
  44. package/dist/test/index.test.js +12 -0
  45. package/dist/test/index.test.js.map +1 -0
  46. package/dist/transport/pty.d.ts +7 -0
  47. package/dist/transport/pty.d.ts.map +1 -0
  48. package/dist/transport/pty.js +26 -0
  49. package/dist/transport/pty.js.map +1 -0
  50. package/dist/transport/websocket.d.ts +20 -0
  51. package/dist/transport/websocket.d.ts.map +1 -0
  52. package/dist/transport/websocket.js +83 -0
  53. package/dist/transport/websocket.js.map +1 -0
  54. package/package.json +43 -0
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Link CLI 命令处理
3
+ */
4
+ import { randomUUID } from 'crypto';
5
+ import { readdir } from 'fs/promises';
6
+ import { join } from 'path';
7
+ import { existsSync } from 'fs';
8
+ import chokidar from 'chokidar';
9
+ import { WebSocketTransport } from '../transport/websocket.js';
10
+ import { readFileWithHash, getRelativePath } from '../core/file-sync.js';
11
+ import * as readline from 'readline';
12
+ export async function linkCommand(url) {
13
+ // 解析 URL
14
+ const parsed = parseUrl(url);
15
+ if (!parsed) {
16
+ console.error('Invalid URL format. Expected: host:port?token=xxx');
17
+ process.exit(1);
18
+ }
19
+ // 检查当前目录
20
+ const cwd = process.cwd();
21
+ if (!existsSync(join(cwd, 'package.json'))) {
22
+ console.error('必须在项目目录中执行 mirror link');
23
+ process.exit(1);
24
+ }
25
+ const clientId = randomUUID();
26
+ const clientVersion = '0.0.0'; // TODO: 从 package.json 读取
27
+ // 连接 WebSocket
28
+ const wsUrl = `ws://${parsed.host}:${parsed.port}`;
29
+ const transport = new WebSocketTransport();
30
+ try {
31
+ await transport.connect(wsUrl);
32
+ // 发送 HELLO
33
+ transport.send({
34
+ type: 'HELLO',
35
+ clientId,
36
+ token: parsed.token,
37
+ clientVersion,
38
+ });
39
+ // 等待连接建立(简化处理,直接开始同步)
40
+ await new Promise((resolve) => setTimeout(resolve, 500));
41
+ // 开始全量同步
42
+ await performSnapshot(transport, cwd);
43
+ // 设置文件监听
44
+ const watcher = setupFileWatcher(cwd, transport);
45
+ // 进入交互式终端
46
+ await startInteractiveTerminal(transport, cwd);
47
+ // 清理
48
+ watcher.close();
49
+ transport.close();
50
+ }
51
+ catch (error) {
52
+ console.error('Failed to connect:', error);
53
+ process.exit(1);
54
+ }
55
+ }
56
+ function parseUrl(url) {
57
+ try {
58
+ const urlObj = new URL(`http://${url}`);
59
+ const host = urlObj.hostname;
60
+ const port = parseInt(urlObj.port, 10);
61
+ const token = urlObj.searchParams.get('token');
62
+ if (!host || isNaN(port) || !token) {
63
+ return null;
64
+ }
65
+ return { host, port, token };
66
+ }
67
+ catch {
68
+ return null;
69
+ }
70
+ }
71
+ let currentVersion = 0;
72
+ async function performSnapshot(transport, cwd) {
73
+ currentVersion = 1;
74
+ // 发送 SNAPSHOT_START
75
+ transport.send({
76
+ type: 'SNAPSHOT_START',
77
+ version: currentVersion,
78
+ });
79
+ // 收集所有文件
80
+ const files = await collectFiles(cwd, cwd);
81
+ // 发送所有文件
82
+ for (const file of files) {
83
+ const { content, hash } = await readFileWithHash(file);
84
+ const relativePath = getRelativePath(file, cwd);
85
+ transport.send({
86
+ type: 'FILE',
87
+ path: relativePath,
88
+ hash,
89
+ content: content.toString('base64'),
90
+ });
91
+ }
92
+ // 发送 SNAPSHOT_END
93
+ transport.send({
94
+ type: 'SNAPSHOT_END',
95
+ version: currentVersion,
96
+ });
97
+ }
98
+ async function collectFiles(dir, baseDir) {
99
+ const files = [];
100
+ const ignorePatterns = ['node_modules', '.git', 'dist', 'build', '.next'];
101
+ async function walk(currentDir) {
102
+ const entries = await readdir(currentDir, { withFileTypes: true });
103
+ for (const entry of entries) {
104
+ const fullPath = join(currentDir, entry.name);
105
+ const relativePath = getRelativePath(fullPath, baseDir);
106
+ // 检查是否应该忽略
107
+ if (ignorePatterns.some((pattern) => relativePath.includes(pattern))) {
108
+ continue;
109
+ }
110
+ if (entry.isDirectory()) {
111
+ await walk(fullPath);
112
+ }
113
+ else if (entry.isFile()) {
114
+ files.push(fullPath);
115
+ }
116
+ }
117
+ }
118
+ await walk(dir);
119
+ return files;
120
+ }
121
+ function setupFileWatcher(cwd, transport) {
122
+ const watcher = chokidar.watch(cwd, {
123
+ ignored: [/node_modules/, /.git/, /dist/, /build/, /.next/],
124
+ ignoreInitial: true,
125
+ persistent: true,
126
+ });
127
+ watcher.on('change', async (path) => {
128
+ currentVersion++;
129
+ try {
130
+ const { content, hash } = await readFileWithHash(path);
131
+ const relativePath = getRelativePath(path, cwd);
132
+ transport.send({
133
+ type: 'FILE_DIFF',
134
+ version: currentVersion,
135
+ op: 'update',
136
+ path: relativePath,
137
+ hash,
138
+ content: content.toString('base64'),
139
+ });
140
+ }
141
+ catch (error) {
142
+ console.error(`Failed to sync file ${path}:`, error);
143
+ }
144
+ });
145
+ watcher.on('unlink', (path) => {
146
+ currentVersion++;
147
+ const relativePath = getRelativePath(path, cwd);
148
+ transport.send({
149
+ type: 'FILE_DIFF',
150
+ version: currentVersion,
151
+ op: 'delete',
152
+ path: relativePath,
153
+ hash: '',
154
+ });
155
+ });
156
+ return watcher;
157
+ }
158
+ async function startInteractiveTerminal(transport, _cwd) {
159
+ const rl = readline.createInterface({
160
+ input: process.stdin,
161
+ output: process.stdout,
162
+ prompt: '> ',
163
+ });
164
+ let currentExecId = null;
165
+ let isExecuting = false;
166
+ // 监听远程输出
167
+ transport.onMessage('EXEC_OUTPUT', (msg) => {
168
+ const outputMsg = msg;
169
+ if (outputMsg.execId === currentExecId) {
170
+ // 直接写入 stdout,不通过 readline
171
+ process.stdout.write(outputMsg.data);
172
+ }
173
+ });
174
+ transport.onMessage('EXEC_EXIT', (msg) => {
175
+ const exitMsg = msg;
176
+ if (exitMsg.execId === currentExecId) {
177
+ currentExecId = null;
178
+ isExecuting = false;
179
+ rl.prompt();
180
+ }
181
+ });
182
+ transport.onMessage('ERROR', (msg) => {
183
+ const errorMsg = msg;
184
+ console.error(`\nError: ${errorMsg.message}`);
185
+ if (currentExecId) {
186
+ currentExecId = null;
187
+ isExecuting = false;
188
+ rl.prompt();
189
+ }
190
+ });
191
+ rl.prompt();
192
+ rl.on('line', (line) => {
193
+ const command = line.trim();
194
+ if (!command) {
195
+ if (!isExecuting) {
196
+ rl.prompt();
197
+ }
198
+ return;
199
+ }
200
+ if (command === 'exit' || command === 'quit') {
201
+ rl.close();
202
+ return;
203
+ }
204
+ if (isExecuting) {
205
+ // 如果正在执行命令,忽略新输入
206
+ return;
207
+ }
208
+ const execId = randomUUID();
209
+ currentExecId = execId;
210
+ isExecuting = true;
211
+ transport.send({
212
+ type: 'EXEC',
213
+ execId,
214
+ version: currentVersion,
215
+ command,
216
+ });
217
+ });
218
+ rl.on('close', () => {
219
+ process.exit(0);
220
+ });
221
+ }
222
+ //# sourceMappingURL=link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.js","sourceRoot":"","sources":["../../src/cli/link.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 文件同步工具函数
3
+ */
4
+ export declare function computeHash(content: Buffer): string;
5
+ export declare function readFileWithHash(filePath: string): Promise<{
6
+ content: Buffer;
7
+ hash: string;
8
+ }>;
9
+ export declare function getRelativePath(filePath: string, baseDir: string): string;
10
+ //# sourceMappingURL=file-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-sync.d.ts","sourceRoot":"","sources":["../../src/core/file-sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAI5C;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEzE"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * 文件同步工具函数
3
+ */
4
+ import { createHash } from 'crypto';
5
+ import { readFile } from 'fs/promises';
6
+ import { relative } from 'path';
7
+ export function computeHash(content) {
8
+ return createHash('sha256').update(content).digest('hex');
9
+ }
10
+ export async function readFileWithHash(filePath) {
11
+ const content = await readFile(filePath);
12
+ const hash = computeHash(content);
13
+ return { content, hash };
14
+ }
15
+ export function getRelativePath(filePath, baseDir) {
16
+ return relative(baseDir, filePath).replace(/\\/g, '/');
17
+ }
18
+ //# sourceMappingURL=file-sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-sync.js","sourceRoot":"","sources":["../../src/core/file-sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAEhC,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAgB;IAEhB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,OAAe;IAC/D,OAAO,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACzD,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 进程管理
3
+ */
4
+ import * as pty from 'node-pty';
5
+ export interface ProcessInfo {
6
+ execId: string;
7
+ process: pty.IPty;
8
+ command: string;
9
+ }
10
+ export declare class ProcessManager {
11
+ private processes;
12
+ private processGroupPids;
13
+ spawn(execId: string, command: string, cwd: string, env?: NodeJS.ProcessEnv): Promise<pty.IPty>;
14
+ getProcess(execId: string): ProcessInfo | undefined;
15
+ removeProcess(execId: string): void;
16
+ killAll(): Promise<void>;
17
+ getAllProcesses(): ProcessInfo[];
18
+ }
19
+ //# sourceMappingURL=process-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-manager.d.ts","sourceRoot":"","sources":["../../src/core/process-manager.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAEhC,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,SAAS,CAAuC;IACxD,OAAO,CAAC,gBAAgB,CAA0B;IAE5C,KAAK,CACT,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,GAAG,GAAE,MAAM,CAAC,UAAe,GAC1B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;IA6BpB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAInD,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAW7B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA6B9B,eAAe,IAAI,WAAW,EAAE;CAGjC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * 进程管理
3
+ */
4
+ import * as pty from 'node-pty';
5
+ export class ProcessManager {
6
+ processes = new Map();
7
+ processGroupPids = new Set();
8
+ async spawn(execId, command, cwd, env = {}) {
9
+ const shell = process.env.SHELL || '/bin/bash';
10
+ const ptyProcess = pty.spawn(shell, ['-c', command], {
11
+ name: 'xterm-color',
12
+ cols: 80,
13
+ rows: 24,
14
+ cwd,
15
+ env: {
16
+ ...process.env,
17
+ ...env,
18
+ },
19
+ });
20
+ // 记录进程组 PID
21
+ const pid = ptyProcess.pid;
22
+ if (pid) {
23
+ this.processGroupPids.add(pid);
24
+ }
25
+ this.processes.set(execId, {
26
+ execId,
27
+ process: ptyProcess,
28
+ command,
29
+ });
30
+ return ptyProcess;
31
+ }
32
+ getProcess(execId) {
33
+ return this.processes.get(execId);
34
+ }
35
+ removeProcess(execId) {
36
+ const info = this.processes.get(execId);
37
+ if (info) {
38
+ const pid = info.process.pid;
39
+ if (pid) {
40
+ this.processGroupPids.delete(pid);
41
+ }
42
+ this.processes.delete(execId);
43
+ }
44
+ }
45
+ async killAll() {
46
+ // 先发送 SIGTERM
47
+ for (const [_execId, info] of this.processes) {
48
+ try {
49
+ info.process.kill('SIGTERM');
50
+ }
51
+ catch (error) {
52
+ // 忽略错误
53
+ }
54
+ }
55
+ // 等待一小段时间
56
+ await new Promise((resolve) => setTimeout(resolve, 1000));
57
+ // 强制 kill 所有进程组
58
+ const pids = Array.from(this.processGroupPids);
59
+ for (const pid of pids) {
60
+ try {
61
+ // 使用负 PID 来 kill 进程组
62
+ process.kill(-pid, 'SIGKILL');
63
+ }
64
+ catch (error) {
65
+ // 忽略错误(进程可能已经退出)
66
+ }
67
+ }
68
+ // 清理所有进程
69
+ this.processes.clear();
70
+ this.processGroupPids.clear();
71
+ }
72
+ getAllProcesses() {
73
+ return Array.from(this.processes.values());
74
+ }
75
+ }
76
+ //# sourceMappingURL=process-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-manager.js","sourceRoot":"","sources":["../../src/core/process-manager.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAQhC,MAAM,OAAO,cAAc;IACjB,SAAS,GAA6B,IAAI,GAAG,EAAE,CAAC;IAChD,gBAAgB,GAAgB,IAAI,GAAG,EAAE,CAAC;IAElD,KAAK,CAAC,KAAK,CACT,MAAc,EACd,OAAe,EACf,GAAW,EACX,MAAyB,EAAE;QAE3B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,WAAW,CAAC;QAE/C,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE;YACnD,IAAI,EAAE,aAAa;YACnB,IAAI,EAAE,EAAE;YACR,IAAI,EAAE,EAAE;YACR,GAAG;YACH,GAAG,EAAE;gBACH,GAAG,OAAO,CAAC,GAAG;gBACd,GAAG,GAAG;aACP;SACF,CAAC,CAAC;QAEH,YAAY;QACZ,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC;QAC3B,IAAI,GAAG,EAAE,CAAC;YACR,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE;YACzB,MAAM;YACN,OAAO,EAAE,UAAU;YACnB,OAAO;SACR,CAAC,CAAC;QAEH,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,UAAU,CAAC,MAAc;QACvB,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,aAAa,CAAC,MAAc;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;YAC7B,IAAI,GAAG,EAAE,CAAC;gBACR,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACpC,CAAC;YACD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO;QACX,cAAc;QACd,KAAK,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC7C,IAAI,CAAC;gBACH,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC/B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO;YACT,CAAC;QACH,CAAC;QAED,UAAU;QACV,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;QAE1D,gBAAgB;QAChB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC/C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC;gBACH,qBAAqB;gBACrB,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YAChC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,iBAAiB;YACnB,CAAC;QACH,CAAC;QAED,SAAS;QACT,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QACvB,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;IAChC,CAAC;IAED,eAAe;QACb,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7C,CAAC;CACF"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Mirror 通信协议定义
3
+ */
4
+ export type MessageType = 'HELLO' | 'SNAPSHOT_START' | 'FILE' | 'SNAPSHOT_END' | 'FILE_DIFF' | 'EXEC' | 'EXEC_OUTPUT' | 'EXEC_EXIT' | 'ERROR';
5
+ export interface BaseMessage {
6
+ type: MessageType;
7
+ }
8
+ export interface HelloMessage extends BaseMessage {
9
+ type: 'HELLO';
10
+ clientId: string;
11
+ token: string;
12
+ clientVersion: string;
13
+ }
14
+ export interface SnapshotStartMessage extends BaseMessage {
15
+ type: 'SNAPSHOT_START';
16
+ version: number;
17
+ }
18
+ export interface FileMessage extends BaseMessage {
19
+ type: 'FILE';
20
+ path: string;
21
+ hash: string;
22
+ content: string;
23
+ }
24
+ export interface SnapshotEndMessage extends BaseMessage {
25
+ type: 'SNAPSHOT_END';
26
+ version: number;
27
+ }
28
+ export interface FileDiffMessage extends BaseMessage {
29
+ type: 'FILE_DIFF';
30
+ version: number;
31
+ op: 'update' | 'delete';
32
+ path: string;
33
+ hash: string;
34
+ content?: string;
35
+ }
36
+ export interface ExecMessage extends BaseMessage {
37
+ type: 'EXEC';
38
+ execId: string;
39
+ version: number;
40
+ command: string;
41
+ }
42
+ export interface ExecOutputMessage extends BaseMessage {
43
+ type: 'EXEC_OUTPUT';
44
+ execId: string;
45
+ stream: 'stdout' | 'stderr';
46
+ data: string;
47
+ }
48
+ export interface ExecExitMessage extends BaseMessage {
49
+ type: 'EXEC_EXIT';
50
+ execId: string;
51
+ code: number;
52
+ }
53
+ export interface ErrorMessage extends BaseMessage {
54
+ type: 'ERROR';
55
+ message: string;
56
+ code?: string;
57
+ }
58
+ export type Message = HelloMessage | SnapshotStartMessage | FileMessage | SnapshotEndMessage | FileDiffMessage | ExecMessage | ExecOutputMessage | ExecExitMessage | ErrorMessage;
59
+ export declare function isMessage(obj: unknown): obj is Message;
60
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/core/protocol.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,WAAW,GACnB,OAAO,GACP,gBAAgB,GAChB,MAAM,GACN,cAAc,GACd,WAAW,GACX,MAAM,GACN,aAAa,GACb,WAAW,GACX,OAAO,CAAC;AAEZ,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC/C,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,oBAAqB,SAAQ,WAAW;IACvD,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC/C,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,OAAO,GACf,YAAY,GACZ,oBAAoB,GACpB,WAAW,GACX,kBAAkB,GAClB,eAAe,GACf,WAAW,GACX,iBAAiB,GACjB,eAAe,GACf,YAAY,CAAC;AAEjB,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,OAAO,CAOtD"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Mirror 通信协议定义
3
+ */
4
+ export function isMessage(obj) {
5
+ return (typeof obj === 'object' &&
6
+ obj !== null &&
7
+ 'type' in obj &&
8
+ typeof obj.type === 'string');
9
+ }
10
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/core/protocol.ts"],"names":[],"mappings":"AAAA;;GAEG;AAuFH,MAAM,UAAU,SAAS,CAAC,GAAY;IACpC,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ;QACvB,GAAG,KAAK,IAAI;QACZ,MAAM,IAAI,GAAG;QACb,OAAQ,GAAyB,CAAC,IAAI,KAAK,QAAQ,CACpD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Host 状态机
3
+ */
4
+ export declare enum HostState {
5
+ EMPTY = "EMPTY",// 无 client
6
+ SYNCING = "SYNCING",// 文件同步中(禁止 exec)
7
+ READY = "READY",// 文件一致,可 exec
8
+ RUNNING = "RUNNING"
9
+ }
10
+ export declare class StateMachine {
11
+ private state;
12
+ getState(): HostState;
13
+ transitionTo(newState: HostState): void;
14
+ canExec(): boolean;
15
+ canSync(): boolean;
16
+ reset(): void;
17
+ }
18
+ //# sourceMappingURL=state-machine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-machine.d.ts","sourceRoot":"","sources":["../../src/core/state-machine.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,oBAAY,SAAS;IACnB,KAAK,UAAU,CAAE,WAAW;IAC5B,OAAO,YAAY,CAAE,iBAAiB;IACtC,KAAK,UAAU,CAAE,cAAc;IAC/B,OAAO,YAAY;CACpB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAA8B;IAE3C,QAAQ,IAAI,SAAS;IAIrB,YAAY,CAAC,QAAQ,EAAE,SAAS,GAAG,IAAI;IAgBvC,OAAO,IAAI,OAAO;IAIlB,OAAO,IAAI,OAAO;IAIlB,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Host 状态机
3
+ */
4
+ export var HostState;
5
+ (function (HostState) {
6
+ HostState["EMPTY"] = "EMPTY";
7
+ HostState["SYNCING"] = "SYNCING";
8
+ HostState["READY"] = "READY";
9
+ HostState["RUNNING"] = "RUNNING";
10
+ })(HostState || (HostState = {}));
11
+ export class StateMachine {
12
+ state = HostState.EMPTY;
13
+ getState() {
14
+ return this.state;
15
+ }
16
+ transitionTo(newState) {
17
+ // 验证状态转换的合法性
18
+ const validTransitions = {
19
+ [HostState.EMPTY]: [HostState.SYNCING],
20
+ [HostState.SYNCING]: [HostState.READY, HostState.EMPTY],
21
+ [HostState.READY]: [HostState.RUNNING, HostState.SYNCING, HostState.EMPTY],
22
+ [HostState.RUNNING]: [HostState.READY, HostState.EMPTY],
23
+ };
24
+ if (!validTransitions[this.state].includes(newState)) {
25
+ throw new Error(`无效的状态转换: ${this.state} -> ${newState}`);
26
+ }
27
+ this.state = newState;
28
+ }
29
+ canExec() {
30
+ return this.state === HostState.READY;
31
+ }
32
+ canSync() {
33
+ return this.state !== HostState.SYNCING;
34
+ }
35
+ reset() {
36
+ this.state = HostState.EMPTY;
37
+ }
38
+ }
39
+ //# sourceMappingURL=state-machine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-machine.js","sourceRoot":"","sources":["../../src/core/state-machine.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,CAAN,IAAY,SAKX;AALD,WAAY,SAAS;IACnB,4BAAe,CAAA;IACf,gCAAmB,CAAA;IACnB,4BAAe,CAAA;IACf,gCAAmB,CAAA;AACrB,CAAC,EALW,SAAS,KAAT,SAAS,QAKpB;AAED,MAAM,OAAO,YAAY;IACf,KAAK,GAAc,SAAS,CAAC,KAAK,CAAC;IAE3C,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,YAAY,CAAC,QAAmB;QAC9B,aAAa;QACb,MAAM,gBAAgB,GAAmC;YACvD,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC;YACtC,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC;YACvD,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,KAAK,CAAC;YAC1E,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC;SACxD,CAAC;QAEF,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrD,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,CAAC,KAAK,OAAO,QAAQ,EAAE,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;IACxB,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,KAAK,CAAC;IACxC,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,OAAO,CAAC;IAC1C,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC;IAC/B,CAAC;CACF"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Workspace 管理
3
+ */
4
+ export declare class Workspace {
5
+ private path;
6
+ private currentVersion;
7
+ private pendingDiffs;
8
+ constructor(path: string);
9
+ static create(tmpDir?: string): Promise<Workspace>;
10
+ getPath(): string;
11
+ getCurrentVersion(): number;
12
+ setVersion(version: number): void;
13
+ writeFile(relativePath: string, content: Buffer): Promise<void>;
14
+ deleteFile(relativePath: string): Promise<void>;
15
+ addPendingDiff(diff: {
16
+ version: number;
17
+ op: string;
18
+ path: string;
19
+ content?: string;
20
+ }): void;
21
+ getPendingDiffs(): Array<{
22
+ version: number;
23
+ op: string;
24
+ path: string;
25
+ content?: string;
26
+ }>;
27
+ clearPendingDiffs(): void;
28
+ applyPendingDiffs(): Promise<void>;
29
+ cleanup(): Promise<void>;
30
+ }
31
+ //# sourceMappingURL=workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/core/workspace.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,qBAAa,SAAS;IACpB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,YAAY,CAA8E;gBAEtF,IAAI,EAAE,MAAM;WAIX,MAAM,CAAC,MAAM,GAAE,MAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IAKhE,OAAO,IAAI,MAAM;IAIjB,iBAAiB,IAAI,MAAM;IAI3B,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI3B,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW/D,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOrD,cAAc,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAI3F,eAAe,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAIzF,iBAAiB,IAAI,IAAI;IAInB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBlC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAO/B"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Workspace 管理
3
+ */
4
+ import { mkdtemp, rm } from 'fs/promises';
5
+ import { join } from 'path';
6
+ import { writeFile, mkdir, unlink } from 'fs/promises';
7
+ import { existsSync } from 'fs';
8
+ export class Workspace {
9
+ path;
10
+ currentVersion = 0;
11
+ pendingDiffs = [];
12
+ constructor(path) {
13
+ this.path = path;
14
+ }
15
+ static async create(tmpDir = '/tmp') {
16
+ const workspacePath = await mkdtemp(join(tmpDir, 'mirror-'));
17
+ return new Workspace(workspacePath);
18
+ }
19
+ getPath() {
20
+ return this.path;
21
+ }
22
+ getCurrentVersion() {
23
+ return this.currentVersion;
24
+ }
25
+ setVersion(version) {
26
+ this.currentVersion = version;
27
+ }
28
+ async writeFile(relativePath, content) {
29
+ const fullPath = join(this.path, relativePath);
30
+ const dir = join(fullPath, '..');
31
+ if (!existsSync(dir)) {
32
+ await mkdir(dir, { recursive: true });
33
+ }
34
+ await writeFile(fullPath, content);
35
+ }
36
+ async deleteFile(relativePath) {
37
+ const fullPath = join(this.path, relativePath);
38
+ if (existsSync(fullPath)) {
39
+ await unlink(fullPath);
40
+ }
41
+ }
42
+ addPendingDiff(diff) {
43
+ this.pendingDiffs.push(diff);
44
+ }
45
+ getPendingDiffs() {
46
+ return [...this.pendingDiffs];
47
+ }
48
+ clearPendingDiffs() {
49
+ this.pendingDiffs = [];
50
+ }
51
+ async applyPendingDiffs() {
52
+ const diffs = this.pendingDiffs;
53
+ this.clearPendingDiffs();
54
+ for (const diff of diffs) {
55
+ if (diff.op === 'update' && diff.content) {
56
+ const content = Buffer.from(diff.content, 'base64');
57
+ await this.writeFile(diff.path, content);
58
+ }
59
+ else if (diff.op === 'delete') {
60
+ await this.deleteFile(diff.path);
61
+ }
62
+ }
63
+ // 更新版本号到最后一个 diff 的版本
64
+ if (diffs.length > 0) {
65
+ const lastDiff = diffs[diffs.length - 1];
66
+ this.currentVersion = lastDiff.version;
67
+ }
68
+ }
69
+ async cleanup() {
70
+ try {
71
+ await rm(this.path, { recursive: true, force: true });
72
+ }
73
+ catch (error) {
74
+ // 忽略清理错误
75
+ }
76
+ }
77
+ }
78
+ //# sourceMappingURL=workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../src/core/workspace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Mirror 包入口
3
+ */
4
+ export * from './core/protocol.js';
5
+ export * from './core/state-machine.js';
6
+ export * from './core/workspace.js';
7
+ export * from './core/process-manager.js';
8
+ export * from './core/file-sync.js';
9
+ export * from './transport/websocket.js';
10
+ export * from './transport/pty.js';
11
+ //# sourceMappingURL=index.d.ts.map