comp-hub 0.27.19 → 0.28.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 +71 -86
- package/bin/cli.js +42 -76
- package/bin/lib/commands.js +147 -0
- package/bin/lib/constants.js +22 -0
- package/bin/lib/http.js +62 -0
- package/bin/lib/startup.js +183 -0
- package/bin/lib/utils.js +80 -0
- package/dist/index.js +16 -16
- package/dist/master.js +403 -0
- package/dist/website/container/assets/{index-CdaB9oWd.js → index-BTW7AmWY.js} +9 -9
- package/dist/website/container/assets/index-DqV5-Zk-.css +1 -0
- package/dist/website/container/index.html +31 -34
- package/dist/website/container/js/vue2-sfc-loader.js +1 -1
- package/dist/website/container/js/vue3-sfc-loader.js +1 -1
- package/dist/website/item/assets/index-DNn8L2ik.css +1 -0
- package/dist/website/item/assets/index-eJgZiYQR.js +40 -0
- package/dist/website/item/index.html +28 -30
- package/dist/website/main/assets/{AuthCodeSendBtn.vue_vue_type_script_setup_true_lang-CfufrKbA.js → AuthCodeSendBtn.vue_vue_type_script_setup_true_lang-C2LcEFL4.js} +1 -1
- package/dist/website/main/assets/{BaseDialog.vue_vue_type_script_setup_true_lang-B6WkXWm7.js → BaseDialog.vue_vue_type_script_setup_true_lang-CgV6_zzd.js} +1 -1
- package/dist/website/main/assets/BaseSegmented.vue_vue_type_script_setup_true_lang-CW16Y3Jx.js +1 -0
- package/dist/website/main/assets/{CanvasAnimation.vue_vue_type_script_setup_true_lang-BwtYEYU5.js → CanvasAnimation.vue_vue_type_script_setup_true_lang-BVY2XxV8.js} +1 -1
- package/dist/website/main/assets/{EmptyStatusContainer.vue_vue_type_script_setup_true_lang-DIgE9MGd.js → EmptyStatusContainer.vue_vue_type_script_setup_true_lang-CofI0Rvq.js} +1 -1
- package/dist/website/main/assets/{LoadingWrapper.vue_vue_type_script_setup_true_lang-5GxduB08.js → LoadingWrapper.vue_vue_type_script_setup_true_lang-bBtkKtmz.js} +1 -1
- package/dist/website/main/assets/{PanelBackground.vue_vue_type_script_setup_true_lang-CM6wkI5V.js → PanelBackground.vue_vue_type_script_setup_true_lang-DBIlZFl0.js} +1 -1
- package/dist/website/main/assets/{cloud-upload-CyIQfnxU.js → cloud-upload-BRjLuSi4.js} +1 -1
- package/dist/website/main/assets/{core-DE5-zGKf.js → core-B1GcTOER.js} +1 -1
- package/dist/website/main/assets/{el-table-column-BiqynxkD.js → el-table-column-8qfe8CVK.js} +1 -1
- package/dist/website/main/assets/{home-rounded-Dq-TBvLG.js → home-rounded-8qNxvN2I.js} +1 -1
- package/dist/website/main/assets/{index-CMvx8gm3.js → index-0Y9kW7YJ.js} +1 -1
- package/dist/website/main/assets/index-2LGHX46G.js +1 -0
- package/dist/website/main/assets/index-B2xvPCaB.js +1 -0
- package/dist/website/main/assets/{index-CGgS1tZ0.js → index-B3dAdOL_.js} +1 -1
- package/dist/website/main/assets/{index-Cf69_nzt.js → index-B5qe6yln.js} +15 -15
- package/dist/website/main/assets/index-BANn4H7f.css +1 -0
- package/dist/website/main/assets/{index-DvyE3TrH.js → index-ByKMTPW3.js} +2 -2
- package/dist/website/main/assets/index-CDnNjG3p.css +1 -0
- package/dist/website/main/assets/index-CEQStqDa.js +1 -0
- package/dist/website/main/assets/index-CP6sOfKF.css +1 -0
- package/dist/website/main/assets/index-Cf5sOeOs.js +1 -0
- package/dist/website/main/assets/index-CmSN0r9I.css +1 -0
- package/dist/website/main/assets/index-Cn8OtDDR.js +1 -0
- package/dist/website/main/assets/{index-BQWeT5qu.js → index-CoWWi7z-.js} +2 -2
- package/dist/website/main/assets/index-Cqi9_pkc.js +1 -0
- package/dist/website/main/assets/{index-vnzmxexj.js → index-CrzWpCC3.js} +1 -1
- package/dist/website/main/assets/index-CtzG8xUz.js +1 -0
- package/dist/website/main/assets/index-DANPdG90.js +1 -0
- package/dist/website/main/assets/index-DI0kvKsV.js +1 -0
- package/dist/website/main/assets/index-DJ4AvgpT.css +1 -0
- package/dist/website/main/assets/index-DSED2-Jv.js +1 -0
- package/dist/website/main/assets/index-DUr8uU-d.js +1 -0
- package/dist/website/main/assets/index-DW-M9dmc.js +1 -0
- package/dist/website/main/assets/{index-BGLjC3BV.js → index-D_jNsofk.js} +1 -1
- package/dist/website/main/assets/index-DffjEud2.js +1 -0
- package/dist/website/main/assets/{index-ctwb4cB7.js → index-DkTzdi1V.js} +1 -1
- package/dist/website/main/assets/index-DoTR7Juh.css +1 -0
- package/dist/website/main/assets/index-DxG4_1zN.js +1 -0
- package/dist/website/main/assets/{index-CTLlcQoM.js → index-DyEkv8OS.js} +1 -1
- package/dist/website/main/assets/{index-CCFHORqg.js → index-RlxCYKYM.js} +1 -1
- package/dist/website/main/assets/index-ZwJcoRf3.js +2 -0
- package/dist/website/main/assets/index-bJb3z37F.js +1 -0
- package/dist/website/main/assets/{index-gOTpUrNM.js → index-dC2trX5C.js} +1 -1
- package/dist/website/main/assets/{index-Cu1YAWdz.js → index-dhG9Kj5W.js} +1 -1
- package/dist/website/main/assets/{index-q4Z3Q7p4.js → index-feqY_hvF.js} +1 -1
- package/dist/website/main/assets/{index-cFhtMyBi.js → index-op0bdgFk.js} +2 -2
- package/dist/website/main/assets/index-xqKWYiCC.js +1 -0
- package/dist/website/main/assets/{index.vue_vue_type_script_setup_true_lang-BDhf8t5D.js → index.vue_vue_type_script_setup_true_lang-B_VrULUy.js} +1 -1
- package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-D3xHCy9f.js +2 -0
- package/dist/website/main/assets/{index.vue_vue_type_script_setup_true_lang-B4echric.js → index.vue_vue_type_script_setup_true_lang-DCQMqfel.js} +1 -1
- package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-DM_Wm4GD.js +1 -0
- package/dist/website/main/assets/{index.vue_vue_type_script_setup_true_lang-qhJpeu7a.js → index.vue_vue_type_script_setup_true_lang-DX9EV5b8.js} +1 -1
- package/dist/website/main/assets/{index.vue_vue_type_script_setup_true_lang-aO0L9Tak.js → index.vue_vue_type_script_setup_true_lang-RJnk970E.js} +1 -1
- package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-rk3Yu4M8.js +1 -0
- package/dist/website/main/assets/{index.vue_vue_type_style_index_0_lang-C3U7f9Ma.js → index.vue_vue_type_style_index_0_lang-BQWxY2F1.js} +5 -5
- package/dist/website/main/assets/{index.vue_vue_type_style_index_0_lang-C4fELX8m.js → index.vue_vue_type_style_index_0_lang-DQhqVyhA.js} +1 -1
- package/dist/website/main/assets/{index.vue_vue_type_style_index_0_lang-D_eT-tA_.js → index.vue_vue_type_style_index_0_lang-xD-wMtaK.js} +1 -1
- package/dist/website/main/assets/{kid-star-CFLIpXDC.js → kid-star-Cr1NlTdT.js} +1 -1
- package/dist/website/main/assets/{md5-BZr8xhBN.js → md5-DzJRYP5-.js} +1 -1
- package/dist/website/main/assets/{settings-B3XnnICj.js → settings-BFJXTJ9K.js} +1 -1
- package/dist/website/main/assets/{supervised-user-circle-CEgKC7n7.js → supervised-user-circle-Bnx5icEm.js} +1 -1
- package/dist/website/main/assets/{sync-saved-locally-rounded-CmlRPNS9.js → sync-saved-locally-rounded-DtyOT0dw.js} +1 -1
- package/dist/website/main/assets/{terminal-DykiTSV6.js → terminal-D72IJB2N.js} +1 -1
- package/dist/website/main/assets/{useCacheOssStore-CU4a8b24.js → useCacheOssStore-DApHt9lQ.js} +1 -1
- package/dist/website/main/assets/{useCompEditStore-Du_-rNtX.css → useCompEditStore-B9HAj9va.css} +1 -1
- package/dist/website/main/assets/{useCompEditStore-B7KaUbOi.js → useCompEditStore-KBTFiinR.js} +94 -94
- package/dist/website/main/assets/{useCompMetaLocalStore-pb1Ikkq3.js → useCompMetaLocalStore-eE-CBcE9.js} +1 -1
- package/dist/website/main/assets/{useKeyword-D1qu8Tz-.js → useKeyword-CoagiMo1.js} +1 -1
- package/dist/website/main/assets/useMessage-CYVXvlqq.js +1 -0
- package/dist/website/main/assets/{useTeamHeaders-DELMIzHt.js → useTeamHeaders-Cc060CP0.js} +1 -1
- package/dist/website/main/assets/useVscodeOpen-Dx3IRL0P.js +1 -0
- package/dist/website/main/assets/{vendor-core-B-Si3WEf.js → vendor-core-CzvSW5ae.js} +3 -3
- package/dist/website/main/assets/{vendor-ui-D6wVBlls.js → vendor-ui-DXwyveTF.js} +12 -12
- package/dist/website/main/index.html +92 -92
- package/package.json +5 -3
- package/dist/website/container/assets/index-BnbvpvZL.css +0 -1
- package/dist/website/item/assets/index-B0s1uwsa.js +0 -39
- package/dist/website/item/assets/index-BIcNxYiU.css +0 -1
- package/dist/website/main/assets/BaseSegmented.vue_vue_type_script_setup_true_lang-D5yw_wF0.js +0 -1
- package/dist/website/main/assets/index-B6f_06zP.js +0 -1
- package/dist/website/main/assets/index-BIGvC1SM.js +0 -1
- package/dist/website/main/assets/index-BfPdLll-.js +0 -1
- package/dist/website/main/assets/index-BkUBwon5.css +0 -1
- package/dist/website/main/assets/index-C9t3Yhe9.js +0 -1
- package/dist/website/main/assets/index-CFH_ugiT.js +0 -1
- package/dist/website/main/assets/index-CWW1M_cy.js +0 -1
- package/dist/website/main/assets/index-CuHNNhtE.js +0 -1
- package/dist/website/main/assets/index-D6Fz5rH9.css +0 -1
- package/dist/website/main/assets/index-D7eOWAPr.js +0 -1
- package/dist/website/main/assets/index-D8WDrGkh.js +0 -1
- package/dist/website/main/assets/index-DBM1x91X.js +0 -1
- package/dist/website/main/assets/index-DC94GyXj.js +0 -1
- package/dist/website/main/assets/index-Dl-mwEbG.js +0 -1
- package/dist/website/main/assets/index-DmO0JQ56.css +0 -1
- package/dist/website/main/assets/index-DrUAgWBX.js +0 -1
- package/dist/website/main/assets/index-Oo11MKfd.js +0 -1
- package/dist/website/main/assets/index-YUZjR2Eq.js +0 -2
- package/dist/website/main/assets/index-tEU6GnSH.js +0 -1
- package/dist/website/main/assets/index-tWc_GbBW.css +0 -1
- package/dist/website/main/assets/index-uu5oSUvx.js +0 -1
- package/dist/website/main/assets/index-wgeiSlf-.css +0 -1
- package/dist/website/main/assets/index-y8GEzH-z.css +0 -1
- package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-COryHg9c.js +0 -1
- package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-D_7jZ1zo.js +0 -2
- package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-Dtuk2wrA.js +0 -1
- package/dist/website/main/assets/useMessage-D7WAp245.js +0 -1
- package/dist/website/main/assets/useVscodeOpen-DIYbcP1e.js +0 -1
package/README.md
CHANGED
|
@@ -1,147 +1,132 @@
|
|
|
1
1
|
# comp-hub
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
CLI for comp-hub — a Vue component sharing platform. Start a local dev server to preview, manage, and share components.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/comp-hub)
|
|
6
|
+
[](https://nodejs.org)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## 🚀 Core Features
|
|
10
|
-
|
|
11
|
-
- **Real-time Preview & Dependency Management**: Online real-time component preview with automatic dependency version matching, ensuring what you see is what you get after download
|
|
12
|
-
- **Lightweight Component Architecture**: Components contain only minimal runtime units, seamlessly integrating into existing project environments
|
|
13
|
-
- **Simple Upload Process**: Complete component publishing in three steps, supporting preview configuration, documentation, and version management
|
|
14
|
-
- **Documentation Support**: Standard Markdown documentation support to help users quickly understand and use components
|
|
15
|
-
- **Version Management**: Semantic versioning with multi-version coexistence and switching
|
|
16
|
-
- **Cross-project Reuse**: Standardized component structure ensures high compatibility and portability across different projects
|
|
17
|
-
|
|
18
|
-
## 📦 Supported Frameworks
|
|
19
|
-
|
|
20
|
-
| Framework | Version | Status |
|
|
21
|
-
|-----------|---------|--------|
|
|
22
|
-
| Vue | 2.x | ✅ Supported |
|
|
23
|
-
| Vue | 3.x | ✅ Supported |
|
|
24
|
-
| React | - | ⏳ Planned |
|
|
25
|
-
|
|
26
|
-
## 💻 Installation & Usage
|
|
27
|
-
|
|
28
|
-
### Requirements
|
|
8
|
+
## Requirements
|
|
29
9
|
|
|
30
10
|
- Node.js >= 16.0.0
|
|
31
11
|
- npm >= 8.0.0
|
|
32
12
|
|
|
33
|
-
|
|
13
|
+
## Installation
|
|
34
14
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Install comp-hub CLI globally:
|
|
15
|
+
**Global (recommended)**
|
|
38
16
|
|
|
39
17
|
```bash
|
|
40
18
|
npm install comp-hub@latest -g
|
|
41
19
|
```
|
|
42
20
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Install only in the current project:
|
|
21
|
+
**Local**
|
|
46
22
|
|
|
47
23
|
```bash
|
|
48
24
|
npm install comp-hub@latest -D
|
|
49
25
|
```
|
|
50
26
|
|
|
51
|
-
|
|
27
|
+
## Quick Start
|
|
52
28
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
Execute in the project directory:
|
|
29
|
+
Run in your project directory:
|
|
56
30
|
|
|
57
31
|
```bash
|
|
58
32
|
comphub
|
|
59
33
|
```
|
|
60
34
|
|
|
61
|
-
|
|
35
|
+
Once started, open the URL shown in the terminal (default `http://localhost:5478`).
|
|
62
36
|
|
|
63
|
-
|
|
37
|
+
### CLI Options
|
|
64
38
|
|
|
65
39
|
```bash
|
|
66
|
-
|
|
40
|
+
comphub [options]
|
|
67
41
|
```
|
|
68
42
|
|
|
69
|
-
|
|
43
|
+
| Option | Alias | Type | Default | Description |
|
|
44
|
+
|--------|-------|------|---------|-------------|
|
|
45
|
+
| `--port` | `-p` | number | 5478 | Server port |
|
|
46
|
+
| `--dir` | `-d` | path | `"./"` | Component directory |
|
|
47
|
+
| `--version` | `-v` | — | — | Show version |
|
|
48
|
+
| `--help` | `-h` | — | — | Show help |
|
|
49
|
+
|
|
50
|
+
Examples:
|
|
70
51
|
|
|
71
|
-
```
|
|
72
|
-
|
|
52
|
+
```bash
|
|
53
|
+
comphub -p 8080 # Custom port
|
|
54
|
+
comphub -d ./src # Custom component directory
|
|
73
55
|
```
|
|
74
56
|
|
|
75
|
-
###
|
|
57
|
+
### Management Commands
|
|
58
|
+
|
|
59
|
+
| Command | Description |
|
|
60
|
+
|---------|-------------|
|
|
61
|
+
| `comphub` | Start dev server |
|
|
62
|
+
| `comphub status` | List all running projects |
|
|
63
|
+
| `comphub stop <hash>` | Stop a specific project |
|
|
64
|
+
| `comphub stop --all` | Stop all projects |
|
|
65
|
+
| `comphub kill` | Shut down all services |
|
|
66
|
+
|
|
67
|
+
Examples:
|
|
76
68
|
|
|
77
69
|
```bash
|
|
78
|
-
|
|
70
|
+
# Check running projects
|
|
71
|
+
comphub status
|
|
72
|
+
|
|
73
|
+
# Stop a specific project
|
|
74
|
+
comphub stop 94525912
|
|
75
|
+
|
|
76
|
+
# Stop all projects
|
|
77
|
+
comphub stop --all
|
|
78
|
+
|
|
79
|
+
# Shut down everything
|
|
80
|
+
comphub kill
|
|
79
81
|
```
|
|
80
82
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
| `--dir` | `-d` | path | "./" | Resource directory relative path |
|
|
85
|
-
| `--log` | `-l` | level | "info" | Log level |
|
|
86
|
-
| `--version` | `-v` | - | - | Show version number |
|
|
87
|
-
| `--help` | `-h` | - | - | Show help information |
|
|
83
|
+
### Multi-Project
|
|
84
|
+
|
|
85
|
+
Run multiple projects simultaneously on separate ports:
|
|
88
86
|
|
|
89
|
-
|
|
87
|
+
```bash
|
|
88
|
+
cd /path/to/project-a && comphub
|
|
89
|
+
cd /path/to/project-b && comphub
|
|
90
|
+
comphub status # List all
|
|
91
|
+
```
|
|
90
92
|
|
|
91
|
-
|
|
93
|
+
## Configuration
|
|
92
94
|
|
|
93
|
-
|
|
95
|
+
Create `.comphub.json` or `.comphub.js` in your project root:
|
|
94
96
|
|
|
95
97
|
```json
|
|
96
98
|
{
|
|
97
|
-
"port": 5000,
|
|
98
99
|
"dir": "./src",
|
|
99
|
-
"
|
|
100
|
+
"allowDebug": false
|
|
100
101
|
}
|
|
101
102
|
```
|
|
102
103
|
|
|
103
|
-
**JS Format:**
|
|
104
|
-
|
|
105
104
|
```javascript
|
|
106
105
|
module.exports = {
|
|
107
|
-
port: 5000,
|
|
108
106
|
dir: './src',
|
|
109
|
-
|
|
107
|
+
allowDebug: false
|
|
110
108
|
}
|
|
111
109
|
```
|
|
112
110
|
|
|
113
|
-
|
|
|
111
|
+
| Option | Type | Default | Description |
|
|
114
112
|
|--------|------|---------|-------------|
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
|
|
119
|
-
## 📚 Documentation
|
|
120
|
-
|
|
121
|
-
Complete usage guide and API documentation:
|
|
122
|
-
|
|
123
|
-
**[comp-hub Official Documentation](https://docs.comphub.cn/en/)**
|
|
124
|
-
|
|
125
|
-
## 🔧 Component Specifications
|
|
126
|
-
|
|
127
|
-
- Components must be uploaded as **folders**, with the folder name as the component name
|
|
128
|
-
- Component folder must contain `index.vue` as the entry file
|
|
129
|
-
- Recommended to provide `README.md` describing component functionality and usage
|
|
130
|
-
- Support `comp.json` file for component metadata configuration
|
|
113
|
+
| `dir` | string | `"./"` | Component root directory |
|
|
114
|
+
| `allowDebug` | boolean | false | Enable debug mode |
|
|
115
|
+
| `proxy` | object | — | Custom proxy rules |
|
|
131
116
|
|
|
132
|
-
|
|
117
|
+
**Priority**: CLI flags > config file > defaults
|
|
133
118
|
|
|
134
|
-
|
|
119
|
+
## Component Specs
|
|
135
120
|
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
-
|
|
139
|
-
- `
|
|
121
|
+
- Upload components as **folders** — folder name is the component name
|
|
122
|
+
- Must include `index.vue` as entry point
|
|
123
|
+
- Include a `README.md` describing usage
|
|
124
|
+
- Optional `demo/` directory for preview configs
|
|
140
125
|
|
|
141
|
-
##
|
|
126
|
+
## Documentation
|
|
142
127
|
|
|
143
|
-
|
|
128
|
+
[comp-hub Docs](https://docs.comphub.cn/)
|
|
144
129
|
|
|
145
|
-
##
|
|
130
|
+
## License
|
|
146
131
|
|
|
147
|
-
[MIT
|
|
132
|
+
[MIT](LICENSE)
|
package/bin/cli.js
CHANGED
|
@@ -1,85 +1,51 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const { program, Option } = require(
|
|
3
|
-
const
|
|
4
|
-
const {
|
|
5
|
-
const
|
|
2
|
+
const { program, Option } = require("commander");
|
|
3
|
+
const { getLocalPkgInfo } = require("./lib/utils");
|
|
4
|
+
const { statusCommand, stopCommand, killCommand } = require("./lib/commands");
|
|
5
|
+
const { main } = require("./lib/startup");
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const { pkg } = getLocalPkgInfo();
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
//读取包信息
|
|
12
|
-
function getLocalPkgInfo() {
|
|
13
|
-
const pkgPath = path.join(projectRoot, 'package.json');
|
|
14
|
-
const pkg = readJson(pkgPath);
|
|
15
|
-
const entry = path.join(projectRoot, pkg.main || 'dist/index.js');
|
|
16
|
-
return { pkg, entry };
|
|
9
|
+
function portArg(v) {
|
|
10
|
+
return parseInt(v, 10);
|
|
17
11
|
}
|
|
18
12
|
|
|
13
|
+
// Global options
|
|
14
|
+
program
|
|
15
|
+
.name("comphub")
|
|
16
|
+
.option("-p, --port <number>", "Startup port", portArg)
|
|
17
|
+
.option("-d, --dir <path>", "Resource directory relative path")
|
|
18
|
+
.version(pkg.version, "-v, --version", "Show version")
|
|
19
|
+
.addOption(new Option("--allow-debug", "Enable debug mode").hideHelp())
|
|
20
|
+
.addOption(new Option("--api <url>", "API URL").hideHelp())
|
|
21
|
+
.helpOption("-h, --help", "Show help");
|
|
22
|
+
|
|
23
|
+
// status subcommand: view master status and all projects
|
|
24
|
+
program
|
|
25
|
+
.command("status")
|
|
26
|
+
.description("View master service status and all running projects")
|
|
27
|
+
.addOption(new Option("-p, --port <number>", "Master port").argParser(portArg).hideHelp())
|
|
28
|
+
.action(statusCommand);
|
|
19
29
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
//命令行
|
|
30
|
+
// stop subcommand: stop projects
|
|
23
31
|
program
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
.command("stop [hash]")
|
|
33
|
+
.description("Stop a specific project (by hash) or stop all projects (--all)")
|
|
34
|
+
.addOption(new Option("-p, --port <number>", "Master port").argParser(portArg).hideHelp())
|
|
35
|
+
.option("-a, --all", "Stop all projects")
|
|
36
|
+
.action(stopCommand);
|
|
37
|
+
|
|
38
|
+
// kill subcommand: shutdown master process
|
|
39
|
+
program.command("kill").description("Shutdown master process").addOption(new Option("-p, --port <number>", "Master port").argParser(portArg).hideHelp()).action(killCommand);
|
|
40
|
+
|
|
41
|
+
// Default startup command
|
|
42
|
+
program.action(async () => {
|
|
43
|
+
try {
|
|
44
|
+
await main(pkg, program.opts());
|
|
45
|
+
} catch (e) {
|
|
46
|
+
console.error(`✗ Startup failed: ${e.message}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
35
50
|
|
|
36
51
|
program.parse();
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
//主流程
|
|
40
|
-
function main() {
|
|
41
|
-
//检查当前工作目录是否是node项目
|
|
42
|
-
if (!fs.existsSync(path.join(process.cwd(), 'package.json'))) {
|
|
43
|
-
console.error('❌ 当前工作目录不是node工程,无法启动');
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const opts = program.opts();
|
|
48
|
-
|
|
49
|
-
console.log(`✅ ${pkg.name} v${pkg.version}`);
|
|
50
|
-
|
|
51
|
-
//忽略传递的参数配置
|
|
52
|
-
const ignoreArgs = ['-v', '--version', '-h', '--help'];
|
|
53
|
-
const userArgs = process.argv.slice(2).filter((a) => !ignoreArgs.includes(a));
|
|
54
|
-
|
|
55
|
-
const env = {
|
|
56
|
-
...process.env,
|
|
57
|
-
COMP_HUB_VERSION: pkg.version,
|
|
58
|
-
COMP_HUB_CONFIG: JSON.stringify(opts),
|
|
59
|
-
MIDWAY_TS_MODE: 'false',
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
if (!fs.existsSync(entry)) {
|
|
63
|
-
console.log('\nℹ️ 入口文件不存在,启动失败');
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const child = spawn(process.execPath, [entry, ...userArgs], {
|
|
68
|
-
stdio: 'inherit',
|
|
69
|
-
cwd: process.cwd(),
|
|
70
|
-
env,
|
|
71
|
-
shell: process.platform === 'win32',
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
child.on('exit', (code) => process.exit(code ?? 0));
|
|
75
|
-
child.on('error', (e) => (console.error(`❌ 启动失败:${e.message}`), process.exit(1)));
|
|
76
|
-
|
|
77
|
-
['SIGINT', 'SIGTERM'].forEach((sig) =>
|
|
78
|
-
process.on(sig, () => {
|
|
79
|
-
console.log('\n⚠️ 终止进程...');
|
|
80
|
-
child.kill(sig);
|
|
81
|
-
})
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
main();
|
|
@@ -0,0 +1,147 @@
|
|
|
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
|
+
function resolveMasterPort(opts) {
|
|
29
|
+
// 用户明确指定了端口,直接使用
|
|
30
|
+
if (opts.port != null) {
|
|
31
|
+
return opts.port;
|
|
32
|
+
}
|
|
33
|
+
// 从 runtime 文件读取 master 实际端口
|
|
34
|
+
const runtime = readMasterRuntime();
|
|
35
|
+
if (runtime) {
|
|
36
|
+
return runtime.managementPort;
|
|
37
|
+
}
|
|
38
|
+
// 兜底:默认端口
|
|
39
|
+
return DEFAULT_PORT;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function statusCommand(opts) {
|
|
43
|
+
const masterPort = resolveMasterPort(opts);
|
|
44
|
+
const runtime = 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 = 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 = 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
|
+
module.exports = { statusCommand, stopCommand, killCommand };
|
|
@@ -0,0 +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 };
|
package/bin/lib/http.js
ADDED
|
@@ -0,0 +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 };
|