@yu_robotics/remote-cli 1.1.33 → 1.1.36

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 (55) hide show
  1. package/dist/client/MessageHandler.d.ts +15 -0
  2. package/dist/client/MessageHandler.d.ts.map +1 -1
  3. package/dist/client/MessageHandler.js +268 -18
  4. package/dist/client/MessageHandler.js.map +1 -1
  5. package/dist/executor/ClaudePersistentExecutor.d.ts +5 -0
  6. package/dist/executor/ClaudePersistentExecutor.d.ts.map +1 -1
  7. package/dist/executor/ClaudePersistentExecutor.js +13 -0
  8. package/dist/executor/ClaudePersistentExecutor.js.map +1 -1
  9. package/dist/executor/GeminiExecutor.d.ts +9 -0
  10. package/dist/executor/GeminiExecutor.d.ts.map +1 -1
  11. package/dist/executor/GeminiExecutor.js +42 -5
  12. package/dist/executor/GeminiExecutor.js.map +1 -1
  13. package/dist/executor/IExecutor.d.ts +6 -0
  14. package/dist/executor/IExecutor.d.ts.map +1 -1
  15. package/dist/executor/acp/AcpClient.d.ts.map +1 -1
  16. package/dist/executor/acp/AcpClient.js +4 -2
  17. package/dist/executor/acp/AcpClient.js.map +1 -1
  18. package/dist/executor/acp/SessionManager.d.ts +5 -0
  19. package/dist/executor/acp/SessionManager.d.ts.map +1 -1
  20. package/dist/executor/acp/SessionManager.js +42 -1
  21. package/dist/executor/acp/SessionManager.js.map +1 -1
  22. package/dist/executor/index.d.ts.map +1 -1
  23. package/dist/executor/index.js +1 -0
  24. package/dist/executor/index.js.map +1 -1
  25. package/dist/machines/BackupStore.d.ts +38 -0
  26. package/dist/machines/BackupStore.d.ts.map +1 -0
  27. package/dist/machines/BackupStore.js +92 -0
  28. package/dist/machines/BackupStore.js.map +1 -0
  29. package/dist/machines/MachineCommands.d.ts +96 -0
  30. package/dist/machines/MachineCommands.d.ts.map +1 -0
  31. package/dist/machines/MachineCommands.js +442 -0
  32. package/dist/machines/MachineCommands.js.map +1 -0
  33. package/dist/machines/SshExecutor.d.ts +54 -0
  34. package/dist/machines/SshExecutor.d.ts.map +1 -0
  35. package/dist/machines/SshExecutor.js +206 -0
  36. package/dist/machines/SshExecutor.js.map +1 -0
  37. package/dist/machines/index.d.ts +6 -0
  38. package/dist/machines/index.d.ts.map +1 -0
  39. package/dist/machines/index.js +20 -0
  40. package/dist/machines/index.js.map +1 -0
  41. package/dist/machines/types.d.ts +113 -0
  42. package/dist/machines/types.d.ts.map +1 -0
  43. package/dist/machines/types.js +71 -0
  44. package/dist/machines/types.js.map +1 -0
  45. package/dist/thread/ThreadExecutorPool.d.ts.map +1 -1
  46. package/dist/thread/ThreadExecutorPool.js +3 -0
  47. package/dist/thread/ThreadExecutorPool.js.map +1 -1
  48. package/dist/thread/ThreadManager.d.ts +11 -5
  49. package/dist/thread/ThreadManager.d.ts.map +1 -1
  50. package/dist/thread/ThreadManager.js +19 -16
  51. package/dist/thread/ThreadManager.js.map +1 -1
  52. package/dist/types/config.d.ts +4 -0
  53. package/dist/types/config.d.ts.map +1 -1
  54. package/dist/types/config.js.map +1 -1
  55. package/package.json +3 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BackupStore.d.ts","sourceRoot":"","sources":["../../src/machines/BackupStore.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAI3C;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAQ;gBAEZ,SAAS,CAAC,EAAE,MAAM;IAK9B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAUxC;;OAEG;YACW,OAAO;IAMrB;;OAEG;IACG,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9C;;OAEG;IACG,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IASlF;;OAEG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAK7D;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQ1C;;OAEG;IACH,MAAM,CAAC,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;CAOxD"}
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BackupStore = void 0;
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const BACKUP_FILE = 'backups.json';
11
+ /**
12
+ * Manages backup records persistence
13
+ * Stores records in ~/.remote-cli/backups.json
14
+ */
15
+ class BackupStore {
16
+ filePath;
17
+ constructor(configDir) {
18
+ const dir = configDir || path_1.default.join(os_1.default.homedir(), '.remote-cli');
19
+ this.filePath = path_1.default.join(dir, BACKUP_FILE);
20
+ }
21
+ /**
22
+ * Load all backup records from disk
23
+ */
24
+ async loadAll() {
25
+ try {
26
+ const content = await promises_1.default.readFile(this.filePath, 'utf-8');
27
+ const records = JSON.parse(content);
28
+ return Array.isArray(records) ? records : [];
29
+ }
30
+ catch {
31
+ return [];
32
+ }
33
+ }
34
+ /**
35
+ * Save all backup records to disk
36
+ */
37
+ async saveAll(records) {
38
+ const dir = path_1.default.dirname(this.filePath);
39
+ await promises_1.default.mkdir(dir, { recursive: true });
40
+ await promises_1.default.writeFile(this.filePath, JSON.stringify(records, null, 2), 'utf-8');
41
+ }
42
+ /**
43
+ * Add a new backup record
44
+ */
45
+ async add(record) {
46
+ const records = await this.loadAll();
47
+ return this.saveAll([...records, record]);
48
+ }
49
+ /**
50
+ * Find backups by machine ID and optional file path
51
+ */
52
+ async findByMachine(machineId, filePath) {
53
+ const records = await this.loadAll();
54
+ return records.filter((r) => {
55
+ if (r.machineId !== machineId)
56
+ return false;
57
+ if (filePath && r.originalPath !== filePath)
58
+ return false;
59
+ return true;
60
+ });
61
+ }
62
+ /**
63
+ * Find a specific backup by ID
64
+ */
65
+ async findById(id) {
66
+ const records = await this.loadAll();
67
+ return records.find((r) => r.id === id);
68
+ }
69
+ /**
70
+ * Remove a backup record by ID
71
+ */
72
+ async remove(id) {
73
+ const records = await this.loadAll();
74
+ const filtered = records.filter((r) => r.id !== id);
75
+ if (filtered.length === records.length)
76
+ return false;
77
+ await this.saveAll(filtered);
78
+ return true;
79
+ }
80
+ /**
81
+ * Generate a backup path from the original file path
82
+ */
83
+ static generateBackupPath(originalPath) {
84
+ const basename = path_1.default.basename(originalPath);
85
+ const now = new Date();
86
+ const pad = (n) => String(n).padStart(2, '0');
87
+ const timestamp = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}T${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
88
+ return `/tmp/.remote-cli-backups/${basename}.${timestamp}.bak`;
89
+ }
90
+ }
91
+ exports.BackupStore = BackupStore;
92
+ //# sourceMappingURL=BackupStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BackupStore.js","sourceRoot":"","sources":["../../src/machines/BackupStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ import { ConfigManager } from '../config/ConfigManager';
2
+ import { SshExecutor } from './SshExecutor';
3
+ import { BackupStore } from './BackupStore';
4
+ import type { PendingReplace } from './types';
5
+ export interface CommandResult {
6
+ success: boolean;
7
+ output?: string;
8
+ error?: string;
9
+ }
10
+ /**
11
+ * Machine command implementations
12
+ * Host is derived from: <machineId>.<hostSuffix> (global config)
13
+ * Proxy is applied globally from config.remote.proxy
14
+ */
15
+ export declare class MachineCommands {
16
+ private config;
17
+ private ssh;
18
+ private backupStore;
19
+ constructor(config: ConfigManager, ssh?: SshExecutor, backupStore?: BackupStore);
20
+ /**
21
+ * Set global proxy configuration
22
+ */
23
+ setProxy(proxyHost: string, proxyPort: number, hostSuffix: string, proxyAuth?: string): Promise<CommandResult>;
24
+ /**
25
+ * Show current proxy configuration
26
+ */
27
+ showProxy(): CommandResult;
28
+ /**
29
+ * List all configured machines
30
+ */
31
+ listMachines(): Promise<CommandResult>;
32
+ /**
33
+ * Add a machine configuration
34
+ * Only needs id + user. Host is derived from id + global hostSuffix.
35
+ */
36
+ addMachine(id: string, user: string, password?: string, port?: number): Promise<CommandResult>;
37
+ /**
38
+ * Remove a machine configuration
39
+ */
40
+ removeMachine(id: string): Promise<CommandResult>;
41
+ /**
42
+ * Show a machine configuration (password masked)
43
+ */
44
+ showMachine(id: string): CommandResult;
45
+ /**
46
+ * List Docker containers on a machine
47
+ */
48
+ listContainers(machineId: string): Promise<CommandResult>;
49
+ /**
50
+ * Search for files on a machine or in a container
51
+ */
52
+ searchFiles(machineId: string, searchPath: string, pattern: string, containerId?: string): Promise<CommandResult>;
53
+ /**
54
+ * View a file on a machine or in a container
55
+ */
56
+ viewFile(machineId: string, filePath: string, containerId?: string, lines?: number): Promise<CommandResult>;
57
+ /**
58
+ * Initiate a file replace (returns pending state info)
59
+ */
60
+ initiateReplace(machineId: string, filePath: string, containerId?: string): CommandResult & {
61
+ pending?: PendingReplace;
62
+ };
63
+ /**
64
+ * Execute a pending file replace with backup
65
+ */
66
+ executeReplace(pending: PendingReplace, newContent: string): Promise<CommandResult>;
67
+ /**
68
+ * List backups for a machine
69
+ */
70
+ listBackups(machineId: string, filePath?: string): Promise<CommandResult>;
71
+ /**
72
+ * Restore a file from backup
73
+ */
74
+ restoreBackup(machineId: string, backupPath: string, targetPath: string, containerId?: string): Promise<CommandResult>;
75
+ /**
76
+ * Get raw machine config (without host/proxy resolution)
77
+ */
78
+ private getRawMachine;
79
+ /**
80
+ * Resolve a machine config: derive host from id + suffix, add global proxy
81
+ */
82
+ private resolveMachine;
83
+ /**
84
+ * Get a fully resolved machine by ID (host + proxy filled in)
85
+ */
86
+ private getResolvedMachine;
87
+ /**
88
+ * Get remote config, falling back to built-in defaults
89
+ */
90
+ private getRemoteConfig;
91
+ /**
92
+ * Detect programming language from file extension
93
+ */
94
+ private detectLanguage;
95
+ }
96
+ //# sourceMappingURL=MachineCommands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MachineCommands.d.ts","sourceRoot":"","sources":["../../src/machines/MachineCommands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAW3C,OAAO,KAAK,EAA+B,cAAc,EAA8C,MAAM,SAAS,CAAA;AActH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;GAIG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,WAAW,CAAa;gBAEpB,MAAM,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,WAAW;IAM/E;;OAEG;IACG,QAAQ,CACZ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC;IAkBzB;;OAEG;IACH,SAAS,IAAI,aAAa;IAe1B;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,aAAa,CAAC;IAoB5C;;;OAGG;IACG,UAAU,CACd,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,EACjB,IAAI,SAAK,GACR,OAAO,CAAC,aAAa,CAAC;IAoBzB;;OAEG;IACG,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAavD;;OAEG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa;IA2BtC;;OAEG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAgC/D;;OAEG;IACG,WAAW,CACf,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,aAAa,CAAC;IAsCzB;;OAEG;IACG,QAAQ,CACZ,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,EACpB,KAAK,SAAqB,GACzB,OAAO,CAAC,aAAa,CAAC;IAkCzB;;OAEG;IACH,eAAe,CACb,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,GACnB,aAAa,GAAG;QAAE,OAAO,CAAC,EAAE,cAAc,CAAA;KAAE;IA8B/C;;OAEG;IACG,cAAc,CAClB,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC;IAmDzB;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA6B/E;;OAEG;IACG,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,aAAa,CAAC;IAgCzB;;OAEG;IACH,OAAO,CAAC,aAAa;IAIrB;;OAEG;IACH,OAAO,CAAC,cAAc;IAatB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAM1B;;OAEG;IACH,OAAO,CAAC,eAAe;IAKvB;;OAEG;IACH,OAAO,CAAC,cAAc;CAqBvB"}
@@ -0,0 +1,442 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MachineCommands = void 0;
4
+ const SshExecutor_1 = require("./SshExecutor");
5
+ const BackupStore_1 = require("./BackupStore");
6
+ const types_1 = require("./types");
7
+ const MAX_SEARCH_RESULTS = 200;
8
+ const DEFAULT_VIEW_LINES = 100;
9
+ const DEFAULT_REMOTE = {
10
+ proxy: {
11
+ host: 'frps.xiaoyubot.com',
12
+ port: 5002,
13
+ auth: 'xiaoyu:IXa7s06Mo5',
14
+ },
15
+ hostSuffix: 'frp.xiaoyu.ai',
16
+ };
17
+ /**
18
+ * Machine command implementations
19
+ * Host is derived from: <machineId>.<hostSuffix> (global config)
20
+ * Proxy is applied globally from config.remote.proxy
21
+ */
22
+ class MachineCommands {
23
+ config;
24
+ ssh;
25
+ backupStore;
26
+ constructor(config, ssh, backupStore) {
27
+ this.config = config;
28
+ this.ssh = ssh || new SshExecutor_1.SshExecutor();
29
+ this.backupStore = backupStore || new BackupStore_1.BackupStore(config.getConfigDir());
30
+ }
31
+ /**
32
+ * Set global proxy configuration
33
+ */
34
+ async setProxy(proxyHost, proxyPort, hostSuffix, proxyAuth) {
35
+ const parsed = types_1.ProxyConfigSchema.safeParse({ host: proxyHost, port: proxyPort, auth: proxyAuth });
36
+ if (!parsed.success) {
37
+ return { success: false, error: `Invalid proxy config: ${parsed.error.issues[0].message}` };
38
+ }
39
+ const remote = {
40
+ proxy: { host: proxyHost, port: proxyPort, ...(proxyAuth ? { auth: proxyAuth } : {}) },
41
+ hostSuffix,
42
+ };
43
+ await this.config.set('remote', remote);
44
+ return {
45
+ success: true,
46
+ output: `Proxy configured: ${proxyHost}:${proxyPort}\nHost suffix: ${hostSuffix}`,
47
+ };
48
+ }
49
+ /**
50
+ * Show current proxy configuration
51
+ */
52
+ showProxy() {
53
+ const userRemote = this.config.get('remote');
54
+ const remote = this.getRemoteConfig();
55
+ const isDefault = !userRemote?.proxy;
56
+ const lines = [
57
+ `Proxy: ${remote.proxy.host}:${remote.proxy.port}${isDefault ? ' (default)' : ''}`,
58
+ `Host suffix: ${remote.hostSuffix}`,
59
+ ];
60
+ if (remote.proxy.auth) {
61
+ lines.push(`Auth: ${'*'.repeat(remote.proxy.auth.length)}`);
62
+ }
63
+ return { success: true, output: lines.join('\n') };
64
+ }
65
+ /**
66
+ * List all configured machines
67
+ */
68
+ async listMachines() {
69
+ const machines = this.config.get('machines');
70
+ const remote = this.getRemoteConfig();
71
+ if (!machines || Object.keys(machines).length === 0) {
72
+ return { success: true, output: 'No machines configured. Use /machine add to add one.' };
73
+ }
74
+ const suffix = remote?.hostSuffix || '';
75
+ const lines = Object.entries(machines).map(([id, m]) => {
76
+ const host = suffix ? `${id}.${suffix}` : id;
77
+ return `${id} | ${host}:${m.port} | ${m.user}`;
78
+ });
79
+ return {
80
+ success: true,
81
+ output: `Configured machines:\nID | Host | User\n${lines.join('\n')}`,
82
+ };
83
+ }
84
+ /**
85
+ * Add a machine configuration
86
+ * Only needs id + user. Host is derived from id + global hostSuffix.
87
+ */
88
+ async addMachine(id, user, password, port = 22) {
89
+ const parsed = types_1.MachineAddArgsSchema.safeParse({ id, user, password, port });
90
+ if (!parsed.success) {
91
+ return { success: false, error: `Invalid arguments: ${parsed.error.issues[0].message}` };
92
+ }
93
+ const machineConfig = {
94
+ user: parsed.data.user,
95
+ port: parsed.data.port,
96
+ ...(parsed.data.password ? { password: parsed.data.password } : {}),
97
+ };
98
+ await this.config.set(`machines.${id}`, machineConfig);
99
+ const remote = this.getRemoteConfig();
100
+ const host = remote.hostSuffix ? `${id}.${remote.hostSuffix}` : id;
101
+ return { success: true, output: `Machine "${id}" added: ${user}@${host}:${port}` };
102
+ }
103
+ /**
104
+ * Remove a machine configuration
105
+ */
106
+ async removeMachine(id) {
107
+ const machine = this.getRawMachine(id);
108
+ if (!machine) {
109
+ return { success: false, error: `Machine "${id}" not found` };
110
+ }
111
+ const machines = { ...this.config.get('machines') };
112
+ delete machines[id];
113
+ await this.config.set('machines', machines);
114
+ return { success: true, output: `Machine "${id}" removed` };
115
+ }
116
+ /**
117
+ * Show a machine configuration (password masked)
118
+ */
119
+ showMachine(id) {
120
+ const machine = this.getRawMachine(id);
121
+ if (!machine) {
122
+ return { success: false, error: `Machine "${id}" not found` };
123
+ }
124
+ const resolved = this.resolveMachine(id, machine);
125
+ const lines = [
126
+ `Machine: ${id}`,
127
+ `Host: ${resolved.host}`,
128
+ `Port: ${resolved.port}`,
129
+ `User: ${resolved.user}`,
130
+ ];
131
+ if (machine.password) {
132
+ lines.push(`Password: ${'*'.repeat(machine.password.length)}`);
133
+ }
134
+ if (resolved.proxy) {
135
+ lines.push(`Proxy: ${resolved.proxy.host}:${resolved.proxy.port} (global)`);
136
+ }
137
+ else {
138
+ lines.push('Connection: direct');
139
+ }
140
+ return { success: true, output: lines.join('\n') };
141
+ }
142
+ /**
143
+ * List Docker containers on a machine
144
+ */
145
+ async listContainers(machineId) {
146
+ const parsed = types_1.ContainersArgsSchema.safeParse({ machineId });
147
+ if (!parsed.success) {
148
+ return { success: false, error: `Invalid arguments: ${parsed.error.issues[0].message}` };
149
+ }
150
+ const machine = this.getResolvedMachine(machineId);
151
+ if (!machine) {
152
+ return { success: false, error: `Machine "${machineId}" not found` };
153
+ }
154
+ try {
155
+ const output = await this.ssh.execRemote(machine, [
156
+ 'docker', 'ps', '--format', '{{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Image}}',
157
+ ]);
158
+ if (!output.trim()) {
159
+ return { success: true, output: 'No running containers found' };
160
+ }
161
+ return {
162
+ success: true,
163
+ output: `Containers on "${machineId}":\nID\tName\tStatus\tImage\n${output.trim()}`,
164
+ };
165
+ }
166
+ catch (error) {
167
+ return {
168
+ success: false,
169
+ error: `Failed to list containers: ${error instanceof Error ? error.message : String(error)}`,
170
+ };
171
+ }
172
+ }
173
+ /**
174
+ * Search for files on a machine or in a container
175
+ */
176
+ async searchFiles(machineId, searchPath, pattern, containerId) {
177
+ const parsed = types_1.SearchArgsSchema.safeParse({ machineId, path: searchPath, pattern, containerId });
178
+ if (!parsed.success) {
179
+ return { success: false, error: `Invalid arguments: ${parsed.error.issues[0].message}` };
180
+ }
181
+ const machine = this.getResolvedMachine(machineId);
182
+ if (!machine) {
183
+ return { success: false, error: `Machine "${machineId}" not found` };
184
+ }
185
+ const findArgs = ['find', searchPath, '-maxdepth', '10', '-name', pattern];
186
+ try {
187
+ const output = containerId
188
+ ? await this.ssh.execInContainer(machine, containerId, findArgs)
189
+ : await this.ssh.execRemote(machine, findArgs);
190
+ const lines = output.trim().split('\n').filter(Boolean);
191
+ const truncated = lines.length > MAX_SEARCH_RESULTS;
192
+ const resultLines = lines.slice(0, MAX_SEARCH_RESULTS);
193
+ const header = containerId
194
+ ? `Search results in container "${containerId}" on "${machineId}":`
195
+ : `Search results on "${machineId}":`;
196
+ return {
197
+ success: true,
198
+ output: `${header}\n${resultLines.join('\n')}${truncated ? `\n... (${lines.length - MAX_SEARCH_RESULTS} more results truncated)` : ''}`,
199
+ };
200
+ }
201
+ catch (error) {
202
+ return {
203
+ success: false,
204
+ error: `Search failed: ${error instanceof Error ? error.message : String(error)}`,
205
+ };
206
+ }
207
+ }
208
+ /**
209
+ * View a file on a machine or in a container
210
+ */
211
+ async viewFile(machineId, filePath, containerId, lines = DEFAULT_VIEW_LINES) {
212
+ const parsed = types_1.ViewArgsSchema.safeParse({ machineId, filePath, containerId, lines });
213
+ if (!parsed.success) {
214
+ return { success: false, error: `Invalid arguments: ${parsed.error.issues[0].message}` };
215
+ }
216
+ const machine = this.getResolvedMachine(machineId);
217
+ if (!machine) {
218
+ return { success: false, error: `Machine "${machineId}" not found` };
219
+ }
220
+ try {
221
+ const content = containerId
222
+ ? await this.ssh.readContainerFile(machine, containerId, filePath, parsed.data.lines)
223
+ : await this.ssh.readRemoteFile(machine, filePath, parsed.data.lines);
224
+ const header = containerId
225
+ ? `File: ${filePath} (container: ${containerId}, machine: ${machineId})`
226
+ : `File: ${filePath} (machine: ${machineId})`;
227
+ const lang = this.detectLanguage(filePath);
228
+ const body = lang
229
+ ? `\`\`\`${lang}\n${content}\n\`\`\``
230
+ : content;
231
+ return { success: true, output: `${header}\n---\n${body}` };
232
+ }
233
+ catch (error) {
234
+ return {
235
+ success: false,
236
+ error: `Failed to read file: ${error instanceof Error ? error.message : String(error)}`,
237
+ };
238
+ }
239
+ }
240
+ /**
241
+ * Initiate a file replace (returns pending state info)
242
+ */
243
+ initiateReplace(machineId, filePath, containerId) {
244
+ const parsed = types_1.ReplaceArgsSchema.safeParse({ machineId, filePath, containerId });
245
+ if (!parsed.success) {
246
+ return { success: false, error: `Invalid arguments: ${parsed.error.issues[0].message}` };
247
+ }
248
+ const machine = this.getResolvedMachine(machineId);
249
+ if (!machine) {
250
+ return { success: false, error: `Machine "${machineId}" not found` };
251
+ }
252
+ const pending = {
253
+ machineId,
254
+ filePath,
255
+ containerId,
256
+ messageId: '',
257
+ createdAt: Date.now(),
258
+ };
259
+ const target = containerId
260
+ ? `${filePath} in container "${containerId}" on "${machineId}"`
261
+ : `${filePath} on "${machineId}"`;
262
+ return {
263
+ success: true,
264
+ output: `Ready to replace ${target}.\nSend the new file content, or /cancel to abort.`,
265
+ pending,
266
+ };
267
+ }
268
+ /**
269
+ * Execute a pending file replace with backup
270
+ */
271
+ async executeReplace(pending, newContent) {
272
+ const machine = this.getResolvedMachine(pending.machineId);
273
+ if (!machine) {
274
+ return { success: false, error: `Machine "${pending.machineId}" not found` };
275
+ }
276
+ const backupPath = BackupStore_1.BackupStore.generateBackupPath(pending.filePath);
277
+ try {
278
+ // Create backup directory on remote
279
+ await this.ssh.execRemote(machine, [
280
+ 'mkdir', '-p', '/tmp/.remote-cli-backups',
281
+ ]);
282
+ if (pending.containerId) {
283
+ // Container: backup from container to host, then write new
284
+ await this.ssh.execRemote(machine, [
285
+ 'docker', 'cp', `${pending.containerId}:${pending.filePath}`, backupPath,
286
+ ]);
287
+ await this.ssh.writeContainerFile(machine, pending.containerId, pending.filePath, newContent);
288
+ }
289
+ else {
290
+ // Host: backup then write
291
+ await this.ssh.execRemote(machine, [
292
+ 'cp', pending.filePath, backupPath,
293
+ ]);
294
+ await this.ssh.writeRemoteFile(machine, pending.filePath, newContent);
295
+ }
296
+ // Record backup
297
+ const record = {
298
+ id: `bk-${Date.now()}`,
299
+ machineId: pending.machineId,
300
+ originalPath: pending.filePath,
301
+ backupPath,
302
+ containerId: pending.containerId,
303
+ createdAt: Date.now(),
304
+ };
305
+ await this.backupStore.add(record);
306
+ return {
307
+ success: true,
308
+ output: `File replaced successfully.\nBackup saved at: ${backupPath}`,
309
+ };
310
+ }
311
+ catch (error) {
312
+ return {
313
+ success: false,
314
+ error: `Replace failed: ${error instanceof Error ? error.message : String(error)}`,
315
+ };
316
+ }
317
+ }
318
+ /**
319
+ * List backups for a machine
320
+ */
321
+ async listBackups(machineId, filePath) {
322
+ const parsed = types_1.BackupsArgsSchema.safeParse({ machineId, filePath });
323
+ if (!parsed.success) {
324
+ return { success: false, error: `Invalid arguments: ${parsed.error.issues[0].message}` };
325
+ }
326
+ const records = await this.backupStore.findByMachine(machineId, filePath);
327
+ if (records.length === 0) {
328
+ return {
329
+ success: true,
330
+ output: filePath
331
+ ? `No backups found for "${filePath}" on "${machineId}"`
332
+ : `No backups found for machine "${machineId}"`,
333
+ };
334
+ }
335
+ const lines = records.map((r) => {
336
+ const date = new Date(r.createdAt).toISOString();
337
+ const container = r.containerId ? ` (container: ${r.containerId})` : '';
338
+ return `${r.originalPath} → ${r.backupPath}${container} [${date}]`;
339
+ });
340
+ return {
341
+ success: true,
342
+ output: `Backups for "${machineId}":\n${lines.join('\n')}`,
343
+ };
344
+ }
345
+ /**
346
+ * Restore a file from backup
347
+ */
348
+ async restoreBackup(machineId, backupPath, targetPath, containerId) {
349
+ const parsed = types_1.RestoreArgsSchema.safeParse({ machineId, backupPath, targetPath, containerId });
350
+ if (!parsed.success) {
351
+ return { success: false, error: `Invalid arguments: ${parsed.error.issues[0].message}` };
352
+ }
353
+ const machine = this.getResolvedMachine(machineId);
354
+ if (!machine) {
355
+ return { success: false, error: `Machine "${machineId}" not found` };
356
+ }
357
+ try {
358
+ if (containerId) {
359
+ await this.ssh.execRemote(machine, [
360
+ 'docker', 'cp', backupPath, `${containerId}:${targetPath}`,
361
+ ]);
362
+ }
363
+ else {
364
+ await this.ssh.execRemote(machine, ['cp', backupPath, targetPath]);
365
+ }
366
+ return {
367
+ success: true,
368
+ output: `Restored ${backupPath} → ${targetPath}${containerId ? ` (container: ${containerId})` : ''}`,
369
+ };
370
+ }
371
+ catch (error) {
372
+ return {
373
+ success: false,
374
+ error: `Restore failed: ${error instanceof Error ? error.message : String(error)}`,
375
+ };
376
+ }
377
+ }
378
+ /**
379
+ * Get raw machine config (without host/proxy resolution)
380
+ */
381
+ getRawMachine(id) {
382
+ return this.config.get(`machines.${id}`);
383
+ }
384
+ /**
385
+ * Resolve a machine config: derive host from id + suffix, add global proxy
386
+ */
387
+ resolveMachine(id, machine) {
388
+ const remote = this.getRemoteConfig();
389
+ const host = remote.hostSuffix ? `${id}.${remote.hostSuffix}` : id;
390
+ return {
391
+ host,
392
+ user: machine.user,
393
+ password: machine.password,
394
+ port: machine.port,
395
+ proxy: remote.proxy,
396
+ };
397
+ }
398
+ /**
399
+ * Get a fully resolved machine by ID (host + proxy filled in)
400
+ */
401
+ getResolvedMachine(id) {
402
+ const machine = this.getRawMachine(id);
403
+ if (!machine)
404
+ return undefined;
405
+ return this.resolveMachine(id, machine);
406
+ }
407
+ /**
408
+ * Get remote config, falling back to built-in defaults
409
+ */
410
+ getRemoteConfig() {
411
+ const remote = this.config.get('remote');
412
+ return remote || DEFAULT_REMOTE;
413
+ }
414
+ /**
415
+ * Detect programming language from file extension
416
+ */
417
+ detectLanguage(filePath) {
418
+ const ext = filePath.split('.').pop()?.toLowerCase();
419
+ const langMap = {
420
+ py: 'python', js: 'javascript', ts: 'typescript', jsx: 'jsx', tsx: 'tsx',
421
+ java: 'java', go: 'go', rs: 'rust', rb: 'ruby', php: 'php',
422
+ c: 'c', cpp: 'cpp', h: 'c', hpp: 'cpp', cs: 'csharp',
423
+ sh: 'bash', bash: 'bash', zsh: 'bash',
424
+ yml: 'yaml', yaml: 'yaml', json: 'json', toml: 'toml', xml: 'xml',
425
+ html: 'html', css: 'css', scss: 'scss', less: 'less',
426
+ sql: 'sql', md: 'markdown', dockerfile: 'dockerfile',
427
+ lua: 'lua', r: 'r', swift: 'swift', kt: 'kotlin',
428
+ conf: 'ini', ini: 'ini', cfg: 'ini', env: 'bash',
429
+ };
430
+ if (!ext) {
431
+ const name = filePath.split('/').pop()?.toLowerCase() || '';
432
+ if (name === 'dockerfile')
433
+ return 'dockerfile';
434
+ if (name === 'makefile')
435
+ return 'makefile';
436
+ return null;
437
+ }
438
+ return langMap[ext] || null;
439
+ }
440
+ }
441
+ exports.MachineCommands = MachineCommands;
442
+ //# sourceMappingURL=MachineCommands.js.map