comp-hub 0.28.3 → 0.28.4
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 +132 -132
- package/bin/cli.js +54 -54
- package/bin/lib/commands.js +173 -173
- package/bin/lib/constants.js +22 -22
- package/bin/lib/http.js +62 -62
- package/bin/lib/startup.js +183 -183
- package/bin/lib/utils.js +112 -112
- package/dist/index.js +17 -17
- package/dist/website/container/assets/index-CVxgDysj.js +14 -0
- package/dist/website/container/assets/index-h6S5dyy4.css +1 -0
- package/dist/website/container/index.html +32 -32
- package/dist/website/container/js/vue2-sfc-loader.js +260 -260
- package/dist/website/container/js/vue3-sfc-loader.js +289 -289
- package/dist/website/item/assets/{index-s-KzvBKE.js → index-CGgDhzaN.js} +19 -19
- package/dist/website/item/assets/index-lq3uP4DP.css +1 -0
- package/dist/website/item/index.html +30 -30
- package/dist/website/main/assets/AuthCodeSendBtn.vue_vue_type_script_setup_true_lang-CP9hjm3T.js +1 -0
- package/dist/website/main/assets/{BaseDialog.vue_vue_type_script_setup_true_lang-CgV6_zzd.js → BaseDialog.vue_vue_type_script_setup_true_lang-BROcUfEh.js} +1 -1
- package/dist/website/main/assets/{BaseSegmented.vue_vue_type_script_setup_true_lang-CW16Y3Jx.js → BaseSegmented.vue_vue_type_script_setup_true_lang-DU79Plk1.js} +1 -1
- package/dist/website/main/assets/{CanvasAnimation.vue_vue_type_script_setup_true_lang-BZ55pr6b.js → CanvasAnimation.vue_vue_type_script_setup_true_lang-gAGMSPxq.js} +1 -1
- package/dist/website/main/assets/{EmptyStatusContainer.vue_vue_type_script_setup_true_lang-CofI0Rvq.js → EmptyStatusContainer.vue_vue_type_script_setup_true_lang-pswB5SUz.js} +1 -1
- package/dist/website/main/assets/{LoadingWrapper.vue_vue_type_script_setup_true_lang-bBtkKtmz.js → LoadingWrapper.vue_vue_type_script_setup_true_lang-kJcgjuK5.js} +1 -1
- package/dist/website/main/assets/{PanelBackground.vue_vue_type_script_setup_true_lang-DBIlZFl0.js → PanelBackground.vue_vue_type_script_setup_true_lang-BELCrrRh.js} +1 -1
- package/dist/website/main/assets/{cloud-upload-BRjLuSi4.js → cloud-upload-Cdco6849.js} +1 -1
- package/dist/website/main/assets/{core-B1GcTOER.js → core-CcVTMiBP.js} +1 -1
- package/dist/website/main/assets/{el-table-column-8qfe8CVK.js → el-table-column-CGzXlVpD.js} +1 -1
- package/dist/website/main/assets/{home-rounded-8qNxvN2I.js → home-rounded-BhKYWFxg.js} +1 -1
- package/dist/website/main/assets/{index-DIKKFNiV.css → index-B-RD5UzD.css} +1 -1
- package/dist/website/main/assets/index-BGAeKS26.js +2 -0
- package/dist/website/main/assets/index-BUoUeU3q.js +1 -0
- package/dist/website/main/assets/index-BXRWapP9.js +1 -0
- package/dist/website/main/assets/index-Bb4dzB_t.js +1 -0
- package/dist/website/main/assets/index-BbVoROfE.js +1 -0
- package/dist/website/main/assets/index-BjMNHNZQ.js +1 -0
- package/dist/website/main/assets/{index-0Y9kW7YJ.js → index-BoIy-5_p.js} +1 -1
- package/dist/website/main/assets/index-C1KlLhF1.js +1 -0
- package/dist/website/main/assets/index-CEizeGti.js +1 -0
- package/dist/website/main/assets/index-CKuqpXAR.js +1 -0
- package/dist/website/main/assets/{index-DoTR7Juh.css → index-CNqahi2L.css} +1 -1
- package/dist/website/main/assets/index-CP6sOfKF.css +1 -0
- package/dist/website/main/assets/index-Cacmn-le.js +1 -0
- package/dist/website/main/assets/index-Car8xTN1.js +1 -0
- package/dist/website/main/assets/{index-C7fXvVHu.js → index-Cd1GqG9-.js} +14 -14
- package/dist/website/main/assets/index-Cdv-VxRL.js +2 -0
- package/dist/website/main/assets/{index-DxyEzkir.css → index-Cl0SK7oa.css} +1 -1
- package/dist/website/main/assets/index-CmSN0r9I.css +1 -0
- package/dist/website/main/assets/{index-C63BGiej.js → index-D26LViqE.js} +1 -1
- package/dist/website/main/assets/index-D4vHIupw.js +1 -0
- package/dist/website/main/assets/index-D62zqHYv.js +1 -0
- package/dist/website/main/assets/{index-Bszb87gP.js → index-DGh9vJu4.js} +1 -1
- package/dist/website/main/assets/index-DJ4AvgpT.css +1 -0
- package/dist/website/main/assets/{index-C057ePT_.js → index-DKb_LuTB.js} +2 -2
- package/dist/website/main/assets/index-DNWHN_5F.js +1 -0
- package/dist/website/main/assets/index-D_mLoa5V.js +1 -0
- package/dist/website/main/assets/{index-CVcB9iso.js → index-DcbQ_ahf.js} +1 -1
- package/dist/website/main/assets/index-Dp1XrCJD.js +1 -0
- package/dist/website/main/assets/index-DroKXXhR.js +1 -0
- package/dist/website/main/assets/index-JcGCRZej.js +1 -0
- package/dist/website/main/assets/{index-Bii_v9I4.js → index-RTrUAzmO.js} +1 -1
- package/dist/website/main/assets/index-VFwGpUE-.css +1 -0
- package/dist/website/main/assets/{index-DBfCLdC1.js → index-WPdppQti.js} +1 -1
- package/dist/website/main/assets/index-XEEaWsyW.js +1 -0
- package/dist/website/main/assets/{index-Crr7YnOX.js → index-eyM0Chsl.js} +1 -1
- package/dist/website/main/assets/index-gswaBdwW.js +4 -0
- package/dist/website/main/assets/index-tCnrZXQC.js +1 -0
- package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-0kHDvgX-.js +2 -0
- package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-CK2dnh4X.js +1 -0
- package/dist/website/main/assets/{index.vue_vue_type_script_setup_true_lang-BaI6O6oA.js → index.vue_vue_type_script_setup_true_lang-CVJdloX3.js} +1 -1
- package/dist/website/main/assets/{index.vue_vue_type_script_setup_true_lang-DX9EV5b8.js → index.vue_vue_type_script_setup_true_lang-CgaIiyg6.js} +1 -1
- package/dist/website/main/assets/{index.vue_vue_type_script_setup_true_lang-DmGfa_ki.js → index.vue_vue_type_script_setup_true_lang-DhkIAGmf.js} +1 -1
- package/dist/website/main/assets/{index.vue_vue_type_script_setup_true_lang-mgCeAY0j.js → index.vue_vue_type_script_setup_true_lang-Q7u6DEB4.js} +1 -1
- package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-_M-s3_Ie.js +1 -0
- package/dist/website/main/assets/{index.vue_vue_type_style_index_0_lang-BwvfJhL1.js → index.vue_vue_type_style_index_0_lang-CIN0y80j.js} +1 -1
- package/dist/website/main/assets/{index.vue_vue_type_style_index_0_lang-Bdg4OOHe.js → index.vue_vue_type_style_index_0_lang-DTKsCZO_.js} +1 -1
- package/dist/website/main/assets/{index.vue_vue_type_style_index_0_lang-BWgZeYuy.js → index.vue_vue_type_style_index_0_lang-iFwx3NZQ.js} +1 -1
- package/dist/website/main/assets/{kid-star-Cr1NlTdT.js → kid-star-DD-9HSxX.js} +1 -1
- package/dist/website/main/assets/{md5-DzJRYP5-.js → md5-ohq5q_Pj.js} +1 -1
- package/dist/website/main/assets/{settings-BFJXTJ9K.js → settings-f8ZwrSyv.js} +1 -1
- package/dist/website/main/assets/{supervised-user-circle-Bnx5icEm.js → supervised-user-circle-C6l0wkza.js} +1 -1
- package/dist/website/main/assets/{sync-saved-locally-rounded-DtyOT0dw.js → sync-saved-locally-rounded-Cgz-NvDy.js} +1 -1
- package/dist/website/main/assets/{terminal-D72IJB2N.js → terminal-DIdElzFh.js} +1 -1
- package/dist/website/main/assets/{useCacheOssStore-DApHt9lQ.js → useCacheOssStore-OAoLDVbw.js} +1 -1
- package/dist/website/main/assets/{useCompEditStore-CYWCa5kU.js → useCompEditStore-CA87jjE-.js} +166 -166
- package/dist/website/main/assets/{useCompEditStore-CY1OeGL8.css → useCompEditStore-CiX1ghqX.css} +1 -1
- package/dist/website/main/assets/{useCompMetaLocalStore-eE-CBcE9.js → useCompMetaLocalStore-BKTMp_k8.js} +1 -1
- package/dist/website/main/assets/{useKeyword-B6suFutt.js → useKeyword-CmJvOYMB.js} +1 -1
- package/dist/website/main/assets/{useMessage-B_QbO-NO.js → useMessage-DRWIT9X-.js} +1 -1
- package/dist/website/main/assets/{useTeamHeaders-Cc060CP0.js → useTeamHeaders-DxHN7-t0.js} +1 -1
- package/dist/website/main/assets/useVscodeOpen-PzIPfNxl.js +1 -0
- package/dist/website/main/assets/vendor-core-BKehvho7.js +3 -0
- package/dist/website/main/assets/{vendor-ui-DXwyveTF.js → vendor-ui-CGT0cG1n.js} +2 -2
- package/dist/website/main/index.html +6 -5
- package/package.json +45 -45
- package/dist/website/container/assets/index-D6braxue.css +0 -1
- package/dist/website/container/assets/index-DJHapE1V.js +0 -14
- package/dist/website/item/assets/index-BWvMfSxu.css +0 -1
- package/dist/website/main/assets/AuthCodeSendBtn.vue_vue_type_script_setup_true_lang-C2LcEFL4.js +0 -1
- package/dist/website/main/assets/index-2gSE6zIr.js +0 -1
- package/dist/website/main/assets/index-B5E28bYj.js +0 -1
- package/dist/website/main/assets/index-BUnPcap8.js +0 -4
- package/dist/website/main/assets/index-BYggsmzn.js +0 -1
- package/dist/website/main/assets/index-B_dBAWT4.js +0 -2
- package/dist/website/main/assets/index-BeN2bdeg.js +0 -1
- package/dist/website/main/assets/index-BhW9quqy.js +0 -1
- package/dist/website/main/assets/index-BhvFP_tJ.js +0 -1
- package/dist/website/main/assets/index-C7ChQ1lq.js +0 -2
- package/dist/website/main/assets/index-CDnNjG3p.css +0 -1
- package/dist/website/main/assets/index-CEi5bhOk.js +0 -1
- package/dist/website/main/assets/index-COVvlML8.js +0 -1
- package/dist/website/main/assets/index-CU7M0IJJ.js +0 -1
- package/dist/website/main/assets/index-CbJoAuov.js +0 -1
- package/dist/website/main/assets/index-CzTcYkCn.css +0 -1
- package/dist/website/main/assets/index-D-uwf4yo.css +0 -1
- package/dist/website/main/assets/index-DEPscuQs.js +0 -1
- package/dist/website/main/assets/index-DF1giQNh.js +0 -1
- package/dist/website/main/assets/index-DKPlBqE0.js +0 -1
- package/dist/website/main/assets/index-DRSvKvKa.js +0 -1
- package/dist/website/main/assets/index-DUgSkj2j.js +0 -1
- package/dist/website/main/assets/index-FFTkVbVX.css +0 -1
- package/dist/website/main/assets/index-G0P-dulE.js +0 -1
- package/dist/website/main/assets/index-I02ydx0r.js +0 -1
- package/dist/website/main/assets/index-IyiuGctN.js +0 -1
- package/dist/website/main/assets/index-JxbnKk2u.js +0 -1
- package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-C5v5wKQi.js +0 -2
- package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-CrwTpowX.js +0 -1
- package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-LVKGPS7z.js +0 -1
- package/dist/website/main/assets/useVscodeOpen-Ckh-vPor.js +0 -1
- package/dist/website/main/assets/vendor-core-CzvSW5ae.js +0 -3
package/bin/lib/commands.js
CHANGED
|
@@ -1,173 +1,173 @@
|
|
|
1
|
-
const { httpGet, httpPost } = require("./http");
|
|
2
|
-
const { ADMIN_API, DEFAULT_PORT } = require("./constants");
|
|
3
|
-
const { readMasterRuntime, colors, apiUrl } = require("./utils");
|
|
4
|
-
const { green, yellow, cyan, white, red } = colors;
|
|
5
|
-
|
|
6
|
-
function formatUptime(seconds) {
|
|
7
|
-
const UNITS = [
|
|
8
|
-
{ label: "d", divisor: 86400 },
|
|
9
|
-
{ label: "h", divisor: 3600 },
|
|
10
|
-
{ label: "m", divisor: 60 },
|
|
11
|
-
{ label: "s", divisor: 1 }
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
let remaining = Math.floor(seconds);
|
|
15
|
-
const parts = [];
|
|
16
|
-
|
|
17
|
-
for (const { label, divisor } of UNITS) {
|
|
18
|
-
if (remaining >= divisor) {
|
|
19
|
-
const value = Math.floor(remaining / divisor);
|
|
20
|
-
remaining = remaining % divisor;
|
|
21
|
-
parts.push(`${value}${label}`);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return parts.length > 0 ? parts.join(" ") : "0s";
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async function resolveMasterPort(opts) {
|
|
29
|
-
// 用户明确指定了端口,直接使用
|
|
30
|
-
if (opts.port != null) {
|
|
31
|
-
return opts.port;
|
|
32
|
-
}
|
|
33
|
-
// 从 runtime 文件读取 master 实际端口
|
|
34
|
-
const runtime = await readMasterRuntime();
|
|
35
|
-
if (runtime) {
|
|
36
|
-
return runtime.managementPort;
|
|
37
|
-
}
|
|
38
|
-
// 兜底:默认端口
|
|
39
|
-
return DEFAULT_PORT;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function statusCommand(opts) {
|
|
43
|
-
const masterPort = await resolveMasterPort(opts);
|
|
44
|
-
const runtime = await readMasterRuntime();
|
|
45
|
-
const displayPort = runtime ? runtime.publicPort : masterPort;
|
|
46
|
-
|
|
47
|
-
console.log(`\n${yellow("●")} Master Status\n`);
|
|
48
|
-
|
|
49
|
-
const health = await httpGet(apiUrl(masterPort, ADMIN_API.HEALTH));
|
|
50
|
-
if (!health.ok || !health.body || typeof health.body.status !== "string") {
|
|
51
|
-
console.error(`${red("✗")} Master service is not running`);
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const info = health.body;
|
|
56
|
-
const statusIcon = info.status === "running" ? green("●") : cyan("●");
|
|
57
|
-
|
|
58
|
-
console.log(` ${green("Status")} ${statusIcon} ${info.status}`);
|
|
59
|
-
console.log(` ${green("Version")} ${white(info.version)}`);
|
|
60
|
-
console.log(` ${green("Uptime")} ${white(formatUptime(info.uptime))}`);
|
|
61
|
-
console.log(` ${green("Projects")} ${white(info.projects)}`);
|
|
62
|
-
console.log("");
|
|
63
|
-
|
|
64
|
-
const projects = await httpGet(apiUrl(masterPort, ADMIN_API.PROJECTS));
|
|
65
|
-
if (projects.ok && projects.body) {
|
|
66
|
-
const list = projects.body.projects;
|
|
67
|
-
if (Array.isArray(list) && list.length > 0) {
|
|
68
|
-
console.log(` ${yellow("▸")} Registered Projects (${list.length}):`);
|
|
69
|
-
console.log(" " + white("─".repeat(56)));
|
|
70
|
-
for (const p of list) {
|
|
71
|
-
console.log(` ${green("hash")} ${white(p.hash)}`);
|
|
72
|
-
console.log(` ${green("path")} ${white(p.cwd)}`);
|
|
73
|
-
// console.log(` ${green('port')} ${p.port}`);
|
|
74
|
-
// if (p.subPort) console.log(` ${green('subPort')} ${p.subPort}`);
|
|
75
|
-
console.log(` ${green("url")} ${cyan(`http://localhost:${displayPort}/${p.hash}/main/`)}`);
|
|
76
|
-
console.log(" " + white("─".repeat(56)));
|
|
77
|
-
}
|
|
78
|
-
} else {
|
|
79
|
-
console.log(` ${white("─".repeat(58))}`);
|
|
80
|
-
console.log(` ${white("(No registered projects)")}`);
|
|
81
|
-
console.log(` ${white("─".repeat(58))}`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
console.log("");
|
|
85
|
-
process.exit(0);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async function stopCommand(hash, opts) {
|
|
89
|
-
const masterPort = await resolveMasterPort(opts);
|
|
90
|
-
|
|
91
|
-
if (opts.all) {
|
|
92
|
-
const projects = await httpGet(apiUrl(masterPort, ADMIN_API.PROJECTS));
|
|
93
|
-
if (!projects.ok || !projects.body) {
|
|
94
|
-
console.error(`${red("✗")} Cannot connect to master service`);
|
|
95
|
-
process.exit(1);
|
|
96
|
-
}
|
|
97
|
-
const list = projects.body.projects;
|
|
98
|
-
if (!Array.isArray(list) || list.length === 0) {
|
|
99
|
-
console.log(`${yellow("ℹ")} No running projects`);
|
|
100
|
-
process.exit(0);
|
|
101
|
-
}
|
|
102
|
-
console.log(`\n${yellow("▸")} Stopping all ${list.length} projects...\n`);
|
|
103
|
-
for (const p of list) {
|
|
104
|
-
const res = await httpPost(apiUrl(masterPort, ADMIN_API.DEREGISTER), { hash: p.hash });
|
|
105
|
-
if (res.ok) {
|
|
106
|
-
console.log(` ${green("✓")} Stopped: ${p.hash} (${p.cwd})`);
|
|
107
|
-
} else {
|
|
108
|
-
console.error(` ${red("✗")} Failed to stop: ${p.hash} - ${res.error}`);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
console.log("");
|
|
112
|
-
process.exit(0);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (!hash) {
|
|
116
|
-
console.error(`${red("✗")} Please specify project hash or use --all to stop all projects`);
|
|
117
|
-
console.error(` Usage: comphub stop <hash>`);
|
|
118
|
-
console.error(` Usage: comphub stop --all`);
|
|
119
|
-
process.exit(1);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
console.log(`\n${yellow("▸")} Stopping project ${hash}...`);
|
|
123
|
-
const res = await httpPost(apiUrl(masterPort, ADMIN_API.DEREGISTER), { hash });
|
|
124
|
-
if (res.ok) {
|
|
125
|
-
console.log(` ${green("✓")} Stopped: ${hash}\n`);
|
|
126
|
-
} else {
|
|
127
|
-
console.error(` ${red("✗")} Failed to stop: ${res.error}\n`);
|
|
128
|
-
process.exit(1);
|
|
129
|
-
}
|
|
130
|
-
process.exit(0);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
async function killCommand(opts) {
|
|
134
|
-
const masterPort = await resolveMasterPort(opts);
|
|
135
|
-
console.log(`\n${yellow("▸")} Shutting down master process...\n`);
|
|
136
|
-
|
|
137
|
-
const res = await httpPost(apiUrl(masterPort, ADMIN_API.SHUTDOWN), {});
|
|
138
|
-
if (res.ok) {
|
|
139
|
-
console.log(`${green("✓")} Master process has been shut down\n`);
|
|
140
|
-
} else {
|
|
141
|
-
console.error(`${red("✗")} Shutdown failed: ${res.error}\n`);
|
|
142
|
-
process.exit(1);
|
|
143
|
-
}
|
|
144
|
-
process.exit(0);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
async function fixCommand() {
|
|
148
|
-
const fs = require("fs");
|
|
149
|
-
const { MASTER_RUNTIME_FILE } = require("./constants");
|
|
150
|
-
|
|
151
|
-
console.log(`\n${yellow("▸")} Checking master runtime...\n`);
|
|
152
|
-
|
|
153
|
-
const runtime = await readMasterRuntime();
|
|
154
|
-
if (runtime) {
|
|
155
|
-
console.log(` ${green("✓")} Master is running normally on port ${runtime.publicPort}\n`);
|
|
156
|
-
process.exit(0);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (fs.existsSync(MASTER_RUNTIME_FILE)) {
|
|
160
|
-
try {
|
|
161
|
-
fs.unlinkSync(MASTER_RUNTIME_FILE);
|
|
162
|
-
console.log(` ${green("✓")} Removed stale runtime file\n`);
|
|
163
|
-
} catch {
|
|
164
|
-
console.error(` ${red("✗")} Failed to remove runtime file\n`);
|
|
165
|
-
process.exit(1);
|
|
166
|
-
}
|
|
167
|
-
} else {
|
|
168
|
-
console.log(` ${yellow("ℹ")} No stale runtime file found\n`);
|
|
169
|
-
}
|
|
170
|
-
process.exit(0);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
module.exports = { statusCommand, stopCommand, killCommand, fixCommand };
|
|
1
|
+
const { httpGet, httpPost } = require("./http");
|
|
2
|
+
const { ADMIN_API, DEFAULT_PORT } = require("./constants");
|
|
3
|
+
const { readMasterRuntime, colors, apiUrl } = require("./utils");
|
|
4
|
+
const { green, yellow, cyan, white, red } = colors;
|
|
5
|
+
|
|
6
|
+
function formatUptime(seconds) {
|
|
7
|
+
const UNITS = [
|
|
8
|
+
{ label: "d", divisor: 86400 },
|
|
9
|
+
{ label: "h", divisor: 3600 },
|
|
10
|
+
{ label: "m", divisor: 60 },
|
|
11
|
+
{ label: "s", divisor: 1 }
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
let remaining = Math.floor(seconds);
|
|
15
|
+
const parts = [];
|
|
16
|
+
|
|
17
|
+
for (const { label, divisor } of UNITS) {
|
|
18
|
+
if (remaining >= divisor) {
|
|
19
|
+
const value = Math.floor(remaining / divisor);
|
|
20
|
+
remaining = remaining % divisor;
|
|
21
|
+
parts.push(`${value}${label}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return parts.length > 0 ? parts.join(" ") : "0s";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function resolveMasterPort(opts) {
|
|
29
|
+
// 用户明确指定了端口,直接使用
|
|
30
|
+
if (opts.port != null) {
|
|
31
|
+
return opts.port;
|
|
32
|
+
}
|
|
33
|
+
// 从 runtime 文件读取 master 实际端口
|
|
34
|
+
const runtime = await readMasterRuntime();
|
|
35
|
+
if (runtime) {
|
|
36
|
+
return runtime.managementPort;
|
|
37
|
+
}
|
|
38
|
+
// 兜底:默认端口
|
|
39
|
+
return DEFAULT_PORT;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function statusCommand(opts) {
|
|
43
|
+
const masterPort = await resolveMasterPort(opts);
|
|
44
|
+
const runtime = await readMasterRuntime();
|
|
45
|
+
const displayPort = runtime ? runtime.publicPort : masterPort;
|
|
46
|
+
|
|
47
|
+
console.log(`\n${yellow("●")} Master Status\n`);
|
|
48
|
+
|
|
49
|
+
const health = await httpGet(apiUrl(masterPort, ADMIN_API.HEALTH));
|
|
50
|
+
if (!health.ok || !health.body || typeof health.body.status !== "string") {
|
|
51
|
+
console.error(`${red("✗")} Master service is not running`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const info = health.body;
|
|
56
|
+
const statusIcon = info.status === "running" ? green("●") : cyan("●");
|
|
57
|
+
|
|
58
|
+
console.log(` ${green("Status")} ${statusIcon} ${info.status}`);
|
|
59
|
+
console.log(` ${green("Version")} ${white(info.version)}`);
|
|
60
|
+
console.log(` ${green("Uptime")} ${white(formatUptime(info.uptime))}`);
|
|
61
|
+
console.log(` ${green("Projects")} ${white(info.projects)}`);
|
|
62
|
+
console.log("");
|
|
63
|
+
|
|
64
|
+
const projects = await httpGet(apiUrl(masterPort, ADMIN_API.PROJECTS));
|
|
65
|
+
if (projects.ok && projects.body) {
|
|
66
|
+
const list = projects.body.projects;
|
|
67
|
+
if (Array.isArray(list) && list.length > 0) {
|
|
68
|
+
console.log(` ${yellow("▸")} Registered Projects (${list.length}):`);
|
|
69
|
+
console.log(" " + white("─".repeat(56)));
|
|
70
|
+
for (const p of list) {
|
|
71
|
+
console.log(` ${green("hash")} ${white(p.hash)}`);
|
|
72
|
+
console.log(` ${green("path")} ${white(p.cwd)}`);
|
|
73
|
+
// console.log(` ${green('port')} ${p.port}`);
|
|
74
|
+
// if (p.subPort) console.log(` ${green('subPort')} ${p.subPort}`);
|
|
75
|
+
console.log(` ${green("url")} ${cyan(`http://localhost:${displayPort}/${p.hash}/main/`)}`);
|
|
76
|
+
console.log(" " + white("─".repeat(56)));
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
console.log(` ${white("─".repeat(58))}`);
|
|
80
|
+
console.log(` ${white("(No registered projects)")}`);
|
|
81
|
+
console.log(` ${white("─".repeat(58))}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
console.log("");
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function stopCommand(hash, opts) {
|
|
89
|
+
const masterPort = await resolveMasterPort(opts);
|
|
90
|
+
|
|
91
|
+
if (opts.all) {
|
|
92
|
+
const projects = await httpGet(apiUrl(masterPort, ADMIN_API.PROJECTS));
|
|
93
|
+
if (!projects.ok || !projects.body) {
|
|
94
|
+
console.error(`${red("✗")} Cannot connect to master service`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
const list = projects.body.projects;
|
|
98
|
+
if (!Array.isArray(list) || list.length === 0) {
|
|
99
|
+
console.log(`${yellow("ℹ")} No running projects`);
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
console.log(`\n${yellow("▸")} Stopping all ${list.length} projects...\n`);
|
|
103
|
+
for (const p of list) {
|
|
104
|
+
const res = await httpPost(apiUrl(masterPort, ADMIN_API.DEREGISTER), { hash: p.hash });
|
|
105
|
+
if (res.ok) {
|
|
106
|
+
console.log(` ${green("✓")} Stopped: ${p.hash} (${p.cwd})`);
|
|
107
|
+
} else {
|
|
108
|
+
console.error(` ${red("✗")} Failed to stop: ${p.hash} - ${res.error}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
console.log("");
|
|
112
|
+
process.exit(0);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!hash) {
|
|
116
|
+
console.error(`${red("✗")} Please specify project hash or use --all to stop all projects`);
|
|
117
|
+
console.error(` Usage: comphub stop <hash>`);
|
|
118
|
+
console.error(` Usage: comphub stop --all`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log(`\n${yellow("▸")} Stopping project ${hash}...`);
|
|
123
|
+
const res = await httpPost(apiUrl(masterPort, ADMIN_API.DEREGISTER), { hash });
|
|
124
|
+
if (res.ok) {
|
|
125
|
+
console.log(` ${green("✓")} Stopped: ${hash}\n`);
|
|
126
|
+
} else {
|
|
127
|
+
console.error(` ${red("✗")} Failed to stop: ${res.error}\n`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
process.exit(0);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function killCommand(opts) {
|
|
134
|
+
const masterPort = await resolveMasterPort(opts);
|
|
135
|
+
console.log(`\n${yellow("▸")} Shutting down master process...\n`);
|
|
136
|
+
|
|
137
|
+
const res = await httpPost(apiUrl(masterPort, ADMIN_API.SHUTDOWN), {});
|
|
138
|
+
if (res.ok) {
|
|
139
|
+
console.log(`${green("✓")} Master process has been shut down\n`);
|
|
140
|
+
} else {
|
|
141
|
+
console.error(`${red("✗")} Shutdown failed: ${res.error}\n`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
process.exit(0);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function fixCommand() {
|
|
148
|
+
const fs = require("fs");
|
|
149
|
+
const { MASTER_RUNTIME_FILE } = require("./constants");
|
|
150
|
+
|
|
151
|
+
console.log(`\n${yellow("▸")} Checking master runtime...\n`);
|
|
152
|
+
|
|
153
|
+
const runtime = await readMasterRuntime();
|
|
154
|
+
if (runtime) {
|
|
155
|
+
console.log(` ${green("✓")} Master is running normally on port ${runtime.publicPort}\n`);
|
|
156
|
+
process.exit(0);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (fs.existsSync(MASTER_RUNTIME_FILE)) {
|
|
160
|
+
try {
|
|
161
|
+
fs.unlinkSync(MASTER_RUNTIME_FILE);
|
|
162
|
+
console.log(` ${green("✓")} Removed stale runtime file\n`);
|
|
163
|
+
} catch {
|
|
164
|
+
console.error(` ${red("✗")} Failed to remove runtime file\n`);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
console.log(` ${yellow("ℹ")} No stale runtime file found\n`);
|
|
169
|
+
}
|
|
170
|
+
process.exit(0);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
module.exports = { statusCommand, stopCommand, killCommand, fixCommand };
|
package/bin/lib/constants.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
const path = require("path");
|
|
2
|
-
const os = require("os");
|
|
3
|
-
|
|
4
|
-
const DEFAULT_PORT = 5478;
|
|
5
|
-
|
|
6
|
-
const MASTER_RUNTIME_FILE = path.join(os.tmpdir(), ".comphub", "master.json");
|
|
7
|
-
|
|
8
|
-
const ADMIN_API = {
|
|
9
|
-
PREFIX: "/__master__",
|
|
10
|
-
HEALTH: "/__master__/health",
|
|
11
|
-
REGISTER: "/__master__/register",
|
|
12
|
-
DEREGISTER: "/__master__/deregister",
|
|
13
|
-
PROJECTS: "/__master__/projects",
|
|
14
|
-
SHUTDOWN: "/__master__/shutdown"
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const POLL_INTERVAL = 500;
|
|
18
|
-
const MAX_RETRIES = 40;
|
|
19
|
-
const HTTP_TIMEOUT_GET = 2000;
|
|
20
|
-
const HTTP_TIMEOUT_POST = 5000;
|
|
21
|
-
|
|
22
|
-
module.exports = { DEFAULT_PORT, MASTER_RUNTIME_FILE, ADMIN_API, POLL_INTERVAL, MAX_RETRIES, HTTP_TIMEOUT_GET, HTTP_TIMEOUT_POST };
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const os = require("os");
|
|
3
|
+
|
|
4
|
+
const DEFAULT_PORT = 5478;
|
|
5
|
+
|
|
6
|
+
const MASTER_RUNTIME_FILE = path.join(os.tmpdir(), ".comphub", "master.json");
|
|
7
|
+
|
|
8
|
+
const ADMIN_API = {
|
|
9
|
+
PREFIX: "/__master__",
|
|
10
|
+
HEALTH: "/__master__/health",
|
|
11
|
+
REGISTER: "/__master__/register",
|
|
12
|
+
DEREGISTER: "/__master__/deregister",
|
|
13
|
+
PROJECTS: "/__master__/projects",
|
|
14
|
+
SHUTDOWN: "/__master__/shutdown"
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const POLL_INTERVAL = 500;
|
|
18
|
+
const MAX_RETRIES = 40;
|
|
19
|
+
const HTTP_TIMEOUT_GET = 2000;
|
|
20
|
+
const HTTP_TIMEOUT_POST = 5000;
|
|
21
|
+
|
|
22
|
+
module.exports = { DEFAULT_PORT, MASTER_RUNTIME_FILE, ADMIN_API, POLL_INTERVAL, MAX_RETRIES, HTTP_TIMEOUT_GET, HTTP_TIMEOUT_POST };
|
package/bin/lib/http.js
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
const http = require("http");
|
|
2
|
-
const { HTTP_TIMEOUT_GET, HTTP_TIMEOUT_POST } = require("./constants");
|
|
3
|
-
|
|
4
|
-
function parseBody(raw) {
|
|
5
|
-
try {
|
|
6
|
-
return JSON.parse(raw);
|
|
7
|
-
} catch {
|
|
8
|
-
return raw;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function httpGet(url) {
|
|
13
|
-
return new Promise(resolve => {
|
|
14
|
-
const req = http.get(url, res => {
|
|
15
|
-
let body = "";
|
|
16
|
-
res.on("data", chunk => (body += chunk));
|
|
17
|
-
res.on("end", () => resolve({ ok: true, status: res.statusCode, body: parseBody(body) }));
|
|
18
|
-
});
|
|
19
|
-
req.on("error", err => resolve({ ok: false, error: err.message }));
|
|
20
|
-
req.setTimeout(HTTP_TIMEOUT_GET, () => {
|
|
21
|
-
req.destroy();
|
|
22
|
-
resolve({ ok: false, error: "请求超时" });
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function httpPost(url, data) {
|
|
28
|
-
return new Promise(resolve => {
|
|
29
|
-
const postData = JSON.stringify(data);
|
|
30
|
-
const urlObj = new URL(url);
|
|
31
|
-
const options = {
|
|
32
|
-
hostname: urlObj.hostname,
|
|
33
|
-
port: urlObj.port,
|
|
34
|
-
path: urlObj.pathname,
|
|
35
|
-
method: "POST",
|
|
36
|
-
headers: {
|
|
37
|
-
"Content-Type": "application/json",
|
|
38
|
-
"Content-Length": Buffer.byteLength(postData)
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
const req = http.request(options, res => {
|
|
42
|
-
let body = "";
|
|
43
|
-
res.on("data", chunk => (body += chunk));
|
|
44
|
-
res.on("end", () => {
|
|
45
|
-
try {
|
|
46
|
-
resolve({ ok: true, body: JSON.parse(body) });
|
|
47
|
-
} catch {
|
|
48
|
-
resolve({ ok: false, error: "Invalid JSON response" });
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
req.on("error", err => resolve({ ok: false, error: err.message }));
|
|
53
|
-
req.setTimeout(HTTP_TIMEOUT_POST, () => {
|
|
54
|
-
req.destroy();
|
|
55
|
-
resolve({ ok: false, error: "请求超时" });
|
|
56
|
-
});
|
|
57
|
-
req.write(postData);
|
|
58
|
-
req.end();
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
module.exports = { httpGet, httpPost };
|
|
1
|
+
const http = require("http");
|
|
2
|
+
const { HTTP_TIMEOUT_GET, HTTP_TIMEOUT_POST } = require("./constants");
|
|
3
|
+
|
|
4
|
+
function parseBody(raw) {
|
|
5
|
+
try {
|
|
6
|
+
return JSON.parse(raw);
|
|
7
|
+
} catch {
|
|
8
|
+
return raw;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function httpGet(url) {
|
|
13
|
+
return new Promise(resolve => {
|
|
14
|
+
const req = http.get(url, res => {
|
|
15
|
+
let body = "";
|
|
16
|
+
res.on("data", chunk => (body += chunk));
|
|
17
|
+
res.on("end", () => resolve({ ok: true, status: res.statusCode, body: parseBody(body) }));
|
|
18
|
+
});
|
|
19
|
+
req.on("error", err => resolve({ ok: false, error: err.message }));
|
|
20
|
+
req.setTimeout(HTTP_TIMEOUT_GET, () => {
|
|
21
|
+
req.destroy();
|
|
22
|
+
resolve({ ok: false, error: "请求超时" });
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function httpPost(url, data) {
|
|
28
|
+
return new Promise(resolve => {
|
|
29
|
+
const postData = JSON.stringify(data);
|
|
30
|
+
const urlObj = new URL(url);
|
|
31
|
+
const options = {
|
|
32
|
+
hostname: urlObj.hostname,
|
|
33
|
+
port: urlObj.port,
|
|
34
|
+
path: urlObj.pathname,
|
|
35
|
+
method: "POST",
|
|
36
|
+
headers: {
|
|
37
|
+
"Content-Type": "application/json",
|
|
38
|
+
"Content-Length": Buffer.byteLength(postData)
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const req = http.request(options, res => {
|
|
42
|
+
let body = "";
|
|
43
|
+
res.on("data", chunk => (body += chunk));
|
|
44
|
+
res.on("end", () => {
|
|
45
|
+
try {
|
|
46
|
+
resolve({ ok: true, body: JSON.parse(body) });
|
|
47
|
+
} catch {
|
|
48
|
+
resolve({ ok: false, error: "Invalid JSON response" });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
req.on("error", err => resolve({ ok: false, error: err.message }));
|
|
53
|
+
req.setTimeout(HTTP_TIMEOUT_POST, () => {
|
|
54
|
+
req.destroy();
|
|
55
|
+
resolve({ ok: false, error: "请求超时" });
|
|
56
|
+
});
|
|
57
|
+
req.write(postData);
|
|
58
|
+
req.end();
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = { httpGet, httpPost };
|