dsh-lark-bot 0.15.5 → 0.15.6
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/README.md +4 -1
- package/dist/cli.js +185 -45
- package/dist/cli.js.map +1 -1
- package/dist/plugin.d.ts +8 -0
- package/dist/plugin.js +185 -45
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -163,9 +163,12 @@ dsh-lark-bot guardian install --dsh-profile dsh-lark
|
|
|
163
163
|
字面密钥不进 settings / 聊天记录。
|
|
164
164
|
- **凭据引用必须关联**:`/key set <引用名> <值>` 只写入凭据文件;provider 要生效还须在其
|
|
165
165
|
`apiKeyEnv` 字段引用同一名字(`/provider add|update ... --api-key-env <引用名>`,或向导中填写)。
|
|
166
|
+
引用名与 provider ID 相同且 provider 未设 `apiKeyEnv` 时,`/key set` 会自动补关联;
|
|
167
|
+
已存在的老配置在下次运行时也会自动补齐。
|
|
166
168
|
- **热重载**:桥接在每轮运行前把模型解析为「provider + model」路由并传给 dsh runtime;SDK 适配器
|
|
167
169
|
在路由变化时自动重建 runtime(下一轮生效)。pi-ai 的 Base URL 填根域名(如
|
|
168
|
-
`https://www.kingapi.xyz`)会自动补全为 `/v1`。
|
|
170
|
+
`https://www.kingapi.xyz`)会自动补全为 `/v1`。dsh runtime 启动后需几百毫秒才注册
|
|
171
|
+
pi-ai 路由,桥接会重试握手直到注册完成(避免 “no adapter registered for provider”)。
|
|
169
172
|
|
|
170
173
|
安全提醒:在飞书会话输入密钥会对可见成员暴露,建议私聊使用或 `--api-key-env` 引用环境变量;bot 不在任何回复中回显密钥值。
|
|
171
174
|
|
package/dist/cli.js
CHANGED
|
@@ -1256,6 +1256,52 @@ function createSdkRun(harness, prompt, options) {
|
|
|
1256
1256
|
}
|
|
1257
1257
|
|
|
1258
1258
|
// src/adapters/dsh/sdk-adapter.ts
|
|
1259
|
+
var RETRYABLE_INIT_ERROR = /no adapter registered for provider/i;
|
|
1260
|
+
var INIT_RETRY_ATTEMPTS = 6;
|
|
1261
|
+
var INIT_RETRY_DELAY_MS = 250;
|
|
1262
|
+
function sleep(ms) {
|
|
1263
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
1264
|
+
}
|
|
1265
|
+
function isRetryableInitError(error) {
|
|
1266
|
+
return error instanceof Error && RETRYABLE_INIT_ERROR.test(error.message);
|
|
1267
|
+
}
|
|
1268
|
+
function withRetryableStart(harness) {
|
|
1269
|
+
const originalStart = harness.start.bind(harness);
|
|
1270
|
+
const internals = harness;
|
|
1271
|
+
let handled = false;
|
|
1272
|
+
harness.start = async () => {
|
|
1273
|
+
if (handled) return originalStart();
|
|
1274
|
+
try {
|
|
1275
|
+
const result = await originalStart();
|
|
1276
|
+
handled = true;
|
|
1277
|
+
return result;
|
|
1278
|
+
} catch (error) {
|
|
1279
|
+
if (!isRetryableInitError(error)) throw error;
|
|
1280
|
+
handled = true;
|
|
1281
|
+
const client = internals.client;
|
|
1282
|
+
if (!client) throw error;
|
|
1283
|
+
client.start();
|
|
1284
|
+
let lastError = error;
|
|
1285
|
+
for (let attempt = 1; attempt <= INIT_RETRY_ATTEMPTS; attempt += 1) {
|
|
1286
|
+
await sleep(INIT_RETRY_DELAY_MS * attempt);
|
|
1287
|
+
try {
|
|
1288
|
+
await client.initialize({
|
|
1289
|
+
cwd: internals.cwd,
|
|
1290
|
+
provider: internals.provider,
|
|
1291
|
+
model: internals.model
|
|
1292
|
+
});
|
|
1293
|
+
internals.initialized = Promise.resolve();
|
|
1294
|
+
return void 0;
|
|
1295
|
+
} catch (retryError) {
|
|
1296
|
+
lastError = retryError;
|
|
1297
|
+
if (!isRetryableInitError(retryError)) throw retryError;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
throw lastError;
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
return harness;
|
|
1304
|
+
}
|
|
1259
1305
|
function waitWithTimeout(promise, timeoutMs) {
|
|
1260
1306
|
return new Promise((resolve5) => {
|
|
1261
1307
|
const timer = setTimeout(() => resolve5(false), timeoutMs > 0 ? timeoutMs : 5e3);
|
|
@@ -1308,21 +1354,35 @@ var SdkDshAdapter = class {
|
|
|
1308
1354
|
});
|
|
1309
1355
|
try {
|
|
1310
1356
|
client.start();
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1357
|
+
let lastError;
|
|
1358
|
+
for (let attempt = 1; attempt <= INIT_RETRY_ATTEMPTS; attempt += 1) {
|
|
1359
|
+
try {
|
|
1360
|
+
const info = await client.initialize({
|
|
1361
|
+
cwd: process.cwd(),
|
|
1362
|
+
provider: this.provider,
|
|
1363
|
+
model: this.model,
|
|
1364
|
+
...this.maxTokens === void 0 ? {} : { maxTokens: this.maxTokens }
|
|
1365
|
+
});
|
|
1366
|
+
return {
|
|
1367
|
+
ok: true,
|
|
1368
|
+
error: void 0,
|
|
1369
|
+
version: `${info.serverInfo.name}@${info.serverInfo.version}`
|
|
1370
|
+
};
|
|
1371
|
+
} catch (error) {
|
|
1372
|
+
lastError = error;
|
|
1373
|
+
if (!isRetryableInitError(error) || attempt === INIT_RETRY_ATTEMPTS) {
|
|
1374
|
+
return {
|
|
1375
|
+
ok: false,
|
|
1376
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1377
|
+
version: void 0
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
await sleep(INIT_RETRY_DELAY_MS * attempt);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1323
1383
|
return {
|
|
1324
1384
|
ok: false,
|
|
1325
|
-
error:
|
|
1385
|
+
error: lastError instanceof Error ? lastError.message : String(lastError),
|
|
1326
1386
|
version: void 0
|
|
1327
1387
|
};
|
|
1328
1388
|
} finally {
|
|
@@ -1408,7 +1468,7 @@ var SdkDshAdapter = class {
|
|
|
1408
1468
|
}
|
|
1409
1469
|
}
|
|
1410
1470
|
const entry = {
|
|
1411
|
-
harness: this.harnessFactory(cwd, route),
|
|
1471
|
+
harness: withRetryableStart(this.harnessFactory(cwd, route)),
|
|
1412
1472
|
provider: route.provider,
|
|
1413
1473
|
model: route.model,
|
|
1414
1474
|
active: 0,
|
|
@@ -4495,7 +4555,7 @@ async function readOptional(file) {
|
|
|
4495
4555
|
throw error;
|
|
4496
4556
|
}
|
|
4497
4557
|
}
|
|
4498
|
-
function
|
|
4558
|
+
function sleep2(ms) {
|
|
4499
4559
|
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
4500
4560
|
}
|
|
4501
4561
|
async function withFileLock(filename, operation) {
|
|
@@ -4511,7 +4571,7 @@ async function withFileLock(filename, operation) {
|
|
|
4511
4571
|
if (Date.now() >= deadline) {
|
|
4512
4572
|
throw new Error(`dsh config lock timed out at ${lockPath}`);
|
|
4513
4573
|
}
|
|
4514
|
-
await
|
|
4574
|
+
await sleep2(80 + Math.random() * 120);
|
|
4515
4575
|
}
|
|
4516
4576
|
}
|
|
4517
4577
|
try {
|
|
@@ -4742,6 +4802,25 @@ var DshProviderManager = class {
|
|
|
4742
4802
|
}
|
|
4743
4803
|
return true;
|
|
4744
4804
|
}
|
|
4805
|
+
/**
|
|
4806
|
+
* Heal the common misconfiguration where a credential was stored under the
|
|
4807
|
+
* provider id (`/key set kingapi …`) but the provider never got an
|
|
4808
|
+
* apiKeyEnv. Links the ref to the matching pi-ai provider once; returns
|
|
4809
|
+
* true when a link was applied. Idempotent and a no-op when the provider
|
|
4810
|
+
* already has a ref or no matching credential exists.
|
|
4811
|
+
*/
|
|
4812
|
+
async linkCredentialRefIfMissing(providerId) {
|
|
4813
|
+
const settings = await this.readSettings();
|
|
4814
|
+
const piAi = isMapLike(settings[PIAI_NAMESPACE]) ? settings[PIAI_NAMESPACE] : {};
|
|
4815
|
+
const providers = isMapLike(piAi.providers) ? piAi.providers : {};
|
|
4816
|
+
const section = isMapLike(providers[providerId]) ? providers[providerId] : {};
|
|
4817
|
+
if (typeof section.apiKeyEnv === "string" && section.apiKeyEnv.length > 0) {
|
|
4818
|
+
return false;
|
|
4819
|
+
}
|
|
4820
|
+
if (!await this.hasCredential(providerId)) return false;
|
|
4821
|
+
await this.upsertPiAiProvider({ id: providerId, apiKeyEnv: providerId });
|
|
4822
|
+
return true;
|
|
4823
|
+
}
|
|
4745
4824
|
async addPiAiModel(providerId, input) {
|
|
4746
4825
|
validateProviderId(providerId);
|
|
4747
4826
|
const settings = await this.readSettings();
|
|
@@ -5127,9 +5206,24 @@ async function handleKey(args, ctx) {
|
|
|
5127
5206
|
await reply(ctx, `\u5199\u5165\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`);
|
|
5128
5207
|
return;
|
|
5129
5208
|
}
|
|
5209
|
+
const providers = await ctx.dshConfig.listProviders();
|
|
5210
|
+
const target = providers.find(
|
|
5211
|
+
(provider) => provider.id === ref && provider.namespace === PIAI_NAMESPACE && provider.credentialRef === void 0
|
|
5212
|
+
);
|
|
5213
|
+
let autoLinked = false;
|
|
5214
|
+
if (target) {
|
|
5215
|
+
await ctx.dshConfig.upsertPiAiProvider({ id: ref, apiKeyEnv: ref });
|
|
5216
|
+
autoLinked = true;
|
|
5217
|
+
}
|
|
5130
5218
|
await reply(
|
|
5131
5219
|
ctx,
|
|
5132
|
-
|
|
5220
|
+
[
|
|
5221
|
+
`\u5DF2\u5199\u5165\u51ED\u636E \`${ref}\` \u5230 \`~/.dsh/.credentials.yaml\`\uFF080600\uFF0C\u503C\u5DF2\u9690\u85CF\uFF09\u3002\u5EFA\u8BAE\u5728\u79C1\u804A\u4E2D\u4F7F\u7528\uFF1B\u7FA4\u804A\u91CC\u7C98\u8D34\u7684\u5BC6\u94A5\u4F1A\u5BF9\u7FA4\u6210\u5458\u53EF\u89C1\u3002`,
|
|
5222
|
+
...autoLinked ? [
|
|
5223
|
+
"",
|
|
5224
|
+
`\u{1F517} \u5DF2\u81EA\u52A8\u628A provider \`${ref}\` \u7684 apiKeyEnv \u5173\u8054\u5230 \`${ref}\`\uFF08\u4E0B\u4E00\u8BF7\u6C42\u751F\u6548\uFF09\u3002`
|
|
5225
|
+
] : []
|
|
5226
|
+
].join("\n")
|
|
5133
5227
|
);
|
|
5134
5228
|
return;
|
|
5135
5229
|
}
|
|
@@ -5788,7 +5882,22 @@ var FLOWS = {
|
|
|
5788
5882
|
const ref = asString(data.ref);
|
|
5789
5883
|
const value = asString(data.value);
|
|
5790
5884
|
await ctx.dshConfig.setCredential(ref, value);
|
|
5791
|
-
|
|
5885
|
+
const providers = await ctx.dshConfig.listProviders();
|
|
5886
|
+
const target = providers.find(
|
|
5887
|
+
(provider) => provider.id === ref && provider.namespace === "llm-pi-ai" && provider.credentialRef === void 0
|
|
5888
|
+
);
|
|
5889
|
+
let autoLinked = false;
|
|
5890
|
+
if (target) {
|
|
5891
|
+
await ctx.dshConfig.upsertPiAiProvider({ id: ref, apiKeyEnv: ref });
|
|
5892
|
+
autoLinked = true;
|
|
5893
|
+
}
|
|
5894
|
+
return [
|
|
5895
|
+
`\u5DF2\u5199\u5165\u51ED\u636E \`${ref}\` \u5230 \`~/.dsh/.credentials.yaml\`\uFF080600\uFF0C\u503C\u5DF2\u9690\u85CF\uFF09\u3002\u5EFA\u8BAE\u5728\u79C1\u804A\u4E2D\u4F7F\u7528\uFF1B\u7FA4\u804A\u91CC\u7C98\u8D34\u7684\u5BC6\u94A5\u4F1A\u5BF9\u7FA4\u6210\u5458\u53EF\u89C1\u3002`,
|
|
5896
|
+
...autoLinked ? [
|
|
5897
|
+
"",
|
|
5898
|
+
`\u{1F517} \u5DF2\u81EA\u52A8\u628A provider \`${ref}\` \u7684 apiKeyEnv \u5173\u8054\u5230 \`${ref}\`\uFF08\u4E0B\u4E00\u8BF7\u6C42\u751F\u6548\uFF09\u3002`
|
|
5899
|
+
] : []
|
|
5900
|
+
].join("\n");
|
|
5792
5901
|
}
|
|
5793
5902
|
},
|
|
5794
5903
|
"key-remove": {
|
|
@@ -5844,17 +5953,24 @@ async function renderCurrentStep(ctx, state) {
|
|
|
5844
5953
|
const options = await stepOptions(step, ctx, state.data);
|
|
5845
5954
|
if (step.kind === "options" && options) {
|
|
5846
5955
|
if (ctx.channel.sendCard) {
|
|
5847
|
-
|
|
5848
|
-
ctx.
|
|
5849
|
-
|
|
5956
|
+
try {
|
|
5957
|
+
await ctx.channel.sendCard(
|
|
5958
|
+
ctx.chatId,
|
|
5959
|
+
renderWizardOptionsCard({
|
|
5960
|
+
flow: flow.id,
|
|
5961
|
+
step: state.step,
|
|
5962
|
+
question: step.question,
|
|
5963
|
+
options,
|
|
5964
|
+
...step.hint === void 0 ? {} : { hint: step.hint }
|
|
5965
|
+
})
|
|
5966
|
+
);
|
|
5967
|
+
return;
|
|
5968
|
+
} catch (error) {
|
|
5969
|
+
log.warn("wizard", "card-send-failed", {
|
|
5850
5970
|
flow: flow.id,
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
...step.hint === void 0 ? {} : { hint: step.hint }
|
|
5855
|
-
})
|
|
5856
|
-
);
|
|
5857
|
-
return;
|
|
5971
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5972
|
+
});
|
|
5973
|
+
}
|
|
5858
5974
|
}
|
|
5859
5975
|
const labels = options.map((option) => `\`${option.label}\``).join("\u3001");
|
|
5860
5976
|
await ctx.channel.sendMarkdown(
|
|
@@ -5868,17 +5984,24 @@ ${labels}
|
|
|
5868
5984
|
return;
|
|
5869
5985
|
}
|
|
5870
5986
|
if (ctx.channel.sendCard) {
|
|
5871
|
-
|
|
5872
|
-
ctx.
|
|
5873
|
-
|
|
5987
|
+
try {
|
|
5988
|
+
await ctx.channel.sendCard(
|
|
5989
|
+
ctx.chatId,
|
|
5990
|
+
renderWizardTextStepCard({
|
|
5991
|
+
flow: flow.id,
|
|
5992
|
+
step: state.step,
|
|
5993
|
+
question: step.question,
|
|
5994
|
+
...step.placeholder === void 0 ? {} : { placeholder: step.placeholder },
|
|
5995
|
+
...step.hint === void 0 ? {} : { hint: step.hint }
|
|
5996
|
+
})
|
|
5997
|
+
);
|
|
5998
|
+
return;
|
|
5999
|
+
} catch (error) {
|
|
6000
|
+
log.warn("wizard", "card-send-failed", {
|
|
5874
6001
|
flow: flow.id,
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
...step.hint === void 0 ? {} : { hint: step.hint }
|
|
5879
|
-
})
|
|
5880
|
-
);
|
|
5881
|
-
return;
|
|
6002
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6003
|
+
});
|
|
6004
|
+
}
|
|
5882
6005
|
}
|
|
5883
6006
|
await ctx.channel.sendMarkdown(
|
|
5884
6007
|
ctx.chatId,
|
|
@@ -6019,11 +6142,17 @@ async function showConfigHub(ctx) {
|
|
|
6019
6142
|
const defaultSelection = await ctx.dshConfig.defaultModelSelection();
|
|
6020
6143
|
const currentModel = ctx.models.get(ctx.scope) ?? ctx.defaultModel;
|
|
6021
6144
|
if (ctx.channel.sendCard) {
|
|
6022
|
-
|
|
6023
|
-
ctx.
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6145
|
+
try {
|
|
6146
|
+
await ctx.channel.sendCard(
|
|
6147
|
+
ctx.chatId,
|
|
6148
|
+
renderConfigHubCard({ providers, defaultSelection, currentModel })
|
|
6149
|
+
);
|
|
6150
|
+
return;
|
|
6151
|
+
} catch (error) {
|
|
6152
|
+
log.warn("wizard", "hub-card-send-failed", {
|
|
6153
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6154
|
+
});
|
|
6155
|
+
}
|
|
6027
6156
|
}
|
|
6028
6157
|
const lines = providers.map((provider) => {
|
|
6029
6158
|
const models = provider.models.map((model) => model.id).join(", ") || "(\u65E0)";
|
|
@@ -6956,9 +7085,17 @@ async function startChannel(deps) {
|
|
|
6956
7085
|
defaultModel: deps.defaultModel,
|
|
6957
7086
|
senderId: msg.senderId
|
|
6958
7087
|
};
|
|
6959
|
-
const handled = await tryHandleCommand(msg.content, context).catch((error) => {
|
|
7088
|
+
const handled = await tryHandleCommand(msg.content, context).catch(async (error) => {
|
|
6960
7089
|
log.fail("channel-command", error, { scope });
|
|
6961
|
-
|
|
7090
|
+
try {
|
|
7091
|
+
await commandChannel.sendMarkdown(
|
|
7092
|
+
msg.chatId,
|
|
7093
|
+
`\u26A0\uFE0F \u547D\u4EE4\u6267\u884C\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`,
|
|
7094
|
+
{ replyTo: msg.messageId }
|
|
7095
|
+
);
|
|
7096
|
+
} catch {
|
|
7097
|
+
}
|
|
7098
|
+
return true;
|
|
6962
7099
|
});
|
|
6963
7100
|
if (!handled) {
|
|
6964
7101
|
const queued = deps.pending.size(scope);
|
|
@@ -8413,6 +8550,9 @@ async function startBridgeEngine(options) {
|
|
|
8413
8550
|
);
|
|
8414
8551
|
return;
|
|
8415
8552
|
}
|
|
8553
|
+
if (modelRoute && modelRoute.provider !== DEEPSEEK_PROVIDER) {
|
|
8554
|
+
await dshConfig.linkCredentialRefIfMissing(modelRoute.provider).catch(() => void 0);
|
|
8555
|
+
}
|
|
8416
8556
|
const runInput = {
|
|
8417
8557
|
scope,
|
|
8418
8558
|
chatId: first.chatId,
|