cicy-desktop 2.1.247 → 2.1.249
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/.github/workflows/linux-app-release.yml +29 -0
- package/.github/workflows/mac-app-release.yml +1 -0
- package/.github/workflows/windows-exe-release.yml +1 -0
- package/package.json +5 -4
- package/scripts/obfuscate.cjs +81 -0
- package/src/app-updater.js +18 -52
- package/workers/render/package-lock.json +1495 -33
- package/workers/render/package.json +1 -0
- package/workers/render/vite.config.js +31 -1
|
@@ -64,6 +64,7 @@ jobs:
|
|
|
64
64
|
shell: bash
|
|
65
65
|
env:
|
|
66
66
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
67
|
+
CICY_OBFUSCATE: "1" # 发版混淆 JS(prebuild 主进程 + Vite 渲染层),仅 release CI
|
|
67
68
|
run: |
|
|
68
69
|
set -euo pipefail
|
|
69
70
|
npm run build:linux -- --publish always
|
|
@@ -76,3 +77,31 @@ jobs:
|
|
|
76
77
|
dist/*.deb
|
|
77
78
|
dist/*.AppImage
|
|
78
79
|
dist/latest-linux.yml
|
|
80
|
+
|
|
81
|
+
# AppImage + 版本指针传到 OSS —— app-updater 现已全走 OSS(不碰 GitHub),linux 也要在
|
|
82
|
+
# OSS 上有 versioned AppImage(CiCy-Desktop-<ver>.AppImage)和自己的指针 linux-latest-version.txt。
|
|
83
|
+
# 和 mac/win 同一 bucket/凭证。electron-builder 默认产物名含空格,重命名成代码约定的连字符名。
|
|
84
|
+
- name: Upload AppImage to OSS (versioned + latest pointer)
|
|
85
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
86
|
+
shell: bash
|
|
87
|
+
env:
|
|
88
|
+
OSS_AK: ${{ secrets.OSS_ACCESS_KEY_ID }}
|
|
89
|
+
OSS_SK: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
|
|
90
|
+
run: |
|
|
91
|
+
set -e
|
|
92
|
+
if [ -z "${OSS_AK:-}" ] || [ -z "${OSS_SK:-}" ]; then echo "::warning::OSS creds missing — skip"; exit 0; fi
|
|
93
|
+
VER="${GITHUB_REF_NAME#v}"
|
|
94
|
+
curl -sL https://gosspublic.alicdn.com/ossutil/1.7.18/ossutil64 -o ossutil && chmod +x ossutil
|
|
95
|
+
test -s ossutil && head -c4 ossutil | grep -q ELF || { echo "ossutil download bad"; exit 1; }
|
|
96
|
+
./ossutil config -e oss-cn-shanghai.aliyuncs.com -i "$OSS_AK" -k "$OSS_SK"
|
|
97
|
+
BASE="oss://cicy-1372193042-cn/releases"
|
|
98
|
+
HOST="https://cicy-1372193042-cn.oss-cn-shanghai.aliyuncs.com/releases"
|
|
99
|
+
APP=$(ls dist/*.AppImage 2>/dev/null | head -1)
|
|
100
|
+
[ -n "$APP" ] && [ -f "$APP" ] || { echo "::error::no AppImage built"; exit 1; }
|
|
101
|
+
DEST="CiCy-Desktop-$VER.AppImage"
|
|
102
|
+
./ossutil cp "$APP" "$BASE/$DEST" -f --acl public-read
|
|
103
|
+
./ossutil cp "$APP" "$BASE/CiCy-Desktop-latest.AppImage" -f --acl public-read || true
|
|
104
|
+
# 版本指针最后写(包传完再更新),避免版本号涨了但包还没就位。
|
|
105
|
+
printf '%s' "$VER" > linux-latest-version.txt
|
|
106
|
+
./ossutil cp linux-latest-version.txt "$BASE/linux-latest-version.txt" -f --acl public-read
|
|
107
|
+
echo "OSS linux: $HOST/$DEST"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cicy-desktop",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.249",
|
|
4
4
|
"description": "CiCy - AI-powered operating system browser",
|
|
5
5
|
"main": "src/main.js",
|
|
6
6
|
"bin": {
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"format:check": "prettier --check \"src/**/*.js\"",
|
|
14
14
|
"build": "electron-builder --publish never",
|
|
15
15
|
"build:homepage": "node scripts/build-homepage.cjs",
|
|
16
|
-
"prebuild:win": "node scripts/build-homepage.cjs",
|
|
17
|
-
"prebuild:mac": "node scripts/build-homepage.cjs",
|
|
18
|
-
"prebuild:linux": "node scripts/build-homepage.cjs",
|
|
16
|
+
"prebuild:win": "node scripts/build-homepage.cjs && node scripts/obfuscate.cjs",
|
|
17
|
+
"prebuild:mac": "node scripts/build-homepage.cjs && node scripts/obfuscate.cjs",
|
|
18
|
+
"prebuild:linux": "node scripts/build-homepage.cjs && node scripts/obfuscate.cjs",
|
|
19
19
|
"prepublishOnly": "node scripts/build-homepage.cjs",
|
|
20
20
|
"build:win": "electron-builder --win",
|
|
21
21
|
"build:mac": "electron-builder --mac",
|
|
@@ -154,6 +154,7 @@
|
|
|
154
154
|
"devDependencies": {
|
|
155
155
|
"electron": "41.0.3",
|
|
156
156
|
"electron-builder": "^26.7.0",
|
|
157
|
+
"javascript-obfuscator": "^5.4.7",
|
|
157
158
|
"prettier": "^3.8.1"
|
|
158
159
|
}
|
|
159
160
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Copyright 2026 CiCy AI
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
// Release-time obfuscation of the MAIN-PROCESS source (src/**/*.js), run as part
|
|
5
|
+
// of the prebuild hooks. Electron ships JS as plaintext inside app.asar (anyone can
|
|
6
|
+
// `asar extract` and read it), so on release we obfuscate our authored source.
|
|
7
|
+
//
|
|
8
|
+
// Gated by CICY_OBFUSCATE=1 — set ONLY in the release workflows. Local/dev builds
|
|
9
|
+
// (Loop B on Mac, plain `npm run build:*`) leave the working tree untouched, so this
|
|
10
|
+
// never dirties source you're editing. CI runs on a disposable checkout.
|
|
11
|
+
//
|
|
12
|
+
// The renderer (src/backends/homepage-react) is EXCLUDED here: it's already
|
|
13
|
+
// Vite-minified (identifiers mangled, comments stripped) and additionally obfuscated
|
|
14
|
+
// by vite-plugin-javascript-obfuscator during its own build. Re-obfuscating the
|
|
15
|
+
// bundled React output post-hoc is high-risk / low-reward, so we don't.
|
|
16
|
+
|
|
17
|
+
const fs = require("fs");
|
|
18
|
+
const path = require("path");
|
|
19
|
+
|
|
20
|
+
if (process.env.CICY_OBFUSCATE !== "1") {
|
|
21
|
+
console.log("[obfuscate] CICY_OBFUSCATE != 1 — skipping (dev build, source untouched).");
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const JavaScriptObfuscator = require("javascript-obfuscator");
|
|
26
|
+
const ROOT = path.join(__dirname, "..");
|
|
27
|
+
const SRC = path.join(ROOT, "src");
|
|
28
|
+
const EXCLUDE_DIRS = [
|
|
29
|
+
path.join(SRC, "backends", "homepage-react"), // Vite-built SPA snapshot (obfuscated by the Vite plugin)
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
// 保守配置:重命名标识符 + 字符串数组(base64)+ compact。**故意关掉** controlFlowFlattening /
|
|
33
|
+
// deadCodeInjection / selfDefending / debugProtection —— 这几项最容易把代码搞坏、且严重拖慢启动。
|
|
34
|
+
// 目标是"读不懂",不是"跑不动"。renameGlobals=false:绝不动 require/module/exports/process 等全局名。
|
|
35
|
+
const OPTIONS = {
|
|
36
|
+
compact: true,
|
|
37
|
+
simplify: true,
|
|
38
|
+
target: "node",
|
|
39
|
+
identifierNamesGenerator: "hexadecimal",
|
|
40
|
+
renameGlobals: false,
|
|
41
|
+
stringArray: true,
|
|
42
|
+
stringArrayThreshold: 0.75,
|
|
43
|
+
stringArrayEncoding: ["base64"],
|
|
44
|
+
splitStrings: false,
|
|
45
|
+
numbersToExpressions: false,
|
|
46
|
+
controlFlowFlattening: false,
|
|
47
|
+
deadCodeInjection: false,
|
|
48
|
+
selfDefending: false,
|
|
49
|
+
debugProtection: false,
|
|
50
|
+
disableConsoleOutput: false,
|
|
51
|
+
unicodeEscapeSequence: false,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
function isExcluded(p) {
|
|
55
|
+
return EXCLUDE_DIRS.some((d) => p === d || p.startsWith(d + path.sep));
|
|
56
|
+
}
|
|
57
|
+
function* walkJs(dir) {
|
|
58
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
59
|
+
const full = path.join(dir, e.name);
|
|
60
|
+
if (isExcluded(full)) continue;
|
|
61
|
+
if (e.isDirectory()) yield* walkJs(full);
|
|
62
|
+
else if (e.isFile() && full.endsWith(".js")) yield full;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let count = 0;
|
|
67
|
+
let outBytes = 0;
|
|
68
|
+
for (const file of walkJs(SRC)) {
|
|
69
|
+
const code = fs.readFileSync(file, "utf8");
|
|
70
|
+
try {
|
|
71
|
+
const out = JavaScriptObfuscator.obfuscate(code, OPTIONS).getObfuscatedCode();
|
|
72
|
+
fs.writeFileSync(file, out);
|
|
73
|
+
count++;
|
|
74
|
+
outBytes += out.length;
|
|
75
|
+
} catch (e) {
|
|
76
|
+
// Fail the build rather than ship a half-obfuscated app.
|
|
77
|
+
console.error(`[obfuscate] FAILED ${path.relative(ROOT, file)}: ${e.message}`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
console.log(`[obfuscate] obfuscated ${count} main-process file(s), ${Math.round(outBytes / 1024)}KB total (renderer handled by the Vite plugin).`);
|
package/src/app-updater.js
CHANGED
|
@@ -19,18 +19,7 @@ const path = require("path");
|
|
|
19
19
|
const fs = require("fs");
|
|
20
20
|
const https = require("https");
|
|
21
21
|
const log = require("electron-log");
|
|
22
|
-
const { OSS_RELEASES_BASE
|
|
23
|
-
|
|
24
|
-
const REPO = "cicy-ai/cicy-desktop";
|
|
25
|
-
const GH_DL = (ver) => `https://github.com/${REPO}/releases/download/v${ver}`;
|
|
26
|
-
|
|
27
|
-
// ── 网络判定(缓存)──────────────────────────────────────────────────────────
|
|
28
|
-
let _network = null;
|
|
29
|
-
async function detectNetwork() {
|
|
30
|
-
if (_network) return _network;
|
|
31
|
-
try { _network = await require("./sidecar/net-detect")(); } catch { _network = "unknown"; }
|
|
32
|
-
return _network;
|
|
33
|
-
}
|
|
22
|
+
const { OSS_RELEASES_BASE } = require("./sidecar/mirrors");
|
|
34
23
|
|
|
35
24
|
// ── 版本比较:>0 a 新于 b ─────────────────────────────────────────────────────
|
|
36
25
|
function cmpVer(a, b) {
|
|
@@ -61,12 +50,6 @@ function getText(url, redirects = 0) {
|
|
|
61
50
|
});
|
|
62
51
|
}
|
|
63
52
|
|
|
64
|
-
// CN 走 ghproxy 兜底(OSS 没有 mac/linux 包)。
|
|
65
|
-
function mirrored(githubUrl) {
|
|
66
|
-
const m = MIRRORS[0];
|
|
67
|
-
return m ? mirrorUrl(githubUrl, m) : githubUrl;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
53
|
// HEAD 探测 URL 是否可下(200/206 = ok)。跟随重定向(GitHub release 会 302 到 CDN)。
|
|
71
54
|
// 用于「先出包再更新版本」的客户端保险:版本号涨了但包还没传 → HEAD 404 → 先不报更新。
|
|
72
55
|
function headOk(url, redirects = 0) {
|
|
@@ -89,40 +72,25 @@ function headOk(url, redirects = 0) {
|
|
|
89
72
|
}
|
|
90
73
|
|
|
91
74
|
// ── 最新版本号 ────────────────────────────────────────────────────────────────
|
|
92
|
-
async function fetchLatestVersion(
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// 才走 GitHub;cn/unknown 一律读 OSS。按平台读各自指针(win/mac 两条独立 CI、版本可能不
|
|
101
|
-
// 同步,读错平台会 404);linux 无独立 OSS 指针、沿用裸名(与 win 同 tag、走 GitHub 下载)。
|
|
102
|
-
if (network !== "global") {
|
|
103
|
-
const file = process.platform === "darwin" ? "mac-latest-version.txt" : "latest-version.txt";
|
|
104
|
-
return (await getText(`${OSS_RELEASES_BASE}/${file}`)).trim().replace(/^v/, "");
|
|
105
|
-
}
|
|
106
|
-
// 确认可达 GitHub:releases/latest 的 tag_name。
|
|
107
|
-
const j = JSON.parse(await getText(`https://api.github.com/repos/${REPO}/releases/latest`));
|
|
108
|
-
return String(j.tag_name || "").trim().replace(/^v/, "");
|
|
75
|
+
async function fetchLatestVersion() {
|
|
76
|
+
// 版本清单**一律读 OSS**(win/mac/linux 各自指针),与网络判定无关 —— 彻底不碰 GitHub,
|
|
77
|
+
// 这样 cicy-desktop 仓库私有也不影响更新检查。win/mac 是两条独立 CI、版本可能不同步,读错
|
|
78
|
+
// 平台会 404,所以各读各的指针文件。
|
|
79
|
+
const file = process.platform === "win32" ? "win-latest-version.txt"
|
|
80
|
+
: process.platform === "darwin" ? "mac-latest-version.txt"
|
|
81
|
+
: "linux-latest-version.txt";
|
|
82
|
+
return (await getText(`${OSS_RELEASES_BASE}/${file}`)).trim().replace(/^v/, "");
|
|
109
83
|
}
|
|
110
84
|
|
|
111
85
|
// ── 安装包 URL(分平台 + 分网络)+ 本地文件名 ────────────────────────────────
|
|
112
|
-
function assetFor(version
|
|
86
|
+
function assetFor(version) {
|
|
113
87
|
const plat = process.platform;
|
|
114
88
|
const arch = process.arch === "arm64" ? "arm64" : "x64";
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const gh = `${GH_DL(version)}/cicy-desktop-${version}-${arch}.pkg`;
|
|
121
|
-
return { url: network === "global" ? gh : mirrored(gh), file: `cicy-desktop-${version}-${arch}.pkg` };
|
|
122
|
-
}
|
|
123
|
-
// linux
|
|
124
|
-
const gh = `${GH_DL(version)}/CiCy-Desktop-${version}.AppImage`;
|
|
125
|
-
return { url: network === "global" ? gh : mirrored(gh), file: `CiCy-Desktop-${version}.AppImage` };
|
|
89
|
+
// 三端安装包**全部从 OSS 拉**,与网络无关 —— 不再拼 GitHub 下载 URL,仓库私有也能更新。
|
|
90
|
+
const file = plat === "win32" ? `cicy-desktop-${version}.exe`
|
|
91
|
+
: plat === "darwin" ? `cicy-desktop-${version}-${arch}.pkg`
|
|
92
|
+
: `CiCy-Desktop-${version}.AppImage`;
|
|
93
|
+
return { url: `${OSS_RELEASES_BASE}/${file}`, file };
|
|
126
94
|
}
|
|
127
95
|
|
|
128
96
|
// ── 下载(带进度,跟随重定向)──────────────────────────────────────────────────
|
|
@@ -187,13 +155,12 @@ function init(mainWin) {
|
|
|
187
155
|
async function check() {
|
|
188
156
|
try {
|
|
189
157
|
broadcast({ status: "checking", error: null });
|
|
190
|
-
const
|
|
191
|
-
const latest = await fetchLatestVersion(net);
|
|
158
|
+
const latest = await fetchLatestVersion();
|
|
192
159
|
const current = app.getVersion();
|
|
193
160
|
if (latest && cmpVer(latest, current) > 0) {
|
|
194
161
|
// 「先出包再更新版本」客户端保险:版本号涨了不代表包传完了。先 HEAD 确认本平台安装包
|
|
195
162
|
// 真能下(200),否则当成「还没就绪」继续显已是最新,避免用户点下载 404。
|
|
196
|
-
const { url } = assetFor(latest
|
|
163
|
+
const { url } = assetFor(latest);
|
|
197
164
|
const ready = await headOk(url);
|
|
198
165
|
if (ready) broadcast({ status: "available", version: latest, current, progress: null, filePath: null });
|
|
199
166
|
else { log.info(`[app-updater] ${latest} 版本号已更新但安装包未就位(HEAD 非 200):${url} — 暂不提示更新`); broadcast({ status: "up-to-date", version: current, current }); }
|
|
@@ -215,8 +182,7 @@ async function downloadUpdate() {
|
|
|
215
182
|
if (!version) { broadcast({ status: "error", error: "no version" }); return _state; }
|
|
216
183
|
_downloading = true;
|
|
217
184
|
try {
|
|
218
|
-
const
|
|
219
|
-
const { url, file } = assetFor(version, net);
|
|
185
|
+
const { url, file } = assetFor(version);
|
|
220
186
|
// 下到 ~/Downloads(用户能直接找到安装包);目录不存在则建。
|
|
221
187
|
const dir = app.getPath("downloads");
|
|
222
188
|
try { fs.mkdirSync(dir, { recursive: true }); } catch {}
|