nikcli-remote 1.0.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/chunk-MCKGQKYU.js +15 -0
- package/dist/chunk-TJTKYXIZ.js +2782 -0
- package/dist/index.cjs +6804 -0
- package/dist/index.d.cts +372 -0
- package/dist/index.d.ts +372 -0
- package/dist/index.js +155 -0
- package/dist/localtunnel-XT32JGNN.js +3805 -0
- package/dist/server-XW6FLG7E.js +7 -0
- package/package.json +58 -0
- package/src/index.ts +82 -0
- package/src/qrcode.ts +164 -0
- package/src/server.ts +562 -0
- package/src/terminal.ts +163 -0
- package/src/tunnel.ts +258 -0
- package/src/types.ts +169 -0
- package/src/web-client.ts +657 -0
|
@@ -0,0 +1,2782 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__commonJS,
|
|
3
|
+
__require
|
|
4
|
+
} from "./chunk-MCKGQKYU.js";
|
|
5
|
+
|
|
6
|
+
// node_modules/node-pty/lib/utils.js
|
|
7
|
+
var require_utils = __commonJS({
|
|
8
|
+
"node_modules/node-pty/lib/utils.js"(exports) {
|
|
9
|
+
"use strict";
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.loadNativeModule = exports.assign = void 0;
|
|
12
|
+
function assign(target) {
|
|
13
|
+
var sources = [];
|
|
14
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
15
|
+
sources[_i - 1] = arguments[_i];
|
|
16
|
+
}
|
|
17
|
+
sources.forEach(function(source) {
|
|
18
|
+
return Object.keys(source).forEach(function(key) {
|
|
19
|
+
return target[key] = source[key];
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
return target;
|
|
23
|
+
}
|
|
24
|
+
exports.assign = assign;
|
|
25
|
+
function loadNativeModule(name) {
|
|
26
|
+
var dirs = ["build/Release", "build/Debug", "prebuilds/" + process.platform + "-" + process.arch];
|
|
27
|
+
var relative = ["..", "."];
|
|
28
|
+
var lastError;
|
|
29
|
+
for (var _i = 0, dirs_1 = dirs; _i < dirs_1.length; _i++) {
|
|
30
|
+
var d = dirs_1[_i];
|
|
31
|
+
for (var _a = 0, relative_1 = relative; _a < relative_1.length; _a++) {
|
|
32
|
+
var r = relative_1[_a];
|
|
33
|
+
var dir = r + "/" + d + "/";
|
|
34
|
+
try {
|
|
35
|
+
return { dir, module: __require(dir + "/" + name + ".node") };
|
|
36
|
+
} catch (e) {
|
|
37
|
+
lastError = e;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
throw new Error("Failed to load native module: " + name + ".node, checked: " + dirs.join(", ") + ": " + lastError);
|
|
42
|
+
}
|
|
43
|
+
exports.loadNativeModule = loadNativeModule;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// node_modules/node-pty/lib/eventEmitter2.js
|
|
48
|
+
var require_eventEmitter2 = __commonJS({
|
|
49
|
+
"node_modules/node-pty/lib/eventEmitter2.js"(exports) {
|
|
50
|
+
"use strict";
|
|
51
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
|
+
exports.EventEmitter2 = void 0;
|
|
53
|
+
var EventEmitter22 = (
|
|
54
|
+
/** @class */
|
|
55
|
+
(function() {
|
|
56
|
+
function EventEmitter23() {
|
|
57
|
+
this._listeners = [];
|
|
58
|
+
}
|
|
59
|
+
Object.defineProperty(EventEmitter23.prototype, "event", {
|
|
60
|
+
get: function() {
|
|
61
|
+
var _this = this;
|
|
62
|
+
if (!this._event) {
|
|
63
|
+
this._event = function(listener) {
|
|
64
|
+
_this._listeners.push(listener);
|
|
65
|
+
var disposable = {
|
|
66
|
+
dispose: function() {
|
|
67
|
+
for (var i = 0; i < _this._listeners.length; i++) {
|
|
68
|
+
if (_this._listeners[i] === listener) {
|
|
69
|
+
_this._listeners.splice(i, 1);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
return disposable;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return this._event;
|
|
79
|
+
},
|
|
80
|
+
enumerable: false,
|
|
81
|
+
configurable: true
|
|
82
|
+
});
|
|
83
|
+
EventEmitter23.prototype.fire = function(data) {
|
|
84
|
+
var queue = [];
|
|
85
|
+
for (var i = 0; i < this._listeners.length; i++) {
|
|
86
|
+
queue.push(this._listeners[i]);
|
|
87
|
+
}
|
|
88
|
+
for (var i = 0; i < queue.length; i++) {
|
|
89
|
+
queue[i].call(void 0, data);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
return EventEmitter23;
|
|
93
|
+
})()
|
|
94
|
+
);
|
|
95
|
+
exports.EventEmitter2 = EventEmitter22;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// node_modules/node-pty/lib/terminal.js
|
|
100
|
+
var require_terminal = __commonJS({
|
|
101
|
+
"node_modules/node-pty/lib/terminal.js"(exports) {
|
|
102
|
+
"use strict";
|
|
103
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
104
|
+
exports.Terminal = exports.DEFAULT_ROWS = exports.DEFAULT_COLS = void 0;
|
|
105
|
+
var events_1 = __require("events");
|
|
106
|
+
var eventEmitter2_1 = require_eventEmitter2();
|
|
107
|
+
exports.DEFAULT_COLS = 80;
|
|
108
|
+
exports.DEFAULT_ROWS = 24;
|
|
109
|
+
var FLOW_CONTROL_PAUSE = "";
|
|
110
|
+
var FLOW_CONTROL_RESUME = "";
|
|
111
|
+
var Terminal = (
|
|
112
|
+
/** @class */
|
|
113
|
+
(function() {
|
|
114
|
+
function Terminal2(opt) {
|
|
115
|
+
this._pid = 0;
|
|
116
|
+
this._fd = 0;
|
|
117
|
+
this._cols = 0;
|
|
118
|
+
this._rows = 0;
|
|
119
|
+
this._readable = false;
|
|
120
|
+
this._writable = false;
|
|
121
|
+
this._onData = new eventEmitter2_1.EventEmitter2();
|
|
122
|
+
this._onExit = new eventEmitter2_1.EventEmitter2();
|
|
123
|
+
this._internalee = new events_1.EventEmitter();
|
|
124
|
+
this.handleFlowControl = !!(opt === null || opt === void 0 ? void 0 : opt.handleFlowControl);
|
|
125
|
+
this._flowControlPause = (opt === null || opt === void 0 ? void 0 : opt.flowControlPause) || FLOW_CONTROL_PAUSE;
|
|
126
|
+
this._flowControlResume = (opt === null || opt === void 0 ? void 0 : opt.flowControlResume) || FLOW_CONTROL_RESUME;
|
|
127
|
+
if (!opt) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
this._checkType("name", opt.name ? opt.name : void 0, "string");
|
|
131
|
+
this._checkType("cols", opt.cols ? opt.cols : void 0, "number");
|
|
132
|
+
this._checkType("rows", opt.rows ? opt.rows : void 0, "number");
|
|
133
|
+
this._checkType("cwd", opt.cwd ? opt.cwd : void 0, "string");
|
|
134
|
+
this._checkType("env", opt.env ? opt.env : void 0, "object");
|
|
135
|
+
this._checkType("uid", opt.uid ? opt.uid : void 0, "number");
|
|
136
|
+
this._checkType("gid", opt.gid ? opt.gid : void 0, "number");
|
|
137
|
+
this._checkType("encoding", opt.encoding ? opt.encoding : void 0, "string");
|
|
138
|
+
}
|
|
139
|
+
Object.defineProperty(Terminal2.prototype, "onData", {
|
|
140
|
+
get: function() {
|
|
141
|
+
return this._onData.event;
|
|
142
|
+
},
|
|
143
|
+
enumerable: false,
|
|
144
|
+
configurable: true
|
|
145
|
+
});
|
|
146
|
+
Object.defineProperty(Terminal2.prototype, "onExit", {
|
|
147
|
+
get: function() {
|
|
148
|
+
return this._onExit.event;
|
|
149
|
+
},
|
|
150
|
+
enumerable: false,
|
|
151
|
+
configurable: true
|
|
152
|
+
});
|
|
153
|
+
Object.defineProperty(Terminal2.prototype, "pid", {
|
|
154
|
+
get: function() {
|
|
155
|
+
return this._pid;
|
|
156
|
+
},
|
|
157
|
+
enumerable: false,
|
|
158
|
+
configurable: true
|
|
159
|
+
});
|
|
160
|
+
Object.defineProperty(Terminal2.prototype, "cols", {
|
|
161
|
+
get: function() {
|
|
162
|
+
return this._cols;
|
|
163
|
+
},
|
|
164
|
+
enumerable: false,
|
|
165
|
+
configurable: true
|
|
166
|
+
});
|
|
167
|
+
Object.defineProperty(Terminal2.prototype, "rows", {
|
|
168
|
+
get: function() {
|
|
169
|
+
return this._rows;
|
|
170
|
+
},
|
|
171
|
+
enumerable: false,
|
|
172
|
+
configurable: true
|
|
173
|
+
});
|
|
174
|
+
Terminal2.prototype.write = function(data) {
|
|
175
|
+
if (this.handleFlowControl) {
|
|
176
|
+
if (data === this._flowControlPause) {
|
|
177
|
+
this.pause();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (data === this._flowControlResume) {
|
|
181
|
+
this.resume();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
this._write(data);
|
|
186
|
+
};
|
|
187
|
+
Terminal2.prototype._forwardEvents = function() {
|
|
188
|
+
var _this = this;
|
|
189
|
+
this.on("data", function(e) {
|
|
190
|
+
return _this._onData.fire(e);
|
|
191
|
+
});
|
|
192
|
+
this.on("exit", function(exitCode, signal) {
|
|
193
|
+
return _this._onExit.fire({ exitCode, signal });
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
Terminal2.prototype._checkType = function(name, value, type, allowArray) {
|
|
197
|
+
if (allowArray === void 0) {
|
|
198
|
+
allowArray = false;
|
|
199
|
+
}
|
|
200
|
+
if (value === void 0) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (allowArray) {
|
|
204
|
+
if (Array.isArray(value)) {
|
|
205
|
+
value.forEach(function(v, i) {
|
|
206
|
+
if (typeof v !== type) {
|
|
207
|
+
throw new Error(name + "[" + i + "] must be a " + type + " (not a " + typeof v[i] + ")");
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (typeof value !== type) {
|
|
214
|
+
throw new Error(name + " must be a " + type + " (not a " + typeof value + ")");
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
Terminal2.prototype.end = function(data) {
|
|
218
|
+
this._socket.end(data);
|
|
219
|
+
};
|
|
220
|
+
Terminal2.prototype.pipe = function(dest, options) {
|
|
221
|
+
return this._socket.pipe(dest, options);
|
|
222
|
+
};
|
|
223
|
+
Terminal2.prototype.pause = function() {
|
|
224
|
+
return this._socket.pause();
|
|
225
|
+
};
|
|
226
|
+
Terminal2.prototype.resume = function() {
|
|
227
|
+
return this._socket.resume();
|
|
228
|
+
};
|
|
229
|
+
Terminal2.prototype.setEncoding = function(encoding) {
|
|
230
|
+
if (this._socket._decoder) {
|
|
231
|
+
delete this._socket._decoder;
|
|
232
|
+
}
|
|
233
|
+
if (encoding) {
|
|
234
|
+
this._socket.setEncoding(encoding);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
Terminal2.prototype.addListener = function(eventName, listener) {
|
|
238
|
+
this.on(eventName, listener);
|
|
239
|
+
};
|
|
240
|
+
Terminal2.prototype.on = function(eventName, listener) {
|
|
241
|
+
if (eventName === "close") {
|
|
242
|
+
this._internalee.on("close", listener);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
this._socket.on(eventName, listener);
|
|
246
|
+
};
|
|
247
|
+
Terminal2.prototype.emit = function(eventName) {
|
|
248
|
+
var args = [];
|
|
249
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
250
|
+
args[_i - 1] = arguments[_i];
|
|
251
|
+
}
|
|
252
|
+
if (eventName === "close") {
|
|
253
|
+
return this._internalee.emit.apply(this._internalee, arguments);
|
|
254
|
+
}
|
|
255
|
+
return this._socket.emit.apply(this._socket, arguments);
|
|
256
|
+
};
|
|
257
|
+
Terminal2.prototype.listeners = function(eventName) {
|
|
258
|
+
return this._socket.listeners(eventName);
|
|
259
|
+
};
|
|
260
|
+
Terminal2.prototype.removeListener = function(eventName, listener) {
|
|
261
|
+
this._socket.removeListener(eventName, listener);
|
|
262
|
+
};
|
|
263
|
+
Terminal2.prototype.removeAllListeners = function(eventName) {
|
|
264
|
+
this._socket.removeAllListeners(eventName);
|
|
265
|
+
};
|
|
266
|
+
Terminal2.prototype.once = function(eventName, listener) {
|
|
267
|
+
this._socket.once(eventName, listener);
|
|
268
|
+
};
|
|
269
|
+
Terminal2.prototype._close = function() {
|
|
270
|
+
this._socket.readable = false;
|
|
271
|
+
this.write = function() {
|
|
272
|
+
};
|
|
273
|
+
this.end = function() {
|
|
274
|
+
};
|
|
275
|
+
this._writable = false;
|
|
276
|
+
this._readable = false;
|
|
277
|
+
};
|
|
278
|
+
Terminal2.prototype._parseEnv = function(env) {
|
|
279
|
+
var keys = Object.keys(env || {});
|
|
280
|
+
var pairs = [];
|
|
281
|
+
for (var i = 0; i < keys.length; i++) {
|
|
282
|
+
if (keys[i] === void 0) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
pairs.push(keys[i] + "=" + env[keys[i]]);
|
|
286
|
+
}
|
|
287
|
+
return pairs;
|
|
288
|
+
};
|
|
289
|
+
return Terminal2;
|
|
290
|
+
})()
|
|
291
|
+
);
|
|
292
|
+
exports.Terminal = Terminal;
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// node_modules/node-pty/lib/shared/conout.js
|
|
297
|
+
var require_conout = __commonJS({
|
|
298
|
+
"node_modules/node-pty/lib/shared/conout.js"(exports) {
|
|
299
|
+
"use strict";
|
|
300
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
301
|
+
exports.getWorkerPipeName = void 0;
|
|
302
|
+
function getWorkerPipeName(conoutPipeName) {
|
|
303
|
+
return conoutPipeName + "-worker";
|
|
304
|
+
}
|
|
305
|
+
exports.getWorkerPipeName = getWorkerPipeName;
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// node_modules/node-pty/lib/windowsConoutConnection.js
|
|
310
|
+
var require_windowsConoutConnection = __commonJS({
|
|
311
|
+
"node_modules/node-pty/lib/windowsConoutConnection.js"(exports) {
|
|
312
|
+
"use strict";
|
|
313
|
+
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
314
|
+
function adopt(value) {
|
|
315
|
+
return value instanceof P ? value : new P(function(resolve) {
|
|
316
|
+
resolve(value);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
return new (P || (P = Promise))(function(resolve, reject) {
|
|
320
|
+
function fulfilled(value) {
|
|
321
|
+
try {
|
|
322
|
+
step(generator.next(value));
|
|
323
|
+
} catch (e) {
|
|
324
|
+
reject(e);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function rejected(value) {
|
|
328
|
+
try {
|
|
329
|
+
step(generator["throw"](value));
|
|
330
|
+
} catch (e) {
|
|
331
|
+
reject(e);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function step(result) {
|
|
335
|
+
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
336
|
+
}
|
|
337
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
338
|
+
});
|
|
339
|
+
};
|
|
340
|
+
var __generator = exports && exports.__generator || function(thisArg, body) {
|
|
341
|
+
var _ = { label: 0, sent: function() {
|
|
342
|
+
if (t[0] & 1) throw t[1];
|
|
343
|
+
return t[1];
|
|
344
|
+
}, trys: [], ops: [] }, f, y, t, g;
|
|
345
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() {
|
|
346
|
+
return this;
|
|
347
|
+
}), g;
|
|
348
|
+
function verb(n) {
|
|
349
|
+
return function(v) {
|
|
350
|
+
return step([n, v]);
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
function step(op) {
|
|
354
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
355
|
+
while (_) try {
|
|
356
|
+
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;
|
|
357
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
358
|
+
switch (op[0]) {
|
|
359
|
+
case 0:
|
|
360
|
+
case 1:
|
|
361
|
+
t = op;
|
|
362
|
+
break;
|
|
363
|
+
case 4:
|
|
364
|
+
_.label++;
|
|
365
|
+
return { value: op[1], done: false };
|
|
366
|
+
case 5:
|
|
367
|
+
_.label++;
|
|
368
|
+
y = op[1];
|
|
369
|
+
op = [0];
|
|
370
|
+
continue;
|
|
371
|
+
case 7:
|
|
372
|
+
op = _.ops.pop();
|
|
373
|
+
_.trys.pop();
|
|
374
|
+
continue;
|
|
375
|
+
default:
|
|
376
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
377
|
+
_ = 0;
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
381
|
+
_.label = op[1];
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
385
|
+
_.label = t[1];
|
|
386
|
+
t = op;
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
if (t && _.label < t[2]) {
|
|
390
|
+
_.label = t[2];
|
|
391
|
+
_.ops.push(op);
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
if (t[2]) _.ops.pop();
|
|
395
|
+
_.trys.pop();
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
op = body.call(thisArg, _);
|
|
399
|
+
} catch (e) {
|
|
400
|
+
op = [6, e];
|
|
401
|
+
y = 0;
|
|
402
|
+
} finally {
|
|
403
|
+
f = t = 0;
|
|
404
|
+
}
|
|
405
|
+
if (op[0] & 5) throw op[1];
|
|
406
|
+
return { value: op[0] ? op[1] : void 0, done: true };
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
410
|
+
exports.ConoutConnection = void 0;
|
|
411
|
+
var worker_threads_1 = __require("worker_threads");
|
|
412
|
+
var conout_1 = require_conout();
|
|
413
|
+
var path_1 = __require("path");
|
|
414
|
+
var eventEmitter2_1 = require_eventEmitter2();
|
|
415
|
+
var FLUSH_DATA_INTERVAL = 1e3;
|
|
416
|
+
var ConoutConnection = (
|
|
417
|
+
/** @class */
|
|
418
|
+
(function() {
|
|
419
|
+
function ConoutConnection2(_conoutPipeName, _useConptyDll) {
|
|
420
|
+
var _this = this;
|
|
421
|
+
this._conoutPipeName = _conoutPipeName;
|
|
422
|
+
this._useConptyDll = _useConptyDll;
|
|
423
|
+
this._isDisposed = false;
|
|
424
|
+
this._onReady = new eventEmitter2_1.EventEmitter2();
|
|
425
|
+
var workerData = {
|
|
426
|
+
conoutPipeName: _conoutPipeName
|
|
427
|
+
};
|
|
428
|
+
var scriptPath = __dirname.replace("node_modules.asar", "node_modules.asar.unpacked");
|
|
429
|
+
this._worker = new worker_threads_1.Worker(path_1.join(scriptPath, "worker/conoutSocketWorker.js"), { workerData });
|
|
430
|
+
this._worker.on("message", function(message) {
|
|
431
|
+
switch (message) {
|
|
432
|
+
case 1:
|
|
433
|
+
_this._onReady.fire();
|
|
434
|
+
return;
|
|
435
|
+
default:
|
|
436
|
+
console.warn("Unexpected ConoutWorkerMessage", message);
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
Object.defineProperty(ConoutConnection2.prototype, "onReady", {
|
|
441
|
+
get: function() {
|
|
442
|
+
return this._onReady.event;
|
|
443
|
+
},
|
|
444
|
+
enumerable: false,
|
|
445
|
+
configurable: true
|
|
446
|
+
});
|
|
447
|
+
ConoutConnection2.prototype.dispose = function() {
|
|
448
|
+
if (!this._useConptyDll && this._isDisposed) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
this._isDisposed = true;
|
|
452
|
+
this._drainDataAndClose();
|
|
453
|
+
};
|
|
454
|
+
ConoutConnection2.prototype.connectSocket = function(socket) {
|
|
455
|
+
socket.connect(conout_1.getWorkerPipeName(this._conoutPipeName));
|
|
456
|
+
};
|
|
457
|
+
ConoutConnection2.prototype._drainDataAndClose = function() {
|
|
458
|
+
var _this = this;
|
|
459
|
+
if (this._drainTimeout) {
|
|
460
|
+
clearTimeout(this._drainTimeout);
|
|
461
|
+
}
|
|
462
|
+
this._drainTimeout = setTimeout(function() {
|
|
463
|
+
return _this._destroySocket();
|
|
464
|
+
}, FLUSH_DATA_INTERVAL);
|
|
465
|
+
};
|
|
466
|
+
ConoutConnection2.prototype._destroySocket = function() {
|
|
467
|
+
return __awaiter(this, void 0, void 0, function() {
|
|
468
|
+
return __generator(this, function(_a) {
|
|
469
|
+
switch (_a.label) {
|
|
470
|
+
case 0:
|
|
471
|
+
return [4, this._worker.terminate()];
|
|
472
|
+
case 1:
|
|
473
|
+
_a.sent();
|
|
474
|
+
return [
|
|
475
|
+
2
|
|
476
|
+
/*return*/
|
|
477
|
+
];
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
};
|
|
482
|
+
return ConoutConnection2;
|
|
483
|
+
})()
|
|
484
|
+
);
|
|
485
|
+
exports.ConoutConnection = ConoutConnection;
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
// node_modules/node-pty/lib/windowsPtyAgent.js
|
|
490
|
+
var require_windowsPtyAgent = __commonJS({
|
|
491
|
+
"node_modules/node-pty/lib/windowsPtyAgent.js"(exports) {
|
|
492
|
+
"use strict";
|
|
493
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
494
|
+
exports.argsToCommandLine = exports.WindowsPtyAgent = void 0;
|
|
495
|
+
var fs = __require("fs");
|
|
496
|
+
var os2 = __require("os");
|
|
497
|
+
var path = __require("path");
|
|
498
|
+
var child_process_1 = __require("child_process");
|
|
499
|
+
var net_1 = __require("net");
|
|
500
|
+
var windowsConoutConnection_1 = require_windowsConoutConnection();
|
|
501
|
+
var utils_1 = require_utils();
|
|
502
|
+
var conptyNative;
|
|
503
|
+
var winptyNative;
|
|
504
|
+
var FLUSH_DATA_INTERVAL = 1e3;
|
|
505
|
+
var WindowsPtyAgent = (
|
|
506
|
+
/** @class */
|
|
507
|
+
(function() {
|
|
508
|
+
function WindowsPtyAgent2(file, args, env, cwd, cols, rows, debug, _useConpty, _useConptyDll, conptyInheritCursor) {
|
|
509
|
+
var _this = this;
|
|
510
|
+
if (_useConptyDll === void 0) {
|
|
511
|
+
_useConptyDll = false;
|
|
512
|
+
}
|
|
513
|
+
if (conptyInheritCursor === void 0) {
|
|
514
|
+
conptyInheritCursor = false;
|
|
515
|
+
}
|
|
516
|
+
this._useConpty = _useConpty;
|
|
517
|
+
this._useConptyDll = _useConptyDll;
|
|
518
|
+
this._pid = 0;
|
|
519
|
+
this._innerPid = 0;
|
|
520
|
+
if (this._useConpty === void 0 || this._useConpty === true) {
|
|
521
|
+
this._useConpty = this._getWindowsBuildNumber() >= 18309;
|
|
522
|
+
}
|
|
523
|
+
if (this._useConpty) {
|
|
524
|
+
if (!conptyNative) {
|
|
525
|
+
conptyNative = utils_1.loadNativeModule("conpty").module;
|
|
526
|
+
}
|
|
527
|
+
} else {
|
|
528
|
+
if (!winptyNative) {
|
|
529
|
+
winptyNative = utils_1.loadNativeModule("pty").module;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
this._ptyNative = this._useConpty ? conptyNative : winptyNative;
|
|
533
|
+
cwd = path.resolve(cwd);
|
|
534
|
+
var commandLine = argsToCommandLine(file, args);
|
|
535
|
+
var term;
|
|
536
|
+
if (this._useConpty) {
|
|
537
|
+
term = this._ptyNative.startProcess(file, cols, rows, debug, this._generatePipeName(), conptyInheritCursor, this._useConptyDll);
|
|
538
|
+
} else {
|
|
539
|
+
term = this._ptyNative.startProcess(file, commandLine, env, cwd, cols, rows, debug);
|
|
540
|
+
this._pid = term.pid;
|
|
541
|
+
this._innerPid = term.innerPid;
|
|
542
|
+
}
|
|
543
|
+
this._fd = term.fd;
|
|
544
|
+
this._pty = term.pty;
|
|
545
|
+
this._outSocket = new net_1.Socket();
|
|
546
|
+
this._outSocket.setEncoding("utf8");
|
|
547
|
+
this._conoutSocketWorker = new windowsConoutConnection_1.ConoutConnection(term.conout, this._useConptyDll);
|
|
548
|
+
this._conoutSocketWorker.onReady(function() {
|
|
549
|
+
_this._conoutSocketWorker.connectSocket(_this._outSocket);
|
|
550
|
+
});
|
|
551
|
+
this._outSocket.on("connect", function() {
|
|
552
|
+
_this._outSocket.emit("ready_datapipe");
|
|
553
|
+
});
|
|
554
|
+
var inSocketFD = fs.openSync(term.conin, "w");
|
|
555
|
+
this._inSocket = new net_1.Socket({
|
|
556
|
+
fd: inSocketFD,
|
|
557
|
+
readable: false,
|
|
558
|
+
writable: true
|
|
559
|
+
});
|
|
560
|
+
this._inSocket.setEncoding("utf8");
|
|
561
|
+
if (this._useConpty) {
|
|
562
|
+
var connect = this._ptyNative.connect(this._pty, commandLine, cwd, env, this._useConptyDll, function(c) {
|
|
563
|
+
return _this._$onProcessExit(c);
|
|
564
|
+
});
|
|
565
|
+
this._innerPid = connect.pid;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
Object.defineProperty(WindowsPtyAgent2.prototype, "inSocket", {
|
|
569
|
+
get: function() {
|
|
570
|
+
return this._inSocket;
|
|
571
|
+
},
|
|
572
|
+
enumerable: false,
|
|
573
|
+
configurable: true
|
|
574
|
+
});
|
|
575
|
+
Object.defineProperty(WindowsPtyAgent2.prototype, "outSocket", {
|
|
576
|
+
get: function() {
|
|
577
|
+
return this._outSocket;
|
|
578
|
+
},
|
|
579
|
+
enumerable: false,
|
|
580
|
+
configurable: true
|
|
581
|
+
});
|
|
582
|
+
Object.defineProperty(WindowsPtyAgent2.prototype, "fd", {
|
|
583
|
+
get: function() {
|
|
584
|
+
return this._fd;
|
|
585
|
+
},
|
|
586
|
+
enumerable: false,
|
|
587
|
+
configurable: true
|
|
588
|
+
});
|
|
589
|
+
Object.defineProperty(WindowsPtyAgent2.prototype, "innerPid", {
|
|
590
|
+
get: function() {
|
|
591
|
+
return this._innerPid;
|
|
592
|
+
},
|
|
593
|
+
enumerable: false,
|
|
594
|
+
configurable: true
|
|
595
|
+
});
|
|
596
|
+
Object.defineProperty(WindowsPtyAgent2.prototype, "pty", {
|
|
597
|
+
get: function() {
|
|
598
|
+
return this._pty;
|
|
599
|
+
},
|
|
600
|
+
enumerable: false,
|
|
601
|
+
configurable: true
|
|
602
|
+
});
|
|
603
|
+
WindowsPtyAgent2.prototype.resize = function(cols, rows) {
|
|
604
|
+
if (this._useConpty) {
|
|
605
|
+
if (this._exitCode !== void 0) {
|
|
606
|
+
throw new Error("Cannot resize a pty that has already exited");
|
|
607
|
+
}
|
|
608
|
+
this._ptyNative.resize(this._pty, cols, rows, this._useConptyDll);
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
this._ptyNative.resize(this._pid, cols, rows);
|
|
612
|
+
};
|
|
613
|
+
WindowsPtyAgent2.prototype.clear = function() {
|
|
614
|
+
if (this._useConpty) {
|
|
615
|
+
this._ptyNative.clear(this._pty, this._useConptyDll);
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
WindowsPtyAgent2.prototype.kill = function() {
|
|
619
|
+
var _this = this;
|
|
620
|
+
if (this._useConpty) {
|
|
621
|
+
if (!this._useConptyDll) {
|
|
622
|
+
this._inSocket.readable = false;
|
|
623
|
+
this._outSocket.readable = false;
|
|
624
|
+
this._getConsoleProcessList().then(function(consoleProcessList) {
|
|
625
|
+
consoleProcessList.forEach(function(pid) {
|
|
626
|
+
try {
|
|
627
|
+
process.kill(pid);
|
|
628
|
+
} catch (e) {
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
});
|
|
632
|
+
this._ptyNative.kill(this._pty, this._useConptyDll);
|
|
633
|
+
this._conoutSocketWorker.dispose();
|
|
634
|
+
} else {
|
|
635
|
+
this._inSocket.destroy();
|
|
636
|
+
this._ptyNative.kill(this._pty, this._useConptyDll);
|
|
637
|
+
this._outSocket.on("data", function() {
|
|
638
|
+
_this._conoutSocketWorker.dispose();
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
} else {
|
|
642
|
+
var processList = this._ptyNative.getProcessList(this._pid);
|
|
643
|
+
this._ptyNative.kill(this._pid, this._innerPid);
|
|
644
|
+
processList.forEach(function(pid) {
|
|
645
|
+
try {
|
|
646
|
+
process.kill(pid);
|
|
647
|
+
} catch (e) {
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
WindowsPtyAgent2.prototype._getConsoleProcessList = function() {
|
|
653
|
+
var _this = this;
|
|
654
|
+
return new Promise(function(resolve) {
|
|
655
|
+
var agent = child_process_1.fork(path.join(__dirname, "conpty_console_list_agent"), [_this._innerPid.toString()]);
|
|
656
|
+
agent.on("message", function(message) {
|
|
657
|
+
clearTimeout(timeout);
|
|
658
|
+
resolve(message.consoleProcessList);
|
|
659
|
+
});
|
|
660
|
+
var timeout = setTimeout(function() {
|
|
661
|
+
agent.kill();
|
|
662
|
+
resolve([_this._innerPid]);
|
|
663
|
+
}, 5e3);
|
|
664
|
+
});
|
|
665
|
+
};
|
|
666
|
+
Object.defineProperty(WindowsPtyAgent2.prototype, "exitCode", {
|
|
667
|
+
get: function() {
|
|
668
|
+
if (this._useConpty) {
|
|
669
|
+
return this._exitCode;
|
|
670
|
+
}
|
|
671
|
+
var winptyExitCode = this._ptyNative.getExitCode(this._innerPid);
|
|
672
|
+
return winptyExitCode === -1 ? void 0 : winptyExitCode;
|
|
673
|
+
},
|
|
674
|
+
enumerable: false,
|
|
675
|
+
configurable: true
|
|
676
|
+
});
|
|
677
|
+
WindowsPtyAgent2.prototype._getWindowsBuildNumber = function() {
|
|
678
|
+
var osVersion = /(\d+)\.(\d+)\.(\d+)/g.exec(os2.release());
|
|
679
|
+
var buildNumber = 0;
|
|
680
|
+
if (osVersion && osVersion.length === 4) {
|
|
681
|
+
buildNumber = parseInt(osVersion[3]);
|
|
682
|
+
}
|
|
683
|
+
return buildNumber;
|
|
684
|
+
};
|
|
685
|
+
WindowsPtyAgent2.prototype._generatePipeName = function() {
|
|
686
|
+
return "conpty-" + Math.random() * 1e7;
|
|
687
|
+
};
|
|
688
|
+
WindowsPtyAgent2.prototype._$onProcessExit = function(exitCode) {
|
|
689
|
+
var _this = this;
|
|
690
|
+
this._exitCode = exitCode;
|
|
691
|
+
if (!this._useConptyDll) {
|
|
692
|
+
this._flushDataAndCleanUp();
|
|
693
|
+
this._outSocket.on("data", function() {
|
|
694
|
+
return _this._flushDataAndCleanUp();
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
WindowsPtyAgent2.prototype._flushDataAndCleanUp = function() {
|
|
699
|
+
var _this = this;
|
|
700
|
+
if (this._useConptyDll) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
if (this._closeTimeout) {
|
|
704
|
+
clearTimeout(this._closeTimeout);
|
|
705
|
+
}
|
|
706
|
+
this._closeTimeout = setTimeout(function() {
|
|
707
|
+
return _this._cleanUpProcess();
|
|
708
|
+
}, FLUSH_DATA_INTERVAL);
|
|
709
|
+
};
|
|
710
|
+
WindowsPtyAgent2.prototype._cleanUpProcess = function() {
|
|
711
|
+
if (this._useConptyDll) {
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
this._inSocket.readable = false;
|
|
715
|
+
this._outSocket.readable = false;
|
|
716
|
+
this._outSocket.destroy();
|
|
717
|
+
};
|
|
718
|
+
return WindowsPtyAgent2;
|
|
719
|
+
})()
|
|
720
|
+
);
|
|
721
|
+
exports.WindowsPtyAgent = WindowsPtyAgent;
|
|
722
|
+
function argsToCommandLine(file, args) {
|
|
723
|
+
if (isCommandLine(args)) {
|
|
724
|
+
if (args.length === 0) {
|
|
725
|
+
return file;
|
|
726
|
+
}
|
|
727
|
+
return argsToCommandLine(file, []) + " " + args;
|
|
728
|
+
}
|
|
729
|
+
var argv = [file];
|
|
730
|
+
Array.prototype.push.apply(argv, args);
|
|
731
|
+
var result = "";
|
|
732
|
+
for (var argIndex = 0; argIndex < argv.length; argIndex++) {
|
|
733
|
+
if (argIndex > 0) {
|
|
734
|
+
result += " ";
|
|
735
|
+
}
|
|
736
|
+
var arg = argv[argIndex];
|
|
737
|
+
var hasLopsidedEnclosingQuote = xOr(arg[0] !== '"', arg[arg.length - 1] !== '"');
|
|
738
|
+
var hasNoEnclosingQuotes = arg[0] !== '"' && arg[arg.length - 1] !== '"';
|
|
739
|
+
var quote = arg === "" || (arg.indexOf(" ") !== -1 || arg.indexOf(" ") !== -1) && (arg.length > 1 && (hasLopsidedEnclosingQuote || hasNoEnclosingQuotes));
|
|
740
|
+
if (quote) {
|
|
741
|
+
result += '"';
|
|
742
|
+
}
|
|
743
|
+
var bsCount = 0;
|
|
744
|
+
for (var i = 0; i < arg.length; i++) {
|
|
745
|
+
var p = arg[i];
|
|
746
|
+
if (p === "\\") {
|
|
747
|
+
bsCount++;
|
|
748
|
+
} else if (p === '"') {
|
|
749
|
+
result += repeatText("\\", bsCount * 2 + 1);
|
|
750
|
+
result += '"';
|
|
751
|
+
bsCount = 0;
|
|
752
|
+
} else {
|
|
753
|
+
result += repeatText("\\", bsCount);
|
|
754
|
+
bsCount = 0;
|
|
755
|
+
result += p;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
if (quote) {
|
|
759
|
+
result += repeatText("\\", bsCount * 2);
|
|
760
|
+
result += '"';
|
|
761
|
+
} else {
|
|
762
|
+
result += repeatText("\\", bsCount);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return result;
|
|
766
|
+
}
|
|
767
|
+
exports.argsToCommandLine = argsToCommandLine;
|
|
768
|
+
function isCommandLine(args) {
|
|
769
|
+
return typeof args === "string";
|
|
770
|
+
}
|
|
771
|
+
function repeatText(text, count) {
|
|
772
|
+
var result = "";
|
|
773
|
+
for (var i = 0; i < count; i++) {
|
|
774
|
+
result += text;
|
|
775
|
+
}
|
|
776
|
+
return result;
|
|
777
|
+
}
|
|
778
|
+
function xOr(arg1, arg2) {
|
|
779
|
+
return arg1 && !arg2 || !arg1 && arg2;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
// node_modules/node-pty/lib/windowsTerminal.js
|
|
785
|
+
var require_windowsTerminal = __commonJS({
|
|
786
|
+
"node_modules/node-pty/lib/windowsTerminal.js"(exports) {
|
|
787
|
+
"use strict";
|
|
788
|
+
var __extends = exports && exports.__extends || /* @__PURE__ */ (function() {
|
|
789
|
+
var extendStatics = function(d, b) {
|
|
790
|
+
extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d2, b2) {
|
|
791
|
+
d2.__proto__ = b2;
|
|
792
|
+
} || function(d2, b2) {
|
|
793
|
+
for (var p in b2) if (b2.hasOwnProperty(p)) d2[p] = b2[p];
|
|
794
|
+
};
|
|
795
|
+
return extendStatics(d, b);
|
|
796
|
+
};
|
|
797
|
+
return function(d, b) {
|
|
798
|
+
extendStatics(d, b);
|
|
799
|
+
function __() {
|
|
800
|
+
this.constructor = d;
|
|
801
|
+
}
|
|
802
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
803
|
+
};
|
|
804
|
+
})();
|
|
805
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
806
|
+
exports.WindowsTerminal = void 0;
|
|
807
|
+
var terminal_1 = require_terminal();
|
|
808
|
+
var windowsPtyAgent_1 = require_windowsPtyAgent();
|
|
809
|
+
var utils_1 = require_utils();
|
|
810
|
+
var DEFAULT_FILE = "cmd.exe";
|
|
811
|
+
var DEFAULT_NAME = "Windows Shell";
|
|
812
|
+
var WindowsTerminal = (
|
|
813
|
+
/** @class */
|
|
814
|
+
(function(_super) {
|
|
815
|
+
__extends(WindowsTerminal2, _super);
|
|
816
|
+
function WindowsTerminal2(file, args, opt) {
|
|
817
|
+
var _this = _super.call(this, opt) || this;
|
|
818
|
+
_this._checkType("args", args, "string", true);
|
|
819
|
+
args = args || [];
|
|
820
|
+
file = file || DEFAULT_FILE;
|
|
821
|
+
opt = opt || {};
|
|
822
|
+
opt.env = opt.env || process.env;
|
|
823
|
+
if (opt.encoding) {
|
|
824
|
+
console.warn("Setting encoding on Windows is not supported");
|
|
825
|
+
}
|
|
826
|
+
var env = utils_1.assign({}, opt.env);
|
|
827
|
+
_this._cols = opt.cols || terminal_1.DEFAULT_COLS;
|
|
828
|
+
_this._rows = opt.rows || terminal_1.DEFAULT_ROWS;
|
|
829
|
+
var cwd = opt.cwd || process.cwd();
|
|
830
|
+
var name = opt.name || env.TERM || DEFAULT_NAME;
|
|
831
|
+
var parsedEnv = _this._parseEnv(env);
|
|
832
|
+
_this._isReady = false;
|
|
833
|
+
_this._deferreds = [];
|
|
834
|
+
_this._agent = new windowsPtyAgent_1.WindowsPtyAgent(file, args, parsedEnv, cwd, _this._cols, _this._rows, false, opt.useConpty, opt.useConptyDll, opt.conptyInheritCursor);
|
|
835
|
+
_this._socket = _this._agent.outSocket;
|
|
836
|
+
_this._pid = _this._agent.innerPid;
|
|
837
|
+
_this._fd = _this._agent.fd;
|
|
838
|
+
_this._pty = _this._agent.pty;
|
|
839
|
+
_this._socket.on("ready_datapipe", function() {
|
|
840
|
+
_this._socket.once("data", function() {
|
|
841
|
+
if (!_this._isReady) {
|
|
842
|
+
_this._isReady = true;
|
|
843
|
+
_this._deferreds.forEach(function(fn) {
|
|
844
|
+
fn.run();
|
|
845
|
+
});
|
|
846
|
+
_this._deferreds = [];
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
_this._socket.on("error", function(err) {
|
|
850
|
+
_this._close();
|
|
851
|
+
if (err.code) {
|
|
852
|
+
if (~err.code.indexOf("errno 5") || ~err.code.indexOf("EIO"))
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
if (_this.listeners("error").length < 2) {
|
|
856
|
+
throw err;
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
_this._socket.on("close", function() {
|
|
860
|
+
_this.emit("exit", _this._agent.exitCode);
|
|
861
|
+
_this._close();
|
|
862
|
+
});
|
|
863
|
+
});
|
|
864
|
+
_this._file = file;
|
|
865
|
+
_this._name = name;
|
|
866
|
+
_this._readable = true;
|
|
867
|
+
_this._writable = true;
|
|
868
|
+
_this._forwardEvents();
|
|
869
|
+
return _this;
|
|
870
|
+
}
|
|
871
|
+
WindowsTerminal2.prototype._write = function(data) {
|
|
872
|
+
this._defer(this._doWrite, data);
|
|
873
|
+
};
|
|
874
|
+
WindowsTerminal2.prototype._doWrite = function(data) {
|
|
875
|
+
this._agent.inSocket.write(data);
|
|
876
|
+
};
|
|
877
|
+
WindowsTerminal2.open = function(options) {
|
|
878
|
+
throw new Error("open() not supported on windows, use Fork() instead.");
|
|
879
|
+
};
|
|
880
|
+
WindowsTerminal2.prototype.resize = function(cols, rows) {
|
|
881
|
+
var _this = this;
|
|
882
|
+
if (cols <= 0 || rows <= 0 || isNaN(cols) || isNaN(rows) || cols === Infinity || rows === Infinity) {
|
|
883
|
+
throw new Error("resizing must be done using positive cols and rows");
|
|
884
|
+
}
|
|
885
|
+
this._deferNoArgs(function() {
|
|
886
|
+
_this._agent.resize(cols, rows);
|
|
887
|
+
_this._cols = cols;
|
|
888
|
+
_this._rows = rows;
|
|
889
|
+
});
|
|
890
|
+
};
|
|
891
|
+
WindowsTerminal2.prototype.clear = function() {
|
|
892
|
+
var _this = this;
|
|
893
|
+
this._deferNoArgs(function() {
|
|
894
|
+
_this._agent.clear();
|
|
895
|
+
});
|
|
896
|
+
};
|
|
897
|
+
WindowsTerminal2.prototype.destroy = function() {
|
|
898
|
+
var _this = this;
|
|
899
|
+
this._deferNoArgs(function() {
|
|
900
|
+
_this.kill();
|
|
901
|
+
});
|
|
902
|
+
};
|
|
903
|
+
WindowsTerminal2.prototype.kill = function(signal) {
|
|
904
|
+
var _this = this;
|
|
905
|
+
this._deferNoArgs(function() {
|
|
906
|
+
if (signal) {
|
|
907
|
+
throw new Error("Signals not supported on windows.");
|
|
908
|
+
}
|
|
909
|
+
_this._close();
|
|
910
|
+
_this._agent.kill();
|
|
911
|
+
});
|
|
912
|
+
};
|
|
913
|
+
WindowsTerminal2.prototype._deferNoArgs = function(deferredFn) {
|
|
914
|
+
var _this = this;
|
|
915
|
+
if (this._isReady) {
|
|
916
|
+
deferredFn.call(this);
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
this._deferreds.push({
|
|
920
|
+
run: function() {
|
|
921
|
+
return deferredFn.call(_this);
|
|
922
|
+
}
|
|
923
|
+
});
|
|
924
|
+
};
|
|
925
|
+
WindowsTerminal2.prototype._defer = function(deferredFn, arg) {
|
|
926
|
+
var _this = this;
|
|
927
|
+
if (this._isReady) {
|
|
928
|
+
deferredFn.call(this, arg);
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
this._deferreds.push({
|
|
932
|
+
run: function() {
|
|
933
|
+
return deferredFn.call(_this, arg);
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
};
|
|
937
|
+
Object.defineProperty(WindowsTerminal2.prototype, "process", {
|
|
938
|
+
get: function() {
|
|
939
|
+
return this._name;
|
|
940
|
+
},
|
|
941
|
+
enumerable: false,
|
|
942
|
+
configurable: true
|
|
943
|
+
});
|
|
944
|
+
Object.defineProperty(WindowsTerminal2.prototype, "master", {
|
|
945
|
+
get: function() {
|
|
946
|
+
throw new Error("master is not supported on Windows");
|
|
947
|
+
},
|
|
948
|
+
enumerable: false,
|
|
949
|
+
configurable: true
|
|
950
|
+
});
|
|
951
|
+
Object.defineProperty(WindowsTerminal2.prototype, "slave", {
|
|
952
|
+
get: function() {
|
|
953
|
+
throw new Error("slave is not supported on Windows");
|
|
954
|
+
},
|
|
955
|
+
enumerable: false,
|
|
956
|
+
configurable: true
|
|
957
|
+
});
|
|
958
|
+
return WindowsTerminal2;
|
|
959
|
+
})(terminal_1.Terminal)
|
|
960
|
+
);
|
|
961
|
+
exports.WindowsTerminal = WindowsTerminal;
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
// node_modules/node-pty/lib/unixTerminal.js
|
|
966
|
+
var require_unixTerminal = __commonJS({
|
|
967
|
+
"node_modules/node-pty/lib/unixTerminal.js"(exports) {
|
|
968
|
+
"use strict";
|
|
969
|
+
var __extends = exports && exports.__extends || /* @__PURE__ */ (function() {
|
|
970
|
+
var extendStatics = function(d, b) {
|
|
971
|
+
extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d2, b2) {
|
|
972
|
+
d2.__proto__ = b2;
|
|
973
|
+
} || function(d2, b2) {
|
|
974
|
+
for (var p in b2) if (b2.hasOwnProperty(p)) d2[p] = b2[p];
|
|
975
|
+
};
|
|
976
|
+
return extendStatics(d, b);
|
|
977
|
+
};
|
|
978
|
+
return function(d, b) {
|
|
979
|
+
extendStatics(d, b);
|
|
980
|
+
function __() {
|
|
981
|
+
this.constructor = d;
|
|
982
|
+
}
|
|
983
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
984
|
+
};
|
|
985
|
+
})();
|
|
986
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
987
|
+
exports.UnixTerminal = void 0;
|
|
988
|
+
var fs = __require("fs");
|
|
989
|
+
var path = __require("path");
|
|
990
|
+
var tty = __require("tty");
|
|
991
|
+
var terminal_1 = require_terminal();
|
|
992
|
+
var utils_1 = require_utils();
|
|
993
|
+
var native = utils_1.loadNativeModule("pty");
|
|
994
|
+
var pty2 = native.module;
|
|
995
|
+
var helperPath = native.dir + "/spawn-helper";
|
|
996
|
+
helperPath = path.resolve(__dirname, helperPath);
|
|
997
|
+
helperPath = helperPath.replace("app.asar", "app.asar.unpacked");
|
|
998
|
+
helperPath = helperPath.replace("node_modules.asar", "node_modules.asar.unpacked");
|
|
999
|
+
var DEFAULT_FILE = "sh";
|
|
1000
|
+
var DEFAULT_NAME = "xterm";
|
|
1001
|
+
var DESTROY_SOCKET_TIMEOUT_MS = 200;
|
|
1002
|
+
var UnixTerminal = (
|
|
1003
|
+
/** @class */
|
|
1004
|
+
(function(_super) {
|
|
1005
|
+
__extends(UnixTerminal2, _super);
|
|
1006
|
+
function UnixTerminal2(file, args, opt) {
|
|
1007
|
+
var _a, _b;
|
|
1008
|
+
var _this = _super.call(this, opt) || this;
|
|
1009
|
+
_this._boundClose = false;
|
|
1010
|
+
_this._emittedClose = false;
|
|
1011
|
+
if (typeof args === "string") {
|
|
1012
|
+
throw new Error("args as a string is not supported on unix.");
|
|
1013
|
+
}
|
|
1014
|
+
args = args || [];
|
|
1015
|
+
file = file || DEFAULT_FILE;
|
|
1016
|
+
opt = opt || {};
|
|
1017
|
+
opt.env = opt.env || process.env;
|
|
1018
|
+
_this._cols = opt.cols || terminal_1.DEFAULT_COLS;
|
|
1019
|
+
_this._rows = opt.rows || terminal_1.DEFAULT_ROWS;
|
|
1020
|
+
var uid = (_a = opt.uid) !== null && _a !== void 0 ? _a : -1;
|
|
1021
|
+
var gid = (_b = opt.gid) !== null && _b !== void 0 ? _b : -1;
|
|
1022
|
+
var env = utils_1.assign({}, opt.env);
|
|
1023
|
+
if (opt.env === process.env) {
|
|
1024
|
+
_this._sanitizeEnv(env);
|
|
1025
|
+
}
|
|
1026
|
+
var cwd = opt.cwd || process.cwd();
|
|
1027
|
+
env.PWD = cwd;
|
|
1028
|
+
var name = opt.name || env.TERM || DEFAULT_NAME;
|
|
1029
|
+
env.TERM = name;
|
|
1030
|
+
var parsedEnv = _this._parseEnv(env);
|
|
1031
|
+
var encoding = opt.encoding === void 0 ? "utf8" : opt.encoding;
|
|
1032
|
+
var onexit = function(code, signal) {
|
|
1033
|
+
if (!_this._emittedClose) {
|
|
1034
|
+
if (_this._boundClose) {
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
_this._boundClose = true;
|
|
1038
|
+
var timeout_1 = setTimeout(function() {
|
|
1039
|
+
timeout_1 = null;
|
|
1040
|
+
_this._socket.destroy();
|
|
1041
|
+
}, DESTROY_SOCKET_TIMEOUT_MS);
|
|
1042
|
+
_this.once("close", function() {
|
|
1043
|
+
if (timeout_1 !== null) {
|
|
1044
|
+
clearTimeout(timeout_1);
|
|
1045
|
+
}
|
|
1046
|
+
_this.emit("exit", code, signal);
|
|
1047
|
+
});
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
_this.emit("exit", code, signal);
|
|
1051
|
+
};
|
|
1052
|
+
var term = pty2.fork(file, args, parsedEnv, cwd, _this._cols, _this._rows, uid, gid, encoding === "utf8", helperPath, onexit);
|
|
1053
|
+
_this._socket = new tty.ReadStream(term.fd);
|
|
1054
|
+
if (encoding !== null) {
|
|
1055
|
+
_this._socket.setEncoding(encoding);
|
|
1056
|
+
}
|
|
1057
|
+
_this._writeStream = new CustomWriteStream(term.fd, encoding || void 0);
|
|
1058
|
+
_this._socket.on("error", function(err) {
|
|
1059
|
+
if (err.code) {
|
|
1060
|
+
if (~err.code.indexOf("EAGAIN")) {
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
_this._close();
|
|
1065
|
+
if (!_this._emittedClose) {
|
|
1066
|
+
_this._emittedClose = true;
|
|
1067
|
+
_this.emit("close");
|
|
1068
|
+
}
|
|
1069
|
+
if (err.code) {
|
|
1070
|
+
if (~err.code.indexOf("errno 5") || ~err.code.indexOf("EIO")) {
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
if (_this.listeners("error").length < 2) {
|
|
1075
|
+
throw err;
|
|
1076
|
+
}
|
|
1077
|
+
});
|
|
1078
|
+
_this._pid = term.pid;
|
|
1079
|
+
_this._fd = term.fd;
|
|
1080
|
+
_this._pty = term.pty;
|
|
1081
|
+
_this._file = file;
|
|
1082
|
+
_this._name = name;
|
|
1083
|
+
_this._readable = true;
|
|
1084
|
+
_this._writable = true;
|
|
1085
|
+
_this._socket.on("close", function() {
|
|
1086
|
+
if (_this._emittedClose) {
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
_this._emittedClose = true;
|
|
1090
|
+
_this._close();
|
|
1091
|
+
_this.emit("close");
|
|
1092
|
+
});
|
|
1093
|
+
_this._forwardEvents();
|
|
1094
|
+
return _this;
|
|
1095
|
+
}
|
|
1096
|
+
Object.defineProperty(UnixTerminal2.prototype, "master", {
|
|
1097
|
+
get: function() {
|
|
1098
|
+
return this._master;
|
|
1099
|
+
},
|
|
1100
|
+
enumerable: false,
|
|
1101
|
+
configurable: true
|
|
1102
|
+
});
|
|
1103
|
+
Object.defineProperty(UnixTerminal2.prototype, "slave", {
|
|
1104
|
+
get: function() {
|
|
1105
|
+
return this._slave;
|
|
1106
|
+
},
|
|
1107
|
+
enumerable: false,
|
|
1108
|
+
configurable: true
|
|
1109
|
+
});
|
|
1110
|
+
UnixTerminal2.prototype._write = function(data) {
|
|
1111
|
+
this._writeStream.write(data);
|
|
1112
|
+
};
|
|
1113
|
+
Object.defineProperty(UnixTerminal2.prototype, "fd", {
|
|
1114
|
+
/* Accessors */
|
|
1115
|
+
get: function() {
|
|
1116
|
+
return this._fd;
|
|
1117
|
+
},
|
|
1118
|
+
enumerable: false,
|
|
1119
|
+
configurable: true
|
|
1120
|
+
});
|
|
1121
|
+
Object.defineProperty(UnixTerminal2.prototype, "ptsName", {
|
|
1122
|
+
get: function() {
|
|
1123
|
+
return this._pty;
|
|
1124
|
+
},
|
|
1125
|
+
enumerable: false,
|
|
1126
|
+
configurable: true
|
|
1127
|
+
});
|
|
1128
|
+
UnixTerminal2.open = function(opt) {
|
|
1129
|
+
var self = Object.create(UnixTerminal2.prototype);
|
|
1130
|
+
opt = opt || {};
|
|
1131
|
+
if (arguments.length > 1) {
|
|
1132
|
+
opt = {
|
|
1133
|
+
cols: arguments[1],
|
|
1134
|
+
rows: arguments[2]
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
var cols = opt.cols || terminal_1.DEFAULT_COLS;
|
|
1138
|
+
var rows = opt.rows || terminal_1.DEFAULT_ROWS;
|
|
1139
|
+
var encoding = opt.encoding === void 0 ? "utf8" : opt.encoding;
|
|
1140
|
+
var term = pty2.open(cols, rows);
|
|
1141
|
+
self._master = new tty.ReadStream(term.master);
|
|
1142
|
+
if (encoding !== null) {
|
|
1143
|
+
self._master.setEncoding(encoding);
|
|
1144
|
+
}
|
|
1145
|
+
self._master.resume();
|
|
1146
|
+
self._slave = new tty.ReadStream(term.slave);
|
|
1147
|
+
if (encoding !== null) {
|
|
1148
|
+
self._slave.setEncoding(encoding);
|
|
1149
|
+
}
|
|
1150
|
+
self._slave.resume();
|
|
1151
|
+
self._socket = self._master;
|
|
1152
|
+
self._pid = -1;
|
|
1153
|
+
self._fd = term.master;
|
|
1154
|
+
self._pty = term.pty;
|
|
1155
|
+
self._file = process.argv[0] || "node";
|
|
1156
|
+
self._name = process.env.TERM || "";
|
|
1157
|
+
self._readable = true;
|
|
1158
|
+
self._writable = true;
|
|
1159
|
+
self._socket.on("error", function(err) {
|
|
1160
|
+
self._close();
|
|
1161
|
+
if (self.listeners("error").length < 2) {
|
|
1162
|
+
throw err;
|
|
1163
|
+
}
|
|
1164
|
+
});
|
|
1165
|
+
self._socket.on("close", function() {
|
|
1166
|
+
self._close();
|
|
1167
|
+
});
|
|
1168
|
+
return self;
|
|
1169
|
+
};
|
|
1170
|
+
UnixTerminal2.prototype.destroy = function() {
|
|
1171
|
+
var _this = this;
|
|
1172
|
+
this._close();
|
|
1173
|
+
this._socket.once("close", function() {
|
|
1174
|
+
_this.kill("SIGHUP");
|
|
1175
|
+
});
|
|
1176
|
+
this._socket.destroy();
|
|
1177
|
+
this._writeStream.dispose();
|
|
1178
|
+
};
|
|
1179
|
+
UnixTerminal2.prototype.kill = function(signal) {
|
|
1180
|
+
try {
|
|
1181
|
+
process.kill(this.pid, signal || "SIGHUP");
|
|
1182
|
+
} catch (e) {
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
Object.defineProperty(UnixTerminal2.prototype, "process", {
|
|
1186
|
+
/**
|
|
1187
|
+
* Gets the name of the process.
|
|
1188
|
+
*/
|
|
1189
|
+
get: function() {
|
|
1190
|
+
if (process.platform === "darwin") {
|
|
1191
|
+
var title = pty2.process(this._fd);
|
|
1192
|
+
return title !== "kernel_task" ? title : this._file;
|
|
1193
|
+
}
|
|
1194
|
+
return pty2.process(this._fd, this._pty) || this._file;
|
|
1195
|
+
},
|
|
1196
|
+
enumerable: false,
|
|
1197
|
+
configurable: true
|
|
1198
|
+
});
|
|
1199
|
+
UnixTerminal2.prototype.resize = function(cols, rows) {
|
|
1200
|
+
if (cols <= 0 || rows <= 0 || isNaN(cols) || isNaN(rows) || cols === Infinity || rows === Infinity) {
|
|
1201
|
+
throw new Error("resizing must be done using positive cols and rows");
|
|
1202
|
+
}
|
|
1203
|
+
pty2.resize(this._fd, cols, rows);
|
|
1204
|
+
this._cols = cols;
|
|
1205
|
+
this._rows = rows;
|
|
1206
|
+
};
|
|
1207
|
+
UnixTerminal2.prototype.clear = function() {
|
|
1208
|
+
};
|
|
1209
|
+
UnixTerminal2.prototype._sanitizeEnv = function(env) {
|
|
1210
|
+
delete env["TMUX"];
|
|
1211
|
+
delete env["TMUX_PANE"];
|
|
1212
|
+
delete env["STY"];
|
|
1213
|
+
delete env["WINDOW"];
|
|
1214
|
+
delete env["WINDOWID"];
|
|
1215
|
+
delete env["TERMCAP"];
|
|
1216
|
+
delete env["COLUMNS"];
|
|
1217
|
+
delete env["LINES"];
|
|
1218
|
+
};
|
|
1219
|
+
return UnixTerminal2;
|
|
1220
|
+
})(terminal_1.Terminal)
|
|
1221
|
+
);
|
|
1222
|
+
exports.UnixTerminal = UnixTerminal;
|
|
1223
|
+
var CustomWriteStream = (
|
|
1224
|
+
/** @class */
|
|
1225
|
+
(function() {
|
|
1226
|
+
function CustomWriteStream2(_fd, _encoding) {
|
|
1227
|
+
this._fd = _fd;
|
|
1228
|
+
this._encoding = _encoding;
|
|
1229
|
+
this._writeQueue = [];
|
|
1230
|
+
}
|
|
1231
|
+
CustomWriteStream2.prototype.dispose = function() {
|
|
1232
|
+
clearImmediate(this._writeImmediate);
|
|
1233
|
+
this._writeImmediate = void 0;
|
|
1234
|
+
};
|
|
1235
|
+
CustomWriteStream2.prototype.write = function(data) {
|
|
1236
|
+
var buffer = typeof data === "string" ? Buffer.from(data, this._encoding) : Buffer.from(data);
|
|
1237
|
+
if (buffer.byteLength !== 0) {
|
|
1238
|
+
this._writeQueue.push({ buffer, offset: 0 });
|
|
1239
|
+
if (this._writeQueue.length === 1) {
|
|
1240
|
+
this._processWriteQueue();
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1244
|
+
CustomWriteStream2.prototype._processWriteQueue = function() {
|
|
1245
|
+
var _this = this;
|
|
1246
|
+
this._writeImmediate = void 0;
|
|
1247
|
+
if (this._writeQueue.length === 0) {
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
var task = this._writeQueue[0];
|
|
1251
|
+
fs.write(this._fd, task.buffer, task.offset, function(err, written) {
|
|
1252
|
+
if (err) {
|
|
1253
|
+
if ("code" in err && err.code === "EAGAIN") {
|
|
1254
|
+
_this._writeImmediate = setImmediate(function() {
|
|
1255
|
+
return _this._processWriteQueue();
|
|
1256
|
+
});
|
|
1257
|
+
} else {
|
|
1258
|
+
_this._writeQueue.length = 0;
|
|
1259
|
+
console.error("Unhandled pty write error", err);
|
|
1260
|
+
}
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
task.offset += written;
|
|
1264
|
+
if (task.offset >= task.buffer.byteLength) {
|
|
1265
|
+
_this._writeQueue.shift();
|
|
1266
|
+
}
|
|
1267
|
+
_this._processWriteQueue();
|
|
1268
|
+
});
|
|
1269
|
+
};
|
|
1270
|
+
return CustomWriteStream2;
|
|
1271
|
+
})()
|
|
1272
|
+
);
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
1275
|
+
|
|
1276
|
+
// node_modules/node-pty/lib/index.js
|
|
1277
|
+
var require_lib = __commonJS({
|
|
1278
|
+
"node_modules/node-pty/lib/index.js"(exports) {
|
|
1279
|
+
"use strict";
|
|
1280
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1281
|
+
exports.native = exports.open = exports.createTerminal = exports.fork = exports.spawn = void 0;
|
|
1282
|
+
var utils_1 = require_utils();
|
|
1283
|
+
var terminalCtor;
|
|
1284
|
+
if (process.platform === "win32") {
|
|
1285
|
+
terminalCtor = require_windowsTerminal().WindowsTerminal;
|
|
1286
|
+
} else {
|
|
1287
|
+
terminalCtor = require_unixTerminal().UnixTerminal;
|
|
1288
|
+
}
|
|
1289
|
+
function spawn3(file, args, opt) {
|
|
1290
|
+
return new terminalCtor(file, args, opt);
|
|
1291
|
+
}
|
|
1292
|
+
exports.spawn = spawn3;
|
|
1293
|
+
function fork(file, args, opt) {
|
|
1294
|
+
return new terminalCtor(file, args, opt);
|
|
1295
|
+
}
|
|
1296
|
+
exports.fork = fork;
|
|
1297
|
+
function createTerminal(file, args, opt) {
|
|
1298
|
+
return new terminalCtor(file, args, opt);
|
|
1299
|
+
}
|
|
1300
|
+
exports.createTerminal = createTerminal;
|
|
1301
|
+
function open(options) {
|
|
1302
|
+
return terminalCtor.open(options);
|
|
1303
|
+
}
|
|
1304
|
+
exports.open = open;
|
|
1305
|
+
exports.native = process.platform !== "win32" ? utils_1.loadNativeModule("pty").module : null;
|
|
1306
|
+
}
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
// src/server.ts
|
|
1310
|
+
import { EventEmitter as EventEmitter2 } from "events";
|
|
1311
|
+
import { createServer } from "http";
|
|
1312
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
1313
|
+
import crypto from "crypto";
|
|
1314
|
+
import os from "os";
|
|
1315
|
+
|
|
1316
|
+
// src/types.ts
|
|
1317
|
+
var DEFAULT_CONFIG = {
|
|
1318
|
+
port: 0,
|
|
1319
|
+
host: "0.0.0.0",
|
|
1320
|
+
enableTunnel: true,
|
|
1321
|
+
tunnelProvider: "localtunnel",
|
|
1322
|
+
maxConnections: 5,
|
|
1323
|
+
heartbeatInterval: 3e4,
|
|
1324
|
+
shell: "/bin/bash",
|
|
1325
|
+
cols: 80,
|
|
1326
|
+
rows: 24,
|
|
1327
|
+
enableTerminal: true,
|
|
1328
|
+
sessionTimeout: 0
|
|
1329
|
+
};
|
|
1330
|
+
var MessageTypes = {
|
|
1331
|
+
// Auth
|
|
1332
|
+
AUTH_REQUIRED: "auth:required",
|
|
1333
|
+
AUTH: "auth",
|
|
1334
|
+
AUTH_SUCCESS: "auth:success",
|
|
1335
|
+
AUTH_FAILED: "auth:failed",
|
|
1336
|
+
// Terminal
|
|
1337
|
+
TERMINAL_OUTPUT: "terminal:output",
|
|
1338
|
+
TERMINAL_INPUT: "terminal:input",
|
|
1339
|
+
TERMINAL_RESIZE: "terminal:resize",
|
|
1340
|
+
TERMINAL_EXIT: "terminal:exit",
|
|
1341
|
+
TERMINAL_CLEAR: "terminal:clear",
|
|
1342
|
+
// Notifications
|
|
1343
|
+
NOTIFICATION: "notification",
|
|
1344
|
+
// Heartbeat
|
|
1345
|
+
PING: "ping",
|
|
1346
|
+
PONG: "pong",
|
|
1347
|
+
// Session
|
|
1348
|
+
SESSION_INFO: "session:info",
|
|
1349
|
+
SESSION_END: "session:end",
|
|
1350
|
+
// Commands (NikCLI specific)
|
|
1351
|
+
COMMAND: "command",
|
|
1352
|
+
COMMAND_RESULT: "command:result",
|
|
1353
|
+
// Agent events
|
|
1354
|
+
AGENT_START: "agent:start",
|
|
1355
|
+
AGENT_PROGRESS: "agent:progress",
|
|
1356
|
+
AGENT_COMPLETE: "agent:complete",
|
|
1357
|
+
AGENT_ERROR: "agent:error"
|
|
1358
|
+
};
|
|
1359
|
+
|
|
1360
|
+
// src/terminal.ts
|
|
1361
|
+
import { EventEmitter } from "events";
|
|
1362
|
+
import { spawn } from "child_process";
|
|
1363
|
+
var pty = null;
|
|
1364
|
+
try {
|
|
1365
|
+
pty = require_lib();
|
|
1366
|
+
} catch {
|
|
1367
|
+
}
|
|
1368
|
+
var TerminalManager = class extends EventEmitter {
|
|
1369
|
+
config;
|
|
1370
|
+
ptyProcess = null;
|
|
1371
|
+
process = null;
|
|
1372
|
+
running = false;
|
|
1373
|
+
constructor(config) {
|
|
1374
|
+
super();
|
|
1375
|
+
this.config = config;
|
|
1376
|
+
}
|
|
1377
|
+
/**
|
|
1378
|
+
* Start the terminal
|
|
1379
|
+
*/
|
|
1380
|
+
start() {
|
|
1381
|
+
if (this.running) return;
|
|
1382
|
+
const { shell, cols, rows, cwd, env } = this.config;
|
|
1383
|
+
const termEnv = {
|
|
1384
|
+
...process.env,
|
|
1385
|
+
...env,
|
|
1386
|
+
TERM: "xterm-256color",
|
|
1387
|
+
COLORTERM: "truecolor"
|
|
1388
|
+
};
|
|
1389
|
+
if (pty) {
|
|
1390
|
+
try {
|
|
1391
|
+
this.ptyProcess = pty.spawn(shell, [], {
|
|
1392
|
+
name: "xterm-256color",
|
|
1393
|
+
cols,
|
|
1394
|
+
rows,
|
|
1395
|
+
cwd: cwd || process.cwd(),
|
|
1396
|
+
env: termEnv
|
|
1397
|
+
});
|
|
1398
|
+
this.ptyProcess.onData((data) => {
|
|
1399
|
+
this.emit("data", data);
|
|
1400
|
+
});
|
|
1401
|
+
this.ptyProcess.onExit(({ exitCode }) => {
|
|
1402
|
+
this.running = false;
|
|
1403
|
+
this.emit("exit", exitCode);
|
|
1404
|
+
});
|
|
1405
|
+
this.running = true;
|
|
1406
|
+
return;
|
|
1407
|
+
} catch (error) {
|
|
1408
|
+
console.warn("node-pty failed, using fallback:", error.message);
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
this.process = spawn(shell, [], {
|
|
1412
|
+
cwd: cwd || process.cwd(),
|
|
1413
|
+
env: termEnv,
|
|
1414
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1415
|
+
shell: true
|
|
1416
|
+
});
|
|
1417
|
+
this.process.stdout?.on("data", (data) => {
|
|
1418
|
+
this.emit("data", data.toString());
|
|
1419
|
+
});
|
|
1420
|
+
this.process.stderr?.on("data", (data) => {
|
|
1421
|
+
this.emit("data", data.toString());
|
|
1422
|
+
});
|
|
1423
|
+
this.process.on("exit", (code) => {
|
|
1424
|
+
this.running = false;
|
|
1425
|
+
this.emit("exit", code || 0);
|
|
1426
|
+
});
|
|
1427
|
+
this.process.on("error", (error) => {
|
|
1428
|
+
this.emit("error", error);
|
|
1429
|
+
});
|
|
1430
|
+
this.running = true;
|
|
1431
|
+
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Write data to terminal
|
|
1434
|
+
*/
|
|
1435
|
+
write(data) {
|
|
1436
|
+
if (this.ptyProcess) {
|
|
1437
|
+
this.ptyProcess.write(data);
|
|
1438
|
+
} else if (this.process?.stdin) {
|
|
1439
|
+
this.process.stdin.write(data);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Resize terminal
|
|
1444
|
+
*/
|
|
1445
|
+
resize(cols, rows) {
|
|
1446
|
+
if (this.ptyProcess) {
|
|
1447
|
+
this.ptyProcess.resize(cols, rows);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Clear terminal
|
|
1452
|
+
*/
|
|
1453
|
+
clear() {
|
|
1454
|
+
this.emit("data", "\x1B[2J\x1B[H");
|
|
1455
|
+
}
|
|
1456
|
+
/**
|
|
1457
|
+
* Kill terminal process
|
|
1458
|
+
*/
|
|
1459
|
+
destroy() {
|
|
1460
|
+
this.running = false;
|
|
1461
|
+
if (this.ptyProcess) {
|
|
1462
|
+
this.ptyProcess.kill();
|
|
1463
|
+
this.ptyProcess = null;
|
|
1464
|
+
}
|
|
1465
|
+
if (this.process) {
|
|
1466
|
+
this.process.kill();
|
|
1467
|
+
this.process = null;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
/**
|
|
1471
|
+
* Check if terminal is running
|
|
1472
|
+
*/
|
|
1473
|
+
isRunning() {
|
|
1474
|
+
return this.running;
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* Check if using PTY
|
|
1478
|
+
*/
|
|
1479
|
+
hasPty() {
|
|
1480
|
+
return this.ptyProcess !== null;
|
|
1481
|
+
}
|
|
1482
|
+
};
|
|
1483
|
+
|
|
1484
|
+
// src/tunnel.ts
|
|
1485
|
+
import { spawn as spawn2 } from "child_process";
|
|
1486
|
+
var TunnelManager = class {
|
|
1487
|
+
provider;
|
|
1488
|
+
process = null;
|
|
1489
|
+
url = null;
|
|
1490
|
+
tunnelInstance = null;
|
|
1491
|
+
constructor(provider) {
|
|
1492
|
+
this.provider = provider;
|
|
1493
|
+
}
|
|
1494
|
+
/**
|
|
1495
|
+
* Create tunnel and return public URL
|
|
1496
|
+
*/
|
|
1497
|
+
async create(port) {
|
|
1498
|
+
switch (this.provider) {
|
|
1499
|
+
case "localtunnel":
|
|
1500
|
+
return this.createLocaltunnel(port);
|
|
1501
|
+
case "cloudflared":
|
|
1502
|
+
return this.createCloudflared(port);
|
|
1503
|
+
case "ngrok":
|
|
1504
|
+
return this.createNgrok(port);
|
|
1505
|
+
default:
|
|
1506
|
+
throw new Error(`Unknown tunnel provider: ${this.provider}`);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* Close tunnel
|
|
1511
|
+
*/
|
|
1512
|
+
async close() {
|
|
1513
|
+
if (this.tunnelInstance?.close) {
|
|
1514
|
+
this.tunnelInstance.close();
|
|
1515
|
+
this.tunnelInstance = null;
|
|
1516
|
+
}
|
|
1517
|
+
if (this.process) {
|
|
1518
|
+
this.process.kill();
|
|
1519
|
+
this.process = null;
|
|
1520
|
+
}
|
|
1521
|
+
this.url = null;
|
|
1522
|
+
}
|
|
1523
|
+
/**
|
|
1524
|
+
* Get tunnel URL
|
|
1525
|
+
*/
|
|
1526
|
+
getUrl() {
|
|
1527
|
+
return this.url;
|
|
1528
|
+
}
|
|
1529
|
+
/**
|
|
1530
|
+
* Create localtunnel
|
|
1531
|
+
*/
|
|
1532
|
+
async createLocaltunnel(port) {
|
|
1533
|
+
try {
|
|
1534
|
+
const localtunnel = await import("./localtunnel-XT32JGNN.js");
|
|
1535
|
+
const tunnel = await localtunnel.default({ port });
|
|
1536
|
+
this.tunnelInstance = tunnel;
|
|
1537
|
+
this.url = tunnel.url;
|
|
1538
|
+
tunnel.on("close", () => {
|
|
1539
|
+
this.url = null;
|
|
1540
|
+
});
|
|
1541
|
+
return tunnel.url;
|
|
1542
|
+
} catch {
|
|
1543
|
+
return this.createLocaltunnelCli(port);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Create localtunnel via CLI
|
|
1548
|
+
*/
|
|
1549
|
+
createLocaltunnelCli(port) {
|
|
1550
|
+
return new Promise((resolve, reject) => {
|
|
1551
|
+
this.process = spawn2("npx", ["localtunnel", "--port", port.toString()], {
|
|
1552
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1553
|
+
shell: true
|
|
1554
|
+
});
|
|
1555
|
+
let output = "";
|
|
1556
|
+
const timeout = setTimeout(() => {
|
|
1557
|
+
reject(new Error("Localtunnel timeout"));
|
|
1558
|
+
}, 3e4);
|
|
1559
|
+
this.process.stdout?.on("data", (data) => {
|
|
1560
|
+
output += data.toString();
|
|
1561
|
+
const match = output.match(/your url is:\s*(https?:\/\/[^\s]+)/i);
|
|
1562
|
+
if (match) {
|
|
1563
|
+
clearTimeout(timeout);
|
|
1564
|
+
this.url = match[1];
|
|
1565
|
+
resolve(match[1]);
|
|
1566
|
+
}
|
|
1567
|
+
});
|
|
1568
|
+
this.process.stderr?.on("data", (data) => {
|
|
1569
|
+
output += data.toString();
|
|
1570
|
+
});
|
|
1571
|
+
this.process.on("error", (error) => {
|
|
1572
|
+
clearTimeout(timeout);
|
|
1573
|
+
reject(error);
|
|
1574
|
+
});
|
|
1575
|
+
this.process.on("exit", (code) => {
|
|
1576
|
+
if (code !== 0 && !this.url) {
|
|
1577
|
+
clearTimeout(timeout);
|
|
1578
|
+
reject(new Error(`Localtunnel exited with code ${code}`));
|
|
1579
|
+
}
|
|
1580
|
+
});
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
/**
|
|
1584
|
+
* Create cloudflared tunnel
|
|
1585
|
+
*/
|
|
1586
|
+
createCloudflared(port) {
|
|
1587
|
+
return new Promise((resolve, reject) => {
|
|
1588
|
+
this.process = spawn2(
|
|
1589
|
+
"cloudflared",
|
|
1590
|
+
["tunnel", "--url", `http://localhost:${port}`],
|
|
1591
|
+
{
|
|
1592
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1593
|
+
}
|
|
1594
|
+
);
|
|
1595
|
+
let output = "";
|
|
1596
|
+
const timeout = setTimeout(() => {
|
|
1597
|
+
reject(new Error("Cloudflared timeout"));
|
|
1598
|
+
}, 3e4);
|
|
1599
|
+
const handleData = (data) => {
|
|
1600
|
+
output += data.toString();
|
|
1601
|
+
const match = output.match(/(https:\/\/[^\s]+\.trycloudflare\.com)/i);
|
|
1602
|
+
if (match) {
|
|
1603
|
+
clearTimeout(timeout);
|
|
1604
|
+
this.url = match[1];
|
|
1605
|
+
resolve(match[1]);
|
|
1606
|
+
}
|
|
1607
|
+
};
|
|
1608
|
+
this.process.stdout?.on("data", handleData);
|
|
1609
|
+
this.process.stderr?.on("data", handleData);
|
|
1610
|
+
this.process.on("error", (error) => {
|
|
1611
|
+
clearTimeout(timeout);
|
|
1612
|
+
reject(error);
|
|
1613
|
+
});
|
|
1614
|
+
this.process.on("exit", (code) => {
|
|
1615
|
+
if (code !== 0 && !this.url) {
|
|
1616
|
+
clearTimeout(timeout);
|
|
1617
|
+
reject(new Error(`Cloudflared exited with code ${code}`));
|
|
1618
|
+
}
|
|
1619
|
+
});
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
/**
|
|
1623
|
+
* Create ngrok tunnel
|
|
1624
|
+
*/
|
|
1625
|
+
createNgrok(port) {
|
|
1626
|
+
return new Promise((resolve, reject) => {
|
|
1627
|
+
this.process = spawn2("ngrok", ["http", port.toString(), "--log=stdout"], {
|
|
1628
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1629
|
+
});
|
|
1630
|
+
let output = "";
|
|
1631
|
+
const timeout = setTimeout(() => {
|
|
1632
|
+
reject(new Error("Ngrok timeout"));
|
|
1633
|
+
}, 3e4);
|
|
1634
|
+
this.process.stdout?.on("data", (data) => {
|
|
1635
|
+
output += data.toString();
|
|
1636
|
+
const match = output.match(/url=(https?:\/\/[^\s]+)/i);
|
|
1637
|
+
if (match) {
|
|
1638
|
+
clearTimeout(timeout);
|
|
1639
|
+
this.url = match[1];
|
|
1640
|
+
resolve(match[1]);
|
|
1641
|
+
}
|
|
1642
|
+
});
|
|
1643
|
+
this.process.stderr?.on("data", (data) => {
|
|
1644
|
+
output += data.toString();
|
|
1645
|
+
});
|
|
1646
|
+
this.process.on("error", (error) => {
|
|
1647
|
+
clearTimeout(timeout);
|
|
1648
|
+
reject(error);
|
|
1649
|
+
});
|
|
1650
|
+
this.process.on("exit", (code) => {
|
|
1651
|
+
if (code !== 0 && !this.url) {
|
|
1652
|
+
clearTimeout(timeout);
|
|
1653
|
+
reject(new Error(`Ngrok exited with code ${code}`));
|
|
1654
|
+
}
|
|
1655
|
+
});
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
};
|
|
1659
|
+
async function checkTunnelAvailability(provider) {
|
|
1660
|
+
try {
|
|
1661
|
+
const { execSync } = await import("child_process");
|
|
1662
|
+
switch (provider) {
|
|
1663
|
+
case "localtunnel":
|
|
1664
|
+
try {
|
|
1665
|
+
await import("./localtunnel-XT32JGNN.js");
|
|
1666
|
+
return true;
|
|
1667
|
+
} catch {
|
|
1668
|
+
execSync("npx localtunnel --version", { stdio: "pipe" });
|
|
1669
|
+
return true;
|
|
1670
|
+
}
|
|
1671
|
+
case "cloudflared":
|
|
1672
|
+
execSync("cloudflared --version", { stdio: "pipe" });
|
|
1673
|
+
return true;
|
|
1674
|
+
case "ngrok":
|
|
1675
|
+
execSync("ngrok version", { stdio: "pipe" });
|
|
1676
|
+
return true;
|
|
1677
|
+
default:
|
|
1678
|
+
return false;
|
|
1679
|
+
}
|
|
1680
|
+
} catch {
|
|
1681
|
+
return false;
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
async function findAvailableTunnel() {
|
|
1685
|
+
const providers = ["localtunnel", "cloudflared", "ngrok"];
|
|
1686
|
+
for (const provider of providers) {
|
|
1687
|
+
if (await checkTunnelAvailability(provider)) {
|
|
1688
|
+
return provider;
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
return null;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
// src/web-client.ts
|
|
1695
|
+
function getWebClient() {
|
|
1696
|
+
return `<!DOCTYPE html>
|
|
1697
|
+
<html lang="en">
|
|
1698
|
+
<head>
|
|
1699
|
+
<meta charset="UTF-8">
|
|
1700
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
1701
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
1702
|
+
<meta name="mobile-web-app-capable" content="yes">
|
|
1703
|
+
<meta name="theme-color" content="#0d1117">
|
|
1704
|
+
<title>NikCLI Remote</title>
|
|
1705
|
+
<style>
|
|
1706
|
+
:root {
|
|
1707
|
+
--bg: #0d1117;
|
|
1708
|
+
--bg-secondary: #161b22;
|
|
1709
|
+
--fg: #e6edf3;
|
|
1710
|
+
--fg-muted: #8b949e;
|
|
1711
|
+
--accent: #58a6ff;
|
|
1712
|
+
--success: #3fb950;
|
|
1713
|
+
--warning: #d29922;
|
|
1714
|
+
--error: #f85149;
|
|
1715
|
+
--border: #30363d;
|
|
1716
|
+
--font-mono: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
* {
|
|
1720
|
+
box-sizing: border-box;
|
|
1721
|
+
margin: 0;
|
|
1722
|
+
padding: 0;
|
|
1723
|
+
-webkit-tap-highlight-color: transparent;
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
html, body {
|
|
1727
|
+
height: 100%;
|
|
1728
|
+
background: var(--bg);
|
|
1729
|
+
color: var(--fg);
|
|
1730
|
+
font-family: var(--font-mono);
|
|
1731
|
+
font-size: 14px;
|
|
1732
|
+
overflow: hidden;
|
|
1733
|
+
touch-action: manipulation;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
#app {
|
|
1737
|
+
display: flex;
|
|
1738
|
+
flex-direction: column;
|
|
1739
|
+
height: 100%;
|
|
1740
|
+
height: 100dvh;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
/* Header */
|
|
1744
|
+
#header {
|
|
1745
|
+
display: flex;
|
|
1746
|
+
align-items: center;
|
|
1747
|
+
justify-content: space-between;
|
|
1748
|
+
padding: 12px 16px;
|
|
1749
|
+
background: var(--bg-secondary);
|
|
1750
|
+
border-bottom: 1px solid var(--border);
|
|
1751
|
+
flex-shrink: 0;
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
#header h1 {
|
|
1755
|
+
font-size: 16px;
|
|
1756
|
+
font-weight: 600;
|
|
1757
|
+
color: var(--accent);
|
|
1758
|
+
display: flex;
|
|
1759
|
+
align-items: center;
|
|
1760
|
+
gap: 8px;
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
#header h1::before {
|
|
1764
|
+
content: '';
|
|
1765
|
+
width: 10px;
|
|
1766
|
+
height: 10px;
|
|
1767
|
+
background: var(--accent);
|
|
1768
|
+
border-radius: 2px;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
#status {
|
|
1772
|
+
display: flex;
|
|
1773
|
+
align-items: center;
|
|
1774
|
+
gap: 6px;
|
|
1775
|
+
font-size: 12px;
|
|
1776
|
+
color: var(--fg-muted);
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
#status-dot {
|
|
1780
|
+
width: 8px;
|
|
1781
|
+
height: 8px;
|
|
1782
|
+
border-radius: 50%;
|
|
1783
|
+
background: var(--error);
|
|
1784
|
+
transition: background 0.3s;
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
#status-dot.connected {
|
|
1788
|
+
background: var(--success);
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
#status-dot.connecting {
|
|
1792
|
+
background: var(--warning);
|
|
1793
|
+
animation: pulse 1s infinite;
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
@keyframes pulse {
|
|
1797
|
+
0%, 100% { opacity: 1; }
|
|
1798
|
+
50% { opacity: 0.5; }
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
/* Terminal */
|
|
1802
|
+
#terminal-container {
|
|
1803
|
+
flex: 1;
|
|
1804
|
+
overflow: hidden;
|
|
1805
|
+
position: relative;
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
#terminal {
|
|
1809
|
+
height: 100%;
|
|
1810
|
+
padding: 12px;
|
|
1811
|
+
overflow-y: auto;
|
|
1812
|
+
overflow-x: hidden;
|
|
1813
|
+
font-size: 13px;
|
|
1814
|
+
line-height: 1.5;
|
|
1815
|
+
white-space: pre-wrap;
|
|
1816
|
+
word-break: break-all;
|
|
1817
|
+
-webkit-overflow-scrolling: touch;
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
#terminal::-webkit-scrollbar {
|
|
1821
|
+
width: 6px;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
#terminal::-webkit-scrollbar-track {
|
|
1825
|
+
background: var(--bg);
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
#terminal::-webkit-scrollbar-thumb {
|
|
1829
|
+
background: var(--border);
|
|
1830
|
+
border-radius: 3px;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
.cursor {
|
|
1834
|
+
display: inline-block;
|
|
1835
|
+
width: 8px;
|
|
1836
|
+
height: 16px;
|
|
1837
|
+
background: var(--fg);
|
|
1838
|
+
animation: blink 1s step-end infinite;
|
|
1839
|
+
vertical-align: text-bottom;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
@keyframes blink {
|
|
1843
|
+
50% { opacity: 0; }
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
/* Notifications */
|
|
1847
|
+
#notifications {
|
|
1848
|
+
position: fixed;
|
|
1849
|
+
top: 60px;
|
|
1850
|
+
left: 12px;
|
|
1851
|
+
right: 12px;
|
|
1852
|
+
z-index: 1000;
|
|
1853
|
+
pointer-events: none;
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
.notification {
|
|
1857
|
+
background: var(--bg-secondary);
|
|
1858
|
+
border: 1px solid var(--border);
|
|
1859
|
+
border-radius: 8px;
|
|
1860
|
+
padding: 12px 16px;
|
|
1861
|
+
margin-bottom: 8px;
|
|
1862
|
+
animation: slideIn 0.3s ease;
|
|
1863
|
+
pointer-events: auto;
|
|
1864
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.4);
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
.notification.success { border-left: 3px solid var(--success); }
|
|
1868
|
+
.notification.error { border-left: 3px solid var(--error); }
|
|
1869
|
+
.notification.warning { border-left: 3px solid var(--warning); }
|
|
1870
|
+
.notification.info { border-left: 3px solid var(--accent); }
|
|
1871
|
+
|
|
1872
|
+
.notification h4 {
|
|
1873
|
+
font-size: 14px;
|
|
1874
|
+
font-weight: 600;
|
|
1875
|
+
margin-bottom: 4px;
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
.notification p {
|
|
1879
|
+
font-size: 12px;
|
|
1880
|
+
color: var(--fg-muted);
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
@keyframes slideIn {
|
|
1884
|
+
from { transform: translateY(-20px); opacity: 0; }
|
|
1885
|
+
to { transform: translateY(0); opacity: 1; }
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
/* Quick Keys */
|
|
1889
|
+
#quickkeys {
|
|
1890
|
+
display: grid;
|
|
1891
|
+
grid-template-columns: repeat(6, 1fr);
|
|
1892
|
+
gap: 6px;
|
|
1893
|
+
padding: 8px 12px;
|
|
1894
|
+
background: var(--bg-secondary);
|
|
1895
|
+
border-top: 1px solid var(--border);
|
|
1896
|
+
flex-shrink: 0;
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
.qkey {
|
|
1900
|
+
background: var(--bg);
|
|
1901
|
+
border: 1px solid var(--border);
|
|
1902
|
+
border-radius: 6px;
|
|
1903
|
+
padding: 10px 4px;
|
|
1904
|
+
color: var(--fg);
|
|
1905
|
+
font-size: 11px;
|
|
1906
|
+
font-family: var(--font-mono);
|
|
1907
|
+
text-align: center;
|
|
1908
|
+
cursor: pointer;
|
|
1909
|
+
user-select: none;
|
|
1910
|
+
transition: background 0.1s, transform 0.1s;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
.qkey:active {
|
|
1914
|
+
background: var(--border);
|
|
1915
|
+
transform: scale(0.95);
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
.qkey.wide {
|
|
1919
|
+
grid-column: span 2;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
.qkey.accent {
|
|
1923
|
+
background: var(--accent);
|
|
1924
|
+
border-color: var(--accent);
|
|
1925
|
+
color: #fff;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
/* Input */
|
|
1929
|
+
#input-container {
|
|
1930
|
+
padding: 12px;
|
|
1931
|
+
background: var(--bg-secondary);
|
|
1932
|
+
border-top: 1px solid var(--border);
|
|
1933
|
+
flex-shrink: 0;
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
#input-row {
|
|
1937
|
+
display: flex;
|
|
1938
|
+
gap: 8px;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
#input {
|
|
1942
|
+
flex: 1;
|
|
1943
|
+
background: var(--bg);
|
|
1944
|
+
border: 1px solid var(--border);
|
|
1945
|
+
border-radius: 8px;
|
|
1946
|
+
padding: 12px 14px;
|
|
1947
|
+
color: var(--fg);
|
|
1948
|
+
font-family: var(--font-mono);
|
|
1949
|
+
font-size: 16px;
|
|
1950
|
+
outline: none;
|
|
1951
|
+
transition: border-color 0.2s;
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
#input:focus {
|
|
1955
|
+
border-color: var(--accent);
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
#input::placeholder {
|
|
1959
|
+
color: var(--fg-muted);
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
#send {
|
|
1963
|
+
background: var(--accent);
|
|
1964
|
+
color: #fff;
|
|
1965
|
+
border: none;
|
|
1966
|
+
border-radius: 8px;
|
|
1967
|
+
padding: 12px 20px;
|
|
1968
|
+
font-size: 14px;
|
|
1969
|
+
font-weight: 600;
|
|
1970
|
+
font-family: var(--font-mono);
|
|
1971
|
+
cursor: pointer;
|
|
1972
|
+
transition: opacity 0.2s, transform 0.1s;
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
#send:active {
|
|
1976
|
+
opacity: 0.8;
|
|
1977
|
+
transform: scale(0.98);
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
/* Auth Screen */
|
|
1981
|
+
#auth-screen {
|
|
1982
|
+
position: fixed;
|
|
1983
|
+
inset: 0;
|
|
1984
|
+
background: var(--bg);
|
|
1985
|
+
display: flex;
|
|
1986
|
+
flex-direction: column;
|
|
1987
|
+
align-items: center;
|
|
1988
|
+
justify-content: center;
|
|
1989
|
+
gap: 20px;
|
|
1990
|
+
z-index: 2000;
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
#auth-screen.hidden {
|
|
1994
|
+
display: none;
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
.spinner {
|
|
1998
|
+
width: 40px;
|
|
1999
|
+
height: 40px;
|
|
2000
|
+
border: 3px solid var(--border);
|
|
2001
|
+
border-top-color: var(--accent);
|
|
2002
|
+
border-radius: 50%;
|
|
2003
|
+
animation: spin 1s linear infinite;
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
@keyframes spin {
|
|
2007
|
+
to { transform: rotate(360deg); }
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
#auth-screen p {
|
|
2011
|
+
color: var(--fg-muted);
|
|
2012
|
+
font-size: 14px;
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
#auth-screen .error {
|
|
2016
|
+
color: var(--error);
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
/* Safe area padding for notched devices */
|
|
2020
|
+
@supports (padding: env(safe-area-inset-bottom)) {
|
|
2021
|
+
#input-container {
|
|
2022
|
+
padding-bottom: calc(12px + env(safe-area-inset-bottom));
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
/* Landscape adjustments */
|
|
2027
|
+
@media (max-height: 500px) {
|
|
2028
|
+
#quickkeys {
|
|
2029
|
+
grid-template-columns: repeat(12, 1fr);
|
|
2030
|
+
padding: 6px 8px;
|
|
2031
|
+
}
|
|
2032
|
+
.qkey {
|
|
2033
|
+
padding: 8px 2px;
|
|
2034
|
+
font-size: 10px;
|
|
2035
|
+
}
|
|
2036
|
+
#terminal {
|
|
2037
|
+
font-size: 12px;
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
</style>
|
|
2041
|
+
</head>
|
|
2042
|
+
<body>
|
|
2043
|
+
<div id="app">
|
|
2044
|
+
<div id="auth-screen">
|
|
2045
|
+
<div class="spinner"></div>
|
|
2046
|
+
<p id="auth-status">Connecting to NikCLI...</p>
|
|
2047
|
+
</div>
|
|
2048
|
+
|
|
2049
|
+
<header id="header">
|
|
2050
|
+
<h1>NikCLI Remote</h1>
|
|
2051
|
+
<div id="status">
|
|
2052
|
+
<span id="status-dot" class="connecting"></span>
|
|
2053
|
+
<span id="status-text">Connecting</span>
|
|
2054
|
+
</div>
|
|
2055
|
+
</header>
|
|
2056
|
+
|
|
2057
|
+
<div id="terminal-container">
|
|
2058
|
+
<div id="terminal"></div>
|
|
2059
|
+
</div>
|
|
2060
|
+
|
|
2061
|
+
<div id="notifications"></div>
|
|
2062
|
+
|
|
2063
|
+
<div id="quickkeys">
|
|
2064
|
+
<button class="qkey" data-key="\\t">Tab</button>
|
|
2065
|
+
<button class="qkey" data-key="\\x1b[A">\u2191</button>
|
|
2066
|
+
<button class="qkey" data-key="\\x1b[B">\u2193</button>
|
|
2067
|
+
<button class="qkey" data-key="\\x1b[D">\u2190</button>
|
|
2068
|
+
<button class="qkey" data-key="\\x1b[C">\u2192</button>
|
|
2069
|
+
<button class="qkey" data-key="\\x1b">Esc</button>
|
|
2070
|
+
<button class="qkey" data-key="\\x03">^C</button>
|
|
2071
|
+
<button class="qkey" data-key="\\x04">^D</button>
|
|
2072
|
+
<button class="qkey" data-key="\\x1a">^Z</button>
|
|
2073
|
+
<button class="qkey" data-key="\\x0c">^L</button>
|
|
2074
|
+
<button class="qkey wide accent" data-key="\\r">Enter \u23CE</button>
|
|
2075
|
+
</div>
|
|
2076
|
+
|
|
2077
|
+
<div id="input-container">
|
|
2078
|
+
<div id="input-row">
|
|
2079
|
+
<input type="text" id="input" placeholder="Type command..." autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
|
|
2080
|
+
<button id="send">Send</button>
|
|
2081
|
+
</div>
|
|
2082
|
+
</div>
|
|
2083
|
+
</div>
|
|
2084
|
+
|
|
2085
|
+
<script>
|
|
2086
|
+
(function() {
|
|
2087
|
+
'use strict';
|
|
2088
|
+
|
|
2089
|
+
// Parse URL params
|
|
2090
|
+
const params = new URLSearchParams(location.search);
|
|
2091
|
+
const token = params.get('t');
|
|
2092
|
+
const sessionId = params.get('s');
|
|
2093
|
+
|
|
2094
|
+
// DOM elements
|
|
2095
|
+
const terminal = document.getElementById('terminal');
|
|
2096
|
+
const input = document.getElementById('input');
|
|
2097
|
+
const sendBtn = document.getElementById('send');
|
|
2098
|
+
const statusDot = document.getElementById('status-dot');
|
|
2099
|
+
const statusText = document.getElementById('status-text');
|
|
2100
|
+
const authScreen = document.getElementById('auth-screen');
|
|
2101
|
+
const authStatus = document.getElementById('auth-status');
|
|
2102
|
+
const notifications = document.getElementById('notifications');
|
|
2103
|
+
|
|
2104
|
+
// State
|
|
2105
|
+
let ws = null;
|
|
2106
|
+
let reconnectAttempts = 0;
|
|
2107
|
+
const maxReconnectAttempts = 5;
|
|
2108
|
+
let terminalEnabled = true;
|
|
2109
|
+
|
|
2110
|
+
// Connect to WebSocket
|
|
2111
|
+
function connect() {
|
|
2112
|
+
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
2113
|
+
ws = new WebSocket(protocol + '//' + location.host);
|
|
2114
|
+
|
|
2115
|
+
ws.onopen = function() {
|
|
2116
|
+
setStatus('connecting', 'Authenticating...');
|
|
2117
|
+
ws.send(JSON.stringify({ type: 'auth', token: token }));
|
|
2118
|
+
};
|
|
2119
|
+
|
|
2120
|
+
ws.onmessage = function(event) {
|
|
2121
|
+
try {
|
|
2122
|
+
const msg = JSON.parse(event.data);
|
|
2123
|
+
handleMessage(msg);
|
|
2124
|
+
} catch (e) {
|
|
2125
|
+
console.error('Parse error:', e);
|
|
2126
|
+
}
|
|
2127
|
+
};
|
|
2128
|
+
|
|
2129
|
+
ws.onclose = function() {
|
|
2130
|
+
setStatus('disconnected', 'Disconnected');
|
|
2131
|
+
if (reconnectAttempts < maxReconnectAttempts) {
|
|
2132
|
+
reconnectAttempts++;
|
|
2133
|
+
const delay = Math.min(2000 * reconnectAttempts, 10000);
|
|
2134
|
+
setTimeout(connect, delay);
|
|
2135
|
+
} else {
|
|
2136
|
+
authStatus.textContent = 'Connection failed. Refresh to retry.';
|
|
2137
|
+
authStatus.classList.add('error');
|
|
2138
|
+
authScreen.classList.remove('hidden');
|
|
2139
|
+
}
|
|
2140
|
+
};
|
|
2141
|
+
|
|
2142
|
+
ws.onerror = function() {
|
|
2143
|
+
console.error('WebSocket error');
|
|
2144
|
+
};
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
// Handle incoming message
|
|
2148
|
+
function handleMessage(msg) {
|
|
2149
|
+
switch (msg.type) {
|
|
2150
|
+
case 'auth:required':
|
|
2151
|
+
// Already sent auth on open
|
|
2152
|
+
break;
|
|
2153
|
+
|
|
2154
|
+
case 'auth:success':
|
|
2155
|
+
authScreen.classList.add('hidden');
|
|
2156
|
+
setStatus('connected', 'Connected');
|
|
2157
|
+
reconnectAttempts = 0;
|
|
2158
|
+
terminalEnabled = msg.payload?.terminalEnabled !== false;
|
|
2159
|
+
if (terminalEnabled) {
|
|
2160
|
+
appendOutput('\\x1b[32mConnected to NikCLI\\x1b[0m\\n\\n');
|
|
2161
|
+
}
|
|
2162
|
+
break;
|
|
2163
|
+
|
|
2164
|
+
case 'auth:failed':
|
|
2165
|
+
authStatus.textContent = 'Authentication failed';
|
|
2166
|
+
authStatus.classList.add('error');
|
|
2167
|
+
break;
|
|
2168
|
+
|
|
2169
|
+
case 'terminal:output':
|
|
2170
|
+
if (msg.payload?.data) {
|
|
2171
|
+
appendOutput(msg.payload.data);
|
|
2172
|
+
}
|
|
2173
|
+
break;
|
|
2174
|
+
|
|
2175
|
+
case 'terminal:exit':
|
|
2176
|
+
appendOutput('\\n\\x1b[33m[Process exited with code ' + (msg.payload?.code || 0) + ']\\x1b[0m\\n');
|
|
2177
|
+
break;
|
|
2178
|
+
|
|
2179
|
+
case 'notification':
|
|
2180
|
+
showNotification(msg.payload);
|
|
2181
|
+
break;
|
|
2182
|
+
|
|
2183
|
+
case 'session:end':
|
|
2184
|
+
appendOutput('\\n\\x1b[31m[Session ended]\\x1b[0m\\n');
|
|
2185
|
+
setStatus('disconnected', 'Session ended');
|
|
2186
|
+
break;
|
|
2187
|
+
|
|
2188
|
+
case 'pong':
|
|
2189
|
+
// Heartbeat response
|
|
2190
|
+
break;
|
|
2191
|
+
|
|
2192
|
+
default:
|
|
2193
|
+
console.log('Unknown message:', msg.type);
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
// Append text to terminal with ANSI support
|
|
2198
|
+
function appendOutput(text) {
|
|
2199
|
+
// Simple ANSI to HTML conversion
|
|
2200
|
+
const html = ansiToHtml(text);
|
|
2201
|
+
terminal.innerHTML += html;
|
|
2202
|
+
terminal.scrollTop = terminal.scrollHeight;
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
// Basic ANSI to HTML
|
|
2206
|
+
function ansiToHtml(text) {
|
|
2207
|
+
const ansiColors = {
|
|
2208
|
+
'30': '#6e7681', '31': '#f85149', '32': '#3fb950', '33': '#d29922',
|
|
2209
|
+
'34': '#58a6ff', '35': '#bc8cff', '36': '#76e3ea', '37': '#e6edf3',
|
|
2210
|
+
'90': '#6e7681', '91': '#f85149', '92': '#3fb950', '93': '#d29922',
|
|
2211
|
+
'94': '#58a6ff', '95': '#bc8cff', '96': '#76e3ea', '97': '#ffffff'
|
|
2212
|
+
};
|
|
2213
|
+
|
|
2214
|
+
let result = '';
|
|
2215
|
+
let currentStyle = '';
|
|
2216
|
+
|
|
2217
|
+
const parts = text.split(/\\x1b\\[([0-9;]+)m/);
|
|
2218
|
+
for (let i = 0; i < parts.length; i++) {
|
|
2219
|
+
if (i % 2 === 0) {
|
|
2220
|
+
// Text content
|
|
2221
|
+
result += escapeHtml(parts[i]);
|
|
2222
|
+
} else {
|
|
2223
|
+
// ANSI code
|
|
2224
|
+
const codes = parts[i].split(';');
|
|
2225
|
+
for (const code of codes) {
|
|
2226
|
+
if (code === '0') {
|
|
2227
|
+
if (currentStyle) {
|
|
2228
|
+
result += '</span>';
|
|
2229
|
+
currentStyle = '';
|
|
2230
|
+
}
|
|
2231
|
+
} else if (code === '1') {
|
|
2232
|
+
currentStyle = 'font-weight:bold;';
|
|
2233
|
+
result += '<span style="' + currentStyle + '">';
|
|
2234
|
+
} else if (ansiColors[code]) {
|
|
2235
|
+
if (currentStyle) result += '</span>';
|
|
2236
|
+
currentStyle = 'color:' + ansiColors[code] + ';';
|
|
2237
|
+
result += '<span style="' + currentStyle + '">';
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
if (currentStyle) result += '</span>';
|
|
2244
|
+
return result;
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
// Escape HTML
|
|
2248
|
+
function escapeHtml(text) {
|
|
2249
|
+
return text
|
|
2250
|
+
.replace(/&/g, '&')
|
|
2251
|
+
.replace(/</g, '<')
|
|
2252
|
+
.replace(/>/g, '>')
|
|
2253
|
+
.replace(/"/g, '"')
|
|
2254
|
+
.replace(/\\n/g, '<br>')
|
|
2255
|
+
.replace(/ /g, ' ');
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
// Set connection status
|
|
2259
|
+
function setStatus(state, text) {
|
|
2260
|
+
statusDot.className = state === 'connected' ? 'connected' :
|
|
2261
|
+
state === 'connecting' ? 'connecting' : '';
|
|
2262
|
+
statusText.textContent = text;
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
// Send data to terminal
|
|
2266
|
+
function send(data) {
|
|
2267
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
2268
|
+
ws.send(JSON.stringify({ type: 'terminal:input', data: data }));
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
// Show notification
|
|
2273
|
+
function showNotification(n) {
|
|
2274
|
+
if (!n) return;
|
|
2275
|
+
const el = document.createElement('div');
|
|
2276
|
+
el.className = 'notification ' + (n.type || 'info');
|
|
2277
|
+
el.innerHTML = '<h4>' + escapeHtml(n.title || 'Notification') + '</h4>' +
|
|
2278
|
+
'<p>' + escapeHtml(n.body || '') + '</p>';
|
|
2279
|
+
notifications.appendChild(el);
|
|
2280
|
+
setTimeout(function() { el.remove(); }, 5000);
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
// Event: Send button
|
|
2284
|
+
sendBtn.onclick = function() {
|
|
2285
|
+
if (input.value) {
|
|
2286
|
+
send(input.value + '\\r');
|
|
2287
|
+
input.value = '';
|
|
2288
|
+
}
|
|
2289
|
+
input.focus();
|
|
2290
|
+
};
|
|
2291
|
+
|
|
2292
|
+
// Event: Enter key in input
|
|
2293
|
+
input.onkeydown = function(e) {
|
|
2294
|
+
if (e.key === 'Enter') {
|
|
2295
|
+
e.preventDefault();
|
|
2296
|
+
sendBtn.click();
|
|
2297
|
+
}
|
|
2298
|
+
};
|
|
2299
|
+
|
|
2300
|
+
// Event: Quick keys
|
|
2301
|
+
document.querySelectorAll('.qkey').forEach(function(btn) {
|
|
2302
|
+
btn.onclick = function() {
|
|
2303
|
+
const key = btn.dataset.key;
|
|
2304
|
+
const decoded = key
|
|
2305
|
+
.replace(/\\\\x([0-9a-f]{2})/gi, function(_, hex) {
|
|
2306
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
2307
|
+
})
|
|
2308
|
+
.replace(/\\\\t/g, '\\t')
|
|
2309
|
+
.replace(/\\\\r/g, '\\r')
|
|
2310
|
+
.replace(/\\\\n/g, '\\n');
|
|
2311
|
+
send(decoded);
|
|
2312
|
+
input.focus();
|
|
2313
|
+
};
|
|
2314
|
+
});
|
|
2315
|
+
|
|
2316
|
+
// Heartbeat
|
|
2317
|
+
setInterval(function() {
|
|
2318
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
2319
|
+
ws.send(JSON.stringify({ type: 'ping' }));
|
|
2320
|
+
}
|
|
2321
|
+
}, 25000);
|
|
2322
|
+
|
|
2323
|
+
// Handle resize
|
|
2324
|
+
function sendResize() {
|
|
2325
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
2326
|
+
const cols = Math.floor(terminal.clientWidth / 8);
|
|
2327
|
+
const rows = Math.floor(terminal.clientHeight / 18);
|
|
2328
|
+
ws.send(JSON.stringify({ type: 'terminal:resize', cols: cols, rows: rows }));
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
window.addEventListener('resize', sendResize);
|
|
2333
|
+
setTimeout(sendResize, 1000);
|
|
2334
|
+
|
|
2335
|
+
// Start connection
|
|
2336
|
+
if (token) {
|
|
2337
|
+
connect();
|
|
2338
|
+
} else {
|
|
2339
|
+
authStatus.textContent = 'Invalid session URL';
|
|
2340
|
+
authStatus.classList.add('error');
|
|
2341
|
+
}
|
|
2342
|
+
})();
|
|
2343
|
+
</script>
|
|
2344
|
+
</body>
|
|
2345
|
+
</html>`;
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
// src/server.ts
|
|
2349
|
+
var RemoteServer = class extends EventEmitter2 {
|
|
2350
|
+
config;
|
|
2351
|
+
httpServer = null;
|
|
2352
|
+
wss = null;
|
|
2353
|
+
clients = /* @__PURE__ */ new Map();
|
|
2354
|
+
session = null;
|
|
2355
|
+
terminal = null;
|
|
2356
|
+
tunnel = null;
|
|
2357
|
+
heartbeatTimer = null;
|
|
2358
|
+
sessionTimeoutTimer = null;
|
|
2359
|
+
isRunning = false;
|
|
2360
|
+
sessionSecret;
|
|
2361
|
+
constructor(config = {}) {
|
|
2362
|
+
super();
|
|
2363
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
2364
|
+
this.sessionSecret = config.sessionSecret || this.generateSecret();
|
|
2365
|
+
}
|
|
2366
|
+
/**
|
|
2367
|
+
* Start the remote server
|
|
2368
|
+
*/
|
|
2369
|
+
async start(options = {}) {
|
|
2370
|
+
if (this.isRunning) {
|
|
2371
|
+
throw new Error("Server already running");
|
|
2372
|
+
}
|
|
2373
|
+
const sessionId = this.generateSessionId();
|
|
2374
|
+
this.httpServer = createServer((req, res) => this.handleHttpRequest(req, res));
|
|
2375
|
+
this.wss = new WebSocketServer({ server: this.httpServer });
|
|
2376
|
+
this.setupWebSocketHandlers();
|
|
2377
|
+
const port = await new Promise((resolve, reject) => {
|
|
2378
|
+
this.httpServer.listen(this.config.port, this.config.host, () => {
|
|
2379
|
+
const addr = this.httpServer.address();
|
|
2380
|
+
resolve(typeof addr === "object" ? addr?.port || 0 : 0);
|
|
2381
|
+
});
|
|
2382
|
+
this.httpServer.on("error", reject);
|
|
2383
|
+
});
|
|
2384
|
+
const localIp = this.getLocalIP();
|
|
2385
|
+
const localUrl = `http://${localIp}:${port}`;
|
|
2386
|
+
this.session = {
|
|
2387
|
+
id: sessionId,
|
|
2388
|
+
name: options.name || `nikcli-${sessionId}`,
|
|
2389
|
+
qrCode: "",
|
|
2390
|
+
qrUrl: localUrl,
|
|
2391
|
+
localUrl,
|
|
2392
|
+
status: "starting",
|
|
2393
|
+
connectedDevices: [],
|
|
2394
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
2395
|
+
lastActivity: /* @__PURE__ */ new Date(),
|
|
2396
|
+
port
|
|
2397
|
+
};
|
|
2398
|
+
if (this.config.enableTunnel && this.config.tunnelProvider !== "none") {
|
|
2399
|
+
try {
|
|
2400
|
+
this.tunnel = new TunnelManager(this.config.tunnelProvider);
|
|
2401
|
+
const tunnelUrl = await this.tunnel.create(port);
|
|
2402
|
+
this.session.tunnelUrl = tunnelUrl;
|
|
2403
|
+
this.session.qrUrl = `${tunnelUrl}?s=${sessionId}&t=${this.sessionSecret}`;
|
|
2404
|
+
this.emit("tunnel:connected", tunnelUrl);
|
|
2405
|
+
} catch (error) {
|
|
2406
|
+
this.emit("tunnel:error", error);
|
|
2407
|
+
this.session.qrUrl = `${localUrl}?s=${sessionId}&t=${this.sessionSecret}`;
|
|
2408
|
+
}
|
|
2409
|
+
} else {
|
|
2410
|
+
this.session.qrUrl = `${localUrl}?s=${sessionId}&t=${this.sessionSecret}`;
|
|
2411
|
+
}
|
|
2412
|
+
if (this.config.enableTerminal) {
|
|
2413
|
+
this.terminal = new TerminalManager({
|
|
2414
|
+
shell: this.config.shell,
|
|
2415
|
+
cols: this.config.cols,
|
|
2416
|
+
rows: this.config.rows,
|
|
2417
|
+
cwd: this.config.cwd,
|
|
2418
|
+
env: this.config.env
|
|
2419
|
+
});
|
|
2420
|
+
this.terminal.on("data", (data) => {
|
|
2421
|
+
this.broadcast({ type: MessageTypes.TERMINAL_OUTPUT, payload: { data } });
|
|
2422
|
+
});
|
|
2423
|
+
this.terminal.on("exit", (code) => {
|
|
2424
|
+
this.broadcast({ type: MessageTypes.TERMINAL_EXIT, payload: { code } });
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2427
|
+
this.startHeartbeat();
|
|
2428
|
+
if (this.config.sessionTimeout > 0) {
|
|
2429
|
+
this.startSessionTimeout();
|
|
2430
|
+
}
|
|
2431
|
+
this.session.status = "waiting";
|
|
2432
|
+
this.isRunning = true;
|
|
2433
|
+
this.emit("started", this.session);
|
|
2434
|
+
return this.session;
|
|
2435
|
+
}
|
|
2436
|
+
/**
|
|
2437
|
+
* Stop the server
|
|
2438
|
+
*/
|
|
2439
|
+
async stop() {
|
|
2440
|
+
if (!this.isRunning) return;
|
|
2441
|
+
this.isRunning = false;
|
|
2442
|
+
if (this.heartbeatTimer) {
|
|
2443
|
+
clearInterval(this.heartbeatTimer);
|
|
2444
|
+
this.heartbeatTimer = null;
|
|
2445
|
+
}
|
|
2446
|
+
if (this.sessionTimeoutTimer) {
|
|
2447
|
+
clearTimeout(this.sessionTimeoutTimer);
|
|
2448
|
+
this.sessionTimeoutTimer = null;
|
|
2449
|
+
}
|
|
2450
|
+
this.broadcast({ type: MessageTypes.SESSION_END, payload: {} });
|
|
2451
|
+
for (const client of this.clients.values()) {
|
|
2452
|
+
client.ws.close(1e3, "Server shutting down");
|
|
2453
|
+
}
|
|
2454
|
+
this.clients.clear();
|
|
2455
|
+
if (this.terminal) {
|
|
2456
|
+
this.terminal.destroy();
|
|
2457
|
+
this.terminal = null;
|
|
2458
|
+
}
|
|
2459
|
+
if (this.tunnel) {
|
|
2460
|
+
await this.tunnel.close();
|
|
2461
|
+
this.tunnel = null;
|
|
2462
|
+
}
|
|
2463
|
+
if (this.wss) {
|
|
2464
|
+
this.wss.close();
|
|
2465
|
+
this.wss = null;
|
|
2466
|
+
}
|
|
2467
|
+
if (this.httpServer) {
|
|
2468
|
+
await new Promise((resolve) => {
|
|
2469
|
+
this.httpServer.close(() => resolve());
|
|
2470
|
+
});
|
|
2471
|
+
this.httpServer = null;
|
|
2472
|
+
}
|
|
2473
|
+
if (this.session) {
|
|
2474
|
+
this.session.status = "stopped";
|
|
2475
|
+
}
|
|
2476
|
+
this.emit("stopped");
|
|
2477
|
+
}
|
|
2478
|
+
/**
|
|
2479
|
+
* Broadcast message to all authenticated clients
|
|
2480
|
+
*/
|
|
2481
|
+
broadcast(message) {
|
|
2482
|
+
const data = JSON.stringify({
|
|
2483
|
+
type: message.type,
|
|
2484
|
+
payload: message.payload,
|
|
2485
|
+
timestamp: message.timestamp || Date.now()
|
|
2486
|
+
});
|
|
2487
|
+
for (const client of this.clients.values()) {
|
|
2488
|
+
if (client.authenticated && client.ws.readyState === WebSocket.OPEN) {
|
|
2489
|
+
client.ws.send(data);
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
/**
|
|
2494
|
+
* Send notification to clients
|
|
2495
|
+
*/
|
|
2496
|
+
notify(notification) {
|
|
2497
|
+
this.broadcast({
|
|
2498
|
+
type: MessageTypes.NOTIFICATION,
|
|
2499
|
+
payload: notification
|
|
2500
|
+
});
|
|
2501
|
+
}
|
|
2502
|
+
/**
|
|
2503
|
+
* Get current session
|
|
2504
|
+
*/
|
|
2505
|
+
getSession() {
|
|
2506
|
+
return this.session;
|
|
2507
|
+
}
|
|
2508
|
+
/**
|
|
2509
|
+
* Check if server is running
|
|
2510
|
+
*/
|
|
2511
|
+
isActive() {
|
|
2512
|
+
return this.isRunning && this.session?.status !== "stopped";
|
|
2513
|
+
}
|
|
2514
|
+
/**
|
|
2515
|
+
* Get connected client count
|
|
2516
|
+
*/
|
|
2517
|
+
getConnectedCount() {
|
|
2518
|
+
let count = 0;
|
|
2519
|
+
for (const client of this.clients.values()) {
|
|
2520
|
+
if (client.authenticated) count++;
|
|
2521
|
+
}
|
|
2522
|
+
return count;
|
|
2523
|
+
}
|
|
2524
|
+
/**
|
|
2525
|
+
* Write to terminal
|
|
2526
|
+
*/
|
|
2527
|
+
writeToTerminal(data) {
|
|
2528
|
+
this.terminal?.write(data);
|
|
2529
|
+
}
|
|
2530
|
+
/**
|
|
2531
|
+
* Resize terminal
|
|
2532
|
+
*/
|
|
2533
|
+
resizeTerminal(cols, rows) {
|
|
2534
|
+
this.terminal?.resize(cols, rows);
|
|
2535
|
+
}
|
|
2536
|
+
/**
|
|
2537
|
+
* Setup WebSocket handlers
|
|
2538
|
+
*/
|
|
2539
|
+
setupWebSocketHandlers() {
|
|
2540
|
+
this.wss.on("connection", (ws, req) => {
|
|
2541
|
+
const clientId = this.generateClientId();
|
|
2542
|
+
const client = {
|
|
2543
|
+
id: clientId,
|
|
2544
|
+
ws,
|
|
2545
|
+
authenticated: false,
|
|
2546
|
+
device: {
|
|
2547
|
+
id: clientId,
|
|
2548
|
+
userAgent: req.headers["user-agent"],
|
|
2549
|
+
ip: req.socket.remoteAddress,
|
|
2550
|
+
connectedAt: /* @__PURE__ */ new Date(),
|
|
2551
|
+
lastActivity: /* @__PURE__ */ new Date()
|
|
2552
|
+
},
|
|
2553
|
+
lastPing: Date.now()
|
|
2554
|
+
};
|
|
2555
|
+
if (this.clients.size >= this.config.maxConnections) {
|
|
2556
|
+
ws.close(1013, "Max connections reached");
|
|
2557
|
+
return;
|
|
2558
|
+
}
|
|
2559
|
+
this.clients.set(clientId, client);
|
|
2560
|
+
ws.send(JSON.stringify({ type: MessageTypes.AUTH_REQUIRED, timestamp: Date.now() }));
|
|
2561
|
+
ws.on("message", (data) => {
|
|
2562
|
+
try {
|
|
2563
|
+
const message = JSON.parse(data.toString());
|
|
2564
|
+
this.handleClientMessage(client, message);
|
|
2565
|
+
} catch {
|
|
2566
|
+
}
|
|
2567
|
+
});
|
|
2568
|
+
ws.on("close", () => {
|
|
2569
|
+
this.clients.delete(clientId);
|
|
2570
|
+
if (this.session && client.authenticated) {
|
|
2571
|
+
this.session.connectedDevices = this.session.connectedDevices.filter(
|
|
2572
|
+
(d) => d.id !== clientId
|
|
2573
|
+
);
|
|
2574
|
+
if (this.session.connectedDevices.length === 0) {
|
|
2575
|
+
this.session.status = "waiting";
|
|
2576
|
+
}
|
|
2577
|
+
this.emit("client:disconnected", client.device);
|
|
2578
|
+
}
|
|
2579
|
+
});
|
|
2580
|
+
ws.on("error", (error) => {
|
|
2581
|
+
this.emit("client:error", clientId, error);
|
|
2582
|
+
});
|
|
2583
|
+
ws.on("pong", () => {
|
|
2584
|
+
client.lastPing = Date.now();
|
|
2585
|
+
});
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
/**
|
|
2589
|
+
* Handle client message
|
|
2590
|
+
*/
|
|
2591
|
+
handleClientMessage(client, message) {
|
|
2592
|
+
client.device.lastActivity = /* @__PURE__ */ new Date();
|
|
2593
|
+
if (this.session) {
|
|
2594
|
+
this.session.lastActivity = /* @__PURE__ */ new Date();
|
|
2595
|
+
}
|
|
2596
|
+
if (this.config.sessionTimeout > 0) {
|
|
2597
|
+
this.resetSessionTimeout();
|
|
2598
|
+
}
|
|
2599
|
+
switch (message.type) {
|
|
2600
|
+
case MessageTypes.AUTH:
|
|
2601
|
+
this.handleAuth(client, message.token);
|
|
2602
|
+
break;
|
|
2603
|
+
case MessageTypes.TERMINAL_INPUT:
|
|
2604
|
+
if (client.authenticated && message.data) {
|
|
2605
|
+
this.terminal?.write(message.data);
|
|
2606
|
+
}
|
|
2607
|
+
break;
|
|
2608
|
+
case MessageTypes.TERMINAL_RESIZE:
|
|
2609
|
+
if (client.authenticated && message.cols && message.rows) {
|
|
2610
|
+
this.terminal?.resize(message.cols, message.rows);
|
|
2611
|
+
}
|
|
2612
|
+
break;
|
|
2613
|
+
case MessageTypes.TERMINAL_CLEAR:
|
|
2614
|
+
if (client.authenticated) {
|
|
2615
|
+
this.terminal?.clear();
|
|
2616
|
+
}
|
|
2617
|
+
break;
|
|
2618
|
+
case MessageTypes.PING:
|
|
2619
|
+
client.ws.send(JSON.stringify({ type: MessageTypes.PONG, timestamp: Date.now() }));
|
|
2620
|
+
break;
|
|
2621
|
+
case MessageTypes.COMMAND:
|
|
2622
|
+
if (client.authenticated) {
|
|
2623
|
+
this.emit("command", {
|
|
2624
|
+
clientId: client.id,
|
|
2625
|
+
command: message.command,
|
|
2626
|
+
args: message.args
|
|
2627
|
+
});
|
|
2628
|
+
}
|
|
2629
|
+
break;
|
|
2630
|
+
default:
|
|
2631
|
+
this.emit("message", client, message);
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
/**
|
|
2635
|
+
* Handle authentication
|
|
2636
|
+
*/
|
|
2637
|
+
handleAuth(client, token) {
|
|
2638
|
+
if (token === this.sessionSecret) {
|
|
2639
|
+
client.authenticated = true;
|
|
2640
|
+
if (this.session) {
|
|
2641
|
+
this.session.connectedDevices.push(client.device);
|
|
2642
|
+
this.session.status = "connected";
|
|
2643
|
+
}
|
|
2644
|
+
client.ws.send(
|
|
2645
|
+
JSON.stringify({
|
|
2646
|
+
type: MessageTypes.AUTH_SUCCESS,
|
|
2647
|
+
payload: {
|
|
2648
|
+
sessionId: this.session?.id,
|
|
2649
|
+
terminalEnabled: this.config.enableTerminal
|
|
2650
|
+
},
|
|
2651
|
+
timestamp: Date.now()
|
|
2652
|
+
})
|
|
2653
|
+
);
|
|
2654
|
+
if (this.config.enableTerminal && !this.terminal?.isRunning()) {
|
|
2655
|
+
this.terminal?.start();
|
|
2656
|
+
}
|
|
2657
|
+
this.emit("client:connected", client.device);
|
|
2658
|
+
} else {
|
|
2659
|
+
client.ws.send(JSON.stringify({ type: MessageTypes.AUTH_FAILED, timestamp: Date.now() }));
|
|
2660
|
+
setTimeout(() => client.ws.close(1008, "Authentication failed"), 100);
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
/**
|
|
2664
|
+
* Handle HTTP request
|
|
2665
|
+
*/
|
|
2666
|
+
handleHttpRequest(req, res) {
|
|
2667
|
+
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
2668
|
+
const path = url.pathname;
|
|
2669
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
2670
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
2671
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
2672
|
+
if (req.method === "OPTIONS") {
|
|
2673
|
+
res.writeHead(204);
|
|
2674
|
+
res.end();
|
|
2675
|
+
return;
|
|
2676
|
+
}
|
|
2677
|
+
if (path === "/" || path === "/index.html") {
|
|
2678
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
2679
|
+
res.end(getWebClient());
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2682
|
+
if (path === "/health") {
|
|
2683
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2684
|
+
res.end(JSON.stringify({ status: "ok", session: this.session?.id }));
|
|
2685
|
+
return;
|
|
2686
|
+
}
|
|
2687
|
+
if (path === "/api/session") {
|
|
2688
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2689
|
+
res.end(
|
|
2690
|
+
JSON.stringify({
|
|
2691
|
+
id: this.session?.id,
|
|
2692
|
+
name: this.session?.name,
|
|
2693
|
+
status: this.session?.status,
|
|
2694
|
+
connectedDevices: this.session?.connectedDevices.length
|
|
2695
|
+
})
|
|
2696
|
+
);
|
|
2697
|
+
return;
|
|
2698
|
+
}
|
|
2699
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
2700
|
+
res.end("Not Found");
|
|
2701
|
+
}
|
|
2702
|
+
/**
|
|
2703
|
+
* Start heartbeat
|
|
2704
|
+
*/
|
|
2705
|
+
startHeartbeat() {
|
|
2706
|
+
this.heartbeatTimer = setInterval(() => {
|
|
2707
|
+
const now = Date.now();
|
|
2708
|
+
for (const [id, client] of this.clients) {
|
|
2709
|
+
if (now - client.lastPing > this.config.heartbeatInterval * 2) {
|
|
2710
|
+
client.ws.terminate();
|
|
2711
|
+
this.clients.delete(id);
|
|
2712
|
+
} else if (client.ws.readyState === WebSocket.OPEN) {
|
|
2713
|
+
client.ws.ping();
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
}, this.config.heartbeatInterval);
|
|
2717
|
+
}
|
|
2718
|
+
/**
|
|
2719
|
+
* Start session timeout
|
|
2720
|
+
*/
|
|
2721
|
+
startSessionTimeout() {
|
|
2722
|
+
this.sessionTimeoutTimer = setTimeout(() => {
|
|
2723
|
+
if (this.session?.connectedDevices.length === 0) {
|
|
2724
|
+
this.stop();
|
|
2725
|
+
}
|
|
2726
|
+
}, this.config.sessionTimeout);
|
|
2727
|
+
}
|
|
2728
|
+
/**
|
|
2729
|
+
* Reset session timeout
|
|
2730
|
+
*/
|
|
2731
|
+
resetSessionTimeout() {
|
|
2732
|
+
if (this.sessionTimeoutTimer) {
|
|
2733
|
+
clearTimeout(this.sessionTimeoutTimer);
|
|
2734
|
+
}
|
|
2735
|
+
if (this.config.sessionTimeout > 0) {
|
|
2736
|
+
this.startSessionTimeout();
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
/**
|
|
2740
|
+
* Get local IP
|
|
2741
|
+
*/
|
|
2742
|
+
getLocalIP() {
|
|
2743
|
+
const interfaces = os.networkInterfaces();
|
|
2744
|
+
for (const name of Object.keys(interfaces)) {
|
|
2745
|
+
for (const iface of interfaces[name] || []) {
|
|
2746
|
+
if (iface.family === "IPv4" && !iface.internal) {
|
|
2747
|
+
return iface.address;
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
return "127.0.0.1";
|
|
2752
|
+
}
|
|
2753
|
+
/**
|
|
2754
|
+
* Generate session ID
|
|
2755
|
+
*/
|
|
2756
|
+
generateSessionId() {
|
|
2757
|
+
return crypto.randomBytes(4).toString("hex");
|
|
2758
|
+
}
|
|
2759
|
+
/**
|
|
2760
|
+
* Generate client ID
|
|
2761
|
+
*/
|
|
2762
|
+
generateClientId() {
|
|
2763
|
+
return "c_" + crypto.randomBytes(4).toString("hex");
|
|
2764
|
+
}
|
|
2765
|
+
/**
|
|
2766
|
+
* Generate secret
|
|
2767
|
+
*/
|
|
2768
|
+
generateSecret() {
|
|
2769
|
+
return crypto.randomBytes(16).toString("hex");
|
|
2770
|
+
}
|
|
2771
|
+
};
|
|
2772
|
+
|
|
2773
|
+
export {
|
|
2774
|
+
DEFAULT_CONFIG,
|
|
2775
|
+
MessageTypes,
|
|
2776
|
+
TerminalManager,
|
|
2777
|
+
TunnelManager,
|
|
2778
|
+
checkTunnelAvailability,
|
|
2779
|
+
findAvailableTunnel,
|
|
2780
|
+
getWebClient,
|
|
2781
|
+
RemoteServer
|
|
2782
|
+
};
|