@tlbx-ai/midterm 0.1.0 → 8.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/bin/midterm.js +143 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Launch MidTerm through `npx`.
|
|
|
6
6
|
npx @tlbx-ai/midterm
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
The launcher downloads the native MidTerm release for your platform, caches it in your user profile,
|
|
9
|
+
The launcher downloads the native MidTerm release for your platform, caches it in your user profile, runs it locally, and opens MidTerm in your default browser.
|
|
10
10
|
|
|
11
11
|
Supported platforms:
|
|
12
12
|
|
|
@@ -24,10 +24,12 @@ npx @tlbx-ai/midterm -- --port 2001 --bind 127.0.0.1
|
|
|
24
24
|
Launcher-only options:
|
|
25
25
|
|
|
26
26
|
- `--channel stable|dev`
|
|
27
|
+
- `--no-browser`
|
|
27
28
|
- `--help-launcher`
|
|
28
29
|
|
|
29
30
|
Notes:
|
|
30
31
|
|
|
31
32
|
- Default channel is `stable`
|
|
32
33
|
- If you do not pass `--bind`, the launcher forces `127.0.0.1`
|
|
34
|
+
- If you do not pass `--port`, the launcher opens `https://127.0.0.1:2000`
|
|
33
35
|
- The launcher sets `MIDTERM_LAUNCH_MODE=npx` for the child process
|
package/bin/midterm.js
CHANGED
|
@@ -4,11 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
const fs = require('node:fs');
|
|
6
6
|
const fsp = require('node:fs/promises');
|
|
7
|
+
const https = require('node:https');
|
|
7
8
|
const os = require('node:os');
|
|
8
9
|
const path = require('node:path');
|
|
9
10
|
const { spawn, spawnSync } = require('node:child_process');
|
|
10
11
|
|
|
11
|
-
const PACKAGE_VERSION = '
|
|
12
|
+
const { version: PACKAGE_VERSION } = require('../package.json');
|
|
13
|
+
const DEFAULT_PORT = 2000;
|
|
14
|
+
const SERVER_READY_TIMEOUT_MS = 15000;
|
|
15
|
+
const SERVER_READY_INTERVAL_MS = 500;
|
|
12
16
|
const REPO_OWNER = 'tlbx-ai';
|
|
13
17
|
const REPO_NAME = 'MidTerm';
|
|
14
18
|
const GITHUB_API = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}`;
|
|
@@ -32,10 +36,14 @@ async function main() {
|
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
const childArgs = passthrough.slice();
|
|
35
|
-
|
|
39
|
+
const explicitBind = getArgValue(childArgs, '--bind');
|
|
40
|
+
const explicitPort = parsePortArg(getArgValue(childArgs, '--port'));
|
|
41
|
+
|
|
42
|
+
if (!explicitBind) {
|
|
36
43
|
childArgs.push('--bind', '127.0.0.1');
|
|
37
44
|
}
|
|
38
45
|
|
|
46
|
+
const browserUrl = buildBrowserUrl(explicitBind ?? '127.0.0.1', explicitPort ?? DEFAULT_PORT);
|
|
39
47
|
const childEnv = {
|
|
40
48
|
...process.env,
|
|
41
49
|
MIDTERM_LAUNCH_MODE: 'npx',
|
|
@@ -49,6 +57,10 @@ async function main() {
|
|
|
49
57
|
env: childEnv
|
|
50
58
|
});
|
|
51
59
|
|
|
60
|
+
if (launcher.openBrowser) {
|
|
61
|
+
void openBrowserWhenReady(browserUrl);
|
|
62
|
+
}
|
|
63
|
+
|
|
52
64
|
forwardSignal(child, 'SIGINT');
|
|
53
65
|
forwardSignal(child, 'SIGTERM');
|
|
54
66
|
forwardSignal(child, 'SIGHUP');
|
|
@@ -66,7 +78,8 @@ async function main() {
|
|
|
66
78
|
function parseArgs(args) {
|
|
67
79
|
const launcher = {
|
|
68
80
|
help: false,
|
|
69
|
-
channel: 'stable'
|
|
81
|
+
channel: 'stable',
|
|
82
|
+
openBrowser: true
|
|
70
83
|
};
|
|
71
84
|
const passthrough = [];
|
|
72
85
|
|
|
@@ -83,6 +96,11 @@ function parseArgs(args) {
|
|
|
83
96
|
continue;
|
|
84
97
|
}
|
|
85
98
|
|
|
99
|
+
if (arg === '--no-browser') {
|
|
100
|
+
launcher.openBrowser = false;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
86
104
|
if (arg === '--channel') {
|
|
87
105
|
const value = args[i + 1];
|
|
88
106
|
if (value !== 'stable' && value !== 'dev') {
|
|
@@ -106,6 +124,7 @@ function printHelp() {
|
|
|
106
124
|
console.log('');
|
|
107
125
|
console.log('Launcher options:');
|
|
108
126
|
console.log(' --channel stable|dev Choose the release channel (default: stable)');
|
|
127
|
+
console.log(' --no-browser Do not auto-open MidTerm in the default browser');
|
|
109
128
|
console.log(' --help-launcher Show launcher help');
|
|
110
129
|
console.log('');
|
|
111
130
|
console.log('All other arguments are passed to mt.');
|
|
@@ -295,6 +314,127 @@ function hasArg(args, name) {
|
|
|
295
314
|
return args.some((arg) => arg === name || arg.startsWith(`${name}=`));
|
|
296
315
|
}
|
|
297
316
|
|
|
317
|
+
function getArgValue(args, name) {
|
|
318
|
+
for (let i = 0; i < args.length; i++) {
|
|
319
|
+
const arg = args[i];
|
|
320
|
+
if (arg === name) {
|
|
321
|
+
return args[i + 1];
|
|
322
|
+
}
|
|
323
|
+
if (arg.startsWith(`${name}=`)) {
|
|
324
|
+
return arg.slice(name.length + 1);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return undefined;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function parsePortArg(value) {
|
|
332
|
+
if (!value) {
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const parsed = Number.parseInt(value, 10);
|
|
337
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
|
|
338
|
+
return undefined;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return parsed;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function buildBrowserUrl(bindAddress, port) {
|
|
345
|
+
const normalized = normalizeHostForBrowser(bindAddress);
|
|
346
|
+
return `https://${normalized}:${port}`;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function normalizeHostForBrowser(bindAddress) {
|
|
350
|
+
const raw = String(bindAddress || '').trim();
|
|
351
|
+
if (!raw || raw === '0.0.0.0' || raw === '::' || raw === '[::]') {
|
|
352
|
+
return '127.0.0.1';
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const host = raw.replace(/^\[(.*)\]$/, '$1');
|
|
356
|
+
if (host.includes(':')) {
|
|
357
|
+
return `[${host}]`;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return host;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function openBrowserWhenReady(url) {
|
|
364
|
+
const ready = await waitForServer(url, SERVER_READY_TIMEOUT_MS);
|
|
365
|
+
if (!ready) {
|
|
366
|
+
console.error(`@tlbx-ai/midterm: server did not become ready within ${SERVER_READY_TIMEOUT_MS}ms, opening browser anyway`);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
openUrl(url);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async function waitForServer(url, timeoutMs) {
|
|
373
|
+
const deadline = Date.now() + timeoutMs;
|
|
374
|
+
|
|
375
|
+
while (Date.now() < deadline) {
|
|
376
|
+
if (await probeUrl(url)) {
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
await sleep(SERVER_READY_INTERVAL_MS);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function probeUrl(url) {
|
|
387
|
+
return new Promise((resolve) => {
|
|
388
|
+
const request = https.request(url, {
|
|
389
|
+
method: 'GET',
|
|
390
|
+
rejectUnauthorized: false,
|
|
391
|
+
timeout: SERVER_READY_INTERVAL_MS
|
|
392
|
+
}, (response) => {
|
|
393
|
+
response.resume();
|
|
394
|
+
resolve(true);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
request.on('error', () => resolve(false));
|
|
398
|
+
request.on('timeout', () => {
|
|
399
|
+
request.destroy();
|
|
400
|
+
resolve(false);
|
|
401
|
+
});
|
|
402
|
+
request.end();
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function sleep(ms) {
|
|
407
|
+
return new Promise((resolve) => {
|
|
408
|
+
setTimeout(resolve, ms);
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function openUrl(url) {
|
|
413
|
+
let command;
|
|
414
|
+
let args;
|
|
415
|
+
|
|
416
|
+
if (process.platform === 'win32') {
|
|
417
|
+
command = 'cmd';
|
|
418
|
+
args = ['/c', 'start', '', url];
|
|
419
|
+
} else if (process.platform === 'darwin') {
|
|
420
|
+
command = 'open';
|
|
421
|
+
args = [url];
|
|
422
|
+
} else {
|
|
423
|
+
command = 'xdg-open';
|
|
424
|
+
args = [url];
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const result = spawn(command, args, {
|
|
428
|
+
detached: true,
|
|
429
|
+
stdio: 'ignore',
|
|
430
|
+
windowsHide: true
|
|
431
|
+
});
|
|
432
|
+
result.on('error', (error) => {
|
|
433
|
+
console.error(`@tlbx-ai/midterm: failed to open browser automatically: ${error.message}`);
|
|
434
|
+
});
|
|
435
|
+
result.unref();
|
|
436
|
+
}
|
|
437
|
+
|
|
298
438
|
function forwardSignal(child, signal) {
|
|
299
439
|
process.on(signal, () => {
|
|
300
440
|
if (!child.killed) {
|