liangzimixin 0.3.69 → 0.3.70
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/dist/index.cjs +133 -49
- package/dist/index.d.cts +6 -0
- package/dist/setup-entry.cjs +133 -49
- package/package.json +1 -1
- package/scripts/liangzimixin_install.bat +107 -93
- package/scripts/liangzimixin_install.sh +45 -23
package/dist/index.cjs
CHANGED
|
@@ -18488,12 +18488,25 @@ async function resolveAndUploadMedia(params) {
|
|
|
18488
18488
|
timeoutMs
|
|
18489
18489
|
});
|
|
18490
18490
|
} catch (err) {
|
|
18491
|
-
|
|
18491
|
+
const errMsg = err.message;
|
|
18492
|
+
log5.warn("media:upload failed, \u964D\u7EA7\u5904\u7406", { error: errMsg });
|
|
18493
|
+
try {
|
|
18494
|
+
const tipText = "\u26A0\uFE0F \u6587\u4EF6\u4E0A\u4F20\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
|
|
18495
|
+
await messagePipe.sendMessage({
|
|
18496
|
+
chatId,
|
|
18497
|
+
senderId: chatId,
|
|
18498
|
+
msgType: "text",
|
|
18499
|
+
content: JSON.stringify({ content: tipText }),
|
|
18500
|
+
skipEncrypt: true
|
|
18501
|
+
});
|
|
18502
|
+
} catch (tipErr) {
|
|
18503
|
+
log5.warn("media:upload-failed tip send error", { error: tipErr.message });
|
|
18504
|
+
}
|
|
18492
18505
|
return {
|
|
18493
18506
|
channel: CHANNEL_ID,
|
|
18494
18507
|
messageId: "",
|
|
18495
18508
|
chatId,
|
|
18496
|
-
warning: `\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25: ${
|
|
18509
|
+
warning: `\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25: ${errMsg}`
|
|
18497
18510
|
};
|
|
18498
18511
|
}
|
|
18499
18512
|
const msgType = fileType;
|
|
@@ -19121,19 +19134,27 @@ function createQuantumImDeliverFn(deps) {
|
|
|
19121
19134
|
const hasMedia = mediaUrls.length > 0;
|
|
19122
19135
|
if (!hasText && !hasMedia) return;
|
|
19123
19136
|
if (hasText) {
|
|
19124
|
-
|
|
19125
|
-
|
|
19126
|
-
|
|
19127
|
-
|
|
19128
|
-
|
|
19129
|
-
|
|
19130
|
-
|
|
19131
|
-
|
|
19132
|
-
|
|
19133
|
-
|
|
19134
|
-
|
|
19135
|
-
|
|
19136
|
-
|
|
19137
|
+
try {
|
|
19138
|
+
await messagePipe.sendMessage({
|
|
19139
|
+
chatId,
|
|
19140
|
+
senderId,
|
|
19141
|
+
msgType: resolveTextMsgType(payload.text),
|
|
19142
|
+
content: JSON.stringify({ content: payload.text }),
|
|
19143
|
+
replyToMessageId,
|
|
19144
|
+
skipEncrypt: shouldSkipEncrypt
|
|
19145
|
+
});
|
|
19146
|
+
log14.info("\u{1F4E4} AI \u6587\u672C\u56DE\u590D\u5DF2\u53D1\u9001", {
|
|
19147
|
+
chatId,
|
|
19148
|
+
\u957F\u5EA6: payload.text.length,
|
|
19149
|
+
\u56DE\u590D\u9884\u89C8: payload.text.slice(0, 200)
|
|
19150
|
+
});
|
|
19151
|
+
} catch (err) {
|
|
19152
|
+
log14.error("\u{1F4E4} AI \u6587\u672C\u56DE\u590D\u53D1\u9001\u5931\u8D25", {
|
|
19153
|
+
chatId,
|
|
19154
|
+
error: err.message
|
|
19155
|
+
});
|
|
19156
|
+
if (!hasMedia) throw err;
|
|
19157
|
+
}
|
|
19137
19158
|
}
|
|
19138
19159
|
if (hasMedia) {
|
|
19139
19160
|
for (const mediaUrl of mediaUrls) {
|
|
@@ -19425,6 +19446,21 @@ var InboundPipeline = class {
|
|
|
19425
19446
|
stack: err.stack,
|
|
19426
19447
|
durationMs
|
|
19427
19448
|
});
|
|
19449
|
+
try {
|
|
19450
|
+
const errorTip = "\u26A0\uFE0F \u6D88\u606F\u5904\u7406\u9047\u5230\u95EE\u9898\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002\u5982\u95EE\u9898\u6301\u7EED\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002";
|
|
19451
|
+
await this.deps.messagePipe.sendMessage({
|
|
19452
|
+
chatId: msg.chatId,
|
|
19453
|
+
senderId: msg.senderId,
|
|
19454
|
+
msgType: resolveTextMsgType(errorTip),
|
|
19455
|
+
content: JSON.stringify({ content: errorTip }),
|
|
19456
|
+
skipEncrypt: true
|
|
19457
|
+
// 系统提示消息不加密,避免加密失败导致的死循环
|
|
19458
|
+
});
|
|
19459
|
+
} catch (tipErr) {
|
|
19460
|
+
log15.warn("\u26A0\uFE0F \u9519\u8BEF\u63D0\u793A\u6D88\u606F\u53D1\u9001\u5931\u8D25\uFF08\u907F\u514D\u6B7B\u5FAA\u73AF\uFF0C\u4E0D\u518D\u91CD\u8BD5\uFF09", {
|
|
19461
|
+
error: tipErr.message
|
|
19462
|
+
});
|
|
19463
|
+
}
|
|
19428
19464
|
clearInboundEncryptionStatus(msg.chatId, msg.messageId);
|
|
19429
19465
|
} finally {
|
|
19430
19466
|
semaphore.release();
|
|
@@ -21143,7 +21179,9 @@ var MessagePipe = class _MessagePipe {
|
|
|
21143
21179
|
body.reply_msg_id = msg.replyToMessageId;
|
|
21144
21180
|
}
|
|
21145
21181
|
const result = await this.callMessageApi("/messages/v1/send", body, "outbound");
|
|
21146
|
-
if (!result)
|
|
21182
|
+
if (!result) {
|
|
21183
|
+
throw new Error(`\u6D88\u606F\u53D1\u9001\u5931\u8D25: POST ${this.messageServiceBaseUrl}/messages/v1/send \u672A\u8FD4\u56DE\u6709\u6548\u54CD\u5E94`);
|
|
21184
|
+
}
|
|
21147
21185
|
log25.info("\u{1F4E4} outbound:sent \u2192 IM \u670D\u52A1\u5668", {
|
|
21148
21186
|
chatId: msg.chatId,
|
|
21149
21187
|
encrypted: Boolean(extra),
|
|
@@ -21167,7 +21205,10 @@ var MessagePipe = class _MessagePipe {
|
|
|
21167
21205
|
conversation_type: ""
|
|
21168
21206
|
};
|
|
21169
21207
|
const result = await this.callMessageApi("/messages/v1/recall", body, "recall");
|
|
21170
|
-
if (!result)
|
|
21208
|
+
if (!result) {
|
|
21209
|
+
log25.warn("recall:failed \u2014 callMessageApi returned null");
|
|
21210
|
+
return;
|
|
21211
|
+
}
|
|
21171
21212
|
log25.info("recall:sent", {
|
|
21172
21213
|
messageId: params.messageId,
|
|
21173
21214
|
requestId: result.request_id
|
|
@@ -21182,6 +21223,43 @@ var MessagePipe = class _MessagePipe {
|
|
|
21182
21223
|
* @param body - 请求体
|
|
21183
21224
|
* @param logTag - 日志标签前缀 (如 'outbound' / 'recall')
|
|
21184
21225
|
*/
|
|
21226
|
+
/** 单次 HTTP 请求封装 — callMessageApi 内部使用 */
|
|
21227
|
+
async _callMessageApiOnce(url2, body, logTag) {
|
|
21228
|
+
const apiStartMs = Date.now();
|
|
21229
|
+
const token = await this.tokenFn();
|
|
21230
|
+
const resp = await fetch(url2, {
|
|
21231
|
+
method: "POST",
|
|
21232
|
+
headers: {
|
|
21233
|
+
"Authorization": `Bearer ${token}`,
|
|
21234
|
+
"Content-Type": "application/json"
|
|
21235
|
+
},
|
|
21236
|
+
body: JSON.stringify(body)
|
|
21237
|
+
});
|
|
21238
|
+
if (!resp.ok) {
|
|
21239
|
+
log25.error(`${logTag}:http-error`, {
|
|
21240
|
+
status: resp.status,
|
|
21241
|
+
statusText: resp.statusText,
|
|
21242
|
+
url: url2
|
|
21243
|
+
});
|
|
21244
|
+
return { code: -1, msg: `HTTP ${resp.status}`, _retryable: resp.status >= 500 };
|
|
21245
|
+
}
|
|
21246
|
+
const result = await resp.json();
|
|
21247
|
+
if (result.code !== 0 && result.code !== 200) {
|
|
21248
|
+
log25.error(`${logTag}:api-error`, {
|
|
21249
|
+
code: result.code,
|
|
21250
|
+
msg: result.msg,
|
|
21251
|
+
requestId: result.request_id
|
|
21252
|
+
});
|
|
21253
|
+
return null;
|
|
21254
|
+
}
|
|
21255
|
+
metrics.increment("outbound.success");
|
|
21256
|
+
metrics.recordLatency("outbound.latency", Date.now() - apiStartMs);
|
|
21257
|
+
return result;
|
|
21258
|
+
}
|
|
21259
|
+
/**
|
|
21260
|
+
* 消息服务 API 公共调用方法 — 含 1 次重试 (仅限 5xx / 网络错误)。
|
|
21261
|
+
* 失败返回 null(不抛异常),错误通过 log 记录。
|
|
21262
|
+
*/
|
|
21185
21263
|
async callMessageApi(path3, body, logTag) {
|
|
21186
21264
|
const url2 = `${this.messageServiceBaseUrl}${path3}`;
|
|
21187
21265
|
const rateLimitResult = await this.rateLimiter.acquire();
|
|
@@ -21194,44 +21272,42 @@ var MessagePipe = class _MessagePipe {
|
|
|
21194
21272
|
return null;
|
|
21195
21273
|
}
|
|
21196
21274
|
try {
|
|
21197
|
-
const
|
|
21198
|
-
|
|
21199
|
-
|
|
21200
|
-
|
|
21201
|
-
|
|
21202
|
-
|
|
21203
|
-
|
|
21204
|
-
|
|
21205
|
-
|
|
21206
|
-
|
|
21207
|
-
|
|
21208
|
-
|
|
21209
|
-
|
|
21210
|
-
|
|
21211
|
-
url: url2
|
|
21212
|
-
});
|
|
21275
|
+
const result = await this._callMessageApiOnce(url2, body, logTag);
|
|
21276
|
+
if (result && "_retryable" in result) {
|
|
21277
|
+
if (result._retryable) {
|
|
21278
|
+
log25.warn(`${logTag}:retrying after 5xx`, { url: url2 });
|
|
21279
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
21280
|
+
try {
|
|
21281
|
+
const retryResult = await this._callMessageApiOnce(url2, body, `${logTag}:retry`);
|
|
21282
|
+
if (retryResult && !("_retryable" in retryResult)) {
|
|
21283
|
+
return retryResult;
|
|
21284
|
+
}
|
|
21285
|
+
} catch (retryErr) {
|
|
21286
|
+
log25.error(`${logTag}:retry-failed`, { error: retryErr.message });
|
|
21287
|
+
}
|
|
21288
|
+
}
|
|
21213
21289
|
metrics.increment("outbound.failed");
|
|
21214
21290
|
return null;
|
|
21215
21291
|
}
|
|
21216
|
-
|
|
21217
|
-
|
|
21218
|
-
|
|
21219
|
-
|
|
21220
|
-
|
|
21221
|
-
|
|
21292
|
+
return result;
|
|
21293
|
+
} catch (err) {
|
|
21294
|
+
log25.warn(`${logTag}:network-error, retrying`, { url: url2, error: err.message });
|
|
21295
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
21296
|
+
try {
|
|
21297
|
+
const retryResult = await this._callMessageApiOnce(url2, body, `${logTag}:retry`);
|
|
21298
|
+
if (retryResult && !("_retryable" in retryResult)) {
|
|
21299
|
+
return retryResult;
|
|
21300
|
+
}
|
|
21301
|
+
metrics.increment("outbound.failed");
|
|
21302
|
+
return null;
|
|
21303
|
+
} catch (retryErr) {
|
|
21304
|
+
metrics.increment("outbound.failed");
|
|
21305
|
+
log25.error(`${logTag}:retry-network-error`, {
|
|
21306
|
+
url: url2,
|
|
21307
|
+
error: retryErr.message
|
|
21222
21308
|
});
|
|
21223
21309
|
return null;
|
|
21224
21310
|
}
|
|
21225
|
-
metrics.increment("outbound.success");
|
|
21226
|
-
metrics.recordLatency("outbound.latency", Date.now() - apiStartMs);
|
|
21227
|
-
return result;
|
|
21228
|
-
} catch (err) {
|
|
21229
|
-
metrics.increment("outbound.failed");
|
|
21230
|
-
log25.error(`${logTag}:network-error`, {
|
|
21231
|
-
url: url2,
|
|
21232
|
-
error: err.message
|
|
21233
|
-
});
|
|
21234
|
-
return null;
|
|
21235
21311
|
}
|
|
21236
21312
|
}
|
|
21237
21313
|
/**
|
|
@@ -21340,6 +21416,10 @@ var MessagePipe = class _MessagePipe {
|
|
|
21340
21416
|
msgUid: callbackData.msgUid,
|
|
21341
21417
|
error: err.message
|
|
21342
21418
|
});
|
|
21419
|
+
await this.sendHintMessage(
|
|
21420
|
+
callbackData,
|
|
21421
|
+
"\u26A0\uFE0F \u6D88\u606F\u89E3\u5BC6\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u3002\u5982\u95EE\u9898\u6301\u7EED\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002"
|
|
21422
|
+
);
|
|
21343
21423
|
return;
|
|
21344
21424
|
}
|
|
21345
21425
|
} else {
|
|
@@ -21361,6 +21441,10 @@ var MessagePipe = class _MessagePipe {
|
|
|
21361
21441
|
msgUid: callbackData.msgUid,
|
|
21362
21442
|
error: err.message
|
|
21363
21443
|
});
|
|
21444
|
+
await this.sendHintMessage(
|
|
21445
|
+
callbackData,
|
|
21446
|
+
"\u26A0\uFE0F \u6D88\u606F\u89E3\u5BC6\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u3002\u5982\u95EE\u9898\u6301\u7EED\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002"
|
|
21447
|
+
);
|
|
21364
21448
|
return;
|
|
21365
21449
|
}
|
|
21366
21450
|
} else {
|
package/dist/index.d.cts
CHANGED
|
@@ -966,6 +966,12 @@ declare class MessagePipe {
|
|
|
966
966
|
* @param body - 请求体
|
|
967
967
|
* @param logTag - 日志标签前缀 (如 'outbound' / 'recall')
|
|
968
968
|
*/
|
|
969
|
+
/** 单次 HTTP 请求封装 — callMessageApi 内部使用 */
|
|
970
|
+
private _callMessageApiOnce;
|
|
971
|
+
/**
|
|
972
|
+
* 消息服务 API 公共调用方法 — 含 1 次重试 (仅限 5xx / 网络错误)。
|
|
973
|
+
* 失败返回 null(不抛异常),错误通过 log 记录。
|
|
974
|
+
*/
|
|
969
975
|
private callMessageApi;
|
|
970
976
|
/**
|
|
971
977
|
* 入站处理流水线 (7 步):
|
package/dist/setup-entry.cjs
CHANGED
|
@@ -4559,12 +4559,25 @@ async function resolveAndUploadMedia(params) {
|
|
|
4559
4559
|
timeoutMs
|
|
4560
4560
|
});
|
|
4561
4561
|
} catch (err) {
|
|
4562
|
-
|
|
4562
|
+
const errMsg = err.message;
|
|
4563
|
+
log5.warn("media:upload failed, \u964D\u7EA7\u5904\u7406", { error: errMsg });
|
|
4564
|
+
try {
|
|
4565
|
+
const tipText = "\u26A0\uFE0F \u6587\u4EF6\u4E0A\u4F20\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
|
|
4566
|
+
await messagePipe.sendMessage({
|
|
4567
|
+
chatId,
|
|
4568
|
+
senderId: chatId,
|
|
4569
|
+
msgType: "text",
|
|
4570
|
+
content: JSON.stringify({ content: tipText }),
|
|
4571
|
+
skipEncrypt: true
|
|
4572
|
+
});
|
|
4573
|
+
} catch (tipErr) {
|
|
4574
|
+
log5.warn("media:upload-failed tip send error", { error: tipErr.message });
|
|
4575
|
+
}
|
|
4563
4576
|
return {
|
|
4564
4577
|
channel: CHANNEL_ID,
|
|
4565
4578
|
messageId: "",
|
|
4566
4579
|
chatId,
|
|
4567
|
-
warning: `\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25: ${
|
|
4580
|
+
warning: `\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25: ${errMsg}`
|
|
4568
4581
|
};
|
|
4569
4582
|
}
|
|
4570
4583
|
const msgType = fileType;
|
|
@@ -20064,7 +20077,9 @@ var MessagePipe = class _MessagePipe {
|
|
|
20064
20077
|
body.reply_msg_id = msg.replyToMessageId;
|
|
20065
20078
|
}
|
|
20066
20079
|
const result = await this.callMessageApi("/messages/v1/send", body, "outbound");
|
|
20067
|
-
if (!result)
|
|
20080
|
+
if (!result) {
|
|
20081
|
+
throw new Error(`\u6D88\u606F\u53D1\u9001\u5931\u8D25: POST ${this.messageServiceBaseUrl}/messages/v1/send \u672A\u8FD4\u56DE\u6709\u6548\u54CD\u5E94`);
|
|
20082
|
+
}
|
|
20068
20083
|
log16.info("\u{1F4E4} outbound:sent \u2192 IM \u670D\u52A1\u5668", {
|
|
20069
20084
|
chatId: msg.chatId,
|
|
20070
20085
|
encrypted: Boolean(extra),
|
|
@@ -20088,7 +20103,10 @@ var MessagePipe = class _MessagePipe {
|
|
|
20088
20103
|
conversation_type: ""
|
|
20089
20104
|
};
|
|
20090
20105
|
const result = await this.callMessageApi("/messages/v1/recall", body, "recall");
|
|
20091
|
-
if (!result)
|
|
20106
|
+
if (!result) {
|
|
20107
|
+
log16.warn("recall:failed \u2014 callMessageApi returned null");
|
|
20108
|
+
return;
|
|
20109
|
+
}
|
|
20092
20110
|
log16.info("recall:sent", {
|
|
20093
20111
|
messageId: params.messageId,
|
|
20094
20112
|
requestId: result.request_id
|
|
@@ -20103,6 +20121,43 @@ var MessagePipe = class _MessagePipe {
|
|
|
20103
20121
|
* @param body - 请求体
|
|
20104
20122
|
* @param logTag - 日志标签前缀 (如 'outbound' / 'recall')
|
|
20105
20123
|
*/
|
|
20124
|
+
/** 单次 HTTP 请求封装 — callMessageApi 内部使用 */
|
|
20125
|
+
async _callMessageApiOnce(url2, body, logTag) {
|
|
20126
|
+
const apiStartMs = Date.now();
|
|
20127
|
+
const token = await this.tokenFn();
|
|
20128
|
+
const resp = await fetch(url2, {
|
|
20129
|
+
method: "POST",
|
|
20130
|
+
headers: {
|
|
20131
|
+
"Authorization": `Bearer ${token}`,
|
|
20132
|
+
"Content-Type": "application/json"
|
|
20133
|
+
},
|
|
20134
|
+
body: JSON.stringify(body)
|
|
20135
|
+
});
|
|
20136
|
+
if (!resp.ok) {
|
|
20137
|
+
log16.error(`${logTag}:http-error`, {
|
|
20138
|
+
status: resp.status,
|
|
20139
|
+
statusText: resp.statusText,
|
|
20140
|
+
url: url2
|
|
20141
|
+
});
|
|
20142
|
+
return { code: -1, msg: `HTTP ${resp.status}`, _retryable: resp.status >= 500 };
|
|
20143
|
+
}
|
|
20144
|
+
const result = await resp.json();
|
|
20145
|
+
if (result.code !== 0 && result.code !== 200) {
|
|
20146
|
+
log16.error(`${logTag}:api-error`, {
|
|
20147
|
+
code: result.code,
|
|
20148
|
+
msg: result.msg,
|
|
20149
|
+
requestId: result.request_id
|
|
20150
|
+
});
|
|
20151
|
+
return null;
|
|
20152
|
+
}
|
|
20153
|
+
metrics.increment("outbound.success");
|
|
20154
|
+
metrics.recordLatency("outbound.latency", Date.now() - apiStartMs);
|
|
20155
|
+
return result;
|
|
20156
|
+
}
|
|
20157
|
+
/**
|
|
20158
|
+
* 消息服务 API 公共调用方法 — 含 1 次重试 (仅限 5xx / 网络错误)。
|
|
20159
|
+
* 失败返回 null(不抛异常),错误通过 log 记录。
|
|
20160
|
+
*/
|
|
20106
20161
|
async callMessageApi(path3, body, logTag) {
|
|
20107
20162
|
const url2 = `${this.messageServiceBaseUrl}${path3}`;
|
|
20108
20163
|
const rateLimitResult = await this.rateLimiter.acquire();
|
|
@@ -20115,44 +20170,42 @@ var MessagePipe = class _MessagePipe {
|
|
|
20115
20170
|
return null;
|
|
20116
20171
|
}
|
|
20117
20172
|
try {
|
|
20118
|
-
const
|
|
20119
|
-
|
|
20120
|
-
|
|
20121
|
-
|
|
20122
|
-
|
|
20123
|
-
|
|
20124
|
-
|
|
20125
|
-
|
|
20126
|
-
|
|
20127
|
-
|
|
20128
|
-
|
|
20129
|
-
|
|
20130
|
-
|
|
20131
|
-
|
|
20132
|
-
url: url2
|
|
20133
|
-
});
|
|
20173
|
+
const result = await this._callMessageApiOnce(url2, body, logTag);
|
|
20174
|
+
if (result && "_retryable" in result) {
|
|
20175
|
+
if (result._retryable) {
|
|
20176
|
+
log16.warn(`${logTag}:retrying after 5xx`, { url: url2 });
|
|
20177
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
20178
|
+
try {
|
|
20179
|
+
const retryResult = await this._callMessageApiOnce(url2, body, `${logTag}:retry`);
|
|
20180
|
+
if (retryResult && !("_retryable" in retryResult)) {
|
|
20181
|
+
return retryResult;
|
|
20182
|
+
}
|
|
20183
|
+
} catch (retryErr) {
|
|
20184
|
+
log16.error(`${logTag}:retry-failed`, { error: retryErr.message });
|
|
20185
|
+
}
|
|
20186
|
+
}
|
|
20134
20187
|
metrics.increment("outbound.failed");
|
|
20135
20188
|
return null;
|
|
20136
20189
|
}
|
|
20137
|
-
|
|
20138
|
-
|
|
20139
|
-
|
|
20140
|
-
|
|
20141
|
-
|
|
20142
|
-
|
|
20190
|
+
return result;
|
|
20191
|
+
} catch (err) {
|
|
20192
|
+
log16.warn(`${logTag}:network-error, retrying`, { url: url2, error: err.message });
|
|
20193
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
20194
|
+
try {
|
|
20195
|
+
const retryResult = await this._callMessageApiOnce(url2, body, `${logTag}:retry`);
|
|
20196
|
+
if (retryResult && !("_retryable" in retryResult)) {
|
|
20197
|
+
return retryResult;
|
|
20198
|
+
}
|
|
20199
|
+
metrics.increment("outbound.failed");
|
|
20200
|
+
return null;
|
|
20201
|
+
} catch (retryErr) {
|
|
20202
|
+
metrics.increment("outbound.failed");
|
|
20203
|
+
log16.error(`${logTag}:retry-network-error`, {
|
|
20204
|
+
url: url2,
|
|
20205
|
+
error: retryErr.message
|
|
20143
20206
|
});
|
|
20144
20207
|
return null;
|
|
20145
20208
|
}
|
|
20146
|
-
metrics.increment("outbound.success");
|
|
20147
|
-
metrics.recordLatency("outbound.latency", Date.now() - apiStartMs);
|
|
20148
|
-
return result;
|
|
20149
|
-
} catch (err) {
|
|
20150
|
-
metrics.increment("outbound.failed");
|
|
20151
|
-
log16.error(`${logTag}:network-error`, {
|
|
20152
|
-
url: url2,
|
|
20153
|
-
error: err.message
|
|
20154
|
-
});
|
|
20155
|
-
return null;
|
|
20156
20209
|
}
|
|
20157
20210
|
}
|
|
20158
20211
|
/**
|
|
@@ -20261,6 +20314,10 @@ var MessagePipe = class _MessagePipe {
|
|
|
20261
20314
|
msgUid: callbackData.msgUid,
|
|
20262
20315
|
error: err.message
|
|
20263
20316
|
});
|
|
20317
|
+
await this.sendHintMessage(
|
|
20318
|
+
callbackData,
|
|
20319
|
+
"\u26A0\uFE0F \u6D88\u606F\u89E3\u5BC6\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u3002\u5982\u95EE\u9898\u6301\u7EED\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002"
|
|
20320
|
+
);
|
|
20264
20321
|
return;
|
|
20265
20322
|
}
|
|
20266
20323
|
} else {
|
|
@@ -20282,6 +20339,10 @@ var MessagePipe = class _MessagePipe {
|
|
|
20282
20339
|
msgUid: callbackData.msgUid,
|
|
20283
20340
|
error: err.message
|
|
20284
20341
|
});
|
|
20342
|
+
await this.sendHintMessage(
|
|
20343
|
+
callbackData,
|
|
20344
|
+
"\u26A0\uFE0F \u6D88\u606F\u89E3\u5BC6\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u3002\u5982\u95EE\u9898\u6301\u7EED\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002"
|
|
20345
|
+
);
|
|
20285
20346
|
return;
|
|
20286
20347
|
}
|
|
20287
20348
|
} else {
|
|
@@ -21459,19 +21520,27 @@ function createQuantumImDeliverFn(deps) {
|
|
|
21459
21520
|
const hasMedia = mediaUrls.length > 0;
|
|
21460
21521
|
if (!hasText && !hasMedia) return;
|
|
21461
21522
|
if (hasText) {
|
|
21462
|
-
|
|
21463
|
-
|
|
21464
|
-
|
|
21465
|
-
|
|
21466
|
-
|
|
21467
|
-
|
|
21468
|
-
|
|
21469
|
-
|
|
21470
|
-
|
|
21471
|
-
|
|
21472
|
-
|
|
21473
|
-
|
|
21474
|
-
|
|
21523
|
+
try {
|
|
21524
|
+
await messagePipe.sendMessage({
|
|
21525
|
+
chatId,
|
|
21526
|
+
senderId,
|
|
21527
|
+
msgType: resolveTextMsgType(payload.text),
|
|
21528
|
+
content: JSON.stringify({ content: payload.text }),
|
|
21529
|
+
replyToMessageId,
|
|
21530
|
+
skipEncrypt: shouldSkipEncrypt
|
|
21531
|
+
});
|
|
21532
|
+
log27.info("\u{1F4E4} AI \u6587\u672C\u56DE\u590D\u5DF2\u53D1\u9001", {
|
|
21533
|
+
chatId,
|
|
21534
|
+
\u957F\u5EA6: payload.text.length,
|
|
21535
|
+
\u56DE\u590D\u9884\u89C8: payload.text.slice(0, 200)
|
|
21536
|
+
});
|
|
21537
|
+
} catch (err) {
|
|
21538
|
+
log27.error("\u{1F4E4} AI \u6587\u672C\u56DE\u590D\u53D1\u9001\u5931\u8D25", {
|
|
21539
|
+
chatId,
|
|
21540
|
+
error: err.message
|
|
21541
|
+
});
|
|
21542
|
+
if (!hasMedia) throw err;
|
|
21543
|
+
}
|
|
21475
21544
|
}
|
|
21476
21545
|
if (hasMedia) {
|
|
21477
21546
|
for (const mediaUrl of mediaUrls) {
|
|
@@ -21751,6 +21820,21 @@ var InboundPipeline = class {
|
|
|
21751
21820
|
stack: err.stack,
|
|
21752
21821
|
durationMs
|
|
21753
21822
|
});
|
|
21823
|
+
try {
|
|
21824
|
+
const errorTip = "\u26A0\uFE0F \u6D88\u606F\u5904\u7406\u9047\u5230\u95EE\u9898\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002\u5982\u95EE\u9898\u6301\u7EED\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002";
|
|
21825
|
+
await this.deps.messagePipe.sendMessage({
|
|
21826
|
+
chatId: msg.chatId,
|
|
21827
|
+
senderId: msg.senderId,
|
|
21828
|
+
msgType: resolveTextMsgType(errorTip),
|
|
21829
|
+
content: JSON.stringify({ content: errorTip }),
|
|
21830
|
+
skipEncrypt: true
|
|
21831
|
+
// 系统提示消息不加密,避免加密失败导致的死循环
|
|
21832
|
+
});
|
|
21833
|
+
} catch (tipErr) {
|
|
21834
|
+
log28.warn("\u26A0\uFE0F \u9519\u8BEF\u63D0\u793A\u6D88\u606F\u53D1\u9001\u5931\u8D25\uFF08\u907F\u514D\u6B7B\u5FAA\u73AF\uFF0C\u4E0D\u518D\u91CD\u8BD5\uFF09", {
|
|
21835
|
+
error: tipErr.message
|
|
21836
|
+
});
|
|
21837
|
+
}
|
|
21754
21838
|
clearInboundEncryptionStatus(msg.chatId, msg.messageId);
|
|
21755
21839
|
} finally {
|
|
21756
21840
|
semaphore.release();
|
package/package.json
CHANGED
|
@@ -3,24 +3,26 @@ setlocal enabledelayedexpansion
|
|
|
3
3
|
chcp 65001 >nul 2>&1
|
|
4
4
|
|
|
5
5
|
REM ============================================================
|
|
6
|
-
REM liangzimixin
|
|
7
|
-
REM
|
|
6
|
+
REM liangzimixin install script (Windows)
|
|
7
|
+
REM Usage: liangzimixin_install.bat <appId> <appSecret> [quantumAccount]
|
|
8
8
|
REM ============================================================
|
|
9
9
|
|
|
10
|
-
set "SCRIPT_VERSION=0.3.
|
|
10
|
+
set "SCRIPT_VERSION=0.3.70"
|
|
11
11
|
set "NPM_PACKAGE=liangzimixin"
|
|
12
|
-
set "REMOTE_SCRIPT_URL=https://unpkg.com/%NPM_PACKAGE%@latest/scripts/liangzimixin_install.bat"
|
|
13
12
|
|
|
14
13
|
set "SKIP_SELF_UPDATE=0"
|
|
14
|
+
set "USE_BETA=0"
|
|
15
15
|
set "APP_ID="
|
|
16
16
|
set "APP_SECRET="
|
|
17
17
|
set "QUANTUM_ACCOUNT="
|
|
18
18
|
|
|
19
|
-
REM
|
|
19
|
+
REM -- Parse arguments -------------------------------------------------
|
|
20
20
|
set "IDX=0"
|
|
21
21
|
for %%a in (%*) do (
|
|
22
22
|
if "%%a"=="--skip-self-update" (
|
|
23
23
|
set "SKIP_SELF_UPDATE=1"
|
|
24
|
+
) else if "%%a"=="--beta" (
|
|
25
|
+
set "USE_BETA=1"
|
|
24
26
|
) else (
|
|
25
27
|
set /a IDX+=1
|
|
26
28
|
if !IDX!==1 set "APP_ID=%%a"
|
|
@@ -29,68 +31,61 @@ for %%a in (%*) do (
|
|
|
29
31
|
)
|
|
30
32
|
)
|
|
31
33
|
|
|
34
|
+
set "TAG=latest"
|
|
35
|
+
if "%USE_BETA%"=="1" set "TAG=beta"
|
|
36
|
+
set "REMOTE_SCRIPT_URL=https://unpkg.com/%NPM_PACKAGE%@%TAG%/scripts/liangzimixin_install.bat"
|
|
37
|
+
|
|
32
38
|
if "%APP_ID%"=="" goto :usage
|
|
33
39
|
if "%APP_SECRET%"=="" goto :usage
|
|
34
|
-
if "%QUANTUM_ACCOUNT%"=="" goto :usage
|
|
35
40
|
goto :main
|
|
36
41
|
|
|
37
42
|
:usage
|
|
38
43
|
echo.
|
|
39
|
-
echo liangzimixin
|
|
44
|
+
echo liangzimixin install script v%SCRIPT_VERSION%
|
|
40
45
|
echo.
|
|
41
|
-
echo
|
|
46
|
+
echo Usage: %~nx0 ^<appId^> ^<appSecret^> [quantumAccount] [--skip-self-update] [--beta]
|
|
42
47
|
echo.
|
|
43
|
-
echo
|
|
44
|
-
echo appId
|
|
45
|
-
echo appSecret
|
|
46
|
-
echo quantumAccount
|
|
48
|
+
echo Arguments:
|
|
49
|
+
echo appId App ID (required)
|
|
50
|
+
echo appSecret App Secret (required)
|
|
51
|
+
echo quantumAccount Quantum Account ID (optional)
|
|
47
52
|
echo.
|
|
48
|
-
echo
|
|
49
|
-
echo --skip-self-update
|
|
53
|
+
echo Options:
|
|
54
|
+
echo --skip-self-update Skip script self-update check
|
|
55
|
+
echo --beta Install beta version
|
|
50
56
|
echo.
|
|
51
57
|
exit /b 1
|
|
52
58
|
|
|
53
|
-
REM
|
|
59
|
+
REM ============================================================
|
|
54
60
|
:main
|
|
55
61
|
echo.
|
|
56
|
-
echo
|
|
57
|
-
echo liangzimixin
|
|
58
|
-
echo
|
|
62
|
+
echo =======================================================
|
|
63
|
+
echo liangzimixin deploy v%SCRIPT_VERSION%
|
|
64
|
+
echo =======================================================
|
|
59
65
|
echo.
|
|
60
66
|
|
|
61
|
-
REM
|
|
62
|
-
REM Step 0:
|
|
63
|
-
REM
|
|
67
|
+
REM ============================================================
|
|
68
|
+
REM Step 0: Script self-update check
|
|
69
|
+
REM ============================================================
|
|
64
70
|
if "%SKIP_SELF_UPDATE%"=="1" (
|
|
65
|
-
echo [INFO]
|
|
71
|
+
echo [INFO] Skip self-update ^(--skip-self-update^)
|
|
66
72
|
goto :step1
|
|
67
73
|
)
|
|
68
74
|
|
|
69
|
-
echo [INFO] Step 0/4
|
|
75
|
+
echo [INFO] Step 0/4 - Checking for script updates...
|
|
70
76
|
|
|
71
77
|
set "REMOTE_VERSION="
|
|
72
|
-
REM
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
for /f "tokens=2 delims=:," %%a in ('findstr /c:"version" "%TEMP%\lzmx_pkg.json"') do (
|
|
76
|
-
set "REMOTE_VERSION=%%~a"
|
|
77
|
-
goto :got_version
|
|
78
|
-
)
|
|
79
|
-
)
|
|
80
|
-
:got_version
|
|
81
|
-
REM 去除可能的前后空格和引号
|
|
82
|
-
if defined REMOTE_VERSION (
|
|
83
|
-
set "REMOTE_VERSION=!REMOTE_VERSION: =!"
|
|
84
|
-
set "REMOTE_VERSION=!REMOTE_VERSION:"=!"
|
|
78
|
+
REM Fetch latest version from npm registry (use PowerShell for JSON parsing)
|
|
79
|
+
for /f "usebackq delims=" %%a in (`powershell -NoProfile -Command "try { (Invoke-RestMethod -Uri 'https://registry.npmjs.org/%NPM_PACKAGE%/%TAG%' -TimeoutSec 10).version } catch { }" 2^>nul`) do (
|
|
80
|
+
set "REMOTE_VERSION=%%a"
|
|
85
81
|
)
|
|
86
|
-
del /q "%TEMP%\lzmx_pkg.json" 2>nul
|
|
87
82
|
|
|
88
83
|
if not defined REMOTE_VERSION (
|
|
89
|
-
echo [WARN]
|
|
84
|
+
echo [WARN] Cannot check remote version, continuing with current version
|
|
90
85
|
goto :step1
|
|
91
86
|
)
|
|
92
87
|
|
|
93
|
-
REM
|
|
88
|
+
REM Semver comparison: prevent prerelease from rolling back to older latest
|
|
94
89
|
for /f "delims=- tokens=1" %%a in ("!REMOTE_VERSION!") do set "CLEAN_REMOTE=%%a"
|
|
95
90
|
for /f "delims=- tokens=1" %%a in ("!SCRIPT_VERSION!") do set "CLEAN_LOCAL=%%a"
|
|
96
91
|
|
|
@@ -111,86 +106,105 @@ if !R1! EQU !L1! (
|
|
|
111
106
|
)
|
|
112
107
|
|
|
113
108
|
if "%REMOTE_VERSION%"=="%SCRIPT_VERSION%" (
|
|
114
|
-
echo [OK]
|
|
109
|
+
echo [OK] Script is up to date ^(v%SCRIPT_VERSION%^)
|
|
115
110
|
goto :step1
|
|
116
111
|
)
|
|
117
112
|
|
|
118
113
|
if "!SHOULD_UPDATE!"=="0" (
|
|
119
|
-
echo [OK]
|
|
114
|
+
echo [OK] Current script ^(v%SCRIPT_VERSION%^) is newer or equal to remote ^(v%REMOTE_VERSION%^)
|
|
120
115
|
goto :step1
|
|
121
116
|
)
|
|
122
117
|
|
|
123
|
-
echo [INFO]
|
|
118
|
+
echo [INFO] New version found: %SCRIPT_VERSION% -^> %REMOTE_VERSION%, updating...
|
|
124
119
|
set "TEMP_SCRIPT=%TEMP%\liangzimixin_install_new.bat"
|
|
125
120
|
curl -fsSL --connect-timeout 10 --max-time 30 "!REMOTE_SCRIPT_URL!" -o "%TEMP_SCRIPT%" 2>nul
|
|
126
121
|
if %errorlevel%==0 (
|
|
127
|
-
echo [OK]
|
|
128
|
-
|
|
122
|
+
echo [OK] Script updated to v%REMOTE_VERSION%
|
|
123
|
+
if "%USE_BETA%"=="1" (
|
|
124
|
+
call "%TEMP_SCRIPT%" --skip-self-update --beta %APP_ID% %APP_SECRET% %QUANTUM_ACCOUNT%
|
|
125
|
+
) else (
|
|
126
|
+
call "%TEMP_SCRIPT%" --skip-self-update %APP_ID% %APP_SECRET% %QUANTUM_ACCOUNT%
|
|
127
|
+
)
|
|
129
128
|
exit /b %errorlevel%
|
|
130
129
|
) else (
|
|
131
|
-
echo [WARN]
|
|
130
|
+
echo [WARN] Script download failed, continuing with current version
|
|
132
131
|
del /q "%TEMP_SCRIPT%" 2>nul
|
|
133
132
|
)
|
|
134
133
|
|
|
135
134
|
echo.
|
|
136
135
|
|
|
137
|
-
REM
|
|
138
|
-
REM Step 1:
|
|
139
|
-
REM
|
|
136
|
+
REM ============================================================
|
|
137
|
+
REM Step 1: Install/update plugin
|
|
138
|
+
REM ============================================================
|
|
140
139
|
:step1
|
|
141
|
-
echo [INFO] Step 1/4
|
|
140
|
+
echo [INFO] Step 1/4 - Install/update liangzimixin plugin (%TAG%)...
|
|
142
141
|
|
|
143
|
-
openclaw plugins list 2>nul | findstr /i "liangzimixin" >nul
|
|
142
|
+
call openclaw plugins list 2>nul | findstr /i "liangzimixin" >nul
|
|
144
143
|
if %errorlevel%==0 (
|
|
145
|
-
echo [INFO]
|
|
146
|
-
|
|
144
|
+
echo [INFO] Plugin installed, updating...
|
|
145
|
+
if "%USE_BETA%"=="1" (
|
|
146
|
+
call openclaw plugins install liangzimixin@beta
|
|
147
|
+
) else (
|
|
148
|
+
call openclaw plugins update liangzimixin
|
|
149
|
+
)
|
|
147
150
|
) else (
|
|
148
|
-
echo [INFO]
|
|
149
|
-
openclaw plugins install liangzimixin
|
|
151
|
+
echo [INFO] Plugin not installed, installing...
|
|
152
|
+
call openclaw plugins install liangzimixin@%TAG%
|
|
150
153
|
)
|
|
151
154
|
|
|
155
|
+
REM Verify plugin is actually installed (don't rely on exit code, as
|
|
156
|
+
REM "already at latest" may return non-zero)
|
|
157
|
+
call openclaw plugins list 2>nul | findstr /i "liangzimixin" >nul
|
|
152
158
|
if %errorlevel% neq 0 (
|
|
153
|
-
echo [ERROR]
|
|
159
|
+
echo [ERROR] Plugin install/update failed, check network or permissions
|
|
154
160
|
exit /b 1
|
|
155
161
|
)
|
|
156
162
|
|
|
157
|
-
echo [OK]
|
|
163
|
+
echo [OK] Plugin ready
|
|
158
164
|
echo.
|
|
159
165
|
|
|
160
|
-
REM
|
|
161
|
-
REM Step 2:
|
|
162
|
-
REM
|
|
163
|
-
echo [INFO] Step 2/4
|
|
166
|
+
REM ============================================================
|
|
167
|
+
REM Step 2: Write configuration
|
|
168
|
+
REM ============================================================
|
|
169
|
+
echo [INFO] Step 2/4 - Writing configuration...
|
|
164
170
|
|
|
165
|
-
|
|
171
|
+
if not "%QUANTUM_ACCOUNT%"=="" (
|
|
172
|
+
call openclaw channels add --channel liangzimixin --token %APP_ID% --password %APP_SECRET% --user-id %QUANTUM_ACCOUNT%
|
|
173
|
+
) else (
|
|
174
|
+
call openclaw channels add --channel liangzimixin --token %APP_ID% --password %APP_SECRET%
|
|
175
|
+
)
|
|
166
176
|
|
|
167
177
|
if %errorlevel% neq 0 (
|
|
168
|
-
echo [ERROR]
|
|
178
|
+
echo [ERROR] Configuration write failed, check parameters
|
|
169
179
|
exit /b 1
|
|
170
180
|
)
|
|
171
181
|
|
|
172
|
-
|
|
182
|
+
if not "%QUANTUM_ACCOUNT%"=="" (
|
|
183
|
+
echo [OK] Configuration saved (appId / appSecret / quantumAccount)
|
|
184
|
+
) else (
|
|
185
|
+
echo [OK] Configuration saved (appId / appSecret)
|
|
186
|
+
)
|
|
173
187
|
echo.
|
|
174
188
|
|
|
175
|
-
REM
|
|
176
|
-
REM Step 3:
|
|
177
|
-
REM
|
|
178
|
-
echo [INFO] Step 3/4
|
|
189
|
+
REM ============================================================
|
|
190
|
+
REM Step 3: Restart gateway
|
|
191
|
+
REM ============================================================
|
|
192
|
+
echo [INFO] Step 3/4 - Restarting OpenClaw gateway...
|
|
179
193
|
|
|
180
|
-
openclaw gateway restart
|
|
194
|
+
call openclaw gateway restart
|
|
181
195
|
|
|
182
196
|
if %errorlevel% neq 0 (
|
|
183
|
-
echo [
|
|
184
|
-
|
|
197
|
+
echo [WARN] Gateway restart command returned an error or timed out
|
|
198
|
+
echo [WARN] Continuing to health check anyway...
|
|
199
|
+
) else (
|
|
200
|
+
echo [OK] Gateway restart command sent
|
|
185
201
|
)
|
|
186
|
-
|
|
187
|
-
echo [OK] 网关重启指令已发送
|
|
188
202
|
echo.
|
|
189
203
|
|
|
190
|
-
REM
|
|
191
|
-
REM Step 4:
|
|
192
|
-
REM
|
|
193
|
-
echo [INFO] Step 4/4
|
|
204
|
+
REM ============================================================
|
|
205
|
+
REM Step 4: Health check
|
|
206
|
+
REM ============================================================
|
|
207
|
+
echo [INFO] Step 4/4 - Health check...
|
|
194
208
|
|
|
195
209
|
set "MAX_RETRIES=3"
|
|
196
210
|
set "RETRY_INTERVAL=5"
|
|
@@ -198,21 +212,21 @@ set "HEALTH_OK=0"
|
|
|
198
212
|
|
|
199
213
|
for /l %%i in (1,1,%MAX_RETRIES%) do (
|
|
200
214
|
if !HEALTH_OK!==0 (
|
|
201
|
-
echo [INFO]
|
|
215
|
+
echo [INFO] Waiting for service ^(%RETRY_INTERVAL%s^)...
|
|
202
216
|
timeout /t %RETRY_INTERVAL% /nobreak >nul
|
|
203
217
|
|
|
204
|
-
echo [INFO]
|
|
218
|
+
echo [INFO] Check %%i/%MAX_RETRIES%...
|
|
205
219
|
|
|
206
220
|
set "GATEWAY_OK=0"
|
|
207
221
|
set "CHANNEL_OK=0"
|
|
208
222
|
|
|
209
|
-
openclaw status > "%TEMP%\lzmx_status.txt" 2>&1
|
|
223
|
+
call openclaw status > "%TEMP%\lzmx_status.txt" 2>&1
|
|
210
224
|
|
|
211
|
-
REM
|
|
225
|
+
REM Check gateway status
|
|
212
226
|
findstr /i /c:"reachable" "%TEMP%\lzmx_status.txt" >nul 2>&1
|
|
213
227
|
if !errorlevel!==0 set "GATEWAY_OK=1"
|
|
214
228
|
|
|
215
|
-
REM
|
|
229
|
+
REM Check QuantumIM channel status
|
|
216
230
|
findstr /i /c:"QuantumIM" "%TEMP%\lzmx_status.txt" | findstr /i /c:"OK" >nul 2>&1
|
|
217
231
|
if !errorlevel!==0 set "CHANNEL_OK=1"
|
|
218
232
|
|
|
@@ -222,9 +236,9 @@ for /l %%i in (1,1,%MAX_RETRIES%) do (
|
|
|
222
236
|
set "HEALTH_OK=1"
|
|
223
237
|
) else (
|
|
224
238
|
if !GATEWAY_OK!==1 (
|
|
225
|
-
echo [WARN]
|
|
239
|
+
echo [WARN] Gateway ready, but channel not connected...
|
|
226
240
|
) else (
|
|
227
|
-
echo [WARN]
|
|
241
|
+
echo [WARN] Service not ready...
|
|
228
242
|
)
|
|
229
243
|
)
|
|
230
244
|
)
|
|
@@ -234,26 +248,26 @@ echo.
|
|
|
234
248
|
echo ---------------------------------------------------
|
|
235
249
|
|
|
236
250
|
if %HEALTH_OK%==1 (
|
|
237
|
-
echo [SUCCESS]
|
|
251
|
+
echo [SUCCESS] Deploy complete!
|
|
238
252
|
echo.
|
|
239
253
|
echo Health:
|
|
240
254
|
echo gateway = reachable
|
|
241
255
|
echo QuantumIM = OK
|
|
242
256
|
echo.
|
|
243
|
-
echo
|
|
257
|
+
echo Details: openclaw status
|
|
244
258
|
echo.
|
|
245
259
|
) else (
|
|
246
|
-
echo [WARN]
|
|
260
|
+
echo [WARN] Deploy finished, but health check failed
|
|
247
261
|
echo.
|
|
248
|
-
echo
|
|
249
|
-
echo -
|
|
250
|
-
echo -
|
|
251
|
-
echo -
|
|
262
|
+
echo Possible causes:
|
|
263
|
+
echo - Service is still starting, check later
|
|
264
|
+
echo - Network connectivity issue
|
|
265
|
+
echo - Invalid credentials
|
|
252
266
|
echo.
|
|
253
|
-
echo
|
|
254
|
-
echo
|
|
267
|
+
echo Manual check: openclaw status
|
|
268
|
+
echo View logs: openclaw gateway logs
|
|
255
269
|
echo.
|
|
256
270
|
exit /b 1
|
|
257
271
|
)
|
|
258
272
|
|
|
259
|
-
endlocal
|
|
273
|
+
endlocal
|
|
@@ -3,12 +3,11 @@ set -euo pipefail
|
|
|
3
3
|
|
|
4
4
|
# ============================================================
|
|
5
5
|
# liangzimixin — 一键部署脚本
|
|
6
|
-
# 用法: ./liangzimixin_install.sh <appId> <appSecret>
|
|
6
|
+
# 用法: ./liangzimixin_install.sh <appId> <appSecret> [quantumAccount]
|
|
7
7
|
# ============================================================
|
|
8
8
|
|
|
9
|
-
SCRIPT_VERSION="0.3.
|
|
9
|
+
SCRIPT_VERSION="0.3.70"
|
|
10
10
|
NPM_PACKAGE="liangzimixin"
|
|
11
|
-
REMOTE_SCRIPT_URL="https://unpkg.com/${NPM_PACKAGE}@latest/scripts/liangzimixin_install.sh"
|
|
12
11
|
|
|
13
12
|
# ── 颜色 ──────────────────────────────────────────────────────
|
|
14
13
|
RED='\033[0;31m'
|
|
@@ -24,11 +23,13 @@ fail() { echo -e "${RED}✗${NC} $*"; exit 1; }
|
|
|
24
23
|
|
|
25
24
|
# ── 参数解析 ──────────────────────────────────────────────────
|
|
26
25
|
SKIP_SELF_UPDATE=false
|
|
26
|
+
USE_BETA=false
|
|
27
27
|
POSITIONAL_ARGS=()
|
|
28
28
|
|
|
29
29
|
for arg in "$@"; do
|
|
30
30
|
case $arg in
|
|
31
31
|
--skip-self-update) SKIP_SELF_UPDATE=true ;;
|
|
32
|
+
--beta) USE_BETA=true ;;
|
|
32
33
|
*) POSITIONAL_ARGS+=("$arg") ;;
|
|
33
34
|
esac
|
|
34
35
|
done
|
|
@@ -37,19 +38,27 @@ APP_ID="${POSITIONAL_ARGS[0]:-}"
|
|
|
37
38
|
APP_SECRET="${POSITIONAL_ARGS[1]:-}"
|
|
38
39
|
QUANTUM_ACCOUNT="${POSITIONAL_ARGS[2]:-}"
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
TAG="latest"
|
|
42
|
+
if [ "$USE_BETA" = true ]; then
|
|
43
|
+
TAG="beta"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
REMOTE_SCRIPT_URL="https://unpkg.com/${NPM_PACKAGE}@${TAG}/scripts/liangzimixin_install.sh"
|
|
47
|
+
|
|
48
|
+
if [ -z "$APP_ID" ] || [ -z "$APP_SECRET" ]; then
|
|
41
49
|
echo ""
|
|
42
50
|
echo "liangzimixin 一键部署脚本 v${SCRIPT_VERSION}"
|
|
43
51
|
echo ""
|
|
44
|
-
echo "用法: $0 <appId> <appSecret>
|
|
52
|
+
echo "用法: $0 <appId> <appSecret> [quantumAccount] [--skip-self-update] [--beta]"
|
|
45
53
|
echo ""
|
|
46
54
|
echo "参数:"
|
|
47
|
-
echo " appId 平台应用 ID"
|
|
48
|
-
echo " appSecret 应用密钥"
|
|
49
|
-
echo " quantumAccount 量子账户标识"
|
|
55
|
+
echo " appId 平台应用 ID (必填)"
|
|
56
|
+
echo " appSecret 应用密钥 (必填)"
|
|
57
|
+
echo " quantumAccount 量子账户标识 (可选)"
|
|
50
58
|
echo ""
|
|
51
59
|
echo "选项:"
|
|
52
60
|
echo " --skip-self-update 跳过脚本自更新检查"
|
|
61
|
+
echo " --beta 安装 beta 测试版本"
|
|
53
62
|
echo ""
|
|
54
63
|
exit 1
|
|
55
64
|
fi
|
|
@@ -101,7 +110,7 @@ if [ "$SKIP_SELF_UPDATE" = false ]; then
|
|
|
101
110
|
if command -v curl &>/dev/null; then
|
|
102
111
|
# 从 npm registry 获取最新版本号
|
|
103
112
|
REMOTE_VERSION=$(curl -fsSL --connect-timeout 5 --max-time 10 \
|
|
104
|
-
"https://registry.npmjs.org/${NPM_PACKAGE}
|
|
113
|
+
"https://registry.npmjs.org/${NPM_PACKAGE}/${TAG}" 2>/dev/null \
|
|
105
114
|
| grep -o '"version":"[^"]*"' | head -1 | cut -d'"' -f4) || true
|
|
106
115
|
fi
|
|
107
116
|
|
|
@@ -117,7 +126,15 @@ if [ "$SKIP_SELF_UPDATE" = false ]; then
|
|
|
117
126
|
if curl -fsSL --connect-timeout 10 --max-time 30 "$REMOTE_SCRIPT_URL" -o "$TEMP_SCRIPT" 2>/dev/null; then
|
|
118
127
|
chmod +x "$TEMP_SCRIPT"
|
|
119
128
|
success "脚本已更新到 v${REMOTE_VERSION}"
|
|
120
|
-
|
|
129
|
+
EXEC_ARGS=(--skip-self-update)
|
|
130
|
+
if [ "$USE_BETA" = true ]; then
|
|
131
|
+
EXEC_ARGS+=(--beta)
|
|
132
|
+
fi
|
|
133
|
+
EXEC_ARGS+=("$APP_ID" "$APP_SECRET")
|
|
134
|
+
if [ -n "$QUANTUM_ACCOUNT" ]; then
|
|
135
|
+
EXEC_ARGS+=("$QUANTUM_ACCOUNT")
|
|
136
|
+
fi
|
|
137
|
+
exec "$TEMP_SCRIPT" "${EXEC_ARGS[@]}"
|
|
121
138
|
else
|
|
122
139
|
warn "脚本下载失败,使用当前版本继续执行"
|
|
123
140
|
rm -f "$TEMP_SCRIPT"
|
|
@@ -137,17 +154,17 @@ echo ""
|
|
|
137
154
|
# ══════════════════════════════════════════════════════════════
|
|
138
155
|
# Step 1: 安装 / 更新插件
|
|
139
156
|
# ══════════════════════════════════════════════════════════════
|
|
140
|
-
info "Step 1/4 — 安装/更新 liangzimixin
|
|
157
|
+
info "Step 1/4 — 安装/更新 liangzimixin 插件 (${TAG})..."
|
|
141
158
|
|
|
142
159
|
if openclaw plugins list 2>/dev/null | grep -q "liangzimixin"; then
|
|
143
|
-
info "
|
|
160
|
+
info "插件已安装,正在更新..."
|
|
144
161
|
if ! openclaw plugins update liangzimixin; then
|
|
145
162
|
fail "插件更新失败,请检查网络或权限"
|
|
146
163
|
fi
|
|
147
164
|
success "插件更新完成"
|
|
148
165
|
else
|
|
149
|
-
info "
|
|
150
|
-
if ! openclaw plugins install liangzimixin
|
|
166
|
+
info "插件未安装,正在安装..."
|
|
167
|
+
if ! openclaw plugins install liangzimixin@${TAG}; then
|
|
151
168
|
fail "插件安装失败,请检查网络或权限"
|
|
152
169
|
fi
|
|
153
170
|
success "插件安装完成"
|
|
@@ -160,15 +177,20 @@ echo ""
|
|
|
160
177
|
# ══════════════════════════════════════════════════════════════
|
|
161
178
|
info "Step 2/4 — 写入配置..."
|
|
162
179
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
--
|
|
166
|
-
|
|
167
|
-
|
|
180
|
+
CHANNEL_ARGS=(--channel liangzimixin --token "$APP_ID" --password "$APP_SECRET")
|
|
181
|
+
if [ -n "$QUANTUM_ACCOUNT" ]; then
|
|
182
|
+
CHANNEL_ARGS+=(--user-id "$QUANTUM_ACCOUNT")
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
if ! openclaw channels add "${CHANNEL_ARGS[@]}"; then
|
|
168
186
|
fail "配置写入失败,请检查参数是否正确"
|
|
169
187
|
fi
|
|
170
188
|
|
|
171
|
-
|
|
189
|
+
if [ -n "$QUANTUM_ACCOUNT" ]; then
|
|
190
|
+
success "配置写入成功 (appId / appSecret / quantumAccount)"
|
|
191
|
+
else
|
|
192
|
+
success "配置写入成功 (appId / appSecret)"
|
|
193
|
+
fi
|
|
172
194
|
echo ""
|
|
173
195
|
|
|
174
196
|
# ══════════════════════════════════════════════════════════════
|
|
@@ -177,10 +199,10 @@ echo ""
|
|
|
177
199
|
info "Step 3/4 — 重启 OpenClaw 网关..."
|
|
178
200
|
|
|
179
201
|
if ! openclaw gateway restart; then
|
|
180
|
-
|
|
202
|
+
warn "网关重启指令返回错误或超时,将继续进行健康检查..."
|
|
203
|
+
else
|
|
204
|
+
success "网关重启指令已发送"
|
|
181
205
|
fi
|
|
182
|
-
|
|
183
|
-
success "网关重启指令已发送"
|
|
184
206
|
echo ""
|
|
185
207
|
|
|
186
208
|
# ══════════════════════════════════════════════════════════════
|