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,102 @@
|
|
|
1
|
+
import TOML from "@iarna/toml";
|
|
2
|
+
import { DEFAULT_PRIMARY_MODEL, DEFAULT_PROVIDER_NAME } from "../constants.js";
|
|
3
|
+
import { getCodexAuthPath, getCodexConfigPath, getCodexDir } from "../paths.js";
|
|
4
|
+
import {
|
|
5
|
+
deepMerge,
|
|
6
|
+
ensureDir,
|
|
7
|
+
isRecord,
|
|
8
|
+
providerKeyFromName,
|
|
9
|
+
readJson,
|
|
10
|
+
readText,
|
|
11
|
+
replaceCaseInsensitiveKey,
|
|
12
|
+
writeJson,
|
|
13
|
+
writeText
|
|
14
|
+
} from "../utils.js";
|
|
15
|
+
|
|
16
|
+
const DEFAULT_CODEX_CONFIG = {
|
|
17
|
+
model: DEFAULT_PRIMARY_MODEL,
|
|
18
|
+
model_reasoning_effort: "xhigh",
|
|
19
|
+
disable_response_storage: true,
|
|
20
|
+
sandbox_mode: "danger-full-access",
|
|
21
|
+
windows_wsl_setup_acknowledged: true,
|
|
22
|
+
approval_policy: "never",
|
|
23
|
+
profile: "auto-max",
|
|
24
|
+
file_opener: "vscode",
|
|
25
|
+
web_search: "cached",
|
|
26
|
+
suppress_unstable_features_warning: true,
|
|
27
|
+
history: {
|
|
28
|
+
persistence: "save-all"
|
|
29
|
+
},
|
|
30
|
+
tui: {
|
|
31
|
+
notifications: true
|
|
32
|
+
},
|
|
33
|
+
shell_environment_policy: {
|
|
34
|
+
inherit: "all",
|
|
35
|
+
ignore_default_excludes: false
|
|
36
|
+
},
|
|
37
|
+
sandbox_workspace_write: {
|
|
38
|
+
network_access: true
|
|
39
|
+
},
|
|
40
|
+
features: {
|
|
41
|
+
plan_tool: true,
|
|
42
|
+
apply_patch_freeform: true,
|
|
43
|
+
view_image_tool: true
|
|
44
|
+
},
|
|
45
|
+
profiles: {
|
|
46
|
+
"auto-max": {
|
|
47
|
+
approval_policy: "never",
|
|
48
|
+
sandbox_mode: "workspace-write"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function loadCodexConfig(configPath) {
|
|
54
|
+
const raw = readText(configPath, "");
|
|
55
|
+
if (!raw.trim()) {
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
return TOML.parse(raw);
|
|
61
|
+
} catch {
|
|
62
|
+
return {};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function buildManagedProvider(provider) {
|
|
67
|
+
return {
|
|
68
|
+
name: provider.name || DEFAULT_PROVIDER_NAME,
|
|
69
|
+
base_url: provider.baseUrl,
|
|
70
|
+
wire_api: "responses",
|
|
71
|
+
requires_openai_auth: true
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function writeCodexConfig(provider, options = {}) {
|
|
76
|
+
ensureDir(getCodexDir());
|
|
77
|
+
|
|
78
|
+
const configPath = getCodexConfigPath();
|
|
79
|
+
const authPath = getCodexAuthPath();
|
|
80
|
+
const providerKey = providerKeyFromName(provider.name);
|
|
81
|
+
const mode = options.mode === "overwrite" ? "overwrite" : "merge";
|
|
82
|
+
const baseConfig = mode === "overwrite" ? {} : loadCodexConfig(configPath);
|
|
83
|
+
const merged = deepMerge(DEFAULT_CODEX_CONFIG, isRecord(baseConfig) ? baseConfig : {});
|
|
84
|
+
const existingProviders =
|
|
85
|
+
mode === "overwrite" || !isRecord(merged.model_providers) ? {} : merged.model_providers;
|
|
86
|
+
|
|
87
|
+
merged.model = provider.model || DEFAULT_PRIMARY_MODEL;
|
|
88
|
+
merged.model_provider = providerKey;
|
|
89
|
+
merged.model_providers = replaceCaseInsensitiveKey(
|
|
90
|
+
existingProviders,
|
|
91
|
+
providerKey,
|
|
92
|
+
buildManagedProvider(provider)
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
writeText(configPath, TOML.stringify(merged));
|
|
96
|
+
|
|
97
|
+
const existingAuth = mode === "overwrite" ? {} : readJson(authPath, {});
|
|
98
|
+
writeJson(authPath, {
|
|
99
|
+
...existingAuth,
|
|
100
|
+
OPENAI_API_KEY: provider.apiKey
|
|
101
|
+
});
|
|
102
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { writeCodexConfig } from "./codex.js";
|
|
2
|
+
import { writeOpenClawConfig } from "./openclaw.js";
|
|
3
|
+
import { writeOpenCodeConfig } from "./opencode.js";
|
|
4
|
+
|
|
5
|
+
export function writePlatformConfig(platform, provider, options = {}) {
|
|
6
|
+
switch (platform) {
|
|
7
|
+
case "codex":
|
|
8
|
+
return writeCodexConfig(provider, options);
|
|
9
|
+
case "opencode":
|
|
10
|
+
return writeOpenCodeConfig(provider, options);
|
|
11
|
+
case "openclaw":
|
|
12
|
+
return writeOpenClawConfig(provider, options);
|
|
13
|
+
default:
|
|
14
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { DEFAULT_PRIMARY_MODEL, DEFAULT_PROVIDER_NAME, MODEL_DEFINITIONS } from "../constants.js";
|
|
3
|
+
import { getHomeDir, getOpenClawConfigPath, getOpenClawDir, getOpenClawModelsPath } from "../paths.js";
|
|
4
|
+
import {
|
|
5
|
+
ensureDir,
|
|
6
|
+
isRecord,
|
|
7
|
+
providerKeyFromName,
|
|
8
|
+
readJson,
|
|
9
|
+
replaceCaseInsensitiveKey,
|
|
10
|
+
writeJson
|
|
11
|
+
} from "../utils.js";
|
|
12
|
+
|
|
13
|
+
function buildOpenClawModels() {
|
|
14
|
+
return MODEL_DEFINITIONS.map((model) => ({
|
|
15
|
+
id: model.id,
|
|
16
|
+
name: model.id,
|
|
17
|
+
api: "openai-responses",
|
|
18
|
+
reasoning: model.reasoning,
|
|
19
|
+
input: ["text", "image"],
|
|
20
|
+
cost: {
|
|
21
|
+
input: 0,
|
|
22
|
+
output: 0,
|
|
23
|
+
cacheRead: 0,
|
|
24
|
+
cacheWrite: 0
|
|
25
|
+
},
|
|
26
|
+
contextWindow: model.contextWindow,
|
|
27
|
+
maxTokens: model.maxTokens
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function buildProviderConfig(provider) {
|
|
32
|
+
return {
|
|
33
|
+
baseUrl: provider.baseUrl,
|
|
34
|
+
apiKey: provider.apiKey,
|
|
35
|
+
api: "openai-responses",
|
|
36
|
+
authHeader: true,
|
|
37
|
+
headers: {
|
|
38
|
+
"User-Agent": "codex-rs/1.0.7"
|
|
39
|
+
},
|
|
40
|
+
models: buildOpenClawModels()
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function writeOpenClawConfig(provider, options = {}) {
|
|
45
|
+
ensureDir(getOpenClawDir());
|
|
46
|
+
ensureDir(path.dirname(getOpenClawModelsPath()));
|
|
47
|
+
|
|
48
|
+
const configPath = getOpenClawConfigPath();
|
|
49
|
+
const modelsPath = getOpenClawModelsPath();
|
|
50
|
+
const mode = options.mode === "overwrite" ? "overwrite" : "merge";
|
|
51
|
+
const providerName = providerKeyFromName(provider.name || DEFAULT_PROVIDER_NAME);
|
|
52
|
+
const providerConfig = buildProviderConfig(provider);
|
|
53
|
+
const existingConfig = mode === "overwrite" ? {} : readJson(configPath, {});
|
|
54
|
+
const existingModels = mode === "overwrite" ? {} : readJson(modelsPath, {});
|
|
55
|
+
const existingConfigProviders =
|
|
56
|
+
mode === "overwrite" || !isRecord(existingConfig?.models?.providers)
|
|
57
|
+
? {}
|
|
58
|
+
: existingConfig.models.providers;
|
|
59
|
+
const existingModelProviders =
|
|
60
|
+
mode === "overwrite" || !isRecord(existingModels?.providers) ? {} : existingModels.providers;
|
|
61
|
+
|
|
62
|
+
const configPayload = {
|
|
63
|
+
...(isRecord(existingConfig) ? existingConfig : {}),
|
|
64
|
+
models: {
|
|
65
|
+
mode: "merge",
|
|
66
|
+
...(isRecord(existingConfig?.models) ? existingConfig.models : {}),
|
|
67
|
+
providers: replaceCaseInsensitiveKey(existingConfigProviders, providerName, providerConfig)
|
|
68
|
+
},
|
|
69
|
+
agents: {
|
|
70
|
+
...(isRecord(existingConfig?.agents) ? existingConfig.agents : {}),
|
|
71
|
+
defaults: {
|
|
72
|
+
...(isRecord(existingConfig?.agents?.defaults) ? existingConfig.agents.defaults : {}),
|
|
73
|
+
workspace: getHomeDir(),
|
|
74
|
+
imageModel: `${providerName}/${provider.model || DEFAULT_PRIMARY_MODEL}`,
|
|
75
|
+
model: {
|
|
76
|
+
...(isRecord(existingConfig?.agents?.defaults?.model)
|
|
77
|
+
? existingConfig.agents.defaults.model
|
|
78
|
+
: {}),
|
|
79
|
+
primary: `${providerName}/${provider.model || DEFAULT_PRIMARY_MODEL}`
|
|
80
|
+
},
|
|
81
|
+
thinkingDefault: "xhigh"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const modelsPayload = {
|
|
87
|
+
...(isRecord(existingModels) ? existingModels : {}),
|
|
88
|
+
providers: replaceCaseInsensitiveKey(existingModelProviders, providerName, providerConfig)
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
writeJson(configPath, configPayload);
|
|
92
|
+
writeJson(modelsPath, modelsPayload);
|
|
93
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { DEFAULT_PRIMARY_MODEL, MODEL_DEFINITIONS } from "../constants.js";
|
|
2
|
+
import { getOpenCodeConfigPath, getOpenCodeDir } from "../paths.js";
|
|
3
|
+
import { deepMerge, ensureDir, isRecord, readJson, writeJson } from "../utils.js";
|
|
4
|
+
|
|
5
|
+
function buildModelMap() {
|
|
6
|
+
return Object.fromEntries(
|
|
7
|
+
MODEL_DEFINITIONS.map((model) => [
|
|
8
|
+
model.id,
|
|
9
|
+
{
|
|
10
|
+
name: model.id,
|
|
11
|
+
options: {
|
|
12
|
+
store: false
|
|
13
|
+
},
|
|
14
|
+
variants: {
|
|
15
|
+
low: {},
|
|
16
|
+
medium: {},
|
|
17
|
+
high: {},
|
|
18
|
+
xhigh: {}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
])
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function writeOpenCodeConfig(provider, options = {}) {
|
|
26
|
+
ensureDir(getOpenCodeDir());
|
|
27
|
+
|
|
28
|
+
const configPath = getOpenCodeConfigPath();
|
|
29
|
+
const mode = options.mode === "overwrite" ? "overwrite" : "merge";
|
|
30
|
+
const existing = mode === "overwrite" ? {} : readJson(configPath, {});
|
|
31
|
+
const existingProviders =
|
|
32
|
+
mode === "overwrite" || !isRecord(existing?.provider) ? {} : existing.provider;
|
|
33
|
+
|
|
34
|
+
const nextConfig = deepMerge(
|
|
35
|
+
{
|
|
36
|
+
$schema: "https://opencode.ai/config.json",
|
|
37
|
+
model: `openai/${provider.model || DEFAULT_PRIMARY_MODEL}`,
|
|
38
|
+
provider: {
|
|
39
|
+
openai: {
|
|
40
|
+
options: {
|
|
41
|
+
baseURL: provider.baseUrl,
|
|
42
|
+
apiKey: provider.apiKey
|
|
43
|
+
},
|
|
44
|
+
models: buildModelMap()
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
agent: {
|
|
48
|
+
build: {
|
|
49
|
+
options: {
|
|
50
|
+
store: false
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
plan: {
|
|
54
|
+
options: {
|
|
55
|
+
store: false
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
existing
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
nextConfig.model = `openai/${provider.model || DEFAULT_PRIMARY_MODEL}`;
|
|
64
|
+
nextConfig.provider = {
|
|
65
|
+
...existingProviders,
|
|
66
|
+
openai: {
|
|
67
|
+
...(isRecord(existingProviders.openai) ? existingProviders.openai : {}),
|
|
68
|
+
options: {
|
|
69
|
+
...(isRecord(existingProviders.openai?.options) ? existingProviders.openai.options : {}),
|
|
70
|
+
baseURL: provider.baseUrl,
|
|
71
|
+
apiKey: provider.apiKey
|
|
72
|
+
},
|
|
73
|
+
models: buildModelMap()
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
nextConfig.agent = {
|
|
77
|
+
...(isRecord(nextConfig.agent) ? nextConfig.agent : {}),
|
|
78
|
+
build: {
|
|
79
|
+
options: {
|
|
80
|
+
store: false
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
plan: {
|
|
84
|
+
options: {
|
|
85
|
+
store: false
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
writeJson(configPath, nextConfig);
|
|
91
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bg" x1="18" y1="12" x2="107" y2="118" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop stop-color="#102532"/>
|
|
5
|
+
<stop offset="1" stop-color="#0E8F88"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
<linearGradient id="stroke" x1="30" y1="32" x2="94" y2="86" gradientUnits="userSpaceOnUse">
|
|
8
|
+
<stop stop-color="#FFF8EE"/>
|
|
9
|
+
<stop offset="1" stop-color="#D7F2F0"/>
|
|
10
|
+
</linearGradient>
|
|
11
|
+
</defs>
|
|
12
|
+
<rect x="8" y="8" width="112" height="112" rx="32" fill="url(#bg)"/>
|
|
13
|
+
<path
|
|
14
|
+
d="M30 88V42C30 38.6863 32.6863 36 36 36H37.2C39.0512 36 40.7999 36.8538 41.9392 38.3138L63.5 65.9307L85.0608 38.3138C86.2001 36.8538 87.9488 36 89.8 36H91C94.3137 36 97 38.6863 97 42V88"
|
|
15
|
+
stroke="url(#stroke)"
|
|
16
|
+
stroke-width="10"
|
|
17
|
+
stroke-linecap="round"
|
|
18
|
+
stroke-linejoin="round"
|
|
19
|
+
/>
|
|
20
|
+
<path d="M85 90H102" stroke="#F2BD67" stroke-width="10" stroke-linecap="round"/>
|
|
21
|
+
</svg>
|
package/desktop/main.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { app, BrowserWindow, ipcMain, shell } from "electron";
|
|
6
|
+
import {
|
|
7
|
+
activateProviderFromDesktop,
|
|
8
|
+
deleteMcpFromDesktop,
|
|
9
|
+
deletePromptFromDesktop,
|
|
10
|
+
deleteSkillFromDesktop,
|
|
11
|
+
getDesktopSnapshot,
|
|
12
|
+
probeCandidateFromDesktop,
|
|
13
|
+
probePlatformProvidersFromDesktop,
|
|
14
|
+
saveProviderFromDesktop,
|
|
15
|
+
toggleMcpFromDesktop,
|
|
16
|
+
togglePromptFromDesktop,
|
|
17
|
+
toggleSkillRepoFromDesktop,
|
|
18
|
+
upsertMcpFromDesktop,
|
|
19
|
+
upsertPromptFromDesktop,
|
|
20
|
+
upsertSkillFromDesktop
|
|
21
|
+
} from "../core/desktop-service.js";
|
|
22
|
+
|
|
23
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
+
const __dirname = path.dirname(__filename);
|
|
25
|
+
|
|
26
|
+
async function exists(filePath) {
|
|
27
|
+
try {
|
|
28
|
+
await fs.access(filePath);
|
|
29
|
+
return true;
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function resolveTargetPath(input) {
|
|
36
|
+
const raw = `${input ?? ""}`.trim();
|
|
37
|
+
if (!raw) {
|
|
38
|
+
throw new Error("目标路径不能为空");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (raw.startsWith("~/")) {
|
|
42
|
+
return path.join(os.homedir(), raw.slice(2));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!path.isAbsolute(raw)) {
|
|
46
|
+
throw new Error("仅支持绝对路径或 ~/ 开头路径");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return raw;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function openPathFromDesktop(targetPath) {
|
|
53
|
+
const resolvedPath = resolveTargetPath(targetPath);
|
|
54
|
+
const error = await shell.openPath(resolvedPath);
|
|
55
|
+
if (error) {
|
|
56
|
+
throw new Error(error);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
ok: true,
|
|
61
|
+
targetPath: resolvedPath
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function loadRenderer(window) {
|
|
66
|
+
const devUrl = `${process.env.MARCH_DESKTOP_DEV_URL || ""}`.trim();
|
|
67
|
+
if (devUrl) {
|
|
68
|
+
await window.loadURL(devUrl);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const appRoot = app.getAppPath();
|
|
73
|
+
const distIndex = path.join(appRoot, "dist", "index.html");
|
|
74
|
+
|
|
75
|
+
if (await exists(distIndex)) {
|
|
76
|
+
await window.loadFile(distIndex);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
await window.loadFile(path.join(__dirname, "renderer", "index.html"));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function createWindow() {
|
|
84
|
+
const win = new BrowserWindow({
|
|
85
|
+
width: 1480,
|
|
86
|
+
height: 960,
|
|
87
|
+
minWidth: 1180,
|
|
88
|
+
minHeight: 760,
|
|
89
|
+
backgroundColor: "#f4efe6",
|
|
90
|
+
autoHideMenuBar: true,
|
|
91
|
+
title: "March 控制台",
|
|
92
|
+
webPreferences: {
|
|
93
|
+
preload: path.join(__dirname, "preload.js"),
|
|
94
|
+
contextIsolation: true,
|
|
95
|
+
nodeIntegration: false
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
loadRenderer(win).catch((error) => {
|
|
100
|
+
console.error("failed to load desktop renderer", error);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return win;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
ipcMain.handle("desktop:getSnapshot", async () => getDesktopSnapshot());
|
|
107
|
+
|
|
108
|
+
ipcMain.handle("desktop:saveProvider", async (_event, payload = {}) => {
|
|
109
|
+
const platform = payload.platform;
|
|
110
|
+
const input = payload.input || {
|
|
111
|
+
name: payload.name,
|
|
112
|
+
baseUrl: payload.baseUrl,
|
|
113
|
+
apiKey: payload.apiKey,
|
|
114
|
+
model: payload.model
|
|
115
|
+
};
|
|
116
|
+
return saveProviderFromDesktop(platform, input, payload.options);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
ipcMain.handle("desktop:activateProvider", async (_event, payload = {}) => {
|
|
120
|
+
const platform = payload.platform;
|
|
121
|
+
const nameOrId = payload.nameOrId || payload.providerId;
|
|
122
|
+
return activateProviderFromDesktop(platform, nameOrId, payload.options);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
ipcMain.handle("desktop:probePlatform", async (_event, payload = {}) => {
|
|
126
|
+
return probePlatformProvidersFromDesktop(payload.platform);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
ipcMain.handle("desktop:probeCandidate", async (_event, payload = {}) => {
|
|
130
|
+
return probeCandidateFromDesktop(payload.platform, payload.baseUrl);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
ipcMain.handle("desktop:toggleMcp", async (_event, payload = {}) => {
|
|
134
|
+
return toggleMcpFromDesktop(payload.serverId, payload.platform);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
ipcMain.handle("desktop:upsertMcp", async (_event, payload = {}) => {
|
|
138
|
+
return upsertMcpFromDesktop(payload);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
ipcMain.handle("desktop:deleteMcp", async (_event, payload = {}) => {
|
|
142
|
+
return deleteMcpFromDesktop(payload.serverId);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
ipcMain.handle("desktop:togglePrompt", async (_event, payload = {}) => {
|
|
146
|
+
return togglePromptFromDesktop(payload.promptId);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
ipcMain.handle("desktop:upsertPrompt", async (_event, payload = {}) => {
|
|
150
|
+
return upsertPromptFromDesktop(payload);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
ipcMain.handle("desktop:deletePrompt", async (_event, payload = {}) => {
|
|
154
|
+
return deletePromptFromDesktop(payload.promptId);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
ipcMain.handle("desktop:upsertSkill", async (_event, payload = {}) => {
|
|
158
|
+
return upsertSkillFromDesktop(payload);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
ipcMain.handle("desktop:deleteSkill", async (_event, payload = {}) => {
|
|
162
|
+
return deleteSkillFromDesktop(payload.skillId);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
ipcMain.handle("desktop:toggleSkillRepo", async (_event, payload = {}) => {
|
|
166
|
+
return toggleSkillRepoFromDesktop(payload.repoId);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
ipcMain.handle("desktop:openPath", async (_event, payload) => {
|
|
170
|
+
const targetPath = typeof payload === "string" ? payload : payload?.targetPath;
|
|
171
|
+
return openPathFromDesktop(targetPath);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
app.whenReady().then(() => {
|
|
175
|
+
const mainWindow = createWindow();
|
|
176
|
+
|
|
177
|
+
mainWindow.once("ready-to-show", () => {
|
|
178
|
+
mainWindow.show();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
app.on("activate", () => {
|
|
182
|
+
if (BrowserWindow.getAllWindows().length === 0) {
|
|
183
|
+
createWindow();
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
app.on("window-all-closed", () => {
|
|
189
|
+
if (process.platform !== "darwin") {
|
|
190
|
+
app.quit();
|
|
191
|
+
}
|
|
192
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { contextBridge, ipcRenderer } from "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
|
+
});
|