@ebowwa/terminal 0.2.1 → 0.3.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.
- package/dist/index.js +6855 -28130
- package/dist/mcp/index.js +7244 -27317
- package/package.json +1 -1
- package/src/api.js +0 -861
- package/src/client.js +0 -92
- package/src/config.js +0 -490
- package/src/error.js +0 -32
- package/src/exec.js +0 -183
- package/src/files.js +0 -521
- package/src/fingerprint.js +0 -336
- package/src/index.js +0 -127
- package/src/manager.js +0 -358
- package/src/mcp/index.js +0 -555
- package/src/mcp/stdio.js +0 -840
- package/src/network-error-detector.js +0 -101
- package/src/pool.js +0 -840
- package/src/pty.js +0 -344
- package/src/resources.js +0 -64
- package/src/scp.js +0 -166
- package/src/sessions.js +0 -895
- package/src/tmux-exec.js +0 -169
- package/src/tmux-local.js +0 -937
- package/src/tmux-manager.js +0 -1026
- package/src/tmux.js +0 -826
- package/src/types.js +0 -5
package/src/manager.js
DELETED
|
@@ -1,358 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* SSH Key Manager - Single Source of Truth
|
|
4
|
-
*
|
|
5
|
-
* Consolidates SSH key creation, upload, and management for Hetzner.
|
|
6
|
-
* Replaces duplicate ensureSSHKey() functions in api.ts and crud.ts.
|
|
7
|
-
*
|
|
8
|
-
* FEATURES:
|
|
9
|
-
* - Create or reuse local SSH key pairs
|
|
10
|
-
* - Upload public key to Hetzner if not exists
|
|
11
|
-
* - Handle fingerprint format conversion (SHA256 <-> MD5)
|
|
12
|
-
* - Provide consistent API for all SSH key operations
|
|
13
|
-
* - OS-specific user data directory for portable key storage
|
|
14
|
-
*
|
|
15
|
-
* ENVIRONMENT VARIABLES:
|
|
16
|
-
* - HETZNER_SSH_KEYS_DIR: Override default SSH keys directory
|
|
17
|
-
*/
|
|
18
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
19
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
20
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
21
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
22
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
23
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
24
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
25
|
-
});
|
|
26
|
-
};
|
|
27
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
28
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
29
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
30
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
31
|
-
function step(op) {
|
|
32
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
33
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
34
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
35
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
36
|
-
switch (op[0]) {
|
|
37
|
-
case 0: case 1: t = op; break;
|
|
38
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
39
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
40
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
41
|
-
default:
|
|
42
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
43
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
44
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
45
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
46
|
-
if (t[2]) _.ops.pop();
|
|
47
|
-
_.trys.pop(); continue;
|
|
48
|
-
}
|
|
49
|
-
op = body.call(thisArg, _);
|
|
50
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
51
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
55
|
-
exports.SSHKeyManager = void 0;
|
|
56
|
-
exports.getDefaultKeysDir = getDefaultKeysDir;
|
|
57
|
-
exports.convertFingerprintFormat = convertFingerprintFormat;
|
|
58
|
-
exports.ensureSSHKey = ensureSSHKey;
|
|
59
|
-
var child_process_1 = require("child_process");
|
|
60
|
-
var util_1 = require("util");
|
|
61
|
-
var fs_1 = require("fs");
|
|
62
|
-
var path_1 = require("path");
|
|
63
|
-
var fingerprint_js_1 = require("./fingerprint.js");
|
|
64
|
-
var execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
65
|
-
/**
|
|
66
|
-
* Get OS-specific user data directory for SSH keys
|
|
67
|
-
* - macOS: ~/Library/Application Support/com.hetzner.codespaces/ssh-keys/
|
|
68
|
-
* - Linux: ~/.config/com.hetzner.codespaces/ssh-keys/
|
|
69
|
-
* - Windows: %APPDATA%\com.hetzner.codespaces\ssh-keys\
|
|
70
|
-
*
|
|
71
|
-
* Can be overridden via .env file:
|
|
72
|
-
* HETZNER_SSH_KEYS_DIR=/custom/path
|
|
73
|
-
*/
|
|
74
|
-
function getDefaultKeysDir() {
|
|
75
|
-
// Allow override via environment variable (Bun reads .env automatically)
|
|
76
|
-
var envDir = Bun.env.HETZNER_SSH_KEYS_DIR || process.env.HETZNER_SSH_KEYS_DIR;
|
|
77
|
-
if (envDir) {
|
|
78
|
-
return envDir;
|
|
79
|
-
}
|
|
80
|
-
var home = process.env.HOME || process.env.USERPROFILE || "~";
|
|
81
|
-
var appName = "com.hetzner.codespaces";
|
|
82
|
-
if (process.platform === "darwin") {
|
|
83
|
-
return (0, path_1.join)(home, "Library", "Application Support", appName, "ssh-keys");
|
|
84
|
-
}
|
|
85
|
-
else if (process.platform === "linux") {
|
|
86
|
-
return (0, path_1.join)(home, ".config", appName, "ssh-keys");
|
|
87
|
-
}
|
|
88
|
-
else if (process.platform === "win32") {
|
|
89
|
-
var appData = process.env.APPDATA || (0, path_1.join)(home, "AppData", "Roaming");
|
|
90
|
-
return (0, path_1.join)(appData, appName, "ssh-keys");
|
|
91
|
-
}
|
|
92
|
-
// Universal fallback
|
|
93
|
-
return (0, path_1.join)(home, ".".concat(appName), "ssh-keys");
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Default SSH key configuration
|
|
97
|
-
*/
|
|
98
|
-
var DEFAULT_KEY_NAME = "hetzner-codespaces-default";
|
|
99
|
-
/**
|
|
100
|
-
* Convert SSH key fingerprint between formats
|
|
101
|
-
* Hetzner uses MD5 with colons, modern tools use SHA256
|
|
102
|
-
*/
|
|
103
|
-
function convertFingerprintFormat(publicKeyPath, format) {
|
|
104
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
105
|
-
var flag, stdout, parts, fingerprint;
|
|
106
|
-
return __generator(this, function (_a) {
|
|
107
|
-
switch (_a.label) {
|
|
108
|
-
case 0:
|
|
109
|
-
flag = format === "md5" ? "-E md5" : "";
|
|
110
|
-
return [4 /*yield*/, execAsync("ssh-keygen -l -f \"".concat(publicKeyPath, "\" ").concat(flag))];
|
|
111
|
-
case 1:
|
|
112
|
-
stdout = (_a.sent()).stdout;
|
|
113
|
-
parts = stdout.trim().split(/\s+/);
|
|
114
|
-
fingerprint = parts[1];
|
|
115
|
-
// Remove "MD5:" prefix if present (Hetzner API returns it without prefix)
|
|
116
|
-
if (fingerprint.startsWith("MD5:")) {
|
|
117
|
-
fingerprint = fingerprint.replace("MD5:", "");
|
|
118
|
-
}
|
|
119
|
-
return [2 /*return*/, fingerprint];
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Get SSH key fingerprint in the format expected by Hetzner API (MD5 with colons)
|
|
126
|
-
*/
|
|
127
|
-
function getHetznerFingerprint(publicKeyPath) {
|
|
128
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
129
|
-
return __generator(this, function (_a) {
|
|
130
|
-
return [2 /*return*/, convertFingerprintFormat(publicKeyPath, "md5")];
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Get SSH key fingerprint in modern SHA256 format (for local use)
|
|
136
|
-
*/
|
|
137
|
-
function getLocalFingerprint(publicKeyPath) {
|
|
138
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
139
|
-
return __generator(this, function (_a) {
|
|
140
|
-
return [2 /*return*/, convertFingerprintFormat(publicKeyPath, "sha256")];
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Create a new SSH key pair
|
|
146
|
-
*/
|
|
147
|
-
function createKeyPair(keyPath, keyName) {
|
|
148
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
149
|
-
return __generator(this, function (_a) {
|
|
150
|
-
switch (_a.label) {
|
|
151
|
-
case 0:
|
|
152
|
-
console.log("[SSH] Generating new SSH key pair: ".concat(keyPath));
|
|
153
|
-
// Quote the path to handle spaces in directory names (e.g., "Application Support")
|
|
154
|
-
return [4 /*yield*/, execAsync("ssh-keygen -t ed25519 -f \"".concat(keyPath, "\" -N \"\" -C \"").concat(keyName, "\""))];
|
|
155
|
-
case 1:
|
|
156
|
-
// Quote the path to handle spaces in directory names (e.g., "Application Support")
|
|
157
|
-
_a.sent();
|
|
158
|
-
return [2 /*return*/];
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Find SSH key in Hetzner by fingerprint
|
|
165
|
-
*/
|
|
166
|
-
function findKeyByFingerprint(hetznerClient, fingerprint) {
|
|
167
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
168
|
-
var existingKeys;
|
|
169
|
-
return __generator(this, function (_a) {
|
|
170
|
-
switch (_a.label) {
|
|
171
|
-
case 0: return [4 /*yield*/, hetznerClient.ssh_keys.list()];
|
|
172
|
-
case 1:
|
|
173
|
-
existingKeys = _a.sent();
|
|
174
|
-
return [2 /*return*/, existingKeys.find(function (k) { return k.fingerprint === fingerprint; }) || null];
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Upload new SSH key to Hetzner
|
|
181
|
-
*/
|
|
182
|
-
function uploadKeyToHetzner(hetznerClient, keyName, publicKey) {
|
|
183
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
184
|
-
return __generator(this, function (_a) {
|
|
185
|
-
switch (_a.label) {
|
|
186
|
-
case 0:
|
|
187
|
-
console.log("[SSH] Uploading SSH key to Hetzner: ".concat(keyName));
|
|
188
|
-
return [4 /*yield*/, hetznerClient.ssh_keys.create({
|
|
189
|
-
name: keyName,
|
|
190
|
-
public_key: publicKey.trim(),
|
|
191
|
-
})];
|
|
192
|
-
case 1: return [2 /*return*/, _a.sent()];
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* SSH Key Manager - Main class for managing SSH keys
|
|
199
|
-
*/
|
|
200
|
-
var SSHKeyManager = /** @class */ (function () {
|
|
201
|
-
function SSHKeyManager(config) {
|
|
202
|
-
if (config === void 0) { config = {}; }
|
|
203
|
-
this.keyName = config.keyName || DEFAULT_KEY_NAME;
|
|
204
|
-
// Use provided keysDir or fall back to OS-specific default
|
|
205
|
-
this.keysDir = config.keysDir || getDefaultKeysDir();
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Get the absolute path to the SSH keys directory
|
|
209
|
-
*/
|
|
210
|
-
SSHKeyManager.prototype.getKeysDirPath = function () {
|
|
211
|
-
return this.keysDir;
|
|
212
|
-
};
|
|
213
|
-
/**
|
|
214
|
-
* Get the full path to the SSH key files (absolute path)
|
|
215
|
-
*/
|
|
216
|
-
SSHKeyManager.prototype.getKeyPath = function () {
|
|
217
|
-
return (0, path_1.join)(this.keysDir, this.keyName);
|
|
218
|
-
};
|
|
219
|
-
/**
|
|
220
|
-
* Ensure SSH key exists locally and on Hetzner
|
|
221
|
-
* This is the main entry point - replaces duplicate ensureSSHKey() functions
|
|
222
|
-
*
|
|
223
|
-
* @param hetznerClient - Hetzner API client
|
|
224
|
-
* @returns SSH key information for server creation
|
|
225
|
-
*/
|
|
226
|
-
SSHKeyManager.prototype.ensureSSHKey = function (hetznerClient) {
|
|
227
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
228
|
-
var keysDirPath, keyPath, publicKeyPath, publicKey, fingerprint, existingKey, newKey, error_1, keyByName;
|
|
229
|
-
var _a, _b;
|
|
230
|
-
return __generator(this, function (_c) {
|
|
231
|
-
switch (_c.label) {
|
|
232
|
-
case 0:
|
|
233
|
-
keysDirPath = this.getKeysDirPath();
|
|
234
|
-
(0, fs_1.mkdirSync)(keysDirPath, { recursive: true });
|
|
235
|
-
keyPath = this.getKeyPath();
|
|
236
|
-
publicKeyPath = "".concat(keyPath, ".pub");
|
|
237
|
-
if (!(0, fs_1.existsSync)(publicKeyPath)) return [3 /*break*/, 3];
|
|
238
|
-
return [4 /*yield*/, Bun.file(publicKeyPath).text()];
|
|
239
|
-
case 1:
|
|
240
|
-
// Read existing public key
|
|
241
|
-
publicKey = _c.sent();
|
|
242
|
-
return [4 /*yield*/, getHetznerFingerprint(publicKeyPath)];
|
|
243
|
-
case 2:
|
|
244
|
-
// Get fingerprint in Hetzner format (MD5 with colons)
|
|
245
|
-
fingerprint = _c.sent();
|
|
246
|
-
return [3 /*break*/, 7];
|
|
247
|
-
case 3:
|
|
248
|
-
// Generate new key pair
|
|
249
|
-
return [4 /*yield*/, createKeyPair(keyPath, this.keyName)];
|
|
250
|
-
case 4:
|
|
251
|
-
// Generate new key pair
|
|
252
|
-
_c.sent();
|
|
253
|
-
return [4 /*yield*/, Bun.file(publicKeyPath).text()];
|
|
254
|
-
case 5:
|
|
255
|
-
publicKey = _c.sent();
|
|
256
|
-
return [4 /*yield*/, getHetznerFingerprint(publicKeyPath)];
|
|
257
|
-
case 6:
|
|
258
|
-
fingerprint = _c.sent();
|
|
259
|
-
_c.label = 7;
|
|
260
|
-
case 7: return [4 /*yield*/, findKeyByFingerprint(hetznerClient, fingerprint)];
|
|
261
|
-
case 8:
|
|
262
|
-
existingKey = _c.sent();
|
|
263
|
-
if (existingKey) {
|
|
264
|
-
console.log("[SSH] Using existing Hetzner SSH key: ".concat(existingKey.name, " (ID: ").concat(existingKey.id, ")"));
|
|
265
|
-
return [2 /*return*/, {
|
|
266
|
-
keyId: existingKey.id,
|
|
267
|
-
keyPath: keyPath,
|
|
268
|
-
fingerprint: fingerprint,
|
|
269
|
-
name: existingKey.name,
|
|
270
|
-
}];
|
|
271
|
-
}
|
|
272
|
-
_c.label = 9;
|
|
273
|
-
case 9:
|
|
274
|
-
_c.trys.push([9, 11, , 14]);
|
|
275
|
-
return [4 /*yield*/, uploadKeyToHetzner(hetznerClient, this.keyName, publicKey)];
|
|
276
|
-
case 10:
|
|
277
|
-
newKey = _c.sent();
|
|
278
|
-
console.log("[SSH] SSH key uploaded: ".concat(newKey.name, " (ID: ").concat(newKey.id, ")"));
|
|
279
|
-
return [2 /*return*/, {
|
|
280
|
-
keyId: newKey.id,
|
|
281
|
-
keyPath: keyPath,
|
|
282
|
-
fingerprint: fingerprint,
|
|
283
|
-
name: newKey.name,
|
|
284
|
-
}];
|
|
285
|
-
case 11:
|
|
286
|
-
error_1 = _c.sent();
|
|
287
|
-
if (!(((_a = error_1 === null || error_1 === void 0 ? void 0 : error_1.message) === null || _a === void 0 ? void 0 : _a.includes("not unique")) ||
|
|
288
|
-
((_b = error_1 === null || error_1 === void 0 ? void 0 : error_1.message) === null || _b === void 0 ? void 0 : _b.includes("already exists")))) return [3 /*break*/, 13];
|
|
289
|
-
console.log("[SSH] Key already exists in Hetzner, finding by name...");
|
|
290
|
-
return [4 /*yield*/, hetznerClient.ssh_keys.findByName(this.keyName)];
|
|
291
|
-
case 12:
|
|
292
|
-
keyByName = _c.sent();
|
|
293
|
-
if (keyByName) {
|
|
294
|
-
console.log("[SSH] Found existing key by name: ".concat(keyByName.name, " (ID: ").concat(keyByName.id, ")"));
|
|
295
|
-
return [2 /*return*/, {
|
|
296
|
-
keyId: keyByName.id,
|
|
297
|
-
keyPath: keyPath,
|
|
298
|
-
fingerprint: keyByName.fingerprint,
|
|
299
|
-
name: keyByName.name,
|
|
300
|
-
}];
|
|
301
|
-
}
|
|
302
|
-
_c.label = 13;
|
|
303
|
-
case 13: throw error_1;
|
|
304
|
-
case 14: return [2 /*return*/];
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
};
|
|
309
|
-
/**
|
|
310
|
-
* Get local key fingerprint in SHA256 format
|
|
311
|
-
*/
|
|
312
|
-
SSHKeyManager.prototype.getLocalFingerprint = function () {
|
|
313
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
314
|
-
var keyPath;
|
|
315
|
-
return __generator(this, function (_a) {
|
|
316
|
-
keyPath = this.getKeyPath();
|
|
317
|
-
return [2 /*return*/, (0, fingerprint_js_1.getLocalKeyFingerprint)(keyPath)];
|
|
318
|
-
});
|
|
319
|
-
});
|
|
320
|
-
};
|
|
321
|
-
/**
|
|
322
|
-
* Get local key fingerprint in MD5 format (for Hetzner comparison)
|
|
323
|
-
*/
|
|
324
|
-
SSHKeyManager.prototype.getHetznerFingerprint = function () {
|
|
325
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
326
|
-
var publicKeyPath;
|
|
327
|
-
return __generator(this, function (_a) {
|
|
328
|
-
publicKeyPath = "".concat(this.getKeyPath(), ".pub");
|
|
329
|
-
return [2 /*return*/, getHetznerFingerprint(publicKeyPath)];
|
|
330
|
-
});
|
|
331
|
-
});
|
|
332
|
-
};
|
|
333
|
-
/**
|
|
334
|
-
* Get the key path
|
|
335
|
-
*/
|
|
336
|
-
SSHKeyManager.prototype.getKeyPathValue = function () {
|
|
337
|
-
return this.getKeyPath();
|
|
338
|
-
};
|
|
339
|
-
return SSHKeyManager;
|
|
340
|
-
}());
|
|
341
|
-
exports.SSHKeyManager = SSHKeyManager;
|
|
342
|
-
/**
|
|
343
|
-
* Convenience function - creates default manager and ensures SSH key
|
|
344
|
-
* This is a drop-in replacement for the old ensureSSHKey() functions
|
|
345
|
-
*
|
|
346
|
-
* @param hetznerClient - Hetzner API client
|
|
347
|
-
* @param config - Optional configuration
|
|
348
|
-
* @returns SSH key information
|
|
349
|
-
*/
|
|
350
|
-
function ensureSSHKey(hetznerClient, config) {
|
|
351
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
352
|
-
var manager;
|
|
353
|
-
return __generator(this, function (_a) {
|
|
354
|
-
manager = new SSHKeyManager(config);
|
|
355
|
-
return [2 /*return*/, manager.ensureSSHKey(hetznerClient)];
|
|
356
|
-
});
|
|
357
|
-
});
|
|
358
|
-
}
|