march-control-cli 0.1.3 → 0.1.5
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 +30 -12
- package/core/constants.js +30 -3
- package/core/desktop-service.js +1 -1
- package/core/index.js +104 -10
- package/desktop/main.js +7 -1
- package/desktop/preload.cjs +49 -0
- package/package.json +8 -7
- package/scripts/postinstall.mjs +4 -4
- package/scripts/serve-site.mjs +1 -1
- package/src/App.tsx +6 -4
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# FHL Control
|
|
2
2
|
|
|
3
3
|
`march` is a provider configuration manager for:
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
It provides both:
|
|
10
10
|
|
|
11
|
-
- CLI (`march` / `mch`)
|
|
11
|
+
- CLI (`march` / `mch` / `ccman`)
|
|
12
12
|
- Desktop/web workspace in this repo (`src/`, `desktop/`, `src-tauri/`)
|
|
13
13
|
|
|
14
14
|
## CLI Features
|
|
@@ -27,6 +27,10 @@ It provides both:
|
|
|
27
27
|
- `preset add`
|
|
28
28
|
- `preset use`
|
|
29
29
|
- `preset remove`
|
|
30
|
+
- FHL quick shortcuts:
|
|
31
|
+
- `fhl`
|
|
32
|
+
- `fhl1`
|
|
33
|
+
- `fhlcode`
|
|
30
34
|
- URL probe before apply
|
|
31
35
|
- Safe backup/rollback before writing config files
|
|
32
36
|
|
|
@@ -36,36 +40,42 @@ It provides both:
|
|
|
36
40
|
npm install -g march-control-cli
|
|
37
41
|
```
|
|
38
42
|
|
|
43
|
+
If `ccman` command already exists from another package, uninstall it first:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm uninstall -g ccman
|
|
47
|
+
```
|
|
48
|
+
|
|
39
49
|
Install now includes:
|
|
40
50
|
|
|
41
51
|
- Chinese postinstall banner with quick-start commands
|
|
42
52
|
- Branded setup flow with step-by-step output (`[1/4] ... [4/4]`)
|
|
43
|
-
- Dual command entrypoints: `march` and `
|
|
53
|
+
- Dual command entrypoints: `march`, `mch`, and `ccman`
|
|
44
54
|
|
|
45
55
|
## Quick Start
|
|
46
56
|
|
|
47
57
|
Interactive quick setup:
|
|
48
58
|
|
|
49
59
|
```bash
|
|
50
|
-
|
|
60
|
+
ccman
|
|
51
61
|
```
|
|
52
62
|
|
|
53
63
|
Non-interactive setup:
|
|
54
64
|
|
|
55
65
|
```bash
|
|
56
|
-
|
|
66
|
+
ccman setup -k sk-your-key
|
|
57
67
|
|
|
58
68
|
# Setup using a preset
|
|
59
|
-
|
|
69
|
+
ccman setup --preset march -k sk-your-key
|
|
60
70
|
```
|
|
61
71
|
|
|
62
72
|
Open full interactive menu:
|
|
63
73
|
|
|
64
74
|
```bash
|
|
65
|
-
|
|
75
|
+
ccman menu
|
|
66
76
|
|
|
67
77
|
# same command alias
|
|
68
|
-
|
|
78
|
+
march menu
|
|
69
79
|
```
|
|
70
80
|
|
|
71
81
|
## Command Examples
|
|
@@ -89,6 +99,12 @@ march preset list
|
|
|
89
99
|
march preset add -n team-a --provider-name teama --base-url https://example.com
|
|
90
100
|
march preset use team-a -k sk-xxx -p codex,opencode
|
|
91
101
|
march preset remove team-a
|
|
102
|
+
|
|
103
|
+
# FHL quick shortcuts (ccman fhl-like)
|
|
104
|
+
ccman fhl sk-xxx
|
|
105
|
+
ccman fhl --platform codex,openclaw
|
|
106
|
+
ccman fhlcode -k sk-xxx --no-probe
|
|
107
|
+
march fhl sk-xxx
|
|
92
108
|
```
|
|
93
109
|
|
|
94
110
|
Probe endpoints:
|
|
@@ -149,6 +165,7 @@ node scripts/postinstall.mjs
|
|
|
149
165
|
node core/index.js --help
|
|
150
166
|
node core/index.js preset --help
|
|
151
167
|
node core/index.js cx --help
|
|
168
|
+
node core/index.js fhl --help
|
|
152
169
|
```
|
|
153
170
|
|
|
154
171
|
隔离目录实测(不污染当前用户目录):
|
|
@@ -168,13 +185,14 @@ npm pack
|
|
|
168
185
|
npm uninstall -g march-control-cli
|
|
169
186
|
|
|
170
187
|
# 安装你刚打出来的 tgz 包(把版本号替换成当前文件名)
|
|
171
|
-
npm install -g .\\march-control-cli-0.1.
|
|
188
|
+
npm install -g .\\march-control-cli-0.1.4.tgz
|
|
172
189
|
|
|
173
190
|
# 客户侧常用验证命令
|
|
191
|
+
ccman --help
|
|
174
192
|
march --help
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
193
|
+
ccman fhl --help
|
|
194
|
+
ccman
|
|
195
|
+
ccman setup --preset march -k sk-demo-1234 --platforms codex --no-probe
|
|
178
196
|
```
|
|
179
197
|
|
|
180
198
|
## Desktop
|
package/core/constants.js
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
|
-
export const APP_NAME = "
|
|
2
|
-
export const DEFAULT_PROVIDER_NAME = "
|
|
1
|
+
export const APP_NAME = "ccman";
|
|
2
|
+
export const DEFAULT_PROVIDER_NAME = "fhl";
|
|
3
3
|
export const DEFAULT_BASE_URL = "https://gmncode.cn";
|
|
4
4
|
export const DEFAULT_OPENCLAW_BASE_URL = "https://gmncode.cn/v1";
|
|
5
5
|
export const DEFAULT_PRIMARY_MODEL = "gpt-5.4";
|
|
6
|
+
export const FHL_PROVIDER_NAME = "fhl";
|
|
7
|
+
export const FHL_BASE_URL = "https://gmn.chuangzuoli.com";
|
|
8
|
+
export const FHL_OPENCLAW_BASE_URL = "https://gmn.chuangzuoli.com";
|
|
9
|
+
export const FHL_CANDIDATE_BASE_URLS = [
|
|
10
|
+
"https://gmn.chuangzuoli.com",
|
|
11
|
+
"https://gmn.chuangzuoli.com/v1",
|
|
12
|
+
"https://gmn.chuangzuoli.com/api"
|
|
13
|
+
];
|
|
14
|
+
// Backward compatibility with previous naming.
|
|
15
|
+
export const GMN_PROVIDER_NAME = FHL_PROVIDER_NAME;
|
|
16
|
+
export const GMN_BASE_URL = FHL_BASE_URL;
|
|
17
|
+
export const GMN_OPENCLAW_BASE_URL = FHL_OPENCLAW_BASE_URL;
|
|
18
|
+
export const GMN_CANDIDATE_BASE_URLS = FHL_CANDIDATE_BASE_URLS;
|
|
6
19
|
|
|
7
20
|
export const PLATFORM_META = {
|
|
8
21
|
codex: {
|
|
@@ -29,7 +42,7 @@ export const DEFAULT_CANDIDATE_BASE_URLS = [DEFAULT_BASE_URL];
|
|
|
29
42
|
export const BUILTIN_PRESETS = [
|
|
30
43
|
{
|
|
31
44
|
name: "march",
|
|
32
|
-
providerName: "
|
|
45
|
+
providerName: "fhl",
|
|
33
46
|
commonBaseUrl: "https://gmncode.cn",
|
|
34
47
|
openclawBaseUrl: "https://gmncode.cn/v1",
|
|
35
48
|
model: DEFAULT_PRIMARY_MODEL
|
|
@@ -40,6 +53,20 @@ export const BUILTIN_PRESETS = [
|
|
|
40
53
|
commonBaseUrl: "https://api.openai.com/v1",
|
|
41
54
|
openclawBaseUrl: "https://api.openai.com/v1",
|
|
42
55
|
model: DEFAULT_PRIMARY_MODEL
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "gmn",
|
|
59
|
+
providerName: FHL_PROVIDER_NAME,
|
|
60
|
+
commonBaseUrl: FHL_BASE_URL,
|
|
61
|
+
openclawBaseUrl: FHL_OPENCLAW_BASE_URL,
|
|
62
|
+
model: DEFAULT_PRIMARY_MODEL
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "fhl",
|
|
66
|
+
providerName: FHL_PROVIDER_NAME,
|
|
67
|
+
commonBaseUrl: FHL_BASE_URL,
|
|
68
|
+
openclawBaseUrl: FHL_OPENCLAW_BASE_URL,
|
|
69
|
+
model: DEFAULT_PRIMARY_MODEL
|
|
43
70
|
}
|
|
44
71
|
];
|
|
45
72
|
|
package/core/desktop-service.js
CHANGED
package/core/index.js
CHANGED
|
@@ -17,6 +17,10 @@ import {
|
|
|
17
17
|
DEFAULT_PLATFORM_SELECTION,
|
|
18
18
|
DEFAULT_PRIMARY_MODEL,
|
|
19
19
|
DEFAULT_PROVIDER_NAME,
|
|
20
|
+
FHL_BASE_URL,
|
|
21
|
+
FHL_CANDIDATE_BASE_URLS,
|
|
22
|
+
FHL_OPENCLAW_BASE_URL,
|
|
23
|
+
FHL_PROVIDER_NAME,
|
|
20
24
|
PLATFORM_META,
|
|
21
25
|
SUPPORTED_PLATFORMS
|
|
22
26
|
} from "./constants.js";
|
|
@@ -32,6 +36,17 @@ import {
|
|
|
32
36
|
import { getBestProbeResult, probeBaseUrls } from "./probe.js";
|
|
33
37
|
import { getPreset, listPresets, removePreset, upsertPreset } from "./presets.js";
|
|
34
38
|
|
|
39
|
+
const CLI_ENTRY_NAME = (() => {
|
|
40
|
+
const script = `${process.argv[1] || ""}`.toLowerCase();
|
|
41
|
+
if (script.includes("ccman")) {
|
|
42
|
+
return "ccman";
|
|
43
|
+
}
|
|
44
|
+
if (script.includes("mch")) {
|
|
45
|
+
return "mch";
|
|
46
|
+
}
|
|
47
|
+
return APP_NAME;
|
|
48
|
+
})();
|
|
49
|
+
|
|
35
50
|
function collect(value, previous) {
|
|
36
51
|
previous.push(value);
|
|
37
52
|
return previous;
|
|
@@ -51,7 +66,7 @@ function printBrandBanner() {
|
|
|
51
66
|
for (const line of logo) {
|
|
52
67
|
console.log(chalk.cyanBright(`│ ${line.padEnd(58, " ")}│`));
|
|
53
68
|
}
|
|
54
|
-
console.log(chalk.yellow("│
|
|
69
|
+
console.log(chalk.yellow("│ FHL 配置管理工具 │"));
|
|
55
70
|
console.log(chalk.gray("│ Codex / OpenCode / OpenClaw 一键管理 │"));
|
|
56
71
|
console.log(chalk.gray("└────────────────────────────────────────────────────────────┘"));
|
|
57
72
|
}
|
|
@@ -105,9 +120,9 @@ function printSetupPlan({
|
|
|
105
120
|
|
|
106
121
|
function printNextActions() {
|
|
107
122
|
printHeader("下一步建议");
|
|
108
|
-
console.log(
|
|
109
|
-
console.log(
|
|
110
|
-
console.log(
|
|
123
|
+
console.log(`1) ${CLI_ENTRY_NAME} menu`);
|
|
124
|
+
console.log(`2) ${CLI_ENTRY_NAME} cx list`);
|
|
125
|
+
console.log(`3) ${CLI_ENTRY_NAME} preset list`);
|
|
111
126
|
}
|
|
112
127
|
|
|
113
128
|
function printProviderSummary(platform, provider) {
|
|
@@ -200,6 +215,19 @@ function parsePlatforms(platformsArg) {
|
|
|
200
215
|
return resolved;
|
|
201
216
|
}
|
|
202
217
|
|
|
218
|
+
function parseShortcutPlatforms(platformsArg) {
|
|
219
|
+
if (!platformsArg) {
|
|
220
|
+
return [...DEFAULT_PLATFORM_SELECTION];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const values = parseCommaList(platformsArg).map((value) => value.toLowerCase());
|
|
224
|
+
if (values.includes("all")) {
|
|
225
|
+
return [...DEFAULT_PLATFORM_SELECTION];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return parsePlatforms(values.join(","));
|
|
229
|
+
}
|
|
230
|
+
|
|
203
231
|
async function resolveBaseUrl(label, rawOptions) {
|
|
204
232
|
const shouldProbe = rawOptions.probe !== false;
|
|
205
233
|
const confirmChoice = rawOptions.confirmChoice === true;
|
|
@@ -341,7 +369,7 @@ async function runSetup(rawOptions = {}) {
|
|
|
341
369
|
console.log(chalk.gray(`使用预设: ${options._resolvedPreset.name}`));
|
|
342
370
|
}
|
|
343
371
|
} else {
|
|
344
|
-
printHeader("
|
|
372
|
+
printHeader("FHL 命令行安装");
|
|
345
373
|
if (options._resolvedPreset) {
|
|
346
374
|
console.log(chalk.gray(`使用预设: ${options._resolvedPreset.name}`));
|
|
347
375
|
}
|
|
@@ -375,6 +403,8 @@ async function runSetup(rawOptions = {}) {
|
|
|
375
403
|
: parsePlatforms(options.platforms);
|
|
376
404
|
|
|
377
405
|
const apiKey = await promptForApiKey(options.apiKey);
|
|
406
|
+
const defaultCandidateBaseUrls = options.defaultCandidateBaseUrls ?? DEFAULT_CANDIDATE_BASE_URLS;
|
|
407
|
+
const defaultOpenclawCandidateBaseUrls = options.defaultOpenclawCandidateBaseUrls ?? [];
|
|
378
408
|
printSetupStep(
|
|
379
409
|
2,
|
|
380
410
|
"检测可用地址",
|
|
@@ -384,7 +414,7 @@ async function runSetup(rawOptions = {}) {
|
|
|
384
414
|
interactive,
|
|
385
415
|
confirmChoice: advanced,
|
|
386
416
|
probe: options.probe,
|
|
387
|
-
candidates: [providerMeta.commonBaseUrl, ...
|
|
417
|
+
candidates: [providerMeta.commonBaseUrl, ...defaultCandidateBaseUrls, ...(options.candidateBaseUrls || [])]
|
|
388
418
|
});
|
|
389
419
|
const openclawBaseUrl = platforms.includes("openclaw")
|
|
390
420
|
? await resolveBaseUrl("OpenClaw", {
|
|
@@ -393,6 +423,7 @@ async function runSetup(rawOptions = {}) {
|
|
|
393
423
|
probe: options.probe,
|
|
394
424
|
candidates: [
|
|
395
425
|
providerMeta.openclawBaseUrl || buildOpenClawBaseUrl(commonBaseUrl),
|
|
426
|
+
...defaultOpenclawCandidateBaseUrls,
|
|
396
427
|
...(options.openclawCandidateBaseUrls || [])
|
|
397
428
|
]
|
|
398
429
|
})
|
|
@@ -450,6 +481,41 @@ async function runSetup(rawOptions = {}) {
|
|
|
450
481
|
printNextActions();
|
|
451
482
|
}
|
|
452
483
|
|
|
484
|
+
async function runFhlShortcut(apiKeyArg, rawOptions = {}) {
|
|
485
|
+
const apiKey = `${apiKeyArg || rawOptions.apiKey || ""}`.trim();
|
|
486
|
+
const providerName = `${rawOptions.name || rawOptions.providerName || FHL_PROVIDER_NAME}`.trim() || FHL_PROVIDER_NAME;
|
|
487
|
+
const platforms = parseShortcutPlatforms(rawOptions.platform || "all");
|
|
488
|
+
const commonBaseUrl = normalizeBaseUrl(rawOptions.baseUrl || FHL_BASE_URL);
|
|
489
|
+
const openclawBaseUrl = normalizeBaseUrl(rawOptions.openclawBaseUrl || rawOptions.baseUrl || FHL_OPENCLAW_BASE_URL);
|
|
490
|
+
const commonCandidates = dedupeStrings([
|
|
491
|
+
commonBaseUrl,
|
|
492
|
+
...(rawOptions.baseUrl ? [] : FHL_CANDIDATE_BASE_URLS),
|
|
493
|
+
...(rawOptions.candidateBaseUrl || [])
|
|
494
|
+
]);
|
|
495
|
+
const openclawCandidates = dedupeStrings([
|
|
496
|
+
openclawBaseUrl,
|
|
497
|
+
...(rawOptions.openclawBaseUrl ? [] : FHL_CANDIDATE_BASE_URLS),
|
|
498
|
+
...(rawOptions.openclawCandidateBaseUrl || [])
|
|
499
|
+
]);
|
|
500
|
+
|
|
501
|
+
await runSetup({
|
|
502
|
+
interactive: false,
|
|
503
|
+
apiKey,
|
|
504
|
+
platforms: platforms.join(","),
|
|
505
|
+
providerName,
|
|
506
|
+
baseUrl: commonBaseUrl,
|
|
507
|
+
openclawBaseUrl,
|
|
508
|
+
model: rawOptions.model || DEFAULT_PRIMARY_MODEL,
|
|
509
|
+
probe: rawOptions.probe,
|
|
510
|
+
backup: rawOptions.backup,
|
|
511
|
+
overwrite: rawOptions.merge === true ? false : true,
|
|
512
|
+
candidateBaseUrls: commonCandidates,
|
|
513
|
+
openclawCandidateBaseUrls: openclawCandidates,
|
|
514
|
+
defaultCandidateBaseUrls: [],
|
|
515
|
+
defaultOpenclawCandidateBaseUrls: []
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
|
|
453
519
|
async function runAdd(platform, rawOptions = {}) {
|
|
454
520
|
const interactive = rawOptions.interactive !== false;
|
|
455
521
|
const meta = PLATFORM_META[platform];
|
|
@@ -993,7 +1059,7 @@ async function runMainMenu() {
|
|
|
993
1059
|
{
|
|
994
1060
|
type: "list",
|
|
995
1061
|
name: "action",
|
|
996
|
-
message: "
|
|
1062
|
+
message: "FHL 主菜单",
|
|
997
1063
|
choices: [
|
|
998
1064
|
{ name: "快速安装", value: "setup" },
|
|
999
1065
|
{ name: "自定义安装", value: "advanced-setup" },
|
|
@@ -1032,9 +1098,9 @@ async function runMainMenu() {
|
|
|
1032
1098
|
}
|
|
1033
1099
|
const program = new Command();
|
|
1034
1100
|
program
|
|
1035
|
-
.name(
|
|
1036
|
-
.description("
|
|
1037
|
-
.version("0.1.
|
|
1101
|
+
.name(CLI_ENTRY_NAME)
|
|
1102
|
+
.description("FHL 服务商配置管理工具")
|
|
1103
|
+
.version("0.1.5");
|
|
1038
1104
|
|
|
1039
1105
|
program
|
|
1040
1106
|
.command("menu")
|
|
@@ -1076,6 +1142,34 @@ program
|
|
|
1076
1142
|
});
|
|
1077
1143
|
});
|
|
1078
1144
|
|
|
1145
|
+
function registerFhlShortcutCommand(commandName) {
|
|
1146
|
+
program
|
|
1147
|
+
.command(`${commandName} [apiKey]`)
|
|
1148
|
+
.description("快捷配置 FHL 到 Codex / OpenCode / OpenClaw(默认覆盖写入)")
|
|
1149
|
+
.option("-k, --api-key <apiKey>", "API Key(可与位置参数二选一)")
|
|
1150
|
+
.option("-p, --platform <platforms>", "指定平台:codex,opencode,openclaw,all", "all")
|
|
1151
|
+
.option("-n, --name <providerName>", "服务商名称", FHL_PROVIDER_NAME)
|
|
1152
|
+
.option("-b, --base-url <baseUrl>", "指定 Codex/OpenCode Base URL(默认自动测速)")
|
|
1153
|
+
.option("--openclaw-base-url <baseUrl>", "指定 OpenClaw Base URL")
|
|
1154
|
+
.option("--model <model>", "模型", DEFAULT_PRIMARY_MODEL)
|
|
1155
|
+
.option("--candidate-base-url <url>", "额外 Codex/OpenCode 备选地址", collect, [])
|
|
1156
|
+
.option("--openclaw-candidate-base-url <url>", "额外 OpenClaw 备选地址", collect, [])
|
|
1157
|
+
.option("--merge", "改为合并写入(默认覆盖写入)")
|
|
1158
|
+
.option("--no-probe", "跳过地址测速")
|
|
1159
|
+
.option("--no-backup", "跳过自动备份")
|
|
1160
|
+
.action(async (apiKey, options) => {
|
|
1161
|
+
await runFhlShortcut(apiKey, options);
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
registerFhlShortcutCommand("fhl");
|
|
1166
|
+
registerFhlShortcutCommand("fhl1");
|
|
1167
|
+
registerFhlShortcutCommand("fhlcode");
|
|
1168
|
+
// Backward compatibility
|
|
1169
|
+
registerFhlShortcutCommand("gmn");
|
|
1170
|
+
registerFhlShortcutCommand("gmn1");
|
|
1171
|
+
registerFhlShortcutCommand("gmncode");
|
|
1172
|
+
|
|
1079
1173
|
program
|
|
1080
1174
|
.command("probe")
|
|
1081
1175
|
.description("测速一个或多个地址")
|
package/desktop/main.js
CHANGED
|
@@ -69,6 +69,12 @@ async function loadRenderer(window) {
|
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
const externalDistIndex = path.join(path.dirname(process.execPath), "dist", "index.html");
|
|
73
|
+
if (await exists(externalDistIndex)) {
|
|
74
|
+
await window.loadFile(externalDistIndex);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
72
78
|
const appRoot = app.getAppPath();
|
|
73
79
|
const distIndex = path.join(appRoot, "dist", "index.html");
|
|
74
80
|
|
|
@@ -90,7 +96,7 @@ function createWindow() {
|
|
|
90
96
|
autoHideMenuBar: true,
|
|
91
97
|
title: "March 控制台",
|
|
92
98
|
webPreferences: {
|
|
93
|
-
preload: path.join(__dirname, "preload.
|
|
99
|
+
preload: path.join(__dirname, "preload.cjs"),
|
|
94
100
|
contextIsolation: true,
|
|
95
101
|
nodeIntegration: false
|
|
96
102
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const { contextBridge, ipcRenderer } = require("electron");
|
|
2
|
+
|
|
3
|
+
contextBridge.exposeInMainWorld("marchDesktop", {
|
|
4
|
+
getSnapshot() {
|
|
5
|
+
return ipcRenderer.invoke("desktop:getSnapshot");
|
|
6
|
+
},
|
|
7
|
+
saveProvider(payload) {
|
|
8
|
+
return ipcRenderer.invoke("desktop:saveProvider", payload);
|
|
9
|
+
},
|
|
10
|
+
activateProvider(payload) {
|
|
11
|
+
return ipcRenderer.invoke("desktop:activateProvider", payload);
|
|
12
|
+
},
|
|
13
|
+
probePlatform(payload) {
|
|
14
|
+
return ipcRenderer.invoke("desktop:probePlatform", payload);
|
|
15
|
+
},
|
|
16
|
+
probeCandidate(payload) {
|
|
17
|
+
return ipcRenderer.invoke("desktop:probeCandidate", payload);
|
|
18
|
+
},
|
|
19
|
+
toggleMcp(payload) {
|
|
20
|
+
return ipcRenderer.invoke("desktop:toggleMcp", payload);
|
|
21
|
+
},
|
|
22
|
+
upsertMcp(payload) {
|
|
23
|
+
return ipcRenderer.invoke("desktop:upsertMcp", payload);
|
|
24
|
+
},
|
|
25
|
+
deleteMcp(payload) {
|
|
26
|
+
return ipcRenderer.invoke("desktop:deleteMcp", payload);
|
|
27
|
+
},
|
|
28
|
+
togglePrompt(payload) {
|
|
29
|
+
return ipcRenderer.invoke("desktop:togglePrompt", payload);
|
|
30
|
+
},
|
|
31
|
+
upsertPrompt(payload) {
|
|
32
|
+
return ipcRenderer.invoke("desktop:upsertPrompt", payload);
|
|
33
|
+
},
|
|
34
|
+
deletePrompt(payload) {
|
|
35
|
+
return ipcRenderer.invoke("desktop:deletePrompt", payload);
|
|
36
|
+
},
|
|
37
|
+
upsertSkill(payload) {
|
|
38
|
+
return ipcRenderer.invoke("desktop:upsertSkill", payload);
|
|
39
|
+
},
|
|
40
|
+
deleteSkill(payload) {
|
|
41
|
+
return ipcRenderer.invoke("desktop:deleteSkill", payload);
|
|
42
|
+
},
|
|
43
|
+
toggleSkillRepo(payload) {
|
|
44
|
+
return ipcRenderer.invoke("desktop:toggleSkillRepo", payload);
|
|
45
|
+
},
|
|
46
|
+
openPath(targetPath) {
|
|
47
|
+
return ipcRenderer.invoke("desktop:openPath", targetPath);
|
|
48
|
+
}
|
|
49
|
+
});
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "march-control-cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "FHL Control desktop-style manager for Codex, OpenCode, and OpenClaw.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "desktop/main.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"march": "core/index.js",
|
|
9
|
-
"mch": "core/index.js"
|
|
9
|
+
"mch": "core/index.js",
|
|
10
|
+
"ccman": "core/index.js"
|
|
10
11
|
},
|
|
11
12
|
"files": [
|
|
12
13
|
"core",
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
"start": "node ./core/index.js",
|
|
26
27
|
"check": "node --check ./core/index.js",
|
|
27
28
|
"desktop:dev": "node ./scripts/desktop-dev.mjs",
|
|
28
|
-
"desktop:check": "node --check ./desktop/main.js && node --check ./desktop/preload.
|
|
29
|
+
"desktop:check": "node --check ./desktop/main.js && node --check ./desktop/preload.cjs && node --check ./desktop/renderer/app.js && node --check ./core/desktop-service.js",
|
|
29
30
|
"desktop:install-runtime": "node ./node_modules/electron/install.js",
|
|
30
31
|
"desktop:pack": "npm run build:web && electron-builder --win nsis portable",
|
|
31
32
|
"site:dev": "node ./scripts/serve-site.mjs",
|
|
@@ -45,7 +46,7 @@
|
|
|
45
46
|
"config",
|
|
46
47
|
"cli"
|
|
47
48
|
],
|
|
48
|
-
"author": "
|
|
49
|
+
"author": "FHL Team",
|
|
49
50
|
"license": "MIT",
|
|
50
51
|
"engines": {
|
|
51
52
|
"node": ">=18.0.0"
|
|
@@ -76,7 +77,7 @@
|
|
|
76
77
|
},
|
|
77
78
|
"build": {
|
|
78
79
|
"appId": "cn.gmn.march.desktop",
|
|
79
|
-
"productName": "
|
|
80
|
+
"productName": "FHL-Control",
|
|
80
81
|
"npmRebuild": false,
|
|
81
82
|
"directories": {
|
|
82
83
|
"output": "release"
|
|
@@ -95,7 +96,7 @@
|
|
|
95
96
|
"nsis",
|
|
96
97
|
"portable"
|
|
97
98
|
],
|
|
98
|
-
"artifactName": "
|
|
99
|
+
"artifactName": "FHL-Control-${version}-${arch}.${ext}"
|
|
99
100
|
},
|
|
100
101
|
"nsis": {
|
|
101
102
|
"oneClick": false,
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const lines = [
|
|
2
2
|
"",
|
|
3
3
|
"============================================================",
|
|
4
|
-
"
|
|
4
|
+
" FHL 安装完成",
|
|
5
5
|
"============================================================",
|
|
6
6
|
"",
|
|
7
7
|
" __ __ _ ____ ____ _ _ ",
|
|
@@ -10,15 +10,15 @@ const lines = [
|
|
|
10
10
|
"| | | |/ ___ \\| _ <| |___| _ |",
|
|
11
11
|
"|_| |_/_/ \\_\\_| \\_\\\\____|_| |_|",
|
|
12
12
|
"",
|
|
13
|
-
"欢迎使用
|
|
13
|
+
"欢迎使用 FHL 配置管理工具",
|
|
14
14
|
"",
|
|
15
15
|
"快速开始:",
|
|
16
16
|
" march",
|
|
17
|
-
"
|
|
17
|
+
" ccman fhl sk-xxxx",
|
|
18
18
|
"",
|
|
19
19
|
"常用命令:",
|
|
20
20
|
" march --help",
|
|
21
|
-
"
|
|
21
|
+
" ccman fhl --help",
|
|
22
22
|
" march preset --help",
|
|
23
23
|
""
|
|
24
24
|
];
|
package/scripts/serve-site.mjs
CHANGED
package/src/App.tsx
CHANGED
|
@@ -18,6 +18,8 @@ import type { PlatformId, Provider } from "./types";
|
|
|
18
18
|
|
|
19
19
|
const emptyForm = { name: "", baseUrl: "", apiKey: "", model: "gpt-5.4" };
|
|
20
20
|
const allPlatforms: PlatformId[] = ["codex", "opencode", "openclaw"];
|
|
21
|
+
const OPENAI_ASSET = `${import.meta.env.BASE_URL}assets/openai-small.svg`;
|
|
22
|
+
const OPENCLAW_ASSET = `${import.meta.env.BASE_URL}assets/pixel-lobster.svg`;
|
|
21
23
|
|
|
22
24
|
type McpEditorForm = {
|
|
23
25
|
kind: "mcp";
|
|
@@ -84,16 +86,16 @@ function shortUrl(value: string) {
|
|
|
84
86
|
function providerArt(provider: Provider, platformId: PlatformId) {
|
|
85
87
|
const name = provider.name.toLowerCase();
|
|
86
88
|
const url = provider.baseUrl.toLowerCase();
|
|
87
|
-
if (name.includes("openai") || url.includes("openai.com")) return { src:
|
|
89
|
+
if (name.includes("openai") || url.includes("openai.com")) return { src: OPENAI_ASSET, alt: "OpenAI" };
|
|
88
90
|
if (platformId === "openclaw" || name.includes("openclaw") || name.includes("lobster")) {
|
|
89
|
-
return { src:
|
|
91
|
+
return { src: OPENCLAW_ASSET, alt: "OpenClaw" };
|
|
90
92
|
}
|
|
91
93
|
return null;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
function platformArt(platformId: PlatformId) {
|
|
95
|
-
if (platformId === "codex") return <img src=
|
|
96
|
-
if (platformId === "openclaw") return <img src=
|
|
97
|
+
if (platformId === "codex") return <img src={OPENAI_ASSET} alt="Codex" className="h-5 w-5 object-contain" />;
|
|
98
|
+
if (platformId === "openclaw") return <img src={OPENCLAW_ASSET} alt="OpenClaw" className="h-5 w-5 object-contain" />;
|
|
97
99
|
return <Bot className="h-4 w-4 text-slate-600" />;
|
|
98
100
|
}
|
|
99
101
|
|