csdn-im 0.1.4 → 0.1.5
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 +102 -54
- package/dist/index.js +102 -54
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4078,10 +4078,12 @@ var NEVER = INVALID;
|
|
|
4078
4078
|
|
|
4079
4079
|
// src/config.ts
|
|
4080
4080
|
var CSDN_PLUGIN_ID = "csdn-im";
|
|
4081
|
+
var CSDN_LOG_TAG = "[csdn-im]";
|
|
4081
4082
|
var FIXED_LONG_POLLING_TIMEOUT_SEC = 30;
|
|
4082
4083
|
var FIXED_TAKE = 1;
|
|
4083
4084
|
var FIXED_RETRY_INTERVAL_MS = 3e3;
|
|
4084
4085
|
var FIXED_RETRY_BACKOFF_MAX_MS = 12e4;
|
|
4086
|
+
var MAX_POLL_INTERVAL_MS = 6e5;
|
|
4085
4087
|
function getCsdnChannelObjectFromRoot(cfg) {
|
|
4086
4088
|
return cfg?.channels?.[CSDN_PLUGIN_ID];
|
|
4087
4089
|
}
|
|
@@ -4101,7 +4103,12 @@ var CsdnConfigSchema = external_exports.object({
|
|
|
4101
4103
|
/**
|
|
4102
4104
|
* 为 true 时打印完整消息体、HTTP 拉取/发送细节、路由与分发路径等调试日志。
|
|
4103
4105
|
*/
|
|
4104
|
-
verbose: external_exports.boolean().default(false)
|
|
4106
|
+
verbose: external_exports.boolean().default(false),
|
|
4107
|
+
/**
|
|
4108
|
+
* 两次成功拉取(poll end → 下一次 poll begin)之间的额外间隔(毫秒)。
|
|
4109
|
+
* `0` 表示仅让出事件循环后立即下一轮(历史默认行为);设为 `1000` 等可减轻对服务端的请求频率。
|
|
4110
|
+
*/
|
|
4111
|
+
pollIntervalMs: external_exports.number().min(0).max(MAX_POLL_INTERVAL_MS).default(0)
|
|
4105
4112
|
});
|
|
4106
4113
|
function clampLongPollingSeconds(n) {
|
|
4107
4114
|
if (!Number.isFinite(n) || n < 1) return 30;
|
|
@@ -4114,6 +4121,10 @@ function clampTake(n) {
|
|
|
4114
4121
|
function normalizeApiBaseUrl(url) {
|
|
4115
4122
|
return url.replace(/\/+$/, "");
|
|
4116
4123
|
}
|
|
4124
|
+
function clampPollIntervalMs(n) {
|
|
4125
|
+
if (!Number.isFinite(n) || n < 0) return 0;
|
|
4126
|
+
return Math.min(MAX_POLL_INTERVAL_MS, Math.floor(n));
|
|
4127
|
+
}
|
|
4117
4128
|
function isConfigured(config) {
|
|
4118
4129
|
if (!config) return false;
|
|
4119
4130
|
const parsed = CsdnConfigSchema.safeParse(config);
|
|
@@ -4204,6 +4215,9 @@ function joinBaseAndPath(base, path) {
|
|
|
4204
4215
|
function isAbortError(e) {
|
|
4205
4216
|
return e instanceof Error && e.name === "AbortError" || typeof DOMException !== "undefined" && e instanceof DOMException && e.name === "AbortError";
|
|
4206
4217
|
}
|
|
4218
|
+
function pullErrorLog(baseUrl, detail) {
|
|
4219
|
+
console.error(`${CSDN_LOG_TAG} pull GET ${PATH_UNREAD} base=${baseUrl} \u2014 ${detail}`);
|
|
4220
|
+
}
|
|
4207
4221
|
var CsdnClient = class {
|
|
4208
4222
|
constructor(config) {
|
|
4209
4223
|
this.config = config;
|
|
@@ -4233,7 +4247,7 @@ var CsdnClient = class {
|
|
|
4233
4247
|
try {
|
|
4234
4248
|
if (this.config.verbose) {
|
|
4235
4249
|
console.info(
|
|
4236
|
-
|
|
4250
|
+
`${CSDN_LOG_TAG} pull GET ${PATH_UNREAD} base=${this.baseUrl} timeout=${sec}s take=${take}${cursor ? ` afterMessageId=${cursor}` : ""}`
|
|
4237
4251
|
);
|
|
4238
4252
|
}
|
|
4239
4253
|
const response = await fetch(unreadUrl.href, {
|
|
@@ -4251,31 +4265,42 @@ var CsdnClient = class {
|
|
|
4251
4265
|
if (fromResult !== null) {
|
|
4252
4266
|
if (this.config.verbose) {
|
|
4253
4267
|
console.info(
|
|
4254
|
-
|
|
4268
|
+
`${CSDN_LOG_TAG} pull response ok code=0 messages=${fromResult.length} httpStatus=${response.status}`
|
|
4255
4269
|
);
|
|
4256
4270
|
}
|
|
4257
4271
|
return fromResult;
|
|
4258
4272
|
}
|
|
4273
|
+
if (response.status === 408 || response.status === 504) {
|
|
4274
|
+
console.warn(
|
|
4275
|
+
`${CSDN_LOG_TAG} pull GET ${PATH_UNREAD} base=${this.baseUrl} httpStatus=${response.status} (timeout/\u7F51\u5173\u8D85\u65F6\uFF0C\u672C\u8F6E\u6309\u7A7A\u7ED3\u679C\u5904\u7406)`
|
|
4276
|
+
);
|
|
4277
|
+
return [];
|
|
4278
|
+
}
|
|
4259
4279
|
const fail = getBotApiFailureInfo(body);
|
|
4260
4280
|
if (fail) {
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4281
|
+
pullErrorLog(
|
|
4282
|
+
this.baseUrl,
|
|
4283
|
+
`business failure httpStatus=${response.status} code=${fail.code} message=${fail.message || "(empty)"}`
|
|
4284
|
+
);
|
|
4285
|
+
if (response.ok) {
|
|
4286
|
+
return [];
|
|
4265
4287
|
}
|
|
4266
4288
|
}
|
|
4267
4289
|
const legacy = tryLegacyUnreadMessages(body);
|
|
4268
4290
|
if (legacy) {
|
|
4269
4291
|
console.warn(
|
|
4270
|
-
|
|
4292
|
+
`${CSDN_LOG_TAG} using legacy unread response format; prefer ClawBot Result with code "0" messages=${legacy.length}`
|
|
4271
4293
|
);
|
|
4272
4294
|
return legacy;
|
|
4273
4295
|
}
|
|
4274
4296
|
if (body !== void 0 && body !== null) {
|
|
4275
|
-
|
|
4297
|
+
pullErrorLog(this.baseUrl, `unexpected response format httpStatus=${response.status} body=${JSON.stringify(body).substring(0, 200)}`);
|
|
4276
4298
|
}
|
|
4277
|
-
if (response.
|
|
4278
|
-
|
|
4299
|
+
if (!response.ok) {
|
|
4300
|
+
const hint = body !== void 0 && body !== null ? (typeof body === "string" ? body : JSON.stringify(body)).slice(0, 200) : "";
|
|
4301
|
+
const detail = `HTTP ${response.status}${hint ? ` body=${hint}` : ""}`;
|
|
4302
|
+
pullErrorLog(this.baseUrl, detail);
|
|
4303
|
+
throw new Error(`${CSDN_LOG_TAG} GET ${PATH_UNREAD} failed: ${detail}`);
|
|
4279
4304
|
}
|
|
4280
4305
|
return [];
|
|
4281
4306
|
} catch (error) {
|
|
@@ -4286,7 +4311,9 @@ var CsdnClient = class {
|
|
|
4286
4311
|
return [];
|
|
4287
4312
|
}
|
|
4288
4313
|
const msg = error instanceof Error ? error.message : String(error);
|
|
4289
|
-
|
|
4314
|
+
if (!(error instanceof Error && msg.startsWith(`${CSDN_LOG_TAG} GET`))) {
|
|
4315
|
+
pullErrorLog(this.baseUrl, `request error: ${msg}`);
|
|
4316
|
+
}
|
|
4290
4317
|
throw error instanceof Error ? error : new Error(msg);
|
|
4291
4318
|
}
|
|
4292
4319
|
}
|
|
@@ -4299,7 +4326,7 @@ var CsdnClient = class {
|
|
|
4299
4326
|
try {
|
|
4300
4327
|
if (this.config.verbose) {
|
|
4301
4328
|
console.info(
|
|
4302
|
-
|
|
4329
|
+
`${CSDN_LOG_TAG} send POST ${PATH_SEND} to=${to} contentLength=${content.length}${opts?.type ? ` type=${opts.type}` : ""}`
|
|
4303
4330
|
);
|
|
4304
4331
|
}
|
|
4305
4332
|
const response = await fetch(sendUrl, {
|
|
@@ -4318,19 +4345,22 @@ var CsdnClient = class {
|
|
|
4318
4345
|
if (ok) {
|
|
4319
4346
|
if (this.config.verbose) {
|
|
4320
4347
|
console.info(
|
|
4321
|
-
|
|
4348
|
+
`${CSDN_LOG_TAG} send response ok code=0 id=${ok.id} httpStatus=${response.status}${ok.timestamp != null ? ` timestamp=${ok.timestamp}` : ""}`
|
|
4322
4349
|
);
|
|
4323
4350
|
}
|
|
4324
4351
|
return ok;
|
|
4325
4352
|
}
|
|
4326
4353
|
const fail = getBotApiFailureInfo(body);
|
|
4327
4354
|
const hint = fail ? `${fail.message} (code=${fail.code})` : `HTTP ${response.status}`;
|
|
4328
|
-
|
|
4355
|
+
console.error(
|
|
4356
|
+
`${CSDN_LOG_TAG} send POST ${PATH_SEND} base=${this.baseUrl} httpStatus=${response.status} ${hint}`
|
|
4357
|
+
);
|
|
4358
|
+
throw new Error(`${CSDN_LOG_TAG} sendMessage failed: ${hint}`);
|
|
4329
4359
|
} catch (error) {
|
|
4330
|
-
if (error instanceof Error && error.message.startsWith(
|
|
4360
|
+
if (error instanceof Error && error.message.startsWith(`${CSDN_LOG_TAG} sendMessage failed:`)) {
|
|
4331
4361
|
throw error;
|
|
4332
4362
|
}
|
|
4333
|
-
console.error(
|
|
4363
|
+
console.error(`${CSDN_LOG_TAG} send POST ${PATH_SEND} base=${this.baseUrl} request error:`, error);
|
|
4334
4364
|
throw error;
|
|
4335
4365
|
}
|
|
4336
4366
|
}
|
|
@@ -4472,7 +4502,7 @@ function findRequireAnchorPackageJson() {
|
|
|
4472
4502
|
dir = parent;
|
|
4473
4503
|
}
|
|
4474
4504
|
throw new Error(
|
|
4475
|
-
|
|
4505
|
+
`${CSDN_LOG_TAG} loadOpenclawPluginSdk: no package.json found walking up from process.cwd(); cannot createRequire for openclaw fallback`
|
|
4476
4506
|
);
|
|
4477
4507
|
}
|
|
4478
4508
|
async function loadOpenclawPluginSdk() {
|
|
@@ -4507,12 +4537,12 @@ var csdnOutbound = {
|
|
|
4507
4537
|
const csdnCfgRaw = getCsdnChannelObjectFromRoot(cfg);
|
|
4508
4538
|
const parsed = CsdnConfigSchema.safeParse(csdnCfgRaw);
|
|
4509
4539
|
if (!parsed.success) {
|
|
4510
|
-
throw new Error(
|
|
4540
|
+
throw new Error(`${CSDN_LOG_TAG} invalid config for outbound`);
|
|
4511
4541
|
}
|
|
4512
4542
|
const client = new CsdnClient(parsed.data);
|
|
4513
4543
|
const result = await client.sendMessage(to, text);
|
|
4514
4544
|
if (parsed.data.verbose) {
|
|
4515
|
-
console.info(
|
|
4545
|
+
console.info(`${CSDN_LOG_TAG} outbound sent messageId=${result.id} to=${to} textLength=${text.length}`);
|
|
4516
4546
|
}
|
|
4517
4547
|
return {
|
|
4518
4548
|
channel: CSDN_PLUGIN_ID,
|
|
@@ -4576,14 +4606,14 @@ function logRoutingFallbackOnce(log) {
|
|
|
4576
4606
|
if (routingFallbackLogged) return;
|
|
4577
4607
|
routingFallbackLogged = true;
|
|
4578
4608
|
log(
|
|
4579
|
-
"
|
|
4609
|
+
"host runtime.channel.routing.resolveAgentRoute unavailable \u2192 embedded session key (normal for some gateways; once per process)"
|
|
4580
4610
|
);
|
|
4581
4611
|
}
|
|
4582
4612
|
function logDispatchFallbackOnce(log) {
|
|
4583
4613
|
if (dispatchFallbackLogged) return;
|
|
4584
4614
|
dispatchFallbackLogged = true;
|
|
4585
4615
|
log(
|
|
4586
|
-
"
|
|
4616
|
+
"host runtime.channel.reply.dispatchReplyFromConfig unavailable \u2192 openclaw/plugin-sdk (normal for some gateways; once per process)"
|
|
4587
4617
|
);
|
|
4588
4618
|
}
|
|
4589
4619
|
|
|
@@ -4626,7 +4656,7 @@ async function handleCsdnMessage(params) {
|
|
|
4626
4656
|
}
|
|
4627
4657
|
if (usedEmbeddedRoute && verbose) {
|
|
4628
4658
|
try {
|
|
4629
|
-
logger.info(`
|
|
4659
|
+
logger.info(`route.fallback sessionKey=${route.sessionKey} accountId=${route.accountId ?? accountId}`);
|
|
4630
4660
|
} catch {
|
|
4631
4661
|
}
|
|
4632
4662
|
}
|
|
@@ -4651,7 +4681,7 @@ async function handleCsdnMessage(params) {
|
|
|
4651
4681
|
};
|
|
4652
4682
|
if (verbose) {
|
|
4653
4683
|
try {
|
|
4654
|
-
logger.info(`
|
|
4684
|
+
logger.info(`inbound.content ${inboundCtx.Body}`);
|
|
4655
4685
|
} catch {
|
|
4656
4686
|
}
|
|
4657
4687
|
}
|
|
@@ -4663,10 +4693,10 @@ async function handleCsdnMessage(params) {
|
|
|
4663
4693
|
if (!text) return;
|
|
4664
4694
|
try {
|
|
4665
4695
|
if (verbose) {
|
|
4666
|
-
logger.info(`
|
|
4667
|
-
logger.info(`
|
|
4696
|
+
logger.info(`outbound text to=${msg.from} len=${text.length}`);
|
|
4697
|
+
logger.info(`outbound.content ${text}`);
|
|
4668
4698
|
} else {
|
|
4669
|
-
logger.info(`
|
|
4699
|
+
logger.info(`reply to=${msg.from} len=${text.length}`);
|
|
4670
4700
|
}
|
|
4671
4701
|
await csdnOutbound.sendText({
|
|
4672
4702
|
cfg: config,
|
|
@@ -4674,21 +4704,21 @@ async function handleCsdnMessage(params) {
|
|
|
4674
4704
|
text
|
|
4675
4705
|
});
|
|
4676
4706
|
if (verbose) {
|
|
4677
|
-
logger.info(`
|
|
4707
|
+
logger.info(`outbound done to=${msg.from}`);
|
|
4678
4708
|
}
|
|
4679
4709
|
} catch (err) {
|
|
4680
|
-
logger.error(`
|
|
4710
|
+
logger.error(`failed to send reply: ${String(err)}`);
|
|
4681
4711
|
}
|
|
4682
4712
|
},
|
|
4683
4713
|
onError: (err, info) => {
|
|
4684
|
-
logger.error(`
|
|
4714
|
+
logger.error(`reply dispatch kind=${info.kind} error: ${String(err)}`);
|
|
4685
4715
|
}
|
|
4686
4716
|
});
|
|
4687
4717
|
const dispatchFromRuntime = runtime?.channel?.reply?.dispatchReplyFromConfig;
|
|
4688
4718
|
const runDispatch = async () => {
|
|
4689
4719
|
if (typeof dispatchFromRuntime === "function") {
|
|
4690
4720
|
if (verbose) {
|
|
4691
|
-
logger.info(`
|
|
4721
|
+
logger.info(`dispatching via runtime.channel.reply session=${route.sessionKey}`);
|
|
4692
4722
|
}
|
|
4693
4723
|
await dispatchFromRuntime({
|
|
4694
4724
|
ctx: inboundCtx,
|
|
@@ -4704,7 +4734,7 @@ async function handleCsdnMessage(params) {
|
|
|
4704
4734
|
const withSettled = sdk.dispatchReplyFromConfigWithSettledDispatcher;
|
|
4705
4735
|
const plain = sdk.dispatchReplyFromConfig;
|
|
4706
4736
|
if (verbose) {
|
|
4707
|
-
logger.info(`
|
|
4737
|
+
logger.info(`dispatching via plugin-sdk session=${route.sessionKey}`);
|
|
4708
4738
|
}
|
|
4709
4739
|
if (typeof withSettled === "function") {
|
|
4710
4740
|
await withSettled({
|
|
@@ -4728,24 +4758,24 @@ async function handleCsdnMessage(params) {
|
|
|
4728
4758
|
);
|
|
4729
4759
|
}
|
|
4730
4760
|
} catch (err) {
|
|
4731
|
-
logger.error(`
|
|
4761
|
+
logger.error(`plugin-sdk dispatch unavailable: ${String(err)}`);
|
|
4732
4762
|
throw err;
|
|
4733
4763
|
}
|
|
4734
4764
|
};
|
|
4735
4765
|
try {
|
|
4736
4766
|
await runDispatch();
|
|
4737
4767
|
if (verbose) {
|
|
4738
|
-
logger.info(`
|
|
4768
|
+
logger.info(`dispatch complete session=${route.sessionKey}`);
|
|
4739
4769
|
}
|
|
4740
4770
|
} catch (err) {
|
|
4741
|
-
logger.error(`
|
|
4771
|
+
logger.error(`dispatch error: ${String(err)}`);
|
|
4742
4772
|
try {
|
|
4743
4773
|
const echo = `\u5DF2\u6536\u5230\uFF1A${inboundCtx.Body}`;
|
|
4744
4774
|
if (verbose) {
|
|
4745
|
-
logger.info(`
|
|
4746
|
-
logger.info(`
|
|
4775
|
+
logger.info(`outbound text(to=${msg.from}) via fallback len=${echo.length}`);
|
|
4776
|
+
logger.info(`outbound.content ${echo}`);
|
|
4747
4777
|
} else {
|
|
4748
|
-
logger.info(`
|
|
4778
|
+
logger.info(`echo fallback to=${msg.from} len=${echo.length}`);
|
|
4749
4779
|
}
|
|
4750
4780
|
await csdnOutbound.sendText({
|
|
4751
4781
|
cfg: config,
|
|
@@ -4753,10 +4783,10 @@ async function handleCsdnMessage(params) {
|
|
|
4753
4783
|
text: echo
|
|
4754
4784
|
});
|
|
4755
4785
|
if (verbose) {
|
|
4756
|
-
logger.info(`
|
|
4786
|
+
logger.info(`outbound done to=${msg.from} (fallback)`);
|
|
4757
4787
|
}
|
|
4758
4788
|
} catch (sendErr) {
|
|
4759
|
-
logger.error(`
|
|
4789
|
+
logger.error(`fallback reply failed: ${String(sendErr)}`);
|
|
4760
4790
|
}
|
|
4761
4791
|
}
|
|
4762
4792
|
}
|
|
@@ -4769,6 +4799,26 @@ function maxMessageId(messages) {
|
|
|
4769
4799
|
}
|
|
4770
4800
|
return max;
|
|
4771
4801
|
}
|
|
4802
|
+
function sleepBetweenPolls(ms, signal) {
|
|
4803
|
+
if (ms <= 0) {
|
|
4804
|
+
return new Promise((resolve) => setImmediate(resolve));
|
|
4805
|
+
}
|
|
4806
|
+
return new Promise((resolve) => {
|
|
4807
|
+
if (signal?.aborted) {
|
|
4808
|
+
resolve();
|
|
4809
|
+
return;
|
|
4810
|
+
}
|
|
4811
|
+
const t = setTimeout(resolve, ms);
|
|
4812
|
+
signal?.addEventListener(
|
|
4813
|
+
"abort",
|
|
4814
|
+
() => {
|
|
4815
|
+
clearTimeout(t);
|
|
4816
|
+
resolve();
|
|
4817
|
+
},
|
|
4818
|
+
{ once: true }
|
|
4819
|
+
);
|
|
4820
|
+
});
|
|
4821
|
+
}
|
|
4772
4822
|
async function monitorCsdnProvider(opts) {
|
|
4773
4823
|
const { config, runtime, accountId = "default", abortSignal } = opts;
|
|
4774
4824
|
const baseLogger = runtime && (runtime.logger || runtime.log) || console;
|
|
@@ -4780,18 +4830,19 @@ async function monitorCsdnProvider(opts) {
|
|
|
4780
4830
|
const csdnCfgRaw = getCsdnChannelObjectFromRoot(config);
|
|
4781
4831
|
const parsed = CsdnConfigSchema.safeParse(csdnCfgRaw);
|
|
4782
4832
|
if (!parsed.success) {
|
|
4783
|
-
logger.error(`
|
|
4833
|
+
logger.error(`invalid config for account ${accountId}: ${parsed.error}`);
|
|
4784
4834
|
return;
|
|
4785
4835
|
}
|
|
4786
4836
|
const csdnCfg = parsed.data;
|
|
4787
4837
|
if (!csdnCfg.token) {
|
|
4788
|
-
logger.warn(`
|
|
4838
|
+
logger.warn(`token missing; skip start for account ${accountId}`);
|
|
4789
4839
|
return;
|
|
4790
4840
|
}
|
|
4791
4841
|
const client = new CsdnClient(csdnCfg);
|
|
4842
|
+
const pollIntervalMs = clampPollIntervalMs(csdnCfg.pollIntervalMs);
|
|
4792
4843
|
const v = csdnCfg.verbose;
|
|
4793
4844
|
logger.info(
|
|
4794
|
-
`
|
|
4845
|
+
`starting long-polling account=${accountId} apiUrl=${csdnCfg.apiUrl} timeout=${FIXED_LONG_POLLING_TIMEOUT_SEC}s take=${FIXED_TAKE} pollIntervalMs=${pollIntervalMs}`
|
|
4795
4846
|
);
|
|
4796
4847
|
let backoffMs = FIXED_RETRY_INTERVAL_MS;
|
|
4797
4848
|
const backoffCap = Math.max(FIXED_RETRY_INTERVAL_MS, FIXED_RETRY_BACKOFF_MAX_MS);
|
|
@@ -4801,9 +4852,6 @@ async function monitorCsdnProvider(opts) {
|
|
|
4801
4852
|
try {
|
|
4802
4853
|
const t0 = Date.now();
|
|
4803
4854
|
if (v) {
|
|
4804
|
-
logger.info(
|
|
4805
|
-
`[csdn] poll begin account=${accountId} timeout=${FIXED_LONG_POLLING_TIMEOUT_SEC}s take=${FIXED_TAKE}${afterMessageId ? ` afterMessageId=${afterMessageId}` : ""}`
|
|
4806
|
-
);
|
|
4807
4855
|
}
|
|
4808
4856
|
const messages = await client.getUnreadMessages(FIXED_LONG_POLLING_TIMEOUT_SEC, {
|
|
4809
4857
|
afterMessageId,
|
|
@@ -4811,12 +4859,11 @@ async function monitorCsdnProvider(opts) {
|
|
|
4811
4859
|
});
|
|
4812
4860
|
backoffMs = FIXED_RETRY_INTERVAL_MS;
|
|
4813
4861
|
const dur = Date.now() - t0;
|
|
4814
|
-
logger.info(`[csdn] poll end account=${accountId} duration=${dur}ms count=${messages.length}`);
|
|
4815
4862
|
for (const msg of messages) {
|
|
4816
|
-
logger.info(`
|
|
4863
|
+
logger.info(`inbound id=${msg.id} from=${msg.from} type=${msg.type} size=${msg.content?.length ?? 0}`);
|
|
4817
4864
|
if (v) {
|
|
4818
4865
|
try {
|
|
4819
|
-
logger.info(`
|
|
4866
|
+
logger.info(`inbound.full ${JSON.stringify(msg)}`);
|
|
4820
4867
|
} catch {
|
|
4821
4868
|
}
|
|
4822
4869
|
}
|
|
@@ -4830,17 +4877,17 @@ async function monitorCsdnProvider(opts) {
|
|
|
4830
4877
|
if (messages.length > 0) {
|
|
4831
4878
|
afterMessageId = maxMessageId(messages);
|
|
4832
4879
|
}
|
|
4833
|
-
await
|
|
4880
|
+
await sleepBetweenPolls(pollIntervalMs, abortSignal);
|
|
4834
4881
|
} catch (err) {
|
|
4835
|
-
logger.error(`
|
|
4882
|
+
logger.error(`polling error: ${err}`);
|
|
4836
4883
|
logger.info(
|
|
4837
|
-
`
|
|
4884
|
+
`waiting ${backoffMs}ms before retry (exponential backoff, cap=${backoffCap}ms)...`
|
|
4838
4885
|
);
|
|
4839
4886
|
await new Promise((resolve) => setTimeout(resolve, backoffMs));
|
|
4840
4887
|
backoffMs = Math.min(backoffCap, Math.max(FIXED_RETRY_INTERVAL_MS, backoffMs * 2));
|
|
4841
4888
|
}
|
|
4842
4889
|
}
|
|
4843
|
-
logger.info(`
|
|
4890
|
+
logger.info(`polling stopped for account ${accountId}`);
|
|
4844
4891
|
};
|
|
4845
4892
|
return pollLoop();
|
|
4846
4893
|
}
|
|
@@ -4903,7 +4950,8 @@ var csdnPlugin = {
|
|
|
4903
4950
|
token: { type: "string" },
|
|
4904
4951
|
apiUrl: { type: "string" },
|
|
4905
4952
|
fallbackAgentId: { type: "string" },
|
|
4906
|
-
verbose: { type: "boolean" }
|
|
4953
|
+
verbose: { type: "boolean" },
|
|
4954
|
+
pollIntervalMs: { type: "number", minimum: 0, maximum: 6e5 }
|
|
4907
4955
|
}
|
|
4908
4956
|
}
|
|
4909
4957
|
},
|
package/dist/index.js
CHANGED
|
@@ -4047,10 +4047,12 @@ var NEVER = INVALID;
|
|
|
4047
4047
|
|
|
4048
4048
|
// src/config.ts
|
|
4049
4049
|
var CSDN_PLUGIN_ID = "csdn-im";
|
|
4050
|
+
var CSDN_LOG_TAG = "[csdn-im]";
|
|
4050
4051
|
var FIXED_LONG_POLLING_TIMEOUT_SEC = 30;
|
|
4051
4052
|
var FIXED_TAKE = 1;
|
|
4052
4053
|
var FIXED_RETRY_INTERVAL_MS = 3e3;
|
|
4053
4054
|
var FIXED_RETRY_BACKOFF_MAX_MS = 12e4;
|
|
4055
|
+
var MAX_POLL_INTERVAL_MS = 6e5;
|
|
4054
4056
|
function getCsdnChannelObjectFromRoot(cfg) {
|
|
4055
4057
|
return cfg?.channels?.[CSDN_PLUGIN_ID];
|
|
4056
4058
|
}
|
|
@@ -4070,7 +4072,12 @@ var CsdnConfigSchema = external_exports.object({
|
|
|
4070
4072
|
/**
|
|
4071
4073
|
* 为 true 时打印完整消息体、HTTP 拉取/发送细节、路由与分发路径等调试日志。
|
|
4072
4074
|
*/
|
|
4073
|
-
verbose: external_exports.boolean().default(false)
|
|
4075
|
+
verbose: external_exports.boolean().default(false),
|
|
4076
|
+
/**
|
|
4077
|
+
* 两次成功拉取(poll end → 下一次 poll begin)之间的额外间隔(毫秒)。
|
|
4078
|
+
* `0` 表示仅让出事件循环后立即下一轮(历史默认行为);设为 `1000` 等可减轻对服务端的请求频率。
|
|
4079
|
+
*/
|
|
4080
|
+
pollIntervalMs: external_exports.number().min(0).max(MAX_POLL_INTERVAL_MS).default(0)
|
|
4074
4081
|
});
|
|
4075
4082
|
function clampLongPollingSeconds(n) {
|
|
4076
4083
|
if (!Number.isFinite(n) || n < 1) return 30;
|
|
@@ -4083,6 +4090,10 @@ function clampTake(n) {
|
|
|
4083
4090
|
function normalizeApiBaseUrl(url) {
|
|
4084
4091
|
return url.replace(/\/+$/, "");
|
|
4085
4092
|
}
|
|
4093
|
+
function clampPollIntervalMs(n) {
|
|
4094
|
+
if (!Number.isFinite(n) || n < 0) return 0;
|
|
4095
|
+
return Math.min(MAX_POLL_INTERVAL_MS, Math.floor(n));
|
|
4096
|
+
}
|
|
4086
4097
|
function isConfigured(config) {
|
|
4087
4098
|
if (!config) return false;
|
|
4088
4099
|
const parsed = CsdnConfigSchema.safeParse(config);
|
|
@@ -4173,6 +4184,9 @@ function joinBaseAndPath(base, path) {
|
|
|
4173
4184
|
function isAbortError(e) {
|
|
4174
4185
|
return e instanceof Error && e.name === "AbortError" || typeof DOMException !== "undefined" && e instanceof DOMException && e.name === "AbortError";
|
|
4175
4186
|
}
|
|
4187
|
+
function pullErrorLog(baseUrl, detail) {
|
|
4188
|
+
console.error(`${CSDN_LOG_TAG} pull GET ${PATH_UNREAD} base=${baseUrl} \u2014 ${detail}`);
|
|
4189
|
+
}
|
|
4176
4190
|
var CsdnClient = class {
|
|
4177
4191
|
constructor(config) {
|
|
4178
4192
|
this.config = config;
|
|
@@ -4202,7 +4216,7 @@ var CsdnClient = class {
|
|
|
4202
4216
|
try {
|
|
4203
4217
|
if (this.config.verbose) {
|
|
4204
4218
|
console.info(
|
|
4205
|
-
|
|
4219
|
+
`${CSDN_LOG_TAG} pull GET ${PATH_UNREAD} base=${this.baseUrl} timeout=${sec}s take=${take}${cursor ? ` afterMessageId=${cursor}` : ""}`
|
|
4206
4220
|
);
|
|
4207
4221
|
}
|
|
4208
4222
|
const response = await fetch(unreadUrl.href, {
|
|
@@ -4220,31 +4234,42 @@ var CsdnClient = class {
|
|
|
4220
4234
|
if (fromResult !== null) {
|
|
4221
4235
|
if (this.config.verbose) {
|
|
4222
4236
|
console.info(
|
|
4223
|
-
|
|
4237
|
+
`${CSDN_LOG_TAG} pull response ok code=0 messages=${fromResult.length} httpStatus=${response.status}`
|
|
4224
4238
|
);
|
|
4225
4239
|
}
|
|
4226
4240
|
return fromResult;
|
|
4227
4241
|
}
|
|
4242
|
+
if (response.status === 408 || response.status === 504) {
|
|
4243
|
+
console.warn(
|
|
4244
|
+
`${CSDN_LOG_TAG} pull GET ${PATH_UNREAD} base=${this.baseUrl} httpStatus=${response.status} (timeout/\u7F51\u5173\u8D85\u65F6\uFF0C\u672C\u8F6E\u6309\u7A7A\u7ED3\u679C\u5904\u7406)`
|
|
4245
|
+
);
|
|
4246
|
+
return [];
|
|
4247
|
+
}
|
|
4228
4248
|
const fail = getBotApiFailureInfo(body);
|
|
4229
4249
|
if (fail) {
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4250
|
+
pullErrorLog(
|
|
4251
|
+
this.baseUrl,
|
|
4252
|
+
`business failure httpStatus=${response.status} code=${fail.code} message=${fail.message || "(empty)"}`
|
|
4253
|
+
);
|
|
4254
|
+
if (response.ok) {
|
|
4255
|
+
return [];
|
|
4234
4256
|
}
|
|
4235
4257
|
}
|
|
4236
4258
|
const legacy = tryLegacyUnreadMessages(body);
|
|
4237
4259
|
if (legacy) {
|
|
4238
4260
|
console.warn(
|
|
4239
|
-
|
|
4261
|
+
`${CSDN_LOG_TAG} using legacy unread response format; prefer ClawBot Result with code "0" messages=${legacy.length}`
|
|
4240
4262
|
);
|
|
4241
4263
|
return legacy;
|
|
4242
4264
|
}
|
|
4243
4265
|
if (body !== void 0 && body !== null) {
|
|
4244
|
-
|
|
4266
|
+
pullErrorLog(this.baseUrl, `unexpected response format httpStatus=${response.status} body=${JSON.stringify(body).substring(0, 200)}`);
|
|
4245
4267
|
}
|
|
4246
|
-
if (response.
|
|
4247
|
-
|
|
4268
|
+
if (!response.ok) {
|
|
4269
|
+
const hint = body !== void 0 && body !== null ? (typeof body === "string" ? body : JSON.stringify(body)).slice(0, 200) : "";
|
|
4270
|
+
const detail = `HTTP ${response.status}${hint ? ` body=${hint}` : ""}`;
|
|
4271
|
+
pullErrorLog(this.baseUrl, detail);
|
|
4272
|
+
throw new Error(`${CSDN_LOG_TAG} GET ${PATH_UNREAD} failed: ${detail}`);
|
|
4248
4273
|
}
|
|
4249
4274
|
return [];
|
|
4250
4275
|
} catch (error) {
|
|
@@ -4255,7 +4280,9 @@ var CsdnClient = class {
|
|
|
4255
4280
|
return [];
|
|
4256
4281
|
}
|
|
4257
4282
|
const msg = error instanceof Error ? error.message : String(error);
|
|
4258
|
-
|
|
4283
|
+
if (!(error instanceof Error && msg.startsWith(`${CSDN_LOG_TAG} GET`))) {
|
|
4284
|
+
pullErrorLog(this.baseUrl, `request error: ${msg}`);
|
|
4285
|
+
}
|
|
4259
4286
|
throw error instanceof Error ? error : new Error(msg);
|
|
4260
4287
|
}
|
|
4261
4288
|
}
|
|
@@ -4268,7 +4295,7 @@ var CsdnClient = class {
|
|
|
4268
4295
|
try {
|
|
4269
4296
|
if (this.config.verbose) {
|
|
4270
4297
|
console.info(
|
|
4271
|
-
|
|
4298
|
+
`${CSDN_LOG_TAG} send POST ${PATH_SEND} to=${to} contentLength=${content.length}${opts?.type ? ` type=${opts.type}` : ""}`
|
|
4272
4299
|
);
|
|
4273
4300
|
}
|
|
4274
4301
|
const response = await fetch(sendUrl, {
|
|
@@ -4287,19 +4314,22 @@ var CsdnClient = class {
|
|
|
4287
4314
|
if (ok) {
|
|
4288
4315
|
if (this.config.verbose) {
|
|
4289
4316
|
console.info(
|
|
4290
|
-
|
|
4317
|
+
`${CSDN_LOG_TAG} send response ok code=0 id=${ok.id} httpStatus=${response.status}${ok.timestamp != null ? ` timestamp=${ok.timestamp}` : ""}`
|
|
4291
4318
|
);
|
|
4292
4319
|
}
|
|
4293
4320
|
return ok;
|
|
4294
4321
|
}
|
|
4295
4322
|
const fail = getBotApiFailureInfo(body);
|
|
4296
4323
|
const hint = fail ? `${fail.message} (code=${fail.code})` : `HTTP ${response.status}`;
|
|
4297
|
-
|
|
4324
|
+
console.error(
|
|
4325
|
+
`${CSDN_LOG_TAG} send POST ${PATH_SEND} base=${this.baseUrl} httpStatus=${response.status} ${hint}`
|
|
4326
|
+
);
|
|
4327
|
+
throw new Error(`${CSDN_LOG_TAG} sendMessage failed: ${hint}`);
|
|
4298
4328
|
} catch (error) {
|
|
4299
|
-
if (error instanceof Error && error.message.startsWith(
|
|
4329
|
+
if (error instanceof Error && error.message.startsWith(`${CSDN_LOG_TAG} sendMessage failed:`)) {
|
|
4300
4330
|
throw error;
|
|
4301
4331
|
}
|
|
4302
|
-
console.error(
|
|
4332
|
+
console.error(`${CSDN_LOG_TAG} send POST ${PATH_SEND} base=${this.baseUrl} request error:`, error);
|
|
4303
4333
|
throw error;
|
|
4304
4334
|
}
|
|
4305
4335
|
}
|
|
@@ -4441,7 +4471,7 @@ function findRequireAnchorPackageJson() {
|
|
|
4441
4471
|
dir = parent;
|
|
4442
4472
|
}
|
|
4443
4473
|
throw new Error(
|
|
4444
|
-
|
|
4474
|
+
`${CSDN_LOG_TAG} loadOpenclawPluginSdk: no package.json found walking up from process.cwd(); cannot createRequire for openclaw fallback`
|
|
4445
4475
|
);
|
|
4446
4476
|
}
|
|
4447
4477
|
async function loadOpenclawPluginSdk() {
|
|
@@ -4476,12 +4506,12 @@ var csdnOutbound = {
|
|
|
4476
4506
|
const csdnCfgRaw = getCsdnChannelObjectFromRoot(cfg);
|
|
4477
4507
|
const parsed = CsdnConfigSchema.safeParse(csdnCfgRaw);
|
|
4478
4508
|
if (!parsed.success) {
|
|
4479
|
-
throw new Error(
|
|
4509
|
+
throw new Error(`${CSDN_LOG_TAG} invalid config for outbound`);
|
|
4480
4510
|
}
|
|
4481
4511
|
const client = new CsdnClient(parsed.data);
|
|
4482
4512
|
const result = await client.sendMessage(to, text);
|
|
4483
4513
|
if (parsed.data.verbose) {
|
|
4484
|
-
console.info(
|
|
4514
|
+
console.info(`${CSDN_LOG_TAG} outbound sent messageId=${result.id} to=${to} textLength=${text.length}`);
|
|
4485
4515
|
}
|
|
4486
4516
|
return {
|
|
4487
4517
|
channel: CSDN_PLUGIN_ID,
|
|
@@ -4545,14 +4575,14 @@ function logRoutingFallbackOnce(log) {
|
|
|
4545
4575
|
if (routingFallbackLogged) return;
|
|
4546
4576
|
routingFallbackLogged = true;
|
|
4547
4577
|
log(
|
|
4548
|
-
"
|
|
4578
|
+
"host runtime.channel.routing.resolveAgentRoute unavailable \u2192 embedded session key (normal for some gateways; once per process)"
|
|
4549
4579
|
);
|
|
4550
4580
|
}
|
|
4551
4581
|
function logDispatchFallbackOnce(log) {
|
|
4552
4582
|
if (dispatchFallbackLogged) return;
|
|
4553
4583
|
dispatchFallbackLogged = true;
|
|
4554
4584
|
log(
|
|
4555
|
-
"
|
|
4585
|
+
"host runtime.channel.reply.dispatchReplyFromConfig unavailable \u2192 openclaw/plugin-sdk (normal for some gateways; once per process)"
|
|
4556
4586
|
);
|
|
4557
4587
|
}
|
|
4558
4588
|
|
|
@@ -4595,7 +4625,7 @@ async function handleCsdnMessage(params) {
|
|
|
4595
4625
|
}
|
|
4596
4626
|
if (usedEmbeddedRoute && verbose) {
|
|
4597
4627
|
try {
|
|
4598
|
-
logger.info(`
|
|
4628
|
+
logger.info(`route.fallback sessionKey=${route.sessionKey} accountId=${route.accountId ?? accountId}`);
|
|
4599
4629
|
} catch {
|
|
4600
4630
|
}
|
|
4601
4631
|
}
|
|
@@ -4620,7 +4650,7 @@ async function handleCsdnMessage(params) {
|
|
|
4620
4650
|
};
|
|
4621
4651
|
if (verbose) {
|
|
4622
4652
|
try {
|
|
4623
|
-
logger.info(`
|
|
4653
|
+
logger.info(`inbound.content ${inboundCtx.Body}`);
|
|
4624
4654
|
} catch {
|
|
4625
4655
|
}
|
|
4626
4656
|
}
|
|
@@ -4632,10 +4662,10 @@ async function handleCsdnMessage(params) {
|
|
|
4632
4662
|
if (!text) return;
|
|
4633
4663
|
try {
|
|
4634
4664
|
if (verbose) {
|
|
4635
|
-
logger.info(`
|
|
4636
|
-
logger.info(`
|
|
4665
|
+
logger.info(`outbound text to=${msg.from} len=${text.length}`);
|
|
4666
|
+
logger.info(`outbound.content ${text}`);
|
|
4637
4667
|
} else {
|
|
4638
|
-
logger.info(`
|
|
4668
|
+
logger.info(`reply to=${msg.from} len=${text.length}`);
|
|
4639
4669
|
}
|
|
4640
4670
|
await csdnOutbound.sendText({
|
|
4641
4671
|
cfg: config,
|
|
@@ -4643,21 +4673,21 @@ async function handleCsdnMessage(params) {
|
|
|
4643
4673
|
text
|
|
4644
4674
|
});
|
|
4645
4675
|
if (verbose) {
|
|
4646
|
-
logger.info(`
|
|
4676
|
+
logger.info(`outbound done to=${msg.from}`);
|
|
4647
4677
|
}
|
|
4648
4678
|
} catch (err) {
|
|
4649
|
-
logger.error(`
|
|
4679
|
+
logger.error(`failed to send reply: ${String(err)}`);
|
|
4650
4680
|
}
|
|
4651
4681
|
},
|
|
4652
4682
|
onError: (err, info) => {
|
|
4653
|
-
logger.error(`
|
|
4683
|
+
logger.error(`reply dispatch kind=${info.kind} error: ${String(err)}`);
|
|
4654
4684
|
}
|
|
4655
4685
|
});
|
|
4656
4686
|
const dispatchFromRuntime = runtime?.channel?.reply?.dispatchReplyFromConfig;
|
|
4657
4687
|
const runDispatch = async () => {
|
|
4658
4688
|
if (typeof dispatchFromRuntime === "function") {
|
|
4659
4689
|
if (verbose) {
|
|
4660
|
-
logger.info(`
|
|
4690
|
+
logger.info(`dispatching via runtime.channel.reply session=${route.sessionKey}`);
|
|
4661
4691
|
}
|
|
4662
4692
|
await dispatchFromRuntime({
|
|
4663
4693
|
ctx: inboundCtx,
|
|
@@ -4673,7 +4703,7 @@ async function handleCsdnMessage(params) {
|
|
|
4673
4703
|
const withSettled = sdk.dispatchReplyFromConfigWithSettledDispatcher;
|
|
4674
4704
|
const plain = sdk.dispatchReplyFromConfig;
|
|
4675
4705
|
if (verbose) {
|
|
4676
|
-
logger.info(`
|
|
4706
|
+
logger.info(`dispatching via plugin-sdk session=${route.sessionKey}`);
|
|
4677
4707
|
}
|
|
4678
4708
|
if (typeof withSettled === "function") {
|
|
4679
4709
|
await withSettled({
|
|
@@ -4697,24 +4727,24 @@ async function handleCsdnMessage(params) {
|
|
|
4697
4727
|
);
|
|
4698
4728
|
}
|
|
4699
4729
|
} catch (err) {
|
|
4700
|
-
logger.error(`
|
|
4730
|
+
logger.error(`plugin-sdk dispatch unavailable: ${String(err)}`);
|
|
4701
4731
|
throw err;
|
|
4702
4732
|
}
|
|
4703
4733
|
};
|
|
4704
4734
|
try {
|
|
4705
4735
|
await runDispatch();
|
|
4706
4736
|
if (verbose) {
|
|
4707
|
-
logger.info(`
|
|
4737
|
+
logger.info(`dispatch complete session=${route.sessionKey}`);
|
|
4708
4738
|
}
|
|
4709
4739
|
} catch (err) {
|
|
4710
|
-
logger.error(`
|
|
4740
|
+
logger.error(`dispatch error: ${String(err)}`);
|
|
4711
4741
|
try {
|
|
4712
4742
|
const echo = `\u5DF2\u6536\u5230\uFF1A${inboundCtx.Body}`;
|
|
4713
4743
|
if (verbose) {
|
|
4714
|
-
logger.info(`
|
|
4715
|
-
logger.info(`
|
|
4744
|
+
logger.info(`outbound text(to=${msg.from}) via fallback len=${echo.length}`);
|
|
4745
|
+
logger.info(`outbound.content ${echo}`);
|
|
4716
4746
|
} else {
|
|
4717
|
-
logger.info(`
|
|
4747
|
+
logger.info(`echo fallback to=${msg.from} len=${echo.length}`);
|
|
4718
4748
|
}
|
|
4719
4749
|
await csdnOutbound.sendText({
|
|
4720
4750
|
cfg: config,
|
|
@@ -4722,10 +4752,10 @@ async function handleCsdnMessage(params) {
|
|
|
4722
4752
|
text: echo
|
|
4723
4753
|
});
|
|
4724
4754
|
if (verbose) {
|
|
4725
|
-
logger.info(`
|
|
4755
|
+
logger.info(`outbound done to=${msg.from} (fallback)`);
|
|
4726
4756
|
}
|
|
4727
4757
|
} catch (sendErr) {
|
|
4728
|
-
logger.error(`
|
|
4758
|
+
logger.error(`fallback reply failed: ${String(sendErr)}`);
|
|
4729
4759
|
}
|
|
4730
4760
|
}
|
|
4731
4761
|
}
|
|
@@ -4738,6 +4768,26 @@ function maxMessageId(messages) {
|
|
|
4738
4768
|
}
|
|
4739
4769
|
return max;
|
|
4740
4770
|
}
|
|
4771
|
+
function sleepBetweenPolls(ms, signal) {
|
|
4772
|
+
if (ms <= 0) {
|
|
4773
|
+
return new Promise((resolve) => setImmediate(resolve));
|
|
4774
|
+
}
|
|
4775
|
+
return new Promise((resolve) => {
|
|
4776
|
+
if (signal?.aborted) {
|
|
4777
|
+
resolve();
|
|
4778
|
+
return;
|
|
4779
|
+
}
|
|
4780
|
+
const t = setTimeout(resolve, ms);
|
|
4781
|
+
signal?.addEventListener(
|
|
4782
|
+
"abort",
|
|
4783
|
+
() => {
|
|
4784
|
+
clearTimeout(t);
|
|
4785
|
+
resolve();
|
|
4786
|
+
},
|
|
4787
|
+
{ once: true }
|
|
4788
|
+
);
|
|
4789
|
+
});
|
|
4790
|
+
}
|
|
4741
4791
|
async function monitorCsdnProvider(opts) {
|
|
4742
4792
|
const { config, runtime, accountId = "default", abortSignal } = opts;
|
|
4743
4793
|
const baseLogger = runtime && (runtime.logger || runtime.log) || console;
|
|
@@ -4749,18 +4799,19 @@ async function monitorCsdnProvider(opts) {
|
|
|
4749
4799
|
const csdnCfgRaw = getCsdnChannelObjectFromRoot(config);
|
|
4750
4800
|
const parsed = CsdnConfigSchema.safeParse(csdnCfgRaw);
|
|
4751
4801
|
if (!parsed.success) {
|
|
4752
|
-
logger.error(`
|
|
4802
|
+
logger.error(`invalid config for account ${accountId}: ${parsed.error}`);
|
|
4753
4803
|
return;
|
|
4754
4804
|
}
|
|
4755
4805
|
const csdnCfg = parsed.data;
|
|
4756
4806
|
if (!csdnCfg.token) {
|
|
4757
|
-
logger.warn(`
|
|
4807
|
+
logger.warn(`token missing; skip start for account ${accountId}`);
|
|
4758
4808
|
return;
|
|
4759
4809
|
}
|
|
4760
4810
|
const client = new CsdnClient(csdnCfg);
|
|
4811
|
+
const pollIntervalMs = clampPollIntervalMs(csdnCfg.pollIntervalMs);
|
|
4761
4812
|
const v = csdnCfg.verbose;
|
|
4762
4813
|
logger.info(
|
|
4763
|
-
`
|
|
4814
|
+
`starting long-polling account=${accountId} apiUrl=${csdnCfg.apiUrl} timeout=${FIXED_LONG_POLLING_TIMEOUT_SEC}s take=${FIXED_TAKE} pollIntervalMs=${pollIntervalMs}`
|
|
4764
4815
|
);
|
|
4765
4816
|
let backoffMs = FIXED_RETRY_INTERVAL_MS;
|
|
4766
4817
|
const backoffCap = Math.max(FIXED_RETRY_INTERVAL_MS, FIXED_RETRY_BACKOFF_MAX_MS);
|
|
@@ -4770,9 +4821,6 @@ async function monitorCsdnProvider(opts) {
|
|
|
4770
4821
|
try {
|
|
4771
4822
|
const t0 = Date.now();
|
|
4772
4823
|
if (v) {
|
|
4773
|
-
logger.info(
|
|
4774
|
-
`[csdn] poll begin account=${accountId} timeout=${FIXED_LONG_POLLING_TIMEOUT_SEC}s take=${FIXED_TAKE}${afterMessageId ? ` afterMessageId=${afterMessageId}` : ""}`
|
|
4775
|
-
);
|
|
4776
4824
|
}
|
|
4777
4825
|
const messages = await client.getUnreadMessages(FIXED_LONG_POLLING_TIMEOUT_SEC, {
|
|
4778
4826
|
afterMessageId,
|
|
@@ -4780,12 +4828,11 @@ async function monitorCsdnProvider(opts) {
|
|
|
4780
4828
|
});
|
|
4781
4829
|
backoffMs = FIXED_RETRY_INTERVAL_MS;
|
|
4782
4830
|
const dur = Date.now() - t0;
|
|
4783
|
-
logger.info(`[csdn] poll end account=${accountId} duration=${dur}ms count=${messages.length}`);
|
|
4784
4831
|
for (const msg of messages) {
|
|
4785
|
-
logger.info(`
|
|
4832
|
+
logger.info(`inbound id=${msg.id} from=${msg.from} type=${msg.type} size=${msg.content?.length ?? 0}`);
|
|
4786
4833
|
if (v) {
|
|
4787
4834
|
try {
|
|
4788
|
-
logger.info(`
|
|
4835
|
+
logger.info(`inbound.full ${JSON.stringify(msg)}`);
|
|
4789
4836
|
} catch {
|
|
4790
4837
|
}
|
|
4791
4838
|
}
|
|
@@ -4799,17 +4846,17 @@ async function monitorCsdnProvider(opts) {
|
|
|
4799
4846
|
if (messages.length > 0) {
|
|
4800
4847
|
afterMessageId = maxMessageId(messages);
|
|
4801
4848
|
}
|
|
4802
|
-
await
|
|
4849
|
+
await sleepBetweenPolls(pollIntervalMs, abortSignal);
|
|
4803
4850
|
} catch (err) {
|
|
4804
|
-
logger.error(`
|
|
4851
|
+
logger.error(`polling error: ${err}`);
|
|
4805
4852
|
logger.info(
|
|
4806
|
-
`
|
|
4853
|
+
`waiting ${backoffMs}ms before retry (exponential backoff, cap=${backoffCap}ms)...`
|
|
4807
4854
|
);
|
|
4808
4855
|
await new Promise((resolve) => setTimeout(resolve, backoffMs));
|
|
4809
4856
|
backoffMs = Math.min(backoffCap, Math.max(FIXED_RETRY_INTERVAL_MS, backoffMs * 2));
|
|
4810
4857
|
}
|
|
4811
4858
|
}
|
|
4812
|
-
logger.info(`
|
|
4859
|
+
logger.info(`polling stopped for account ${accountId}`);
|
|
4813
4860
|
};
|
|
4814
4861
|
return pollLoop();
|
|
4815
4862
|
}
|
|
@@ -4872,7 +4919,8 @@ var csdnPlugin = {
|
|
|
4872
4919
|
token: { type: "string" },
|
|
4873
4920
|
apiUrl: { type: "string" },
|
|
4874
4921
|
fallbackAgentId: { type: "string" },
|
|
4875
|
-
verbose: { type: "boolean" }
|
|
4922
|
+
verbose: { type: "boolean" },
|
|
4923
|
+
pollIntervalMs: { type: "number", minimum: 0, maximum: 6e5 }
|
|
4876
4924
|
}
|
|
4877
4925
|
}
|
|
4878
4926
|
},
|