@geminilight/mindos 0.6.13 → 0.6.15
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/app/components/settings/UpdateTab.tsx +8 -1
- package/bin/cli.js +37 -14
- package/package.json +1 -1
- package/scripts/test-oss-upload.sh +100 -0
|
@@ -202,7 +202,14 @@ function DesktopUpdateTab() {
|
|
|
202
202
|
|
|
203
203
|
{state === 'ready' && (
|
|
204
204
|
<button
|
|
205
|
-
onClick={() =>
|
|
205
|
+
onClick={async () => {
|
|
206
|
+
try {
|
|
207
|
+
await bridge.installUpdate();
|
|
208
|
+
} catch {
|
|
209
|
+
setState('error');
|
|
210
|
+
setErrorMsg(u?.error ?? 'Failed to install update. Please try again.');
|
|
211
|
+
}
|
|
212
|
+
}}
|
|
206
213
|
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg font-medium text-[var(--amber-foreground)] bg-[var(--amber)] transition-colors"
|
|
207
214
|
>
|
|
208
215
|
<RefreshCw size={12} />
|
package/bin/cli.js
CHANGED
|
@@ -771,16 +771,19 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
771
771
|
|
|
772
772
|
// Stage 3: Rebuild
|
|
773
773
|
writeUpdateStatus('rebuilding', vOpts);
|
|
774
|
-
|
|
774
|
+
let daemonBuildFailed = '';
|
|
775
|
+
try {
|
|
776
|
+
buildIfNeeded(updatedRoot);
|
|
777
|
+
} catch (err) {
|
|
778
|
+
daemonBuildFailed = err instanceof Error ? err.message : String(err);
|
|
779
|
+
console.error(yellow(`\n Pre-build failed: ${daemonBuildFailed}`));
|
|
780
|
+
console.error(yellow(' Daemon will attempt to rebuild on startup...\n'));
|
|
781
|
+
}
|
|
775
782
|
|
|
776
|
-
// Stage 4: Restart
|
|
783
|
+
// Stage 4: Restart — always attempt, even if pre-build failed
|
|
784
|
+
// (daemon has auto-restart; `mindos start` retries the build)
|
|
777
785
|
writeUpdateStatus('restarting', vOpts);
|
|
778
786
|
await runGatewayCommand('install');
|
|
779
|
-
// install() starts the service:
|
|
780
|
-
// - systemd: daemon-reload + enable + start
|
|
781
|
-
// - launchd: bootstrap (RunAtLoad=true auto-starts)
|
|
782
|
-
// Do NOT call start() again — on macOS kickstart -k would kill the
|
|
783
|
-
// just-started process, causing a port-conflict race with KeepAlive.
|
|
784
787
|
const updateConfig = (() => {
|
|
785
788
|
try { return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')); } catch { return {}; }
|
|
786
789
|
})();
|
|
@@ -799,8 +802,11 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
799
802
|
console.log(`${'─'.repeat(53)}\n`);
|
|
800
803
|
writeUpdateStatus('done', vOpts);
|
|
801
804
|
} else {
|
|
802
|
-
|
|
803
|
-
|
|
805
|
+
const failMsg = daemonBuildFailed
|
|
806
|
+
? `Build failed (${daemonBuildFailed}), server did not come back up`
|
|
807
|
+
: 'Server did not come back up in time';
|
|
808
|
+
writeUpdateFailed('restarting', failMsg, vOpts);
|
|
809
|
+
console.error(red(`✘ ${failMsg}. Check logs: mindos logs\n`));
|
|
804
810
|
process.exit(1);
|
|
805
811
|
}
|
|
806
812
|
} else {
|
|
@@ -828,9 +834,17 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
828
834
|
|
|
829
835
|
// Stage 3: Rebuild
|
|
830
836
|
writeUpdateStatus('rebuilding', vOpts);
|
|
831
|
-
|
|
837
|
+
let buildFailed = '';
|
|
838
|
+
try {
|
|
839
|
+
buildIfNeeded(updatedRoot);
|
|
840
|
+
} catch (err) {
|
|
841
|
+
buildFailed = err instanceof Error ? err.message : String(err);
|
|
842
|
+
console.error(yellow(`\n Pre-build failed: ${buildFailed}`));
|
|
843
|
+
console.error(yellow(' Starting server anyway (it will retry the build)...\n'));
|
|
844
|
+
}
|
|
832
845
|
|
|
833
|
-
// Stage 4: Restart
|
|
846
|
+
// Stage 4: Restart — always attempt, even if pre-build failed
|
|
847
|
+
// (`mindos start` has its own build-on-startup logic)
|
|
834
848
|
writeUpdateStatus('restarting', vOpts);
|
|
835
849
|
const newCliPath = resolve(updatedRoot, 'bin', 'cli.js');
|
|
836
850
|
const childEnv = { ...process.env };
|
|
@@ -858,13 +872,22 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
858
872
|
console.log(`${'─'.repeat(53)}\n`);
|
|
859
873
|
writeUpdateStatus('done', vOpts);
|
|
860
874
|
} else {
|
|
861
|
-
|
|
862
|
-
|
|
875
|
+
const failMsg = buildFailed
|
|
876
|
+
? `Build failed (${buildFailed}), server did not come back up`
|
|
877
|
+
: 'Server did not come back up in time';
|
|
878
|
+
writeUpdateFailed('restarting', failMsg, vOpts);
|
|
879
|
+
console.error(red(`✘ ${failMsg}. Check logs: mindos logs\n`));
|
|
863
880
|
process.exit(1);
|
|
864
881
|
}
|
|
865
882
|
} else {
|
|
866
883
|
// No running instance — just build and tell user to start manually
|
|
867
|
-
|
|
884
|
+
try {
|
|
885
|
+
buildIfNeeded(updatedRoot);
|
|
886
|
+
} catch (err) {
|
|
887
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
888
|
+
console.error(yellow(`\n Pre-build failed: ${msg}`));
|
|
889
|
+
console.error(dim(' The build will be retried when you run `mindos start`.'));
|
|
890
|
+
}
|
|
868
891
|
console.log(`\n${green('✔')} ${bold(`Updated: ${currentVersion} → ${newVersion}`)}`);
|
|
869
892
|
console.log(dim(' Run `mindos start` to start the updated version.'));
|
|
870
893
|
console.log(` ${dim('View changelog:')} ${cyan('https://github.com/GeminiLight/MindOS/releases')}\n`);
|
package/package.json
CHANGED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 阿里云 OSS 上传测试(纯 curl,和 CI workflow 用同样的签名方式)
|
|
3
|
+
# 用法:
|
|
4
|
+
# export OSS_ACCESS_KEY_ID=你的ID
|
|
5
|
+
# export OSS_ACCESS_KEY_SECRET=你的Secret
|
|
6
|
+
# export OSS_ENDPOINT=oss-cn-hangzhou.aliyuncs.com # 不带 https://
|
|
7
|
+
# export OSS_BUCKET=mindos-cn-releases
|
|
8
|
+
# bash scripts/test-oss-upload.sh
|
|
9
|
+
|
|
10
|
+
set -e
|
|
11
|
+
|
|
12
|
+
: "${OSS_ACCESS_KEY_ID:?请设置 OSS_ACCESS_KEY_ID}"
|
|
13
|
+
: "${OSS_ACCESS_KEY_SECRET:?请设置 OSS_ACCESS_KEY_SECRET}"
|
|
14
|
+
: "${OSS_ENDPOINT:?请设置 OSS_ENDPOINT (不带 https://)}"
|
|
15
|
+
: "${OSS_BUCKET:?请设置 OSS_BUCKET}"
|
|
16
|
+
|
|
17
|
+
echo "=== OSS 上传测试 ==="
|
|
18
|
+
echo "Bucket: $OSS_BUCKET"
|
|
19
|
+
echo "Endpoint: $OSS_ENDPOINT"
|
|
20
|
+
echo "URL: https://${OSS_BUCKET}.${OSS_ENDPOINT}/"
|
|
21
|
+
echo ""
|
|
22
|
+
|
|
23
|
+
# 检查 endpoint 格式
|
|
24
|
+
if [[ "$OSS_ENDPOINT" == https://* ]]; then
|
|
25
|
+
echo "错误: OSS_ENDPOINT 不要带 https:// 前缀"
|
|
26
|
+
echo "当前值: $OSS_ENDPOINT"
|
|
27
|
+
echo "应该是: oss-cn-hangzhou.aliyuncs.com"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# 创建测试文件
|
|
32
|
+
OBJECT_KEY="test-upload-$(date +%s).txt"
|
|
33
|
+
CONTENT_TYPE="text/plain"
|
|
34
|
+
TMP_FILE="/tmp/oss-test-$$.txt"
|
|
35
|
+
echo "hello from MindOS CI test at $(date)" > "$TMP_FILE"
|
|
36
|
+
|
|
37
|
+
# 上传函数(和 CI workflow 完全一致)
|
|
38
|
+
oss_upload() {
|
|
39
|
+
local file="$1"
|
|
40
|
+
local object_key="$2"
|
|
41
|
+
local content_type="${3:-application/octet-stream}"
|
|
42
|
+
local date_header=$(date -u +"%a, %d %b %Y %H:%M:%S GMT")
|
|
43
|
+
local resource="/${OSS_BUCKET}/${object_key}"
|
|
44
|
+
local string_to_sign="PUT\n\n${content_type}\n${date_header}\n${resource}"
|
|
45
|
+
local signature=$(printf "$string_to_sign" | openssl dgst -sha1 -hmac "$OSS_ACCESS_KEY_SECRET" -binary | base64)
|
|
46
|
+
local url="https://${OSS_BUCKET}.${OSS_ENDPOINT}/${object_key}"
|
|
47
|
+
|
|
48
|
+
echo " URL: $url"
|
|
49
|
+
local http_code=$(curl -s -o /tmp/oss-response.txt -w "%{http_code}" \
|
|
50
|
+
-X PUT \
|
|
51
|
+
-H "Date: ${date_header}" \
|
|
52
|
+
-H "Content-Type: ${content_type}" \
|
|
53
|
+
-H "Authorization: OSS ${OSS_ACCESS_KEY_ID}:${signature}" \
|
|
54
|
+
-T "$file" \
|
|
55
|
+
"$url")
|
|
56
|
+
|
|
57
|
+
if [ "$http_code" = "200" ]; then
|
|
58
|
+
echo " 上传成功 (HTTP $http_code)"
|
|
59
|
+
return 0
|
|
60
|
+
else
|
|
61
|
+
echo " 上传失败 (HTTP $http_code)"
|
|
62
|
+
cat /tmp/oss-response.txt 2>/dev/null
|
|
63
|
+
echo ""
|
|
64
|
+
return 1
|
|
65
|
+
fi
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# 1. 上传
|
|
69
|
+
echo "1. 上传测试文件: $OBJECT_KEY"
|
|
70
|
+
oss_upload "$TMP_FILE" "$OBJECT_KEY" "text/plain"
|
|
71
|
+
|
|
72
|
+
# 2. 公网访问
|
|
73
|
+
echo ""
|
|
74
|
+
echo "2. 公网访问测试:"
|
|
75
|
+
PUBLIC_URL="https://${OSS_BUCKET}.${OSS_ENDPOINT}/${OBJECT_KEY}"
|
|
76
|
+
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$PUBLIC_URL")
|
|
77
|
+
if [ "$HTTP_CODE" = "200" ]; then
|
|
78
|
+
echo " 公网访问成功 (HTTP $HTTP_CODE)"
|
|
79
|
+
echo " 内容: $(curl -s "$PUBLIC_URL")"
|
|
80
|
+
else
|
|
81
|
+
echo " 公网访问失败 (HTTP $HTTP_CODE) — 请在 OSS 控制台开启公共读"
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# 3. 清理
|
|
85
|
+
echo ""
|
|
86
|
+
echo "3. 清理测试文件..."
|
|
87
|
+
DATE_HEADER=$(date -u +"%a, %d %b %Y %H:%M:%S GMT")
|
|
88
|
+
RESOURCE="/${OSS_BUCKET}/${OBJECT_KEY}"
|
|
89
|
+
STRING_TO_SIGN="DELETE\n\n\n${DATE_HEADER}\n${RESOURCE}"
|
|
90
|
+
SIGNATURE=$(printf "$STRING_TO_SIGN" | openssl dgst -sha1 -hmac "$OSS_ACCESS_KEY_SECRET" -binary | base64)
|
|
91
|
+
curl -s -o /dev/null \
|
|
92
|
+
-X DELETE \
|
|
93
|
+
-H "Date: ${DATE_HEADER}" \
|
|
94
|
+
-H "Authorization: OSS ${OSS_ACCESS_KEY_ID}:${SIGNATURE}" \
|
|
95
|
+
"https://${OSS_BUCKET}.${OSS_ENDPOINT}/${OBJECT_KEY}"
|
|
96
|
+
echo " 已清理"
|
|
97
|
+
|
|
98
|
+
rm -f "$TMP_FILE" /tmp/oss-response.txt
|
|
99
|
+
echo ""
|
|
100
|
+
echo "=== 测试完成 ==="
|