aaden-cli 1.6.117 → 1.6.119
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/AadenDeploy/appCenterSync.sh +22 -10
- package/dist/index.js +71 -8
- package/modules/tse-info.js +70 -7
- package/package.json +1 -1
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
#
|
|
5
5
|
# light-update 只发 PHP/,AppCenter 以前只能跟 465MB 的 latest.zip 走;这里单独按应用同步。
|
|
6
6
|
# - 灰度门:manifest 的 rollout = {"devices":[...], "percent":N},本机在名单里或 deviceId%100 < N 才动;没有 rollout = 全量
|
|
7
|
+
# - 按应用灰度:manifest 带 perAppRollout=true 时,每个应用按自己的 apps.<App>.rollout 判断(没有 = 全量),
|
|
8
|
+
# 顶层 rollout 只管 ensureNoCache;老版本 CLI 不认 perAppRollout,照旧整体按顶层 rollout
|
|
7
9
|
# - ensureNoCache:manifest 打开时先确保 AppCenter 的 index.html 带 no-cache(ensureAppCenterNoCache.sh)
|
|
8
10
|
# - manifest 门:本机 .appcenter-manifest 的 sha256 == manifest 的 treeSha256 且 `sha256sum -c` 通过 → 什么都不做,
|
|
9
11
|
# 每天只花一次几 KB 的请求。被 latest.zip 全量覆盖过、文件对不上 → 校验失败 → 重新铺一遍(自愈)
|
|
@@ -19,7 +21,7 @@
|
|
|
19
21
|
# APPCENTER_DEVICE_ID 本机 deviceId(CLI 会传);不传就问本机 PHP
|
|
20
22
|
# APPCENTER_RESULT 结果行另存的文件(CLI 会传)
|
|
21
23
|
# 结果行(同时写进 $RESULT 给 CLI 上报):APP_OK <App> <sha12> | APP_UP_TO_DATE <App> | APP_SKIP <App> <原因> |
|
|
22
|
-
# APP_ROLLED_BACK <App> | APP_ABORT_* <App> | NOCACHE_* | SKIP_ROLLOUT | SKIP_DEPS | SKIP_LOCKED | ABORT_*
|
|
24
|
+
# APP_ROLLED_BACK <App> | APP_ABORT_* <App> | APP_SKIP_ROLLOUT <App> | NOCACHE_* | SKIP_ROLLOUT | SKIP_DEPS | SKIP_LOCKED | ABORT_*
|
|
23
25
|
# 退出码:0=全部成功、无需更新或不在灰度内;1=有失败(失败的已回滚或未动线上目录)
|
|
24
26
|
set -uo pipefail
|
|
25
27
|
BASE="${APPCENTER_BASE:-https://aaden-backend.s3.eu-central-1.amazonaws.com/appcenter}"
|
|
@@ -48,21 +50,29 @@ flock -n 9 || { res "SKIP_LOCKED"; exit 0; }
|
|
|
48
50
|
|
|
49
51
|
MANIFEST=$(curl -fsSL --max-time 30 "$BASE/$CHANNEL/manifest.json") \
|
|
50
52
|
|| { echo "取不到 $BASE/$CHANNEL/manifest.json"; res "ABORT_MANIFEST"; exit 1; }
|
|
51
|
-
# 读 manifest:mf apps.Admin.zip / mf ensureNoCache / mf eligible <deviceId>
|
|
53
|
+
# 读 manifest:mf apps.Admin.zip / mf ensureNoCache / mf eligible <deviceId> / mf eligible_app <App> <deviceId>
|
|
52
54
|
mf(){
|
|
53
55
|
printf '%s' "$MANIFEST" | python3 -c '
|
|
54
56
|
import json, sys
|
|
55
57
|
m = json.load(sys.stdin)
|
|
56
|
-
|
|
57
|
-
r = m.get("rollout")
|
|
58
|
+
def gate(r, dev):
|
|
58
59
|
if r is None:
|
|
59
|
-
|
|
60
|
+
return "yes"
|
|
60
61
|
try:
|
|
61
|
-
d = int(
|
|
62
|
+
d = int(dev)
|
|
62
63
|
except ValueError:
|
|
63
|
-
|
|
64
|
+
return "no"
|
|
64
65
|
ok = d in [int(x) for x in r.get("devices", [])] or d % 100 < int(r.get("percent", 0))
|
|
65
|
-
|
|
66
|
+
return "yes" if ok else "no"
|
|
67
|
+
if sys.argv[1] == "eligible":
|
|
68
|
+
print(gate(m.get("rollout"), sys.argv[2])); sys.exit()
|
|
69
|
+
if sys.argv[1] == "eligible_app":
|
|
70
|
+
if m.get("perAppRollout") is True:
|
|
71
|
+
app = (m.get("apps") or {}).get(sys.argv[2]) or {}
|
|
72
|
+
print(gate(app.get("rollout"), sys.argv[3]))
|
|
73
|
+
else:
|
|
74
|
+
print(gate(m.get("rollout"), sys.argv[3]))
|
|
75
|
+
sys.exit()
|
|
66
76
|
v = m
|
|
67
77
|
for k in sys.argv[1].split("."):
|
|
68
78
|
v = v.get(k) if isinstance(v, dict) else None
|
|
@@ -76,13 +86,14 @@ if [ -z "$DEVICE_ID" ] || [ "$DEVICE_ID" = "-1" ]; then
|
|
|
76
86
|
DEVICE_ID=$(curl -s --max-time 10 "http://localhost/PHP/AccessLog.php?op=deviceId" \
|
|
77
87
|
| python3 -c 'import json,sys; print(json.load(sys.stdin).get("content",""))' 2>/dev/null)
|
|
78
88
|
fi
|
|
79
|
-
|
|
89
|
+
TOP_ELIGIBLE=$(mf eligible "${DEVICE_ID:-x}")
|
|
90
|
+
if [ "$TOP_ELIGIBLE" != "yes" ] && [ "$(mf perAppRollout)" != "true" ]; then
|
|
80
91
|
res "SKIP_ROLLOUT device=${DEVICE_ID:-?}"; exit 0
|
|
81
92
|
fi
|
|
82
93
|
|
|
83
94
|
FAILED=0
|
|
84
95
|
# ---- index.html no-cache(先于换前端:让之后拿到的 index.html 都不再被启发式缓存)----
|
|
85
|
-
if [ "$(mf ensureNoCache)" = "true" ]; then
|
|
96
|
+
if [ "$TOP_ELIGIBLE" = "yes" ] && [ "$(mf ensureNoCache)" = "true" ]; then
|
|
86
97
|
NC=$(bash "$HERE/ensureAppCenterNoCache.sh"); NC_CODE=$?
|
|
87
98
|
printf '%s\n' "$NC" | sed '$d'
|
|
88
99
|
res "$(printf '%s\n' "$NC" | tail -1)"
|
|
@@ -101,6 +112,7 @@ sync_app(){
|
|
|
101
112
|
local APP="$1" LIVE="$WEB/$1" NEW="$STAGE/$1" TREE ZIP ZSHA
|
|
102
113
|
TREE=$(mf "apps.$APP.treeSha256"); ZIP=$(mf "apps.$APP.zip"); ZSHA=$(mf "apps.$APP.zipSha256")
|
|
103
114
|
if [ -z "$TREE" ]; then res "APP_SKIP $APP 不在manifest里"; return 0; fi
|
|
115
|
+
if [ "$(mf eligible_app "$APP" "${DEVICE_ID:-x}")" != "yes" ]; then res "APP_SKIP_ROLLOUT $APP"; return 0; fi
|
|
104
116
|
# manifest 来自网络:只接受预期格式,防止路径穿越
|
|
105
117
|
if ! [[ "$TREE" =~ ^[0-9a-f]{64}$ && "$ZSHA" =~ ^[0-9a-f]{64}$ && "$ZIP" =~ ^blobs/[A-Za-z0-9._-]+\.zip$ ]]; then
|
|
106
118
|
res "APP_ABORT_BAD_MANIFEST $APP"; return 1
|
package/dist/index.js
CHANGED
|
@@ -694,6 +694,15 @@ const WINDOWS_TSE_DIR = 'Desktop/AadenTseOnWindows'
|
|
|
694
694
|
const WINDOWS_DEFAULT_JAR = 'tse-0.0.3.jar'
|
|
695
695
|
// 和 PHP 的 Api.php 一样给 3 秒
|
|
696
696
|
const PROBE_TIMEOUT_MS = 3000
|
|
697
|
+
const PING_RETRY_DELAY_MS = 20000
|
|
698
|
+
|
|
699
|
+
function safeSync(fn, fallback = null) {
|
|
700
|
+
try {
|
|
701
|
+
return fn()
|
|
702
|
+
} catch (e) {
|
|
703
|
+
return fallback
|
|
704
|
+
}
|
|
705
|
+
}
|
|
697
706
|
|
|
698
707
|
async function safe(fn, fallback = null) {
|
|
699
708
|
try {
|
|
@@ -776,20 +785,67 @@ function servingJarPath() {
|
|
|
776
785
|
return match ? match[1] : LINUX_DEFAULT_JAR
|
|
777
786
|
}
|
|
778
787
|
|
|
779
|
-
//
|
|
780
|
-
function
|
|
788
|
+
// 只读门店库,返回第一行按 tab 分开的各列(没有结果为 null)
|
|
789
|
+
function queryDb(sql) {
|
|
781
790
|
const db = readIniSection('DB')
|
|
782
791
|
const out = execFileSync('mysql', [
|
|
783
792
|
'-h', db.host || 'localhost', '-u', db.user || 'aaden', '-N', '-B', '--raw', db.name || 'aaden',
|
|
784
|
-
'-e',
|
|
793
|
+
'-e', sql,
|
|
785
794
|
], {
|
|
786
795
|
encoding: 'utf-8', timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'],
|
|
787
796
|
// 密码走环境变量,不出现在进程列表里
|
|
788
797
|
env: Object.assign({}, process.env, {MYSQL_PWD: db.password || ''}),
|
|
789
798
|
}).trim()
|
|
790
799
|
if (!out) return null
|
|
791
|
-
|
|
792
|
-
|
|
800
|
+
return out.split('\n')[0].split('\t').map(v => (v === 'NULL' ? null : v))
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// 门店库里最新的那块 TSE(换过 TSE 会有多行);只读数据库,TSE 坏了也读得到
|
|
804
|
+
function readCertFromDb() {
|
|
805
|
+
const row = queryDb('select serialNumber, certificate from RealTseInfo order by id desc limit 1')
|
|
806
|
+
if (!row) return null
|
|
807
|
+
const [serial, certificate] = row
|
|
808
|
+
return {serial, certificate: certificate || null}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// 结账后签名任务进 TseExecuteQueue(finish / finish-zero),后台几秒内执行;
|
|
812
|
+
// 结账超过 5 分钟还没有签名的就算漏签(任务报错、卡在队列里、或者 TSE 回了错)。
|
|
813
|
+
// 同一张单可能有多条任务(补签),按单算
|
|
814
|
+
const DUE_ORDERS = `select q.orderId, max(q.createdAt) as createdAt, max(q.id) as taskId,
|
|
815
|
+
exists (select 1 from RealTseLog l
|
|
816
|
+
where l.orderId = q.orderId and l.bestellungId is null and l.signature <> '') as signed
|
|
817
|
+
from TseExecuteQueue q
|
|
818
|
+
where q.executeType in ('finish', 'finish-zero')
|
|
819
|
+
and q.createdAt >= now() - interval 24 hour
|
|
820
|
+
and q.createdAt <= now() - interval 5 minute
|
|
821
|
+
group by q.orderId`
|
|
822
|
+
|
|
823
|
+
// 最近 24 小时结账的单有没有真的签上:这是 TSE 有没有坏的实际结果,比 ping 准——
|
|
824
|
+
// 关店、睡眠、开机自检时没人结账,不会算成故障;真坏了也一定有单签不上
|
|
825
|
+
function readSigningFromDb() {
|
|
826
|
+
const counts = queryDb(`select count(*), coalesce(sum(not signed), 0),
|
|
827
|
+
unix_timestamp(max(if(signed, createdAt, null))),
|
|
828
|
+
unix_timestamp(max(if(signed, null, createdAt)))
|
|
829
|
+
from (${DUE_ORDERS}) t`)
|
|
830
|
+
if (!counts) return null
|
|
831
|
+
const [due, unsigned, lastSigned, lastUnsigned] = counts
|
|
832
|
+
// 最近一张漏签的单为什么没签上:TSE 回的错(RealTseLog.tseError),没有就看队列任务的报错 / 还在排队
|
|
833
|
+
// 单独兜底:原因查不出来(比如两张表排序规则不一样)也不能连单数一起丢
|
|
834
|
+
const reason = Number(unsigned) > 0 ? safeSync(() => queryDb(`select coalesce(
|
|
835
|
+
(select l.tseError from RealTseLog l where l.orderId = t.orderId and l.tseError <> '' order by l.id desc limit 1),
|
|
836
|
+
nullif(q.note, ''),
|
|
837
|
+
if(q.finished = 0, '签名任务还在排队', null))
|
|
838
|
+
from (${DUE_ORDERS}) t join TseExecuteQueue q on q.id = t.taskId
|
|
839
|
+
where not t.signed
|
|
840
|
+
order by t.createdAt desc limit 1`)) : null
|
|
841
|
+
const toIso = sec => (sec ? new Date(Number(sec) * 1000).toISOString() : null)
|
|
842
|
+
return {
|
|
843
|
+
tseOrders24h: Number(due) || 0,
|
|
844
|
+
tseUnsigned24h: Number(unsigned) || 0,
|
|
845
|
+
tseLastSignedAt: toIso(lastSigned),
|
|
846
|
+
tseLastUnsignedAt: toIso(lastUnsigned),
|
|
847
|
+
tseLastUnsignedReason: reason && reason[0] ? reason[0].slice(0, 250) : null,
|
|
848
|
+
}
|
|
793
849
|
}
|
|
794
850
|
|
|
795
851
|
// TSE 自己的序列号、证书有效期和型号(dnQualifier:BSI-DSZ-CC-1121 是第一代,-V2 是第二代)
|
|
@@ -825,7 +881,12 @@ async function probeTse() {
|
|
|
825
881
|
}
|
|
826
882
|
|
|
827
883
|
// ok:TSE 正常;error:服务在但 TSE 在自检 / 没初始化 / 硬件报错;down:服务连不上
|
|
828
|
-
|
|
884
|
+
// 开机 / 唤醒时 TseHost 先自检约 15 秒,这期间 /ping 回 400:失败就等 20 秒再试一次,免得报成故障
|
|
885
|
+
let ping = await httpGet(`${tseHost}/ping`)
|
|
886
|
+
if (ping.status !== 200) {
|
|
887
|
+
await new Promise(resolve => setTimeout(resolve, PING_RETRY_DELAY_MS))
|
|
888
|
+
ping = await httpGet(`${tseHost}/ping`)
|
|
889
|
+
}
|
|
829
890
|
const tseStatus = ping.status === 0 ? 'down' : (ping.status === 200 ? 'ok' : 'error')
|
|
830
891
|
|
|
831
892
|
// 连不上时报 null,云端保留上次的版本;服务在但没有这个接口(2024-09 之前的构建)报空串
|
|
@@ -843,8 +904,10 @@ async function probeTse() {
|
|
|
843
904
|
}
|
|
844
905
|
|
|
845
906
|
const cert = await collectCert(tseHost, ping.status !== 0)
|
|
907
|
+
// 老版本门店库没有 TseExecuteQueue(2025-03 之前)就不报,云端保留上次的值
|
|
908
|
+
const signing = (await safe(readSigningFromDb)) || {}
|
|
846
909
|
|
|
847
|
-
return {tseEnabled, tseHost, tseStatus, tseAdapterVersion, tseJarSize, ...cert}
|
|
910
|
+
return {tseEnabled, tseHost, tseStatus, tseAdapterVersion, tseJarSize, ...cert, ...signing}
|
|
848
911
|
}
|
|
849
912
|
|
|
850
913
|
// 整体出错就什么都不报,云端保留上次的值
|
|
@@ -19183,7 +19246,7 @@ module.exports = JSON.parse('{"name":"axios","version":"0.19.2","description":"P
|
|
|
19183
19246
|
/***/ ((module) => {
|
|
19184
19247
|
|
|
19185
19248
|
"use strict";
|
|
19186
|
-
module.exports = {"i8":"1.6.
|
|
19249
|
+
module.exports = {"i8":"1.6.119"};
|
|
19187
19250
|
|
|
19188
19251
|
/***/ })
|
|
19189
19252
|
|
package/modules/tse-info.js
CHANGED
|
@@ -21,6 +21,15 @@ const WINDOWS_TSE_DIR = 'Desktop/AadenTseOnWindows'
|
|
|
21
21
|
const WINDOWS_DEFAULT_JAR = 'tse-0.0.3.jar'
|
|
22
22
|
// 和 PHP 的 Api.php 一样给 3 秒
|
|
23
23
|
const PROBE_TIMEOUT_MS = 3000
|
|
24
|
+
const PING_RETRY_DELAY_MS = 20000
|
|
25
|
+
|
|
26
|
+
function safeSync(fn, fallback = null) {
|
|
27
|
+
try {
|
|
28
|
+
return fn()
|
|
29
|
+
} catch (e) {
|
|
30
|
+
return fallback
|
|
31
|
+
}
|
|
32
|
+
}
|
|
24
33
|
|
|
25
34
|
async function safe(fn, fallback = null) {
|
|
26
35
|
try {
|
|
@@ -103,20 +112,67 @@ function servingJarPath() {
|
|
|
103
112
|
return match ? match[1] : LINUX_DEFAULT_JAR
|
|
104
113
|
}
|
|
105
114
|
|
|
106
|
-
//
|
|
107
|
-
function
|
|
115
|
+
// 只读门店库,返回第一行按 tab 分开的各列(没有结果为 null)
|
|
116
|
+
function queryDb(sql) {
|
|
108
117
|
const db = readIniSection('DB')
|
|
109
118
|
const out = execFileSync('mysql', [
|
|
110
119
|
'-h', db.host || 'localhost', '-u', db.user || 'aaden', '-N', '-B', '--raw', db.name || 'aaden',
|
|
111
|
-
'-e',
|
|
120
|
+
'-e', sql,
|
|
112
121
|
], {
|
|
113
122
|
encoding: 'utf-8', timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'],
|
|
114
123
|
// 密码走环境变量,不出现在进程列表里
|
|
115
124
|
env: Object.assign({}, process.env, {MYSQL_PWD: db.password || ''}),
|
|
116
125
|
}).trim()
|
|
117
126
|
if (!out) return null
|
|
118
|
-
|
|
119
|
-
|
|
127
|
+
return out.split('\n')[0].split('\t').map(v => (v === 'NULL' ? null : v))
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 门店库里最新的那块 TSE(换过 TSE 会有多行);只读数据库,TSE 坏了也读得到
|
|
131
|
+
function readCertFromDb() {
|
|
132
|
+
const row = queryDb('select serialNumber, certificate from RealTseInfo order by id desc limit 1')
|
|
133
|
+
if (!row) return null
|
|
134
|
+
const [serial, certificate] = row
|
|
135
|
+
return {serial, certificate: certificate || null}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 结账后签名任务进 TseExecuteQueue(finish / finish-zero),后台几秒内执行;
|
|
139
|
+
// 结账超过 5 分钟还没有签名的就算漏签(任务报错、卡在队列里、或者 TSE 回了错)。
|
|
140
|
+
// 同一张单可能有多条任务(补签),按单算
|
|
141
|
+
const DUE_ORDERS = `select q.orderId, max(q.createdAt) as createdAt, max(q.id) as taskId,
|
|
142
|
+
exists (select 1 from RealTseLog l
|
|
143
|
+
where l.orderId = q.orderId and l.bestellungId is null and l.signature <> '') as signed
|
|
144
|
+
from TseExecuteQueue q
|
|
145
|
+
where q.executeType in ('finish', 'finish-zero')
|
|
146
|
+
and q.createdAt >= now() - interval 24 hour
|
|
147
|
+
and q.createdAt <= now() - interval 5 minute
|
|
148
|
+
group by q.orderId`
|
|
149
|
+
|
|
150
|
+
// 最近 24 小时结账的单有没有真的签上:这是 TSE 有没有坏的实际结果,比 ping 准——
|
|
151
|
+
// 关店、睡眠、开机自检时没人结账,不会算成故障;真坏了也一定有单签不上
|
|
152
|
+
function readSigningFromDb() {
|
|
153
|
+
const counts = queryDb(`select count(*), coalesce(sum(not signed), 0),
|
|
154
|
+
unix_timestamp(max(if(signed, createdAt, null))),
|
|
155
|
+
unix_timestamp(max(if(signed, null, createdAt)))
|
|
156
|
+
from (${DUE_ORDERS}) t`)
|
|
157
|
+
if (!counts) return null
|
|
158
|
+
const [due, unsigned, lastSigned, lastUnsigned] = counts
|
|
159
|
+
// 最近一张漏签的单为什么没签上:TSE 回的错(RealTseLog.tseError),没有就看队列任务的报错 / 还在排队
|
|
160
|
+
// 单独兜底:原因查不出来(比如两张表排序规则不一样)也不能连单数一起丢
|
|
161
|
+
const reason = Number(unsigned) > 0 ? safeSync(() => queryDb(`select coalesce(
|
|
162
|
+
(select l.tseError from RealTseLog l where l.orderId = t.orderId and l.tseError <> '' order by l.id desc limit 1),
|
|
163
|
+
nullif(q.note, ''),
|
|
164
|
+
if(q.finished = 0, '签名任务还在排队', null))
|
|
165
|
+
from (${DUE_ORDERS}) t join TseExecuteQueue q on q.id = t.taskId
|
|
166
|
+
where not t.signed
|
|
167
|
+
order by t.createdAt desc limit 1`)) : null
|
|
168
|
+
const toIso = sec => (sec ? new Date(Number(sec) * 1000).toISOString() : null)
|
|
169
|
+
return {
|
|
170
|
+
tseOrders24h: Number(due) || 0,
|
|
171
|
+
tseUnsigned24h: Number(unsigned) || 0,
|
|
172
|
+
tseLastSignedAt: toIso(lastSigned),
|
|
173
|
+
tseLastUnsignedAt: toIso(lastUnsigned),
|
|
174
|
+
tseLastUnsignedReason: reason && reason[0] ? reason[0].slice(0, 250) : null,
|
|
175
|
+
}
|
|
120
176
|
}
|
|
121
177
|
|
|
122
178
|
// TSE 自己的序列号、证书有效期和型号(dnQualifier:BSI-DSZ-CC-1121 是第一代,-V2 是第二代)
|
|
@@ -152,7 +208,12 @@ async function probeTse() {
|
|
|
152
208
|
}
|
|
153
209
|
|
|
154
210
|
// ok:TSE 正常;error:服务在但 TSE 在自检 / 没初始化 / 硬件报错;down:服务连不上
|
|
155
|
-
|
|
211
|
+
// 开机 / 唤醒时 TseHost 先自检约 15 秒,这期间 /ping 回 400:失败就等 20 秒再试一次,免得报成故障
|
|
212
|
+
let ping = await httpGet(`${tseHost}/ping`)
|
|
213
|
+
if (ping.status !== 200) {
|
|
214
|
+
await new Promise(resolve => setTimeout(resolve, PING_RETRY_DELAY_MS))
|
|
215
|
+
ping = await httpGet(`${tseHost}/ping`)
|
|
216
|
+
}
|
|
156
217
|
const tseStatus = ping.status === 0 ? 'down' : (ping.status === 200 ? 'ok' : 'error')
|
|
157
218
|
|
|
158
219
|
// 连不上时报 null,云端保留上次的版本;服务在但没有这个接口(2024-09 之前的构建)报空串
|
|
@@ -170,8 +231,10 @@ async function probeTse() {
|
|
|
170
231
|
}
|
|
171
232
|
|
|
172
233
|
const cert = await collectCert(tseHost, ping.status !== 0)
|
|
234
|
+
// 老版本门店库没有 TseExecuteQueue(2025-03 之前)就不报,云端保留上次的值
|
|
235
|
+
const signing = (await safe(readSigningFromDb)) || {}
|
|
173
236
|
|
|
174
|
-
return {tseEnabled, tseHost, tseStatus, tseAdapterVersion, tseJarSize, ...cert}
|
|
237
|
+
return {tseEnabled, tseHost, tseStatus, tseAdapterVersion, tseJarSize, ...cert, ...signing}
|
|
175
238
|
}
|
|
176
239
|
|
|
177
240
|
// 整体出错就什么都不报,云端保留上次的值
|