tmex-cli 0.12.3-beta1 → 0.12.3
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/CHANGELOG.md +3 -11
- package/dist/cli-node.js +98 -287
- package/dist/runtime/{cpufeatures-nvqtn6ne.node → cpufeatures-dxrn1j88.node} +0 -0
- package/dist/runtime/server.js +255 -31
- package/dist/runtime/{sshcrypto-rfw667cw.node → sshcrypto-fjcj736m.node} +0 -0
- package/package.json +1 -1
- package/resources/fe-dist/assets/DevicePage-EoPdR0a8.js +15 -0
- package/resources/fe-dist/assets/{DevicesPage-BXVj9MYn.js → DevicesPage-DaO7f0iC.js} +2 -2
- package/resources/fe-dist/assets/{FilePage-DFvyeBg8.js → FilePage-BQsLFpIe.js} +2 -2
- package/resources/fe-dist/assets/{SettingsPage-B8daCB8Y.js → SettingsPage-DNq70CL7.js} +5 -10
- package/resources/fe-dist/assets/{arc-B20iCBM1.js → arc-Bw8pvFsh.js} +2 -2
- package/resources/fe-dist/assets/architectureDiagram-3BPJPVTR-ClXPEeos.js +37 -0
- package/resources/fe-dist/assets/{blockDiagram-GPEHLZMM-BqP9dRVx.js → blockDiagram-GPEHLZMM-Sp24bCmA.js} +2 -2
- package/resources/fe-dist/assets/{c4Diagram-AAUBKEIU-DbqKeZkc.js → c4Diagram-AAUBKEIU-DjyOwpgl.js} +2 -2
- package/resources/fe-dist/assets/{card-DnWixV3q.js → card-CMyk5OFt.js} +2 -2
- package/resources/fe-dist/assets/channel-B9WrZCGJ.js +2 -0
- package/resources/fe-dist/assets/{chunk-2J33WTMH-BHZUy1d7.js → chunk-2J33WTMH-_i-tgxvN.js} +2 -2
- package/resources/fe-dist/assets/{chunk-4BX2VUAB-QKjwiFAV.js → chunk-4BX2VUAB-CYDjMAJT.js} +2 -2
- package/resources/fe-dist/assets/{chunk-55IACEB6-aUtPslwp.js → chunk-55IACEB6-CGcgNWHA.js} +2 -2
- package/resources/fe-dist/assets/{chunk-727SXJPM-DetOPTEw.js → chunk-727SXJPM-CVF1o51L.js} +2 -2
- package/resources/fe-dist/assets/{chunk-AQP2D5EJ-Cj4pjkf1.js → chunk-AQP2D5EJ-BzXiGlmX.js} +2 -2
- package/resources/fe-dist/assets/{chunk-FMBD7UC4-DuMfjqmS.js → chunk-FMBD7UC4-DG7P62CK.js} +2 -2
- package/resources/fe-dist/assets/{chunk-ND2GUHAM-De6BOrhf.js → chunk-ND2GUHAM-C82kWDQB.js} +2 -2
- package/resources/fe-dist/assets/{chunk-QZHKN3VN-C6UKMTXf.js → chunk-QZHKN3VN-FB0r6Osl.js} +2 -2
- package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-B1-CPF_4.js +2 -0
- package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-B1-CPF_4.js +2 -0
- package/resources/fe-dist/assets/cose-bilkent-S5V4N54A--v8cfu0h.js +2 -0
- package/resources/fe-dist/assets/{dagre-BM42HDAG-DLE28RsS.js → dagre-BM42HDAG-BTLOD-KX.js} +2 -2
- package/resources/fe-dist/assets/{diagram-2AECGRRQ-CrloCw3m.js → diagram-2AECGRRQ-29BFdqPY.js} +2 -2
- package/resources/fe-dist/assets/{diagram-5GNKFQAL-BGEyXIkF.js → diagram-5GNKFQAL-t_wHTq5Q.js} +2 -2
- package/resources/fe-dist/assets/{diagram-KO2AKTUF-CZa19ANY.js → diagram-KO2AKTUF-JNh9Pmt9.js} +2 -2
- package/resources/fe-dist/assets/{diagram-LMA3HP47-DC7BsuGl.js → diagram-LMA3HP47-DAifhNss.js} +2 -2
- package/resources/fe-dist/assets/{diagram-OG6HWLK6-BH4wPrwj.js → diagram-OG6HWLK6-BOqtQL5W.js} +2 -2
- package/resources/fe-dist/assets/{erDiagram-TEJ5UH35-CsQIlfSm.js → erDiagram-TEJ5UH35-W-aJKAOK.js} +2 -2
- package/resources/fe-dist/assets/{flowDiagram-I6XJVG4X-CkjuxggR.js → flowDiagram-I6XJVG4X-DEt1TjbJ.js} +2 -2
- package/resources/fe-dist/assets/ganttDiagram-6RSMTGT7-CrYu9aI_.js +293 -0
- package/resources/fe-dist/assets/{gitGraphDiagram-PVQCEYII-0Ni5AA_m.js → gitGraphDiagram-PVQCEYII-BlltYekf.js} +2 -2
- package/resources/fe-dist/assets/index-CXIj7HZi.css +1 -0
- package/resources/fe-dist/assets/{index-96GlCNDm.js → index-FNpsxYPN.js} +53 -53
- package/resources/fe-dist/assets/{infoDiagram-5YYISTIA-tIpZWSDh.js → infoDiagram-5YYISTIA-nhdfbNPF.js} +2 -2
- package/resources/fe-dist/assets/{ishikawaDiagram-YF4QCWOH-BLnWokk9.js → ishikawaDiagram-YF4QCWOH-CwluZjLD.js} +2 -2
- package/resources/fe-dist/assets/{journeyDiagram-JHISSGLW-NBzojNKg.js → journeyDiagram-JHISSGLW-BkvLX_wz.js} +2 -2
- package/resources/fe-dist/assets/{kanban-definition-UN3LZRKU-BFDAtm6I.js → kanban-definition-UN3LZRKU-DU_bbXWH.js} +2 -2
- package/resources/fe-dist/assets/{linear-B5DOEe_O.js → linear-ByJrBbSQ.js} +2 -2
- package/resources/fe-dist/assets/{markdown-preview-DD3cdI4r.js → markdown-preview-D9O-wO1U.js} +4 -4
- package/resources/fe-dist/assets/{mermaid.core-BWq4oZoC.js → mermaid.core-DR6r9S_a.js} +6 -6
- package/resources/fe-dist/assets/{mindmap-definition-RKZ34NQL-Bi2fp38o.js → mindmap-definition-RKZ34NQL-BYXsfrSb.js} +2 -2
- package/resources/fe-dist/assets/{pieDiagram-4H26LBE5-CKK8p15L.js → pieDiagram-4H26LBE5-leRJNTgW.js} +2 -2
- package/resources/fe-dist/assets/{quadrantDiagram-W4KKPZXB-QslRPIFi.js → quadrantDiagram-W4KKPZXB-BfxS6APJ.js} +2 -2
- package/resources/fe-dist/assets/{requirementDiagram-4Y6WPE33-BRo6E1BH.js → requirementDiagram-4Y6WPE33-D1ViYHCD.js} +2 -2
- package/resources/fe-dist/assets/{sankeyDiagram-5OEKKPKP-B16TNk4Z.js → sankeyDiagram-5OEKKPKP-CFjujjK9.js} +2 -2
- package/resources/fe-dist/assets/{sequenceDiagram-3UESZ5HK-DiZuSmvo.js → sequenceDiagram-3UESZ5HK-DYKl7nA9.js} +2 -2
- package/resources/fe-dist/assets/{stateDiagram-AJRCARHV-BOn6CbQ6.js → stateDiagram-AJRCARHV-C3e0GwoJ.js} +2 -2
- package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-Bj3cAhsB.js +2 -0
- package/resources/fe-dist/assets/terminal-settings-panel-DLuBkEEZ.js +26 -0
- package/resources/fe-dist/assets/{timeline-definition-PNZ67QCA-B74c_y_8.js → timeline-definition-PNZ67QCA-DO38TAIg.js} +2 -2
- package/resources/fe-dist/assets/{vennDiagram-CIIHVFJN-BZvPBIfU.js → vennDiagram-CIIHVFJN-Co9hBQx5.js} +2 -2
- package/resources/fe-dist/assets/{wardley-L42UT6IY-CgvxGMxD.js → wardley-L42UT6IY-lf2QB-Cd.js} +2 -2
- package/resources/fe-dist/assets/{wardleyDiagram-YWT4CUSO-BI54Re1E.js → wardleyDiagram-YWT4CUSO-fJgWAjlp.js} +2 -2
- package/resources/fe-dist/assets/{xychartDiagram-2RQKCTM6-C9AmQfwS.js → xychartDiagram-2RQKCTM6-DdxhqP-F.js} +2 -2
- package/resources/fe-dist/index.html +2 -2
- package/resources/gateway-drizzle/0009_lying_lethal_legion.sql +9 -0
- package/resources/gateway-drizzle/meta/_journal.json +7 -0
- package/resources/fe-dist/assets/DevicePage-Cc6BDsCB.js +0 -25
- package/resources/fe-dist/assets/architectureDiagram-3BPJPVTR-DkJzy--B.js +0 -37
- package/resources/fe-dist/assets/channel-B9gGKu0d.js +0 -2
- package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-C5AFJpT6.js +0 -2
- package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-C5AFJpT6.js +0 -2
- package/resources/fe-dist/assets/cose-bilkent-S5V4N54A-giRkzps1.js +0 -2
- package/resources/fe-dist/assets/ganttDiagram-6RSMTGT7-eEGLUumD.js +0 -293
- package/resources/fe-dist/assets/index-BhU3LRKb.css +0 -1
- package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-DA0F4iLh.js +0 -2
- package/resources/fe-dist/assets/terminal-settings-panel-DVSfGH3m.js +0 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,25 +1,17 @@
|
|
|
1
|
-
# 0.12.3
|
|
1
|
+
# 0.12.3
|
|
2
2
|
|
|
3
3
|
_2026-06-15_
|
|
4
4
|
|
|
5
5
|
## English
|
|
6
6
|
|
|
7
|
-
### Fixes
|
|
8
|
-
|
|
9
|
-
- Bun detection is now reliable across install methods (Homebrew, the official installer, etc.). It no longer reports Bun as "not installed" when your shell startup prints to the terminal.
|
|
10
|
-
|
|
11
7
|
### New
|
|
12
8
|
|
|
13
|
-
-
|
|
9
|
+
- Custom terminal shortcuts: build your own quick-action bar for the terminal — add, remove, drag to reorder, rename, and capture key combinations by pressing them. Your shortcuts are saved on the server and shared across all your browsers. Built-in special actions (paste, toggle the text-input keyboard, start a new Agent session, scroll the terminal to the bottom) can be added too, and an optional icon mode shows keys as Apple-style symbols (⌃⇧⏎). A live preview lets you see the bar before you save.
|
|
14
10
|
|
|
15
11
|
---
|
|
16
12
|
|
|
17
13
|
## 中文
|
|
18
14
|
|
|
19
|
-
### 修复
|
|
20
|
-
|
|
21
|
-
- 修复 Bun 检测:Homebrew、官方安装脚本等各种方式装的 Bun 现在都能被正确识别,不再因为 shell 启动时向终端打印内容而误判为「未安装」。
|
|
22
|
-
|
|
23
15
|
### 新增
|
|
24
16
|
|
|
25
|
-
-
|
|
17
|
+
- 自定义终端快捷键:可以自己定制终端的快捷按钮栏——增删、拖拽排序、重命名,还能直接按下组合键来录入。快捷键保存在服务器、多端共享。内置特殊动作(粘贴、切换文本框键盘、新建 Agent 会话、终端回到最下方)也能一键加入;并可选「图标模式」用苹果风格符号(⌃⇧⏎)显示按键。编辑时提供实时预览,保存后生效。
|
package/dist/cli-node.js
CHANGED
|
@@ -29,9 +29,9 @@ var MESSAGES = {
|
|
|
29
29
|
"cli.help": `tmex CLI
|
|
30
30
|
|
|
31
31
|
Usage:
|
|
32
|
-
tmex init [--no-interactive --install-dir <path> --host <host> --port <port> --db-path <path> --autostart <true|false>
|
|
33
|
-
tmex doctor [--install-dir <path>] [--json]
|
|
34
|
-
tmex upgrade [--version <version>] [--install-dir <path>]
|
|
32
|
+
tmex init [--no-interactive --install-dir <path> --host <host> --port <port> --db-path <path> --autostart <true|false>]
|
|
33
|
+
tmex doctor [--install-dir <path>] [--json]
|
|
34
|
+
tmex upgrade [--version <version>] [--install-dir <path>]
|
|
35
35
|
tmex uninstall [--install-dir <path>] [--yes] [--purge]
|
|
36
36
|
|
|
37
37
|
Global flags:
|
|
@@ -52,8 +52,6 @@ Global flags:
|
|
|
52
52
|
"bun.versionExecFailed": "Failed to execute bun --version. Please verify Bun installation.",
|
|
53
53
|
"bun.versionTooLow": "Bun version too low: current {{version}}, required >= {{minVersion}}",
|
|
54
54
|
"bun.checkFailed": "Bun check failed.",
|
|
55
|
-
"bun.explicitInvalid": "Specified bun path is invalid or not executable: {{path}}",
|
|
56
|
-
"bun.unsafePath": "Unsafe bun path (contains shell metacharacters): {{path}}",
|
|
57
55
|
"service.install.unsupportedPlatform": "Automatic service installation is not supported on this platform: {{platform}}",
|
|
58
56
|
"service.systemd.daemonReloadFailed": "systemctl daemon-reload failed: {{detail}}",
|
|
59
57
|
"service.systemd.enableFailed": "systemctl enable failed: {{detail}}",
|
|
@@ -130,9 +128,9 @@ Global flags:
|
|
|
130
128
|
"cli.help": `tmex CLI
|
|
131
129
|
|
|
132
130
|
用法:
|
|
133
|
-
tmex init [--no-interactive --install-dir <path> --host <host> --port <port> --db-path <path> --autostart <true|false>
|
|
134
|
-
tmex doctor [--install-dir <path>] [--json]
|
|
135
|
-
tmex upgrade [--version <version>] [--install-dir <path>]
|
|
131
|
+
tmex init [--no-interactive --install-dir <path> --host <host> --port <port> --db-path <path> --autostart <true|false>]
|
|
132
|
+
tmex doctor [--install-dir <path>] [--json]
|
|
133
|
+
tmex upgrade [--version <version>] [--install-dir <path>]
|
|
136
134
|
tmex uninstall [--install-dir <path>] [--yes] [--purge]
|
|
137
135
|
|
|
138
136
|
全局参数:
|
|
@@ -153,8 +151,6 @@ Global flags:
|
|
|
153
151
|
"bun.versionExecFailed": "无法执行 bun --version,请检查 Bun 安装是否完整。",
|
|
154
152
|
"bun.versionTooLow": "Bun 版本过低:当前 {{version}},要求 >= {{minVersion}}",
|
|
155
153
|
"bun.checkFailed": "Bun 检查失败。",
|
|
156
|
-
"bun.explicitInvalid": "指定的 bun 路径无效或不可执行:{{path}}",
|
|
157
|
-
"bun.unsafePath": "bun 路径包含 shell 特殊字符,不安全:{{path}}",
|
|
158
154
|
"service.install.unsupportedPlatform": "当前平台不支持自动安装服务:{{platform}}",
|
|
159
155
|
"service.systemd.daemonReloadFailed": "systemctl daemon-reload 失败:{{detail}}",
|
|
160
156
|
"service.systemd.enableFailed": "systemctl enable 失败:{{detail}}",
|
|
@@ -263,7 +259,7 @@ function t(key, vars) {
|
|
|
263
259
|
// src/lib/bun.ts
|
|
264
260
|
import { existsSync } from "node:fs";
|
|
265
261
|
import { homedir as homedir2 } from "node:os";
|
|
266
|
-
import {
|
|
262
|
+
import { join } from "node:path";
|
|
267
263
|
|
|
268
264
|
// src/lib/process.ts
|
|
269
265
|
import { spawn } from "node:child_process";
|
|
@@ -273,21 +269,10 @@ async function runCommand(command, args, options = {}) {
|
|
|
273
269
|
const child = spawn(command, args, {
|
|
274
270
|
cwd: options.cwd,
|
|
275
271
|
env: options.env,
|
|
276
|
-
stdio
|
|
272
|
+
stdio
|
|
277
273
|
});
|
|
278
274
|
let stdout = "";
|
|
279
275
|
let stderr = "";
|
|
280
|
-
let settled = false;
|
|
281
|
-
let timer = null;
|
|
282
|
-
const finish = (fn) => {
|
|
283
|
-
if (settled)
|
|
284
|
-
return;
|
|
285
|
-
settled = true;
|
|
286
|
-
if (timer) {
|
|
287
|
-
clearTimeout(timer);
|
|
288
|
-
}
|
|
289
|
-
fn();
|
|
290
|
-
};
|
|
291
276
|
if (stdio === "pipe") {
|
|
292
277
|
child.stdout?.on("data", (chunk) => {
|
|
293
278
|
stdout += chunk.toString();
|
|
@@ -296,21 +281,13 @@ async function runCommand(command, args, options = {}) {
|
|
|
296
281
|
stderr += chunk.toString();
|
|
297
282
|
});
|
|
298
283
|
}
|
|
299
|
-
|
|
300
|
-
timer = setTimeout(() => {
|
|
301
|
-
child.kill("SIGKILL");
|
|
302
|
-
finish(() => reject(new Error(`Command timed out after ${options.timeoutMs}ms: ${command}`)));
|
|
303
|
-
}, options.timeoutMs);
|
|
304
|
-
}
|
|
305
|
-
child.on("error", (error) => {
|
|
306
|
-
finish(() => reject(error));
|
|
307
|
-
});
|
|
284
|
+
child.on("error", reject);
|
|
308
285
|
child.on("close", (code) => {
|
|
309
|
-
|
|
286
|
+
resolve2({
|
|
310
287
|
code: code ?? 1,
|
|
311
288
|
stdout,
|
|
312
289
|
stderr
|
|
313
|
-
})
|
|
290
|
+
});
|
|
314
291
|
});
|
|
315
292
|
});
|
|
316
293
|
}
|
|
@@ -339,239 +316,63 @@ function compareSemver(left, right) {
|
|
|
339
316
|
return 0;
|
|
340
317
|
}
|
|
341
318
|
|
|
342
|
-
// src/lib/validate.ts
|
|
343
|
-
function asString(value) {
|
|
344
|
-
if (typeof value === "string")
|
|
345
|
-
return value;
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
function asBoolean(value) {
|
|
349
|
-
if (typeof value === "boolean")
|
|
350
|
-
return value;
|
|
351
|
-
if (typeof value !== "string")
|
|
352
|
-
return;
|
|
353
|
-
const normalized = value.trim().toLowerCase();
|
|
354
|
-
if (normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "y") {
|
|
355
|
-
return true;
|
|
356
|
-
}
|
|
357
|
-
if (normalized === "false" || normalized === "0" || normalized === "no" || normalized === "n") {
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
function parsePort(value) {
|
|
363
|
-
const port = Number(value);
|
|
364
|
-
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
365
|
-
throw new Error(t("errors.validate.invalidPort", { value }));
|
|
366
|
-
}
|
|
367
|
-
return port;
|
|
368
|
-
}
|
|
369
|
-
function assertNonEmpty(value, fieldName) {
|
|
370
|
-
const trimmed = value.trim();
|
|
371
|
-
if (!trimmed) {
|
|
372
|
-
throw new Error(t("errors.validate.emptyField", { field: fieldName }));
|
|
373
|
-
}
|
|
374
|
-
return trimmed;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
319
|
// src/lib/bun.ts
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
var OSC_OPEN = 93;
|
|
381
|
-
var BEL = 7;
|
|
382
|
-
var ST_TAIL = 92;
|
|
383
|
-
var LF = 10;
|
|
384
|
-
var CR = 13;
|
|
385
|
-
var DEL = 127;
|
|
386
|
-
var SLASH = 47;
|
|
387
|
-
var PROBE_TIMEOUT_MS = 5000;
|
|
388
|
-
function sanitizeBunPath(raw) {
|
|
389
|
-
let stripped = "";
|
|
390
|
-
for (let i = 0;i < raw.length; i += 1) {
|
|
391
|
-
const code = raw.charCodeAt(i);
|
|
392
|
-
if (code === ESC) {
|
|
393
|
-
const next = raw.charCodeAt(i + 1);
|
|
394
|
-
if (next === CSI_OPEN) {
|
|
395
|
-
i += 1;
|
|
396
|
-
while (i + 1 < raw.length) {
|
|
397
|
-
const c = raw.charCodeAt(i + 1);
|
|
398
|
-
i += 1;
|
|
399
|
-
if (c >= 64 && c <= 126) {
|
|
400
|
-
break;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
} else if (next === OSC_OPEN) {
|
|
404
|
-
i += 1;
|
|
405
|
-
while (i + 1 < raw.length) {
|
|
406
|
-
const c = raw.charCodeAt(i + 1);
|
|
407
|
-
if (c === BEL) {
|
|
408
|
-
i += 1;
|
|
409
|
-
break;
|
|
410
|
-
}
|
|
411
|
-
if (c === ESC && raw.charCodeAt(i + 2) === ST_TAIL) {
|
|
412
|
-
i += 2;
|
|
413
|
-
break;
|
|
414
|
-
}
|
|
415
|
-
i += 1;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
stripped += raw[i];
|
|
421
|
-
}
|
|
422
|
-
const lines = [];
|
|
423
|
-
let current = "";
|
|
424
|
-
const flush = () => {
|
|
425
|
-
let cleaned = "";
|
|
426
|
-
for (let i = 0;i < current.length; i += 1) {
|
|
427
|
-
const code = current.charCodeAt(i);
|
|
428
|
-
if (code <= 31 || code === DEL) {
|
|
429
|
-
continue;
|
|
430
|
-
}
|
|
431
|
-
cleaned += current[i];
|
|
432
|
-
}
|
|
433
|
-
cleaned = cleaned.trim();
|
|
434
|
-
if (cleaned.length > 0) {
|
|
435
|
-
lines.push(cleaned);
|
|
436
|
-
}
|
|
437
|
-
current = "";
|
|
438
|
-
};
|
|
439
|
-
for (let i = 0;i < stripped.length; i += 1) {
|
|
440
|
-
const code = stripped.charCodeAt(i);
|
|
441
|
-
if (code === LF || code === CR) {
|
|
442
|
-
flush();
|
|
443
|
-
continue;
|
|
444
|
-
}
|
|
445
|
-
current += stripped[i];
|
|
446
|
-
}
|
|
447
|
-
flush();
|
|
448
|
-
if (lines.length === 0) {
|
|
449
|
-
return "";
|
|
450
|
-
}
|
|
451
|
-
for (let i = lines.length - 1;i >= 0; i -= 1) {
|
|
452
|
-
if (lines[i].charCodeAt(0) === SLASH) {
|
|
453
|
-
return lines[i];
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
return lines[lines.length - 1];
|
|
457
|
-
}
|
|
458
|
-
function readExplicitBunPath(flags) {
|
|
459
|
-
return asString(flags["bun-path"]) || process.env.TMEX_BUN_PATH;
|
|
460
|
-
}
|
|
461
|
-
function isBunRuntime() {
|
|
462
|
-
const bunVersion = process.versions.bun;
|
|
463
|
-
return typeof bunVersion === "string" && bunVersion.length > 0;
|
|
464
|
-
}
|
|
465
|
-
function hardCandidatePaths() {
|
|
466
|
-
return [
|
|
467
|
-
join(homedir2(), ".bun", "bin", "bun"),
|
|
468
|
-
"/opt/homebrew/bin/bun",
|
|
469
|
-
"/usr/local/bin/bun",
|
|
470
|
-
"/home/linuxbrew/.linuxbrew/bin/bun"
|
|
471
|
-
];
|
|
472
|
-
}
|
|
473
|
-
async function locateBunFromShellWith(shell) {
|
|
474
|
-
const result = await runCommand(shell, ["-lic", "command -v bun"], {
|
|
475
|
-
stdio: "pipe",
|
|
476
|
-
timeoutMs: PROBE_TIMEOUT_MS
|
|
477
|
-
}).catch(() => null);
|
|
320
|
+
async function locateBunFromShell() {
|
|
321
|
+
const result = await runCommand("zsh", ["-lic", "command -v bun"], { stdio: "pipe" }).catch(() => null);
|
|
478
322
|
if (!result || result.code !== 0) {
|
|
479
323
|
return null;
|
|
480
324
|
}
|
|
481
|
-
const bin =
|
|
482
|
-
if (!bin
|
|
325
|
+
const bin = result.stdout.trim();
|
|
326
|
+
if (!bin) {
|
|
483
327
|
return null;
|
|
484
328
|
}
|
|
485
329
|
return bin;
|
|
486
330
|
}
|
|
487
|
-
async function
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
shells.push(envShell);
|
|
492
|
-
}
|
|
493
|
-
for (const fallbackShell of ["zsh", "bash"]) {
|
|
494
|
-
if (!shells.includes(fallbackShell)) {
|
|
495
|
-
shells.push(fallbackShell);
|
|
496
|
-
}
|
|
331
|
+
async function findBunBinary() {
|
|
332
|
+
const zshBin = await locateBunFromShell();
|
|
333
|
+
if (zshBin) {
|
|
334
|
+
return zshBin;
|
|
497
335
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
336
|
+
const fallback = join(homedir2(), ".bun", "bin", "bun");
|
|
337
|
+
if (existsSync(fallback)) {
|
|
338
|
+
return fallback;
|
|
339
|
+
}
|
|
340
|
+
const direct = await runCommand("bun", ["--version"], { stdio: "pipe" }).catch(() => null);
|
|
341
|
+
if (direct?.code === 0) {
|
|
342
|
+
return "bun";
|
|
503
343
|
}
|
|
504
344
|
return null;
|
|
505
345
|
}
|
|
506
|
-
async function
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
if (sanitized && !candidates.includes(sanitized)) {
|
|
514
|
-
candidates.push(sanitized);
|
|
515
|
-
}
|
|
516
|
-
};
|
|
517
|
-
if (isBunRuntime()) {
|
|
518
|
-
add(process.execPath);
|
|
519
|
-
}
|
|
520
|
-
add(metaBunPath);
|
|
521
|
-
add(await locateBunFromShell());
|
|
522
|
-
candidates.push("bun");
|
|
523
|
-
for (const candidate of hardCandidatePaths()) {
|
|
524
|
-
add(candidate);
|
|
346
|
+
async function checkBunVersion(minVersion = MIN_BUN_VERSION) {
|
|
347
|
+
const bunPath = await findBunBinary();
|
|
348
|
+
if (!bunPath) {
|
|
349
|
+
return {
|
|
350
|
+
ok: false,
|
|
351
|
+
reason: t("bun.notFound")
|
|
352
|
+
};
|
|
525
353
|
}
|
|
526
|
-
|
|
527
|
-
}
|
|
528
|
-
async function validateBunAt(candidate, minVersion) {
|
|
529
|
-
const versionResult = await runCommand(candidate, ["--version"], {
|
|
530
|
-
stdio: "pipe",
|
|
531
|
-
timeoutMs: PROBE_TIMEOUT_MS
|
|
532
|
-
}).catch(() => null);
|
|
354
|
+
const versionResult = await runCommand(bunPath, ["--version"], { stdio: "pipe" }).catch(() => null);
|
|
533
355
|
if (!versionResult || versionResult.code !== 0) {
|
|
534
|
-
return {
|
|
356
|
+
return {
|
|
357
|
+
ok: false,
|
|
358
|
+
reason: t("bun.versionExecFailed"),
|
|
359
|
+
path: bunPath
|
|
360
|
+
};
|
|
535
361
|
}
|
|
536
|
-
const version =
|
|
362
|
+
const version = versionResult.stdout.trim();
|
|
537
363
|
if (compareSemver(version, minVersion) < 0) {
|
|
538
364
|
return {
|
|
539
365
|
ok: false,
|
|
540
|
-
path:
|
|
366
|
+
path: bunPath,
|
|
541
367
|
version,
|
|
542
368
|
reason: t("bun.versionTooLow", { version, minVersion })
|
|
543
369
|
};
|
|
544
370
|
}
|
|
545
|
-
return {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
if (!explicit || !isAbsolute(explicit) || !existsSync(explicit)) {
|
|
551
|
-
const reported = explicit || opts.explicitPath;
|
|
552
|
-
return {
|
|
553
|
-
ok: false,
|
|
554
|
-
path: reported,
|
|
555
|
-
reason: t("bun.explicitInvalid", { path: reported })
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
return await validateBunAt(explicit, minVersion);
|
|
559
|
-
}
|
|
560
|
-
const candidates = await probeBunCandidates(opts.metaBunPath);
|
|
561
|
-
let firstFailure = null;
|
|
562
|
-
for (const candidate of candidates) {
|
|
563
|
-
if (candidate !== "bun" && isAbsolute(candidate) && !existsSync(candidate)) {
|
|
564
|
-
continue;
|
|
565
|
-
}
|
|
566
|
-
const result = await validateBunAt(candidate, minVersion);
|
|
567
|
-
if (result.ok) {
|
|
568
|
-
return result;
|
|
569
|
-
}
|
|
570
|
-
if (!firstFailure) {
|
|
571
|
-
firstFailure = result;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
return firstFailure ?? { ok: false, reason: t("bun.notFound") };
|
|
371
|
+
return {
|
|
372
|
+
ok: true,
|
|
373
|
+
path: bunPath,
|
|
374
|
+
version
|
|
375
|
+
};
|
|
575
376
|
}
|
|
576
377
|
|
|
577
378
|
// src/lib/env-file.ts
|
|
@@ -967,6 +768,41 @@ function serviceHint(serviceName) {
|
|
|
967
768
|
return t("service.hint.none");
|
|
968
769
|
}
|
|
969
770
|
|
|
771
|
+
// src/lib/validate.ts
|
|
772
|
+
function asString(value) {
|
|
773
|
+
if (typeof value === "string")
|
|
774
|
+
return value;
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
function asBoolean(value) {
|
|
778
|
+
if (typeof value === "boolean")
|
|
779
|
+
return value;
|
|
780
|
+
if (typeof value !== "string")
|
|
781
|
+
return;
|
|
782
|
+
const normalized = value.trim().toLowerCase();
|
|
783
|
+
if (normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "y") {
|
|
784
|
+
return true;
|
|
785
|
+
}
|
|
786
|
+
if (normalized === "false" || normalized === "0" || normalized === "no" || normalized === "n") {
|
|
787
|
+
return false;
|
|
788
|
+
}
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
function parsePort(value) {
|
|
792
|
+
const port = Number(value);
|
|
793
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
794
|
+
throw new Error(t("errors.validate.invalidPort", { value }));
|
|
795
|
+
}
|
|
796
|
+
return port;
|
|
797
|
+
}
|
|
798
|
+
function assertNonEmpty(value, fieldName) {
|
|
799
|
+
const trimmed = value.trim();
|
|
800
|
+
if (!trimmed) {
|
|
801
|
+
throw new Error(t("errors.validate.emptyField", { field: fieldName }));
|
|
802
|
+
}
|
|
803
|
+
return trimmed;
|
|
804
|
+
}
|
|
805
|
+
|
|
970
806
|
// src/commands/doctor.ts
|
|
971
807
|
async function checkCommandExists(bin, args, id, okMessage, failMessage) {
|
|
972
808
|
const result = await runCommand(bin, args, { stdio: "pipe" }).catch(() => null);
|
|
@@ -1007,12 +843,7 @@ async function runDoctor(parsed) {
|
|
|
1007
843
|
message: t("doctor.platform.supported", { platform: process.platform })
|
|
1008
844
|
});
|
|
1009
845
|
}
|
|
1010
|
-
const
|
|
1011
|
-
const meta = await pathExists(installLayout.metaPath) ? await readJsonFile(installLayout.metaPath).catch(() => null) : null;
|
|
1012
|
-
const bun = await checkBunVersion(undefined, {
|
|
1013
|
-
explicitPath: explicitBunPath,
|
|
1014
|
-
metaBunPath: meta?.bunPath
|
|
1015
|
-
});
|
|
846
|
+
const bun = await checkBunVersion();
|
|
1016
847
|
if (bun.ok) {
|
|
1017
848
|
checks.push({
|
|
1018
849
|
id: "bun",
|
|
@@ -1115,8 +946,11 @@ async function runDoctor(parsed) {
|
|
|
1115
946
|
});
|
|
1116
947
|
}
|
|
1117
948
|
let serviceName = asString(parsed.flags["service-name"]) || "tmex";
|
|
1118
|
-
if (
|
|
1119
|
-
|
|
949
|
+
if (await pathExists(installLayout.metaPath)) {
|
|
950
|
+
const meta = await readJsonFile(installLayout.metaPath).catch(() => null);
|
|
951
|
+
if (meta?.serviceName) {
|
|
952
|
+
serviceName = meta.serviceName;
|
|
953
|
+
}
|
|
1120
954
|
}
|
|
1121
955
|
const status = await getServiceStatus(serviceName, installDir);
|
|
1122
956
|
if (status.manager === "none") {
|
|
@@ -1174,13 +1008,12 @@ async function runDoctor(parsed) {
|
|
|
1174
1008
|
|
|
1175
1009
|
// src/commands/init.ts
|
|
1176
1010
|
import { readdir } from "node:fs/promises";
|
|
1177
|
-
import { dirname as
|
|
1011
|
+
import { dirname as dirname4, resolve as resolve6 } from "node:path";
|
|
1178
1012
|
|
|
1179
1013
|
// src/lib/install.ts
|
|
1180
1014
|
import { randomBytes } from "node:crypto";
|
|
1181
1015
|
import { chmod, copyFile, rm as rm2 } from "node:fs/promises";
|
|
1182
|
-
import {
|
|
1183
|
-
import { dirname as dirname4, isAbsolute as isAbsolute2, join as join4, resolve as resolve5 } from "node:path";
|
|
1016
|
+
import { resolve as resolve5 } from "node:path";
|
|
1184
1017
|
function generateMasterKey() {
|
|
1185
1018
|
return randomBytes(32).toString("base64");
|
|
1186
1019
|
}
|
|
@@ -1217,20 +1050,6 @@ async function deployRuntimeFiles(packageLayout, installLayout) {
|
|
|
1217
1050
|
await copyDirectory(packageLayout.resourceDrizzlePath, installLayout.drizzleDir);
|
|
1218
1051
|
}
|
|
1219
1052
|
async function writeRunScript(installLayout, bunPath) {
|
|
1220
|
-
for (let i = 0;i < bunPath.length; i += 1) {
|
|
1221
|
-
const code = bunPath.charCodeAt(i);
|
|
1222
|
-
if (code === 34 || code === 96 || code === 36 || code === 92 || code === 10 || code === 13) {
|
|
1223
|
-
throw new Error(t("bun.unsafePath", { path: bunPath }));
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
const homeBunBin = join4(homedir4(), ".bun", "bin");
|
|
1227
|
-
const bunDir = isAbsolute2(bunPath) ? dirname4(bunPath) : "";
|
|
1228
|
-
const extraPathDirs = [
|
|
1229
|
-
bunDir,
|
|
1230
|
-
"/opt/homebrew/bin",
|
|
1231
|
-
"/usr/local/bin",
|
|
1232
|
-
"/home/linuxbrew/.linuxbrew/bin"
|
|
1233
|
-
].filter((dir, index, arr) => dir.length > 0 && dir !== homeBunBin && arr.indexOf(dir) === index);
|
|
1234
1053
|
const lines = [
|
|
1235
1054
|
"#!/usr/bin/env bash",
|
|
1236
1055
|
"set -euo pipefail",
|
|
@@ -1246,7 +1065,6 @@ async function writeRunScript(installLayout, bunPath) {
|
|
|
1246
1065
|
'if [[ -n "${HOME:-}" ]] && [[ -d "${HOME}/.bun/bin" ]]; then',
|
|
1247
1066
|
' export PATH="${HOME}/.bun/bin:${PATH:-}"',
|
|
1248
1067
|
"fi",
|
|
1249
|
-
`export PATH="${[...extraPathDirs, "${PATH:-}"].join(":")}"`,
|
|
1250
1068
|
"",
|
|
1251
1069
|
`export TMEX_FE_DIST_DIR="${installLayout.feDir}"`,
|
|
1252
1070
|
`export TMEX_MIGRATIONS_DIR="${installLayout.drizzleDir}"`,
|
|
@@ -1336,9 +1154,9 @@ async function promptConfirm(ctx, message, defaultValue) {
|
|
|
1336
1154
|
}
|
|
1337
1155
|
|
|
1338
1156
|
// src/lib/version.ts
|
|
1339
|
-
import { join as
|
|
1157
|
+
import { join as join4 } from "node:path";
|
|
1340
1158
|
async function readPackageVersion(packageRoot) {
|
|
1341
|
-
const pkg = await readJsonFile(
|
|
1159
|
+
const pkg = await readJsonFile(join4(packageRoot, "package.json"));
|
|
1342
1160
|
return pkg.version || "0.0.0";
|
|
1343
1161
|
}
|
|
1344
1162
|
|
|
@@ -1410,8 +1228,7 @@ async function buildInitConfig(parsed) {
|
|
|
1410
1228
|
}
|
|
1411
1229
|
async function runInit(parsed) {
|
|
1412
1230
|
const config = await buildInitConfig(parsed);
|
|
1413
|
-
const
|
|
1414
|
-
const bun = await checkBunVersion(undefined, { explicitPath: explicitBunPath });
|
|
1231
|
+
const bun = await checkBunVersion();
|
|
1415
1232
|
if (!bun.ok || !bun.path) {
|
|
1416
1233
|
throw new Error(bun.reason || t("bun.checkFailed"));
|
|
1417
1234
|
}
|
|
@@ -1427,7 +1244,7 @@ async function runInit(parsed) {
|
|
|
1427
1244
|
const packageLayout = await resolvePackageLayout(import.meta.url);
|
|
1428
1245
|
const installLayout = createInstallLayout(config.installDir);
|
|
1429
1246
|
await ensureInstallDir(config.installDir, config.force);
|
|
1430
|
-
await ensureDir(
|
|
1247
|
+
await ensureDir(dirname4(config.databasePath));
|
|
1431
1248
|
await deployRuntimeFiles(packageLayout, installLayout);
|
|
1432
1249
|
const masterKey = generateMasterKey();
|
|
1433
1250
|
const envValues = buildAppEnvValues({
|
|
@@ -1456,8 +1273,7 @@ async function runInit(parsed) {
|
|
|
1456
1273
|
autostart: config.autostart,
|
|
1457
1274
|
installDir: config.installDir,
|
|
1458
1275
|
updatedAt: new Date().toISOString(),
|
|
1459
|
-
cliVersion
|
|
1460
|
-
bunPath: bun.path
|
|
1276
|
+
cliVersion
|
|
1461
1277
|
};
|
|
1462
1278
|
await writeInstallMeta(installLayout, meta);
|
|
1463
1279
|
console.log(`[tmex] ${t("init.done")}`);
|
|
@@ -1529,7 +1345,7 @@ async function runUninstall(parsed) {
|
|
|
1529
1345
|
// src/commands/upgrade.ts
|
|
1530
1346
|
import { mkdtemp, rm as rm4 } from "node:fs/promises";
|
|
1531
1347
|
import { tmpdir } from "node:os";
|
|
1532
|
-
import { join as
|
|
1348
|
+
import { join as join5 } from "node:path";
|
|
1533
1349
|
async function delegateUpgrade(parsed, targetVersion) {
|
|
1534
1350
|
const args = ["--yes", `tmex-cli@${targetVersion}`, "upgrade", "--apply-current-package"];
|
|
1535
1351
|
const passthrough = ["install-dir", "service-name", "yes", "lang"];
|
|
@@ -1585,17 +1401,13 @@ async function runUpgrade(parsed) {
|
|
|
1585
1401
|
if (!await pathExists(installLayout.metaPath)) {
|
|
1586
1402
|
throw new Error(t("upgrade.missingMeta", { path: installLayout.metaPath }));
|
|
1587
1403
|
}
|
|
1588
|
-
const
|
|
1589
|
-
const explicitBunPath = readExplicitBunPath(parsed.flags);
|
|
1590
|
-
const bun = await checkBunVersion(undefined, {
|
|
1591
|
-
explicitPath: explicitBunPath,
|
|
1592
|
-
metaBunPath: meta.bunPath
|
|
1593
|
-
});
|
|
1404
|
+
const bun = await checkBunVersion();
|
|
1594
1405
|
if (!bun.ok || !bun.path) {
|
|
1595
1406
|
throw new Error(bun.reason || t("bun.checkFailed"));
|
|
1596
1407
|
}
|
|
1408
|
+
const meta = await readJsonFile(installLayout.metaPath);
|
|
1597
1409
|
const packageLayout = await resolvePackageLayout(import.meta.url);
|
|
1598
|
-
const backupDir = await mkdtemp(
|
|
1410
|
+
const backupDir = await mkdtemp(join5(tmpdir(), "tmex-upgrade-"));
|
|
1599
1411
|
try {
|
|
1600
1412
|
await stopService(meta.serviceName, installDir);
|
|
1601
1413
|
await backupInstallArtifacts(installLayout, backupDir);
|
|
@@ -1604,7 +1416,6 @@ async function runUpgrade(parsed) {
|
|
|
1604
1416
|
const cliVersion = await readPackageVersion(packageLayout.packageRoot);
|
|
1605
1417
|
meta.updatedAt = new Date().toISOString();
|
|
1606
1418
|
meta.cliVersion = cliVersion;
|
|
1607
|
-
meta.bunPath = bun.path;
|
|
1608
1419
|
await writeInstallMeta(installLayout, meta);
|
|
1609
1420
|
await installService({
|
|
1610
1421
|
serviceName: meta.serviceName,
|
|
Binary file
|