march-control-cli 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +220 -0
- package/core/apply.js +152 -0
- package/core/backup.js +53 -0
- package/core/constants.js +55 -0
- package/core/desktop-service.js +219 -0
- package/core/desktop-state.js +511 -0
- package/core/index.js +1293 -0
- package/core/paths.js +71 -0
- package/core/presets.js +171 -0
- package/core/probe.js +70 -0
- package/core/store.js +218 -0
- package/core/utils.js +178 -0
- package/core/writers/codex.js +102 -0
- package/core/writers/index.js +16 -0
- package/core/writers/openclaw.js +93 -0
- package/core/writers/opencode.js +91 -0
- package/desktop/assets/march-mark.svg +21 -0
- package/desktop/main.js +192 -0
- package/desktop/preload.js +49 -0
- package/desktop/renderer/app.js +327 -0
- package/desktop/renderer/index.html +130 -0
- package/desktop/renderer/styles.css +413 -0
- package/package.json +106 -0
- package/scripts/desktop-dev.mjs +90 -0
- package/scripts/postinstall.mjs +28 -0
- package/scripts/serve-site.mjs +51 -0
- package/site/app.js +10 -0
- package/site/assets/march-mark.svg +22 -0
- package/site/index.html +286 -0
- package/site/styles.css +566 -0
- package/src/App.tsx +1186 -0
- package/src/components/layout/app-sidebar.tsx +103 -0
- package/src/components/layout/top-toolbar.tsx +44 -0
- package/src/components/layout/workspace-tabs.tsx +32 -0
- package/src/components/providers/inspector-panel.tsx +84 -0
- package/src/components/providers/metric-strip.tsx +26 -0
- package/src/components/providers/provider-editor.tsx +87 -0
- package/src/components/providers/provider-table.tsx +85 -0
- package/src/components/ui/logo-mark.tsx +16 -0
- package/src/features/mcp/mcp-view.tsx +45 -0
- package/src/features/prompts/prompts-view.tsx +40 -0
- package/src/features/providers/providers-view.tsx +40 -0
- package/src/features/providers/types.ts +8 -0
- package/src/features/skills/skills-view.tsx +44 -0
- package/src/hooks/use-control-workspace.ts +184 -0
- package/src/index.css +22 -0
- package/src/lib/client.ts +944 -0
- package/src/lib/query-client.ts +3 -0
- package/src/lib/workspace-sections.ts +34 -0
- package/src/main.tsx +14 -0
- package/src/types.ts +76 -0
- package/src/vite-env.d.ts +56 -0
- package/src-tauri/README.md +11 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const skills = [
|
|
2
|
+
{ name: "openai-docs", category: "System", status: "Installed" },
|
|
3
|
+
{ name: "plugin-creator", category: "System", status: "Installed" },
|
|
4
|
+
{ name: "skill-installer", category: "System", status: "Installed" },
|
|
5
|
+
{ name: "xianyu-keyword-research", category: "Business", status: "Installed" }
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
export function SkillsView() {
|
|
9
|
+
return (
|
|
10
|
+
<section className="mt-4 grid min-h-0 flex-1 grid-cols-[1fr_1fr] gap-4">
|
|
11
|
+
<article className="rounded-[24px] border border-white/6 bg-white/[0.025] p-5">
|
|
12
|
+
<div className="text-[11px] uppercase tracking-[0.32em] text-slate-500">Skill Registry</div>
|
|
13
|
+
<h2 className="mt-2 font-['Space_Grotesk'] text-[22px] font-bold tracking-[-0.04em] text-slate-50">
|
|
14
|
+
Installed skills
|
|
15
|
+
</h2>
|
|
16
|
+
<div className="mt-5 space-y-3">
|
|
17
|
+
{skills.map((item) => (
|
|
18
|
+
<div key={item.name} className="flex items-center justify-between rounded-[20px] border border-white/6 bg-white/[0.03] px-4 py-4">
|
|
19
|
+
<div>
|
|
20
|
+
<div className="text-sm font-medium text-slate-100">{item.name}</div>
|
|
21
|
+
<div className="mt-1 text-xs text-slate-500">{item.category}</div>
|
|
22
|
+
</div>
|
|
23
|
+
<div className="rounded-full bg-cyan-400/12 px-2.5 py-1 text-[11px] text-cyan-200">{item.status}</div>
|
|
24
|
+
</div>
|
|
25
|
+
))}
|
|
26
|
+
</div>
|
|
27
|
+
</article>
|
|
28
|
+
|
|
29
|
+
<article className="rounded-[24px] border border-white/6 bg-white/[0.025] p-5">
|
|
30
|
+
<div className="text-[11px] uppercase tracking-[0.32em] text-slate-500">Skill Runtime</div>
|
|
31
|
+
<h2 className="mt-2 font-['Space_Grotesk'] text-[22px] font-bold tracking-[-0.04em] text-slate-50">
|
|
32
|
+
Load behavior
|
|
33
|
+
</h2>
|
|
34
|
+
<div className="mt-5 space-y-3">
|
|
35
|
+
{["Trigger rules", "Skill path resolution", "Progressive disclosure", "Context hygiene"].map((item) => (
|
|
36
|
+
<div key={item} className="rounded-[18px] border border-white/6 bg-white/[0.03] px-4 py-3 text-sm text-slate-300">
|
|
37
|
+
{item}
|
|
38
|
+
</div>
|
|
39
|
+
))}
|
|
40
|
+
</div>
|
|
41
|
+
</article>
|
|
42
|
+
</section>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
|
+
import { client } from "../lib/client";
|
|
4
|
+
import { workspaceSections, type WorkspaceSectionId } from "../lib/workspace-sections";
|
|
5
|
+
import type { PlatformId, Provider } from "../types";
|
|
6
|
+
|
|
7
|
+
export function useControlWorkspace() {
|
|
8
|
+
const queryClient = useQueryClient();
|
|
9
|
+
const [activePlatform, setActivePlatform] = useState<PlatformId>("codex");
|
|
10
|
+
const [activeSection, setActiveSection] = useState<WorkspaceSectionId>("providers");
|
|
11
|
+
const [editorForm, setEditorForm] = useState({
|
|
12
|
+
name: "March CN",
|
|
13
|
+
baseUrl: "https://gmncode.cn",
|
|
14
|
+
apiKey: "",
|
|
15
|
+
model: "gpt-5.4"
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const snapshotQuery = useQuery({
|
|
19
|
+
queryKey: ["snapshot"],
|
|
20
|
+
queryFn: client.getSnapshot
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const probeQuery = useQuery({
|
|
24
|
+
queryKey: ["probe", activePlatform],
|
|
25
|
+
queryFn: () => client.probePlatform(activePlatform),
|
|
26
|
+
enabled: false
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const saveMutation = useMutation({
|
|
30
|
+
mutationFn: client.saveProvider,
|
|
31
|
+
onSuccess: (data) => {
|
|
32
|
+
queryClient.setQueryData(["snapshot"], data);
|
|
33
|
+
setEditorForm((current) => ({ ...current, apiKey: "" }));
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const activateMutation = useMutation({
|
|
38
|
+
mutationFn: client.activateProvider,
|
|
39
|
+
onSuccess: (data) => {
|
|
40
|
+
queryClient.setQueryData(["snapshot"], data);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const probeCandidateMutation = useMutation({
|
|
45
|
+
mutationFn: client.probeCandidate
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const openPathMutation = useMutation({
|
|
49
|
+
mutationFn: client.openPath
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const toggleMcpMutation = useMutation({
|
|
53
|
+
mutationFn: client.toggleMcpServer,
|
|
54
|
+
onSuccess: (data) => {
|
|
55
|
+
queryClient.setQueryData(["snapshot"], data);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const upsertMcpMutation = useMutation({
|
|
60
|
+
mutationFn: client.upsertMcp,
|
|
61
|
+
onSuccess: (data) => {
|
|
62
|
+
queryClient.setQueryData(["snapshot"], data);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const deleteMcpMutation = useMutation({
|
|
67
|
+
mutationFn: client.deleteMcp,
|
|
68
|
+
onSuccess: (data) => {
|
|
69
|
+
queryClient.setQueryData(["snapshot"], data);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const togglePromptMutation = useMutation({
|
|
74
|
+
mutationFn: client.togglePrompt,
|
|
75
|
+
onSuccess: (data) => {
|
|
76
|
+
queryClient.setQueryData(["snapshot"], data);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const upsertPromptMutation = useMutation({
|
|
81
|
+
mutationFn: client.upsertPrompt,
|
|
82
|
+
onSuccess: (data) => {
|
|
83
|
+
queryClient.setQueryData(["snapshot"], data);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const deletePromptMutation = useMutation({
|
|
88
|
+
mutationFn: client.deletePrompt,
|
|
89
|
+
onSuccess: (data) => {
|
|
90
|
+
queryClient.setQueryData(["snapshot"], data);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const upsertSkillMutation = useMutation({
|
|
95
|
+
mutationFn: client.upsertSkill,
|
|
96
|
+
onSuccess: (data) => {
|
|
97
|
+
queryClient.setQueryData(["snapshot"], data);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const deleteSkillMutation = useMutation({
|
|
102
|
+
mutationFn: client.deleteSkill,
|
|
103
|
+
onSuccess: (data) => {
|
|
104
|
+
queryClient.setQueryData(["snapshot"], data);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const toggleSkillRepoMutation = useMutation({
|
|
109
|
+
mutationFn: client.toggleSkillRepo,
|
|
110
|
+
onSuccess: (data) => {
|
|
111
|
+
queryClient.setQueryData(["snapshot"], data);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const snapshot = snapshotQuery.data;
|
|
116
|
+
const platform = snapshot?.platforms.find((item) => item.id === activePlatform) || snapshot?.platforms[0];
|
|
117
|
+
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
if (!platform) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const activeProvider = platform.providers.find((item) => item.isActive);
|
|
124
|
+
setEditorForm({
|
|
125
|
+
name: activeProvider?.name || "March CN",
|
|
126
|
+
baseUrl: activeProvider?.baseUrl || (platform.id === "openclaw" ? "https://gmncode.cn/v1" : "https://gmncode.cn"),
|
|
127
|
+
apiKey: "",
|
|
128
|
+
model: activeProvider?.model || (snapshot?.models[0] ?? "gpt-5.4")
|
|
129
|
+
});
|
|
130
|
+
}, [platform, snapshot?.models]);
|
|
131
|
+
|
|
132
|
+
const metrics = useMemo(() => {
|
|
133
|
+
if (!platform) {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return [
|
|
138
|
+
{ label: "当前客户端", value: platform.label, hint: "已选择工作区" },
|
|
139
|
+
{ label: "已保存服务商", value: `${platform.providerCount}`, hint: "可切换路由" },
|
|
140
|
+
{ label: "当前路由", value: platform.currentProviderName || "无", hint: "正在生效" },
|
|
141
|
+
{ label: "目标文件", value: `${platform.targetFiles.length}`, hint: "关联本地配置文件" }
|
|
142
|
+
];
|
|
143
|
+
}, [platform]);
|
|
144
|
+
|
|
145
|
+
const activeSectionMeta = workspaceSections.find((item) => item.id === activeSection) || workspaceSections[0];
|
|
146
|
+
|
|
147
|
+
function openProvider(provider: Provider) {
|
|
148
|
+
setEditorForm({
|
|
149
|
+
name: provider.name,
|
|
150
|
+
baseUrl: provider.baseUrl,
|
|
151
|
+
apiKey: "",
|
|
152
|
+
model: provider.model
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
activePlatform,
|
|
158
|
+
setActivePlatform,
|
|
159
|
+
activeSection,
|
|
160
|
+
setActiveSection,
|
|
161
|
+
activeSectionMeta,
|
|
162
|
+
editorForm,
|
|
163
|
+
setEditorForm,
|
|
164
|
+
snapshot,
|
|
165
|
+
platform,
|
|
166
|
+
metrics,
|
|
167
|
+
snapshotQuery,
|
|
168
|
+
probeQuery,
|
|
169
|
+
saveMutation,
|
|
170
|
+
activateMutation,
|
|
171
|
+
probeCandidateMutation,
|
|
172
|
+
openPathMutation,
|
|
173
|
+
toggleMcpMutation,
|
|
174
|
+
upsertMcpMutation,
|
|
175
|
+
deleteMcpMutation,
|
|
176
|
+
togglePromptMutation,
|
|
177
|
+
upsertPromptMutation,
|
|
178
|
+
deletePromptMutation,
|
|
179
|
+
upsertSkillMutation,
|
|
180
|
+
deleteSkillMutation,
|
|
181
|
+
toggleSkillRepoMutation,
|
|
182
|
+
openProvider
|
|
183
|
+
};
|
|
184
|
+
}
|
package/src/index.css
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
color: #0f172a;
|
|
5
|
+
background:
|
|
6
|
+
radial-gradient(circle at top left, rgba(37, 99, 235, 0.05), transparent 18%),
|
|
7
|
+
linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
|
|
8
|
+
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
body {
|
|
12
|
+
margin: 0;
|
|
13
|
+
min-width: 320px;
|
|
14
|
+
min-height: 100vh;
|
|
15
|
+
background: transparent;
|
|
16
|
+
-webkit-font-smoothing: antialiased;
|
|
17
|
+
-moz-osx-font-smoothing: grayscale;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
#root {
|
|
21
|
+
min-height: 100vh;
|
|
22
|
+
}
|