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.
Files changed (125) hide show
  1. package/README.md +71 -86
  2. package/bin/cli.js +42 -76
  3. package/bin/lib/commands.js +147 -0
  4. package/bin/lib/constants.js +22 -0
  5. package/bin/lib/http.js +62 -0
  6. package/bin/lib/startup.js +183 -0
  7. package/bin/lib/utils.js +80 -0
  8. package/dist/index.js +16 -16
  9. package/dist/master.js +403 -0
  10. package/dist/website/container/assets/{index-CdaB9oWd.js → index-BTW7AmWY.js} +9 -9
  11. package/dist/website/container/assets/index-DqV5-Zk-.css +1 -0
  12. package/dist/website/container/index.html +31 -34
  13. package/dist/website/container/js/vue2-sfc-loader.js +1 -1
  14. package/dist/website/container/js/vue3-sfc-loader.js +1 -1
  15. package/dist/website/item/assets/index-DNn8L2ik.css +1 -0
  16. package/dist/website/item/assets/index-eJgZiYQR.js +40 -0
  17. package/dist/website/item/index.html +28 -30
  18. 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
  19. 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
  20. package/dist/website/main/assets/BaseSegmented.vue_vue_type_script_setup_true_lang-CW16Y3Jx.js +1 -0
  21. 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
  22. 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
  23. 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
  24. 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
  25. package/dist/website/main/assets/{cloud-upload-CyIQfnxU.js → cloud-upload-BRjLuSi4.js} +1 -1
  26. package/dist/website/main/assets/{core-DE5-zGKf.js → core-B1GcTOER.js} +1 -1
  27. package/dist/website/main/assets/{el-table-column-BiqynxkD.js → el-table-column-8qfe8CVK.js} +1 -1
  28. package/dist/website/main/assets/{home-rounded-Dq-TBvLG.js → home-rounded-8qNxvN2I.js} +1 -1
  29. package/dist/website/main/assets/{index-CMvx8gm3.js → index-0Y9kW7YJ.js} +1 -1
  30. package/dist/website/main/assets/index-2LGHX46G.js +1 -0
  31. package/dist/website/main/assets/index-B2xvPCaB.js +1 -0
  32. package/dist/website/main/assets/{index-CGgS1tZ0.js → index-B3dAdOL_.js} +1 -1
  33. package/dist/website/main/assets/{index-Cf69_nzt.js → index-B5qe6yln.js} +15 -15
  34. package/dist/website/main/assets/index-BANn4H7f.css +1 -0
  35. package/dist/website/main/assets/{index-DvyE3TrH.js → index-ByKMTPW3.js} +2 -2
  36. package/dist/website/main/assets/index-CDnNjG3p.css +1 -0
  37. package/dist/website/main/assets/index-CEQStqDa.js +1 -0
  38. package/dist/website/main/assets/index-CP6sOfKF.css +1 -0
  39. package/dist/website/main/assets/index-Cf5sOeOs.js +1 -0
  40. package/dist/website/main/assets/index-CmSN0r9I.css +1 -0
  41. package/dist/website/main/assets/index-Cn8OtDDR.js +1 -0
  42. package/dist/website/main/assets/{index-BQWeT5qu.js → index-CoWWi7z-.js} +2 -2
  43. package/dist/website/main/assets/index-Cqi9_pkc.js +1 -0
  44. package/dist/website/main/assets/{index-vnzmxexj.js → index-CrzWpCC3.js} +1 -1
  45. package/dist/website/main/assets/index-CtzG8xUz.js +1 -0
  46. package/dist/website/main/assets/index-DANPdG90.js +1 -0
  47. package/dist/website/main/assets/index-DI0kvKsV.js +1 -0
  48. package/dist/website/main/assets/index-DJ4AvgpT.css +1 -0
  49. package/dist/website/main/assets/index-DSED2-Jv.js +1 -0
  50. package/dist/website/main/assets/index-DUr8uU-d.js +1 -0
  51. package/dist/website/main/assets/index-DW-M9dmc.js +1 -0
  52. package/dist/website/main/assets/{index-BGLjC3BV.js → index-D_jNsofk.js} +1 -1
  53. package/dist/website/main/assets/index-DffjEud2.js +1 -0
  54. package/dist/website/main/assets/{index-ctwb4cB7.js → index-DkTzdi1V.js} +1 -1
  55. package/dist/website/main/assets/index-DoTR7Juh.css +1 -0
  56. package/dist/website/main/assets/index-DxG4_1zN.js +1 -0
  57. package/dist/website/main/assets/{index-CTLlcQoM.js → index-DyEkv8OS.js} +1 -1
  58. package/dist/website/main/assets/{index-CCFHORqg.js → index-RlxCYKYM.js} +1 -1
  59. package/dist/website/main/assets/index-ZwJcoRf3.js +2 -0
  60. package/dist/website/main/assets/index-bJb3z37F.js +1 -0
  61. package/dist/website/main/assets/{index-gOTpUrNM.js → index-dC2trX5C.js} +1 -1
  62. package/dist/website/main/assets/{index-Cu1YAWdz.js → index-dhG9Kj5W.js} +1 -1
  63. package/dist/website/main/assets/{index-q4Z3Q7p4.js → index-feqY_hvF.js} +1 -1
  64. package/dist/website/main/assets/{index-cFhtMyBi.js → index-op0bdgFk.js} +2 -2
  65. package/dist/website/main/assets/index-xqKWYiCC.js +1 -0
  66. 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
  67. package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-D3xHCy9f.js +2 -0
  68. 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
  69. package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-DM_Wm4GD.js +1 -0
  70. 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
  71. 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
  72. package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-rk3Yu4M8.js +1 -0
  73. 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
  74. 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
  75. 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
  76. package/dist/website/main/assets/{kid-star-CFLIpXDC.js → kid-star-Cr1NlTdT.js} +1 -1
  77. package/dist/website/main/assets/{md5-BZr8xhBN.js → md5-DzJRYP5-.js} +1 -1
  78. package/dist/website/main/assets/{settings-B3XnnICj.js → settings-BFJXTJ9K.js} +1 -1
  79. package/dist/website/main/assets/{supervised-user-circle-CEgKC7n7.js → supervised-user-circle-Bnx5icEm.js} +1 -1
  80. package/dist/website/main/assets/{sync-saved-locally-rounded-CmlRPNS9.js → sync-saved-locally-rounded-DtyOT0dw.js} +1 -1
  81. package/dist/website/main/assets/{terminal-DykiTSV6.js → terminal-D72IJB2N.js} +1 -1
  82. package/dist/website/main/assets/{useCacheOssStore-CU4a8b24.js → useCacheOssStore-DApHt9lQ.js} +1 -1
  83. package/dist/website/main/assets/{useCompEditStore-Du_-rNtX.css → useCompEditStore-B9HAj9va.css} +1 -1
  84. package/dist/website/main/assets/{useCompEditStore-B7KaUbOi.js → useCompEditStore-KBTFiinR.js} +94 -94
  85. package/dist/website/main/assets/{useCompMetaLocalStore-pb1Ikkq3.js → useCompMetaLocalStore-eE-CBcE9.js} +1 -1
  86. package/dist/website/main/assets/{useKeyword-D1qu8Tz-.js → useKeyword-CoagiMo1.js} +1 -1
  87. package/dist/website/main/assets/useMessage-CYVXvlqq.js +1 -0
  88. package/dist/website/main/assets/{useTeamHeaders-DELMIzHt.js → useTeamHeaders-Cc060CP0.js} +1 -1
  89. package/dist/website/main/assets/useVscodeOpen-Dx3IRL0P.js +1 -0
  90. package/dist/website/main/assets/{vendor-core-B-Si3WEf.js → vendor-core-CzvSW5ae.js} +3 -3
  91. package/dist/website/main/assets/{vendor-ui-D6wVBlls.js → vendor-ui-DXwyveTF.js} +12 -12
  92. package/dist/website/main/index.html +92 -92
  93. package/package.json +5 -3
  94. package/dist/website/container/assets/index-BnbvpvZL.css +0 -1
  95. package/dist/website/item/assets/index-B0s1uwsa.js +0 -39
  96. package/dist/website/item/assets/index-BIcNxYiU.css +0 -1
  97. package/dist/website/main/assets/BaseSegmented.vue_vue_type_script_setup_true_lang-D5yw_wF0.js +0 -1
  98. package/dist/website/main/assets/index-B6f_06zP.js +0 -1
  99. package/dist/website/main/assets/index-BIGvC1SM.js +0 -1
  100. package/dist/website/main/assets/index-BfPdLll-.js +0 -1
  101. package/dist/website/main/assets/index-BkUBwon5.css +0 -1
  102. package/dist/website/main/assets/index-C9t3Yhe9.js +0 -1
  103. package/dist/website/main/assets/index-CFH_ugiT.js +0 -1
  104. package/dist/website/main/assets/index-CWW1M_cy.js +0 -1
  105. package/dist/website/main/assets/index-CuHNNhtE.js +0 -1
  106. package/dist/website/main/assets/index-D6Fz5rH9.css +0 -1
  107. package/dist/website/main/assets/index-D7eOWAPr.js +0 -1
  108. package/dist/website/main/assets/index-D8WDrGkh.js +0 -1
  109. package/dist/website/main/assets/index-DBM1x91X.js +0 -1
  110. package/dist/website/main/assets/index-DC94GyXj.js +0 -1
  111. package/dist/website/main/assets/index-Dl-mwEbG.js +0 -1
  112. package/dist/website/main/assets/index-DmO0JQ56.css +0 -1
  113. package/dist/website/main/assets/index-DrUAgWBX.js +0 -1
  114. package/dist/website/main/assets/index-Oo11MKfd.js +0 -1
  115. package/dist/website/main/assets/index-YUZjR2Eq.js +0 -2
  116. package/dist/website/main/assets/index-tEU6GnSH.js +0 -1
  117. package/dist/website/main/assets/index-tWc_GbBW.css +0 -1
  118. package/dist/website/main/assets/index-uu5oSUvx.js +0 -1
  119. package/dist/website/main/assets/index-wgeiSlf-.css +0 -1
  120. package/dist/website/main/assets/index-y8GEzH-z.css +0 -1
  121. package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-COryHg9c.js +0 -1
  122. package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-D_7jZ1zo.js +0 -2
  123. package/dist/website/main/assets/index.vue_vue_type_script_setup_true_lang-Dtuk2wrA.js +0 -1
  124. package/dist/website/main/assets/useMessage-D7WAp245.js +0 -1
  125. 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
- A Vue-based component preview and sharing platform that makes component reuse simple and efficient.
3
+ CLI for comp-hub a Vue component sharing platform. Start a local dev server to preview, manage, and share components.
4
4
 
5
- ## 📖 Introduction
5
+ [![npm version](https://img.shields.io/npm/v/comp-hub)](https://www.npmjs.com/package/comp-hub)
6
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D16-brightgreen)](https://nodejs.org)
6
7
 
7
- comp-hub is a component management platform designed for frontend developers, dedicated to solving pain points such as component reuse, missing documentation, and low standardization. By providing real-time preview, dependency management, and version control, it makes component sharing and usage simple and efficient.
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
- ### Installation
13
+ ## Installation
34
14
 
35
- #### Method 1: Global Installation (Recommended)
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
- #### Method 2: Local Dev Dependency Installation
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
- ### Start Service
27
+ ## Quick Start
52
28
 
53
- #### Global Installation
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
- #### Local Installation
35
+ Once started, open the URL shown in the terminal (default `http://localhost:5478`).
62
36
 
63
- If installed locally with `-D`, use `npx`:
37
+ ### CLI Options
64
38
 
65
39
  ```bash
66
- npx comphub
40
+ comphub [options]
67
41
  ```
68
42
 
69
- After successful startup, access the platform at:
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
- http://localhost:5000
52
+ ```bash
53
+ comphub -p 8080 # Custom port
54
+ comphub -d ./src # Custom component directory
73
55
  ```
74
56
 
75
- ### CLI Options
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
- comphub [options]
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
- | Option | Short | Type | Default | Description |
82
- |--------|-------|------|---------|-------------|
83
- | `--port` | `-p` | number | 5000 | Service port |
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
- ### Configuration File
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
- Create `.comphub.json` or `.comphub.js` in the project root:
93
+ ## Configuration
92
94
 
93
- **JSON Format:**
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
- "log": "info"
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
- log: 'info'
107
+ allowDebug: false
110
108
  }
111
109
  ```
112
110
 
113
- | Config | Type | Default | Description |
111
+ | Option | Type | Default | Description |
114
112
  |--------|------|---------|-------------|
115
- | `port` | number | 5000 | Service startup port |
116
- | `dir` | string | "./" | Component code root directory path |
117
- | `log` | string | "info" | Log level: "debug", "info", "warn", "error" |
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
- ## 🎯 Version Management
117
+ **Priority**: CLI flags > config file > defaults
133
118
 
134
- comp-hub supports semantic versioning:
119
+ ## Component Specs
135
120
 
136
- - `X.Y.Z` format (e.g., 1.2.3)
137
- - `X`: Major version, incompatible API changes
138
- - `Y`: Minor version, backward-compatible functionality additions
139
- - `Z`: Patch version, backward-compatible bug fixes
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
- ## 🤝 Contributing
126
+ ## Documentation
142
127
 
143
- Contributions and suggestions are welcome!
128
+ [comp-hub Docs](https://docs.comphub.cn/)
144
129
 
145
- ## 📝 License
130
+ ## License
146
131
 
147
- [MIT License](LICENSE)
132
+ [MIT](LICENSE)
package/bin/cli.js CHANGED
@@ -1,85 +1,51 @@
1
1
  #!/usr/bin/env node
2
- const { program, Option } = require('commander');
3
- const path = require('path');
4
- const { spawn } = require('child_process');
5
- const fs = require('fs');
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 projectRoot = path.resolve(__dirname, '..');
7
+ const { pkg } = getLocalPkgInfo();
8
8
 
9
- const readJson = (p) => JSON.parse(fs.readFileSync(p, 'utf8'));
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
- const { pkg, entry } = getLocalPkgInfo();
21
-
22
- //命令行
30
+ // stop subcommand: stop projects
23
31
  program
24
- .option('-p, --port <number>', '启动端口', (v) => parseInt(v, 10), 5000)
25
- .option('-d, --dir <path>', '资源目录相对路径', './')
26
- .option('-l, --log <level>', '日志等级', (v) => {
27
- const ok = ['debug', 'info', 'warn', 'error'];
28
- if (!ok.includes(v)) throw new Error(`日志等级必须是 ${ok.join(', ')}`);
29
- return v;
30
- }, 'info')
31
- .version(pkg.version, '-v, --version', '显示版本号')
32
- .addOption(new Option('--allow-debug', '是否允许调试模式').hideHelp())
33
- .addOption(new Option('--api <url>', 'API地址').hideHelp())
34
- .helpOption('-h, --help', '显示帮助信息');
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 };
@@ -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 };