cicy-desktop 2.1.184 → 2.1.186
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.
|
@@ -40,12 +40,21 @@ jobs:
|
|
|
40
40
|
# linux-arm64 用户的包里塞的是 x64 二进制。强制把两个 linux 架构都装进 node_modules,
|
|
41
41
|
# electron-builder 两架构都打包,localbin 按用户机器选。必须在所有 npm install 之后。
|
|
42
42
|
- name: Bundle BOTH linux arches of cicy-code/mihomo (runner is single-arch)
|
|
43
|
+
shell: bash
|
|
43
44
|
run: |
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
set -e
|
|
46
|
+
# npm pack(不看 os/cpu/lockfile)+ 解压,绕开 `npm install --force` 在已有 lockfile 上的 no-op。
|
|
47
|
+
for pkg in cicy-code-linux-x64 cicy-code-linux-arm64 cicy-mihomo-linux-x64 cicy-mihomo-linux-arm64; do
|
|
48
|
+
ver=$(node -e "const o=require('./package.json').optionalDependencies||{}; process.stdout.write(o['$pkg']||'')")
|
|
49
|
+
[ -z "$ver" ] && { echo "skip $pkg"; continue; }
|
|
50
|
+
bin=cicy-code; case "$pkg" in cicy-mihomo*) bin=mihomo;; esac
|
|
51
|
+
if [ -f "node_modules/$pkg/$bin" ]; then echo "$pkg present"; continue; fi
|
|
52
|
+
echo "npm pack $pkg@$ver → node_modules/$pkg"
|
|
53
|
+
tmp=$(mktemp -d); ( cd "$tmp" && npm pack "$pkg@$ver" --silent ); tar -xzf "$tmp"/*.tgz -C "$tmp"
|
|
54
|
+
rm -rf "node_modules/$pkg"; mkdir -p "node_modules/$pkg"; cp -R "$tmp/package/." "node_modules/$pkg/"; rm -rf "$tmp"
|
|
55
|
+
done
|
|
47
56
|
ls node_modules | grep -E 'cicy-(code|mihomo)-linux' || true
|
|
48
|
-
for a in x64 arm64; do test -f "node_modules/cicy-code-linux-$a/cicy-code" || { echo "::error::cicy-code-linux-$a
|
|
57
|
+
for a in x64 arm64; do test -f "node_modules/cicy-code-linux-$a/cicy-code" || { echo "::error::cicy-code-linux-$a missing"; exit 1; }; done
|
|
49
58
|
|
|
50
59
|
# NOTE: the homepage SPA is rebuilt automatically by the prebuild:linux npm
|
|
51
60
|
# hook (scripts/build-homepage.cjs) right before `npm run build:linux` below,
|
|
@@ -44,13 +44,22 @@ jobs:
|
|
|
44
44
|
# 装进 node_modules(--force 绕过 EBADPLATFORM),让 electron-builder 两架构都打包,
|
|
45
45
|
# localbin 按用户机器选对应那份。必须在所有 npm install 之后(否则被剪枝)。
|
|
46
46
|
- name: Bundle BOTH macOS arches of cicy-code/mihomo (runner is single-arch)
|
|
47
|
+
shell: bash
|
|
47
48
|
run: |
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
npm
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
set -e
|
|
50
|
+
# `npm install --force` 在已有 lockfile 的项目里会判 "up to date" 跳过非本机架构
|
|
51
|
+
# → 改用 `npm pack`(纯按名下 tarball,不看 os/cpu/lockfile)+ 解压进 node_modules。
|
|
52
|
+
for pkg in cicy-code-darwin-x64 cicy-code-darwin-arm64 cicy-mihomo-darwin-x64 cicy-mihomo-darwin-arm64; do
|
|
53
|
+
ver=$(node -e "const o=require('./package.json').optionalDependencies||{}; process.stdout.write(o['$pkg']||'')")
|
|
54
|
+
[ -z "$ver" ] && { echo "skip $pkg (not pinned)"; continue; }
|
|
55
|
+
bin=cicy-code; case "$pkg" in cicy-mihomo*) bin=mihomo;; esac
|
|
56
|
+
if [ -f "node_modules/$pkg/$bin" ]; then echo "$pkg present (native arch)"; continue; fi
|
|
57
|
+
echo "npm pack $pkg@$ver → node_modules/$pkg"
|
|
58
|
+
tmp=$(mktemp -d); ( cd "$tmp" && npm pack "$pkg@$ver" --silent ); tar -xzf "$tmp"/*.tgz -C "$tmp"
|
|
59
|
+
rm -rf "node_modules/$pkg"; mkdir -p "node_modules/$pkg"; cp -R "$tmp/package/." "node_modules/$pkg/"; rm -rf "$tmp"
|
|
60
|
+
done
|
|
61
|
+
echo "--- bundled darwin subpackages ---"; ls node_modules | grep -E 'cicy-(code|mihomo)-darwin' || true
|
|
62
|
+
for a in x64 arm64; do test -f "node_modules/cicy-code-darwin-$a/cicy-code" || { echo "::error::cicy-code-darwin-$a binary missing"; exit 1; }; done
|
|
54
63
|
|
|
55
64
|
# NOTE: the homepage SPA is rebuilt automatically by the prebuild:mac npm
|
|
56
65
|
# hook (scripts/build-homepage.cjs) right before `npm run build:mac` below,
|
package/package.json
CHANGED
package/src/main.js
CHANGED
|
@@ -881,32 +881,37 @@ electronApp.whenReady().then(async () => {
|
|
|
881
881
|
// colima VM 在 16G mac 上把内存压垮被 jetsam SIGKILL。Windows 仍走 docker(WSL :8009,
|
|
882
882
|
// 由 sidecar-ipc 管),不在这里起 native。
|
|
883
883
|
if (process.platform !== "win32") {
|
|
884
|
-
|
|
885
|
-
|
|
884
|
+
const sidecarPort = Number(process.env.CICY_CODE_PORT || 8008);
|
|
885
|
+
const lt = require("./backends/local-teams");
|
|
886
|
+
|
|
887
|
+
// 占位卡(主人: "先有一个 :8008 占位"): 开机**立刻无条件**注册本地团队,homepage 永远有
|
|
888
|
+
// 这张卡 —— :8008 没起来时显示「未运行」+ 启动/安装入口(不会再空首页);起来后下面的
|
|
889
|
+
// probe 循环 upsert 一次,状态翻 running + 触发 gateway key/teamId 注入。addTeam 幂等。
|
|
890
|
+
lt.addTeam({ base_url: `http://127.0.0.1:${sidecarPort}`, name: i18n.t("localTeams.defaultName") })
|
|
891
|
+
.then((r) => { if (r && r.id) log.info(`[Sidecar] local team placeholder registered (${r.id})`); })
|
|
892
|
+
.catch((e) => log.warn(`[Sidecar] placeholder register failed: ${e.message}`));
|
|
893
|
+
|
|
894
|
+
// 起 native cicy-code(npx cicy-code,见 cicy-code.js)。已在跑就 adopt,不双开。
|
|
886
895
|
cicyCodeSidecar
|
|
887
896
|
.start({ logPath: path.join(os.homedir(), "logs", "cicy-code-sidecar.log") })
|
|
888
897
|
.then((c) => { if (c) log.info(`[Sidecar] cicy-code spawned pid=${c.pid}`); })
|
|
889
898
|
.catch((e) => log.warn(`[Sidecar] cicy-code start failed: ${e.message}`));
|
|
890
899
|
startSidecarWatchdog();
|
|
891
900
|
|
|
892
|
-
//
|
|
893
|
-
//
|
|
894
|
-
// no-ops. A fresh boot may seed/npm-pull the binary first, so probe up to ~90s.
|
|
901
|
+
// :8008 起来后 upsert 一次:刷新卡状态 + 让 addTeam 去云端拿这个本地 team 的 gateway
|
|
902
|
+
// key + teamId 注入 global.json(主人: 启动之后先去拿 llm api_key 和 team id)。
|
|
895
903
|
(async () => {
|
|
896
|
-
|
|
897
|
-
const lt = require("./backends/local-teams");
|
|
898
|
-
for (let i = 0; i < 30; i++) {
|
|
904
|
+
for (let i = 0; i < 40; i++) {
|
|
899
905
|
try {
|
|
900
906
|
if (await cicyCodeSidecar.probeExisting(sidecarPort)) {
|
|
901
907
|
const r = await lt.addTeam({ base_url: `http://127.0.0.1:${sidecarPort}`, name: i18n.t("localTeams.defaultName") });
|
|
902
|
-
if (r && r.
|
|
903
|
-
else log.warn(`[Sidecar] local team auto-register failed: ${r && r.error}`);
|
|
908
|
+
if (r && r.id) log.info(`[Sidecar] local team ready (${r.id}) — key/teamId synced`);
|
|
904
909
|
return;
|
|
905
910
|
}
|
|
906
|
-
} catch (e) { log.warn(`[Sidecar] local team
|
|
911
|
+
} catch (e) { log.warn(`[Sidecar] local team refresh error: ${e.message}`); }
|
|
907
912
|
await new Promise((res) => setTimeout(res, 3000));
|
|
908
913
|
}
|
|
909
|
-
log.warn(`[Sidecar]
|
|
914
|
+
log.warn(`[Sidecar] :${sidecarPort} never came up within ~2min (card stays as 未运行)`);
|
|
910
915
|
})();
|
|
911
916
|
}
|
|
912
917
|
|
package/src/sidecar/cicy-code.js
CHANGED
|
@@ -18,7 +18,56 @@ const os = require("os");
|
|
|
18
18
|
const http = require("http");
|
|
19
19
|
const net = require("net");
|
|
20
20
|
const path = require("path");
|
|
21
|
-
const { spawn, execFileSync } = require("child_process");
|
|
21
|
+
const { spawn, execFileSync, execFile } = require("child_process");
|
|
22
|
+
|
|
23
|
+
// ── Node runtime bootstrap ───────────────────────────────────────────────────
|
|
24
|
+
// 主人(2026-06): native cicy-code 走 `npx cicy-code`,需要一个**可用**的 Node。但用户
|
|
25
|
+
// 机器可能没 node,或 node 太老(实测 josephs 是 node v13/npx6,老 npx 跑不动)。所以:
|
|
26
|
+
// 系统有 node≥20 就用;没有/太老 → 下载 Node 24 到 ~/cicy-ai/runtime/node(免 sudo,
|
|
27
|
+
// 用户自己拥有);下载也失败 → 提示用户去 nodejs.org 自己装。
|
|
28
|
+
const NODE_VER = process.env.CICY_NODE_VERSION || "v24.18.0";
|
|
29
|
+
const NODE_HOME = path.join(os.homedir(), "cicy-ai", "runtime", "node");
|
|
30
|
+
const NODE_SEARCH = ["/usr/local/bin", "/opt/homebrew/bin", path.join(os.homedir(), ".local", "bin"), path.join(NODE_HOME, "bin")];
|
|
31
|
+
function nodeMajor(bin) {
|
|
32
|
+
try { const m = String(execFileSync(bin, ["-v"], { encoding: "utf8", timeout: 5000 })).match(/v(\d+)\./); return m ? Number(m[1]) : 0; } catch { return 0; }
|
|
33
|
+
}
|
|
34
|
+
function findUsableNode() {
|
|
35
|
+
for (const d of NODE_SEARCH) {
|
|
36
|
+
const node = path.join(d, "node"), npx = path.join(d, "npx");
|
|
37
|
+
try { if (fs.existsSync(node) && fs.existsSync(npx) && nodeMajor(node) >= 20) return d; } catch {}
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const pexec = (cmd, args, timeout) => new Promise((res, rej) => execFile(cmd, args, { timeout, windowsHide: true }, (e) => (e ? rej(e) : res())));
|
|
42
|
+
// Returns a bin dir with node+npx (node≥20), or null. emit streams progress to a drawer.
|
|
43
|
+
async function ensureNode({ emit } = {}) {
|
|
44
|
+
const e = emit || (() => {});
|
|
45
|
+
let dir = findUsableNode();
|
|
46
|
+
if (dir) return dir;
|
|
47
|
+
const arch = process.arch === "arm64" ? "arm64" : "x64";
|
|
48
|
+
const fname = `node-${NODE_VER}-darwin-${arch}.tar.gz`;
|
|
49
|
+
const urls = [
|
|
50
|
+
`https://cdn.npmmirror.com/binaries/node/${NODE_VER}/${fname}`, // CN-fast
|
|
51
|
+
`https://nodejs.org/dist/${NODE_VER}/${fname}`, // 官方兜底
|
|
52
|
+
];
|
|
53
|
+
e({ phase: "node", status: "running", message: `未检测到可用 Node,正在安装 Node ${NODE_VER}(约 50MB)…` });
|
|
54
|
+
fs.mkdirSync(NODE_HOME, { recursive: true });
|
|
55
|
+
const tmp = path.join(os.tmpdir(), `cicy-${fname}`);
|
|
56
|
+
for (const url of urls) {
|
|
57
|
+
try {
|
|
58
|
+
await pexec("curl", ["-fL", "--retry", "2", "-o", tmp, url], 300000);
|
|
59
|
+
await pexec("tar", ["-xzf", tmp, "-C", NODE_HOME, "--strip-components", "1"], 120000);
|
|
60
|
+
if (fs.existsSync(path.join(NODE_HOME, "bin", "node")) && fs.existsSync(path.join(NODE_HOME, "bin", "npx"))) {
|
|
61
|
+
try { fs.unlinkSync(tmp); } catch {}
|
|
62
|
+
e({ phase: "node", status: "done", message: `Node ${NODE_VER} 安装完成` });
|
|
63
|
+
return path.join(NODE_HOME, "bin");
|
|
64
|
+
}
|
|
65
|
+
} catch (err) { console.warn(`[cicy-code-sidecar] node install failed (${url}): ${err.message}`); }
|
|
66
|
+
}
|
|
67
|
+
try { fs.unlinkSync(tmp); } catch {}
|
|
68
|
+
e({ phase: "node", status: "error", message: "Node 自动安装失败 —— 请打开 https://nodejs.org 下载安装 Node(LTS),装好后点「重试」" });
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
22
71
|
|
|
23
72
|
const DEFAULT_PORT = Number(process.env.CICY_CODE_PORT || 8008);
|
|
24
73
|
|
|
@@ -91,13 +140,11 @@ async function startFromRuntime({ logPath, port }) {
|
|
|
91
140
|
return c;
|
|
92
141
|
}
|
|
93
142
|
|
|
94
|
-
async function start({ logPath, port = DEFAULT_PORT, force = false, version = null } = {}) {
|
|
143
|
+
async function start({ logPath, port = DEFAULT_PORT, force = false, version = null, emit = null } = {}) {
|
|
95
144
|
if (child && !force) return child;
|
|
96
145
|
|
|
97
|
-
// 主人(2026-06 方向回调): mac 资源吃不消 docker
|
|
98
|
-
//
|
|
99
|
-
// (:8008,本机直接跑二进制,省掉 4–8G 的 VM)。Windows 仍走 docker(WSL :8009,由
|
|
100
|
-
// sidecar-ipc 管),native 一律不起。
|
|
146
|
+
// 主人(2026-06 方向回调): mac 资源吃不消 docker(colima VM 压垮内存被 jetsam 杀)→
|
|
147
|
+
// macOS/Linux 改回 native cicy-code(:8008,走 `npx cicy-code`)。Windows 仍走 docker。
|
|
101
148
|
if (process.platform === "win32") return null;
|
|
102
149
|
|
|
103
150
|
if (!force && await probeExisting(port)) {
|
|
@@ -105,47 +152,35 @@ async function start({ logPath, port = DEFAULT_PORT, force = false, version = nu
|
|
|
105
152
|
return null;
|
|
106
153
|
}
|
|
107
154
|
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// run (zero network) and uses npm ONLY as a download channel for updates; the run
|
|
112
|
-
// path is always stable.
|
|
113
|
-
const localbin = require("./localbin");
|
|
114
|
-
let exe = localbin.currentLink();
|
|
115
|
-
if (!exe) {
|
|
116
|
-
try { exe = (await localbin.ensure({ version }))?.exe; }
|
|
117
|
-
catch (e) { console.warn(`[cicy-code-sidecar] localbin ensure failed: ${e.message}`); }
|
|
118
|
-
} else {
|
|
119
|
-
// Present — let ensure() do a zero-network bundle upgrade if cicy-desktop
|
|
120
|
-
// itself was updated and now ships a newer cicy-code (版本高了就更新).
|
|
121
|
-
try { await localbin.ensure({ version }); } catch {}
|
|
122
|
-
}
|
|
123
|
-
if (!exe) { console.warn("[cicy-code-sidecar] no cicy-code binary available"); return null; }
|
|
124
|
-
|
|
125
|
-
// 主人: 不再 seed host mihomo —— docker-only 后 mihomo 只在容器里跑(host 不用)。
|
|
126
|
-
// 内置的 cicy-mihomo-<plat> 依赖已从 package.json 移除。
|
|
155
|
+
// 1) 确保有可用 Node(系统 node≥20,否则装 Node 24;再不行提示用户去 nodejs.org 自己装)。
|
|
156
|
+
const nodeBinDir = await ensureNode({ emit });
|
|
157
|
+
if (!nodeBinDir) { console.warn("[cicy-code-sidecar] no usable Node — cannot npx cicy-code"); return null; }
|
|
127
158
|
|
|
159
|
+
// 2) `npx cicy-code` —— npm 按本机真实架构拉 cicy-code-<plat>,文件用户自己拥有(无跨架构/
|
|
160
|
+
// 权限坑)。用上面那个 Node 的 npx,并把它的 bin 放 PATH 首位让 npx 找到自己的 node/npm。
|
|
128
161
|
let stdio = ["ignore", "ignore", "ignore"];
|
|
129
162
|
if (logPath) {
|
|
130
163
|
fs.mkdirSync(path.dirname(logPath), { recursive: true });
|
|
131
164
|
const fd = fs.openSync(logPath, "a");
|
|
132
165
|
stdio = ["ignore", fd, fd];
|
|
133
166
|
}
|
|
167
|
+
const npxAbs = path.join(nodeBinDir, "npx");
|
|
168
|
+
const childPath = `${nodeBinDir}:${NODE_SEARCH.join(":")}:${process.env.PATH || ""}`;
|
|
169
|
+
const registry = process.env.CICY_NPM_REGISTRY || "https://registry.npmmirror.com";
|
|
134
170
|
const env = {
|
|
135
171
|
...process.env,
|
|
136
172
|
CICY_CODE_PORT: String(port),
|
|
137
173
|
PORT: String(port),
|
|
174
|
+
npm_config_registry: registry,
|
|
175
|
+
PATH: childPath, // 给 npx 自己找 node/npm
|
|
138
176
|
};
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// 退出保活(主人):mac/linux 把 native cicy-code spawn 成 detached + unref,让它脱离
|
|
144
|
-
// Electron 的进程组——退出 App 不带走 daemon(及其 tmux agent),下次启动 probeExisting
|
|
145
|
-
// 探到就 adopt。Windows 不走这条(docker)。
|
|
177
|
+
const spec = version ? `cicy-code@${version}`
|
|
178
|
+
: (process.env.CICY_CODE_VERSION ? `cicy-code@${process.env.CICY_CODE_VERSION}` : "cicy-code");
|
|
179
|
+
emit && emit({ phase: "cicy-code", status: "running", message: "启动 cicy-code(首次会下载 + 装依赖,请稍候)…" });
|
|
180
|
+
// 退出保活(主人):detached + unref → 关 App 不带走 daemon(及其 tmux agent),下次 adopt。
|
|
146
181
|
const detached = process.platform !== "win32";
|
|
147
|
-
child = spawn(
|
|
148
|
-
console.log(`[cicy-code-sidecar] spawned ${
|
|
182
|
+
child = spawn(npxAbs, ["-y", spec], { stdio, detached, windowsHide: true, env });
|
|
183
|
+
console.log(`[cicy-code-sidecar] spawned ${npxAbs} -y ${spec} pid=${child.pid} port=${port} registry=${registry} detached=${detached} log=${logPath || "(none)"}`);
|
|
149
184
|
if (detached) { try { child.unref(); } catch {} }
|
|
150
185
|
|
|
151
186
|
child.on("exit", (code, signal) => {
|