@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.
- package/dist/client/MessageHandler.d.ts +15 -0
- package/dist/client/MessageHandler.d.ts.map +1 -1
- package/dist/client/MessageHandler.js +268 -18
- package/dist/client/MessageHandler.js.map +1 -1
- package/dist/executor/ClaudePersistentExecutor.d.ts +5 -0
- package/dist/executor/ClaudePersistentExecutor.d.ts.map +1 -1
- package/dist/executor/ClaudePersistentExecutor.js +13 -0
- package/dist/executor/ClaudePersistentExecutor.js.map +1 -1
- package/dist/executor/GeminiExecutor.d.ts +9 -0
- package/dist/executor/GeminiExecutor.d.ts.map +1 -1
- package/dist/executor/GeminiExecutor.js +42 -5
- package/dist/executor/GeminiExecutor.js.map +1 -1
- package/dist/executor/IExecutor.d.ts +6 -0
- package/dist/executor/IExecutor.d.ts.map +1 -1
- package/dist/executor/acp/AcpClient.d.ts.map +1 -1
- package/dist/executor/acp/AcpClient.js +4 -2
- package/dist/executor/acp/AcpClient.js.map +1 -1
- package/dist/executor/acp/SessionManager.d.ts +5 -0
- package/dist/executor/acp/SessionManager.d.ts.map +1 -1
- package/dist/executor/acp/SessionManager.js +42 -1
- package/dist/executor/acp/SessionManager.js.map +1 -1
- package/dist/executor/index.d.ts.map +1 -1
- package/dist/executor/index.js +1 -0
- package/dist/executor/index.js.map +1 -1
- package/dist/machines/BackupStore.d.ts +38 -0
- package/dist/machines/BackupStore.d.ts.map +1 -0
- package/dist/machines/BackupStore.js +92 -0
- package/dist/machines/BackupStore.js.map +1 -0
- package/dist/machines/MachineCommands.d.ts +96 -0
- package/dist/machines/MachineCommands.d.ts.map +1 -0
- package/dist/machines/MachineCommands.js +442 -0
- package/dist/machines/MachineCommands.js.map +1 -0
- package/dist/machines/SshExecutor.d.ts +54 -0
- package/dist/machines/SshExecutor.d.ts.map +1 -0
- package/dist/machines/SshExecutor.js +206 -0
- package/dist/machines/SshExecutor.js.map +1 -0
- package/dist/machines/index.d.ts +6 -0
- package/dist/machines/index.d.ts.map +1 -0
- package/dist/machines/index.js +20 -0
- package/dist/machines/index.js.map +1 -0
- package/dist/machines/types.d.ts +113 -0
- package/dist/machines/types.d.ts.map +1 -0
- package/dist/machines/types.js +71 -0
- package/dist/machines/types.js.map +1 -0
- package/dist/thread/ThreadExecutorPool.d.ts.map +1 -1
- package/dist/thread/ThreadExecutorPool.js +3 -0
- package/dist/thread/ThreadExecutorPool.js.map +1 -1
- package/dist/thread/ThreadManager.d.ts +11 -5
- package/dist/thread/ThreadManager.d.ts.map +1 -1
- package/dist/thread/ThreadManager.js +19 -16
- package/dist/thread/ThreadManager.js.map +1 -1
- package/dist/types/config.d.ts +4 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js.map +1 -1
- 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
|