ccnew 0.1.10
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 +107 -0
- package/build/icon.ico +0 -0
- package/build/icon.png +0 -0
- package/core/apply.js +152 -0
- package/core/backup.js +53 -0
- package/core/constants.js +78 -0
- package/core/desktop-service.js +403 -0
- package/core/desktop-state.js +1021 -0
- package/core/index.js +1468 -0
- package/core/paths.js +99 -0
- package/core/presets.js +171 -0
- package/core/probe.js +70 -0
- package/core/routing.js +334 -0
- package/core/store.js +218 -0
- package/core/utils.js +225 -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/fml-icon.png +0 -0
- package/desktop/assets/march-mark.svg +26 -0
- package/desktop/main.js +275 -0
- package/desktop/preload.cjs +67 -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 +490 -0
- package/package.json +111 -0
- package/scripts/build-web.mjs +95 -0
- package/scripts/desktop-dev.mjs +90 -0
- package/scripts/desktop-pack-win.mjs +81 -0
- package/scripts/postinstall.mjs +49 -0
- package/scripts/prepublish-check.mjs +57 -0
- package/scripts/serve-site.mjs +51 -0
- package/site/app.js +10 -0
- package/site/assets/fml-icon.png +0 -0
- package/site/assets/march-mark.svg +26 -0
- package/site/index.html +337 -0
- package/site/styles.css +840 -0
- package/src/App.tsx +1557 -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 +32 -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 +26 -0
- package/src/features/skills/skills-view.tsx +44 -0
- package/src/hooks/use-control-workspace.ts +235 -0
- package/src/index.css +22 -0
- package/src/lib/client.ts +726 -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 +137 -0
- package/src/vite-env.d.ts +64 -0
- package/src-tauri/README.md +11 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
4
|
+
|
|
5
|
+
const rootDir = process.cwd();
|
|
6
|
+
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
7
|
+
const electronBin =
|
|
8
|
+
process.platform === "win32"
|
|
9
|
+
? path.join(rootDir, "node_modules", ".bin", "electron.cmd")
|
|
10
|
+
: path.join(rootDir, "node_modules", ".bin", "electron");
|
|
11
|
+
|
|
12
|
+
function stopProcess(child) {
|
|
13
|
+
if (!child || child.killed) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
child.kill();
|
|
19
|
+
} catch {
|
|
20
|
+
// ignore
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function waitForServer(url, timeoutMs = 60000) {
|
|
25
|
+
const start = Date.now();
|
|
26
|
+
|
|
27
|
+
while (Date.now() - start < timeoutMs) {
|
|
28
|
+
try {
|
|
29
|
+
const response = await fetch(url, { cache: "no-store" });
|
|
30
|
+
if (response.ok || response.status < 500) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
// server not ready yet
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
await delay(500);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
throw new Error(`Vite dev server did not start within ${timeoutMs}ms: ${url}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const viteProc = spawn(npmCmd, ["run", "dev", "--", "--host", "127.0.0.1", "--port", "3001", "--strictPort"], {
|
|
44
|
+
cwd: rootDir,
|
|
45
|
+
stdio: "inherit",
|
|
46
|
+
env: process.env
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
let electronProc = null;
|
|
50
|
+
let shuttingDown = false;
|
|
51
|
+
|
|
52
|
+
function shutdown(code = 0) {
|
|
53
|
+
if (shuttingDown) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
shuttingDown = true;
|
|
57
|
+
|
|
58
|
+
stopProcess(electronProc);
|
|
59
|
+
stopProcess(viteProc);
|
|
60
|
+
process.exit(code);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
process.on("SIGINT", () => shutdown(0));
|
|
64
|
+
process.on("SIGTERM", () => shutdown(0));
|
|
65
|
+
|
|
66
|
+
viteProc.on("exit", (code) => {
|
|
67
|
+
if (!electronProc && !shuttingDown) {
|
|
68
|
+
shutdown(code ?? 1);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
await waitForServer("http://127.0.0.1:3001", 90000);
|
|
74
|
+
|
|
75
|
+
electronProc = spawn(electronBin, ["."], {
|
|
76
|
+
cwd: rootDir,
|
|
77
|
+
stdio: "inherit",
|
|
78
|
+
env: {
|
|
79
|
+
...process.env,
|
|
80
|
+
MARCH_DESKTOP_DEV_URL: "http://127.0.0.1:3001"
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
electronProc.on("exit", (code) => {
|
|
85
|
+
shutdown(code ?? 0);
|
|
86
|
+
});
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error(error instanceof Error ? error.message : error);
|
|
89
|
+
shutdown(1);
|
|
90
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
const rootDir = path.resolve(__dirname, "..");
|
|
9
|
+
const outputDir = path.join(rootDir, "release", "ccon-win-unpacked");
|
|
10
|
+
const appDir = path.join(outputDir, "resources", "app");
|
|
11
|
+
const electronDist = path.join(rootDir, "node_modules", "electron", "dist");
|
|
12
|
+
|
|
13
|
+
const copyEntries = [
|
|
14
|
+
"build",
|
|
15
|
+
"core",
|
|
16
|
+
"desktop",
|
|
17
|
+
"dist",
|
|
18
|
+
"site",
|
|
19
|
+
"node_modules",
|
|
20
|
+
"package.json",
|
|
21
|
+
"README.md"
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
async function copyIntoApp(entry) {
|
|
25
|
+
const source = path.join(rootDir, entry);
|
|
26
|
+
const target = path.join(appDir, entry);
|
|
27
|
+
|
|
28
|
+
if (entry === "node_modules" && process.platform === "win32") {
|
|
29
|
+
await fs.mkdir(target, { recursive: true });
|
|
30
|
+
const result = spawnSync(
|
|
31
|
+
"robocopy",
|
|
32
|
+
[source, target, "/E", "/R:2", "/W:1", "/NFL", "/NDL", "/NJH", "/NJS", "/NP"],
|
|
33
|
+
{
|
|
34
|
+
stdio: "inherit",
|
|
35
|
+
shell: false
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
if ((result.status ?? 16) >= 8) {
|
|
40
|
+
throw new Error(`robocopy failed for ${entry} with exit code ${result.status}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await fs.cp(source, target, {
|
|
47
|
+
recursive: true,
|
|
48
|
+
force: true
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function main() {
|
|
53
|
+
await fs.rm(outputDir, { recursive: true, force: true });
|
|
54
|
+
await fs.mkdir(appDir, { recursive: true });
|
|
55
|
+
await fs.cp(electronDist, outputDir, {
|
|
56
|
+
recursive: true,
|
|
57
|
+
force: true
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const electronExe = path.join(outputDir, "electron.exe");
|
|
61
|
+
const cconExe = path.join(outputDir, "ccon.exe");
|
|
62
|
+
await fs.copyFile(electronExe, cconExe);
|
|
63
|
+
await fs.rm(electronExe, { force: true });
|
|
64
|
+
|
|
65
|
+
for (const entry of copyEntries) {
|
|
66
|
+
await copyIntoApp(entry);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
await fs.writeFile(
|
|
70
|
+
path.join(outputDir, "PACKED_BY.txt"),
|
|
71
|
+
`ccon windows package\ncreatedAt=${new Date().toISOString()}\nmode=custom-electron-copy\n`,
|
|
72
|
+
"utf8"
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
console.log(`Packed desktop app to ${outputDir}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
main().catch((error) => {
|
|
79
|
+
console.error(error);
|
|
80
|
+
process.exitCode = 1;
|
|
81
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const useColor = process.stderr.isTTY && process.env.NO_COLOR == null;
|
|
2
|
+
|
|
3
|
+
function color(code, text) {
|
|
4
|
+
return useColor ? `\u001b[${code}m${text}\u001b[0m` : text;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function dim(text) {
|
|
8
|
+
return color("2", text);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function cyan(text) {
|
|
12
|
+
return color("96", text);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function green(text) {
|
|
16
|
+
return color("92", text);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function yellow(text) {
|
|
20
|
+
return color("93", text);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function blue(text) {
|
|
24
|
+
return color("94", text);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const banner = [
|
|
28
|
+
"",
|
|
29
|
+
blue(".--------------------------------------------------------------."),
|
|
30
|
+
`${blue("|")} ${cyan("ccon".padEnd(60, " "))} ${blue("|")}`,
|
|
31
|
+
`${blue("|")} ${dim("quiet relay setup for Codex / OpenCode / OpenClaw".padEnd(60, " "))} ${blue("|")}`,
|
|
32
|
+
`${blue("|")} ${green("route https://www.fhl.mom".padEnd(60, " "))} ${blue("|")}`,
|
|
33
|
+
`${blue("|")} ${yellow("start ccon fhl".padEnd(60, " "))} ${blue("|")}`,
|
|
34
|
+
blue("'--------------------------------------------------------------'"),
|
|
35
|
+
"",
|
|
36
|
+
" Quick Start:",
|
|
37
|
+
` ${cyan("npm i -g ccon")}`,
|
|
38
|
+
` ${cyan("ccon fhl")}`,
|
|
39
|
+
"",
|
|
40
|
+
" Useful Commands:",
|
|
41
|
+
` ${cyan("ccon --help")}`,
|
|
42
|
+
` ${cyan("ccon fhl --help")}`,
|
|
43
|
+
` ${cyan("ccon preset --help")}`,
|
|
44
|
+
"",
|
|
45
|
+
dim(" If install output looks quiet, run `ccon --help` to verify the CLI is ready."),
|
|
46
|
+
""
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
process.stderr.write(`${banner.join("\n")}\n`);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { DEFAULT_BASE_URL, DEFAULT_OPENCLAW_BASE_URL } from "../core/constants.js";
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
const rootDir = path.resolve(__dirname, "..");
|
|
10
|
+
|
|
11
|
+
function readText(relativePath) {
|
|
12
|
+
return fs.readFileSync(path.join(rootDir, relativePath), "utf8");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function assertContains(relativePath, expected) {
|
|
16
|
+
const text = readText(relativePath);
|
|
17
|
+
if (!text.includes(expected)) {
|
|
18
|
+
throw new Error(`${relativePath} is missing required content: ${expected}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function run(label, command, args) {
|
|
23
|
+
const result = spawnSync(command, args, {
|
|
24
|
+
cwd: rootDir,
|
|
25
|
+
stdio: "inherit",
|
|
26
|
+
shell: false
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (result.status !== 0) {
|
|
30
|
+
throw new Error(`${label} failed with exit code ${result.status ?? "unknown"}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (DEFAULT_BASE_URL !== "https://www.fhl.mom") {
|
|
35
|
+
throw new Error(`Unexpected default base URL: ${DEFAULT_BASE_URL}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (DEFAULT_OPENCLAW_BASE_URL !== "https://www.fhl.mom/v1") {
|
|
39
|
+
throw new Error(`Unexpected OpenClaw base URL: ${DEFAULT_OPENCLAW_BASE_URL}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
assertContains("site/index.html", "ccon");
|
|
43
|
+
assertContains("site/index.html", "https://www.fhl.mom");
|
|
44
|
+
assertContains("site/index.html", "https://www.fhl.mom/v1");
|
|
45
|
+
assertContains("site/index.html", "ccon fhl");
|
|
46
|
+
assertContains("desktop/renderer/index.html", "ccon");
|
|
47
|
+
assertContains("desktop/renderer/index.html", "https://www.fhl.mom");
|
|
48
|
+
|
|
49
|
+
run("CLI syntax check", process.execPath, ["--check", "./core/index.js"]);
|
|
50
|
+
run("Desktop syntax check", process.execPath, ["--check", "./desktop/main.js"]);
|
|
51
|
+
run("Desktop preload syntax check", process.execPath, ["--check", "./desktop/preload.cjs"]);
|
|
52
|
+
run("Desktop renderer syntax check", process.execPath, ["--check", "./desktop/renderer/app.js"]);
|
|
53
|
+
run("Desktop service syntax check", process.execPath, ["--check", "./core/desktop-service.js"]);
|
|
54
|
+
run("Shortcut help check", process.execPath, ["./core/index.js", "fhl", "--help"]);
|
|
55
|
+
run("Postinstall banner check", process.execPath, ["./scripts/postinstall.mjs"]);
|
|
56
|
+
|
|
57
|
+
console.log("prepublish verification passed");
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
const siteRoot = path.resolve(__dirname, "..", "site");
|
|
9
|
+
const port = Number(process.env.PORT || 4173);
|
|
10
|
+
|
|
11
|
+
const mimeTypes = {
|
|
12
|
+
".css": "text/css; charset=utf-8",
|
|
13
|
+
".html": "text/html; charset=utf-8",
|
|
14
|
+
".js": "application/javascript; charset=utf-8",
|
|
15
|
+
".json": "application/json; charset=utf-8",
|
|
16
|
+
".svg": "image/svg+xml",
|
|
17
|
+
".txt": "text/plain; charset=utf-8"
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function resolveFile(requestUrl) {
|
|
21
|
+
const parsed = new URL(requestUrl, `http://localhost:${port}`);
|
|
22
|
+
const requestedPath = decodeURIComponent(parsed.pathname === "/" ? "/index.html" : parsed.pathname);
|
|
23
|
+
const absolutePath = path.resolve(siteRoot, `.${requestedPath}`);
|
|
24
|
+
|
|
25
|
+
if (!absolutePath.startsWith(siteRoot)) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return absolutePath;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const server = http.createServer((req, res) => {
|
|
33
|
+
const target = resolveFile(req.url || "/");
|
|
34
|
+
|
|
35
|
+
if (!target || !fs.existsSync(target) || fs.statSync(target).isDirectory()) {
|
|
36
|
+
res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
|
|
37
|
+
res.end("Not found");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const ext = path.extname(target).toLowerCase();
|
|
42
|
+
res.writeHead(200, {
|
|
43
|
+
"content-type": mimeTypes[ext] || "application/octet-stream",
|
|
44
|
+
"cache-control": "no-store"
|
|
45
|
+
});
|
|
46
|
+
fs.createReadStream(target).pipe(res);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
server.listen(port, () => {
|
|
50
|
+
console.log(`FHL site preview: http://localhost:${port}`);
|
|
51
|
+
});
|
package/site/app.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const downloadCards = [...document.querySelectorAll(".download-card")];
|
|
2
|
+
|
|
3
|
+
for (const card of downloadCards) {
|
|
4
|
+
card.addEventListener("mouseenter", () => {
|
|
5
|
+
for (const item of downloadCards) {
|
|
6
|
+
item.classList.remove("active");
|
|
7
|
+
}
|
|
8
|
+
card.classList.add("active");
|
|
9
|
+
});
|
|
10
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,26 @@
|
|
|
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="fml-bg" x1="16" y1="12" x2="108" y2="116" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop stop-color="#071A24"/>
|
|
5
|
+
<stop offset="0.55" stop-color="#103847"/>
|
|
6
|
+
<stop offset="1" stop-color="#0C8E7F"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
<radialGradient id="fml-glow" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(96 28) rotate(135) scale(42)">
|
|
9
|
+
<stop stop-color="#5EEAD4" stop-opacity="0.85"/>
|
|
10
|
+
<stop offset="1" stop-color="#5EEAD4" stop-opacity="0"/>
|
|
11
|
+
</radialGradient>
|
|
12
|
+
<linearGradient id="fml-stroke" x1="24" y1="30" x2="98" y2="98" gradientUnits="userSpaceOnUse">
|
|
13
|
+
<stop stop-color="#FFF9EE"/>
|
|
14
|
+
<stop offset="1" stop-color="#D5FBF5"/>
|
|
15
|
+
</linearGradient>
|
|
16
|
+
</defs>
|
|
17
|
+
<rect x="8" y="8" width="112" height="112" rx="30" fill="url(#fml-bg)"/>
|
|
18
|
+
<rect x="8.75" y="8.75" width="110.5" height="110.5" rx="29.25" stroke="rgba(255,255,255,0.12)" stroke-width="1.5"/>
|
|
19
|
+
<circle cx="96" cy="28" r="30" fill="url(#fml-glow)" opacity="0.72"/>
|
|
20
|
+
<path d="M26 36V92" stroke="url(#fml-stroke)" stroke-width="10" stroke-linecap="round"/>
|
|
21
|
+
<path d="M26 36H54" stroke="url(#fml-stroke)" stroke-width="10" stroke-linecap="round"/>
|
|
22
|
+
<path d="M26 62H47" stroke="url(#fml-stroke)" stroke-width="10" stroke-linecap="round"/>
|
|
23
|
+
<path d="M60 92V36L74 62L88 36V92" stroke="url(#fml-stroke)" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/>
|
|
24
|
+
<path d="M102 36V92" stroke="#F3C46C" stroke-width="10" stroke-linecap="round"/>
|
|
25
|
+
<path d="M88 92H102" stroke="#F3C46C" stroke-width="10" stroke-linecap="round"/>
|
|
26
|
+
</svg>
|