larkin 0.4.8 → 0.4.10
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 +2 -0
- package/dist/agent/context-prompt.cjs +2 -1
- package/dist/agent/context-prompt.mjs +2 -1
- package/dist/app/cli.mjs +2 -2
- package/dist/app/runtime-agent-config.mjs +7 -3
- package/dist/app/setup.mjs +11 -0
- package/dist/feishu/host-shell.cjs +4 -3
- package/dist/feishu/host-shell.mjs +4 -3
- package/dist/feishu/platform-hosts.cjs +49 -0
- package/dist/feishu/platform-hosts.mjs +40 -0
- package/dist/runtime/pi-subagents-notification.mjs +161 -0
- package/dist/runtime/runtime-adapters.mjs +32 -1
- package/dist/runtime/runtime-host.cjs +250 -1
- package/dist/runtime/runtime-host.mjs +250 -1
- package/dist/setup/bot-register.mjs +50 -4
- package/dist/setup/grant-scopes.mjs +16 -3
- package/package.json +1 -1
|
@@ -17,6 +17,7 @@ import { probeNativeRuntimeReadiness } from "../runtime/runtime-readiness.mjs";
|
|
|
17
17
|
import { configureBuiltinPiProviderModel, stageBuiltinPiProvider, validatePiBaseUrl, validateBuiltinPiProviderSelection, listProviderModels, PI_PROVIDER_PRESETS } from "../runtime/pi-provider-config.mjs";
|
|
18
18
|
import { beginBuiltinPiCredentialTransaction, createOfficialPiCredentialRuntime, createOfficialPiAuthInteraction, createOfficialPiModelRuntime, createOfficialPiLogoutRuntime, createOfficialPiRegistryRuntime, listOfficialPiAuthProviders, logoutOfficialPiProvider, officialPiAuthStatus, runOfficialPiLogin, verifyOfficialPiProviderTurn, } from "../runtime/pi-official-auth.mjs";
|
|
19
19
|
import { documentCommentSubscriptionCapability, markDocumentCommentSubscriptionVerified, } from "../platform/callback-capability.mjs";
|
|
20
|
+
import { openPlatformHost, parseLarkinTenant, registerAppAccountsHost, } from "../feishu/platform-hosts.mjs";
|
|
20
21
|
// qrcode-terminal does not publish TypeScript declarations.
|
|
21
22
|
// @ts-expect-error bundled CommonJS dependency
|
|
22
23
|
import qrcodePackage from "qrcode-terminal";
|
|
@@ -217,6 +218,45 @@ export async function applyDocumentCommentSubscription(input) {
|
|
|
217
218
|
throw new Error("document comment local verified-state persistence failed; external subscription rollback was verified");
|
|
218
219
|
}
|
|
219
220
|
}
|
|
221
|
+
async function resolveSelectedTenant() {
|
|
222
|
+
const raw = flag("--tenant");
|
|
223
|
+
if (raw !== undefined) {
|
|
224
|
+
const parsed = parseLarkinTenant(raw);
|
|
225
|
+
if (parsed === "feishu" || parsed === "lark")
|
|
226
|
+
return parsed;
|
|
227
|
+
die("--tenant 只支持 feishu 或 lark");
|
|
228
|
+
}
|
|
229
|
+
if (process.stdin.isTTY && process.stdout.isTTY && !testFixture) {
|
|
230
|
+
const questioner = terminalSetupQuestioner();
|
|
231
|
+
try {
|
|
232
|
+
const answer = (await questioner.ask("选择平台(授权二维码必须对应正确品牌;lark-cli --domain 是业务域,品牌是 feishu|lark):\n"
|
|
233
|
+
+ " 1. 中国飞书 Feishu(accounts.feishu.cn / https://open.feishu.cn)\n"
|
|
234
|
+
+ " 2. International Lark(accounts.larksuite.com / https://open.larksuite.com)\n> ")).trim();
|
|
235
|
+
if (answer === "1" || /^feishu$/i.test(answer))
|
|
236
|
+
return "feishu";
|
|
237
|
+
if (answer === "2" || /^lark$/i.test(answer))
|
|
238
|
+
return "lark";
|
|
239
|
+
die("必须选择 1/feishu 或 2/lark;未开始授权");
|
|
240
|
+
}
|
|
241
|
+
finally {
|
|
242
|
+
questioner.close?.();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return "feishu";
|
|
246
|
+
}
|
|
247
|
+
function persistTenant(selected, userInfo) {
|
|
248
|
+
const brand = userInfo?.tenant_brand;
|
|
249
|
+
if (brand === undefined || brand === null || brand === "")
|
|
250
|
+
return selected;
|
|
251
|
+
const observed = parseLarkinTenant(brand);
|
|
252
|
+
if (observed !== "feishu" && observed !== "lark") {
|
|
253
|
+
die("授权返回的租户品牌非法;未执行凭证同步、文件写入或 Agent 绑定");
|
|
254
|
+
}
|
|
255
|
+
if (observed !== selected) {
|
|
256
|
+
die(`授权返回的租户 ${observed} 与预先选择的 ${selected} 不一致;未执行凭证同步、文件写入或 Agent 绑定`);
|
|
257
|
+
}
|
|
258
|
+
return selected;
|
|
259
|
+
}
|
|
220
260
|
function botVerificationRetryable(result) {
|
|
221
261
|
const text = `${typeof result.stdout === "string" ? result.stdout : ""}\n${typeof result.stderr === "string" ? result.stderr : ""}`;
|
|
222
262
|
return /\binvalid_client\b|specified app does not exist|EAI_AGAIN|ENOTFOUND|ECONNREFUSED|ECONNRESET|ETIMEDOUT|fetch failed|temporary network|too many requests|\b(?:429|502|503|504)\b/i.test(text);
|
|
@@ -374,7 +414,7 @@ async function fetchBotInfoViaHttp(appId, tenant) {
|
|
|
374
414
|
}
|
|
375
415
|
if (typeof credential.appSecret !== "string" || !credential.appSecret)
|
|
376
416
|
return null;
|
|
377
|
-
const base = tenant === "lark" ? "
|
|
417
|
+
const base = openPlatformHost(tenant === "lark" ? "lark" : "feishu");
|
|
378
418
|
try {
|
|
379
419
|
const tokenResponse = await fetch(`${base}/open-apis/auth/v3/tenant_access_token/internal`, {
|
|
380
420
|
method: "POST",
|
|
@@ -424,7 +464,10 @@ export async function main() {
|
|
|
424
464
|
say(`setup 内部机器人注册阶段
|
|
425
465
|
|
|
426
466
|
用法:
|
|
427
|
-
bun bot-register.mjs --auto
|
|
467
|
+
bun bot-register.mjs --auto [--tenant feishu|lark]
|
|
468
|
+
|
|
469
|
+
--tenant 在授权二维码之前选择品牌。默认 feishu(accounts.feishu.cn);
|
|
470
|
+
Lark 必须用 --tenant lark 或交互选择,授权主机是 accounts.larksuite.com。
|
|
428
471
|
|
|
429
472
|
完成后自动:按返回 appId 判定新旧 → 同步并校验 lark-cli bot 凭证 →
|
|
430
473
|
原子写 bots/<appId>.json(0600)→ 绑定 Agent。`);
|
|
@@ -447,7 +490,8 @@ export async function main() {
|
|
|
447
490
|
}
|
|
448
491
|
if (!autoSelect)
|
|
449
492
|
die("setup 注册阶段必须由交互式选择流程启动");
|
|
450
|
-
|
|
493
|
+
const selectedTenant = await resolveSelectedTenant();
|
|
494
|
+
say(`[setup 1/5] 在${selectedTenant === "lark" ? " International Lark" : "中国飞书 Feishu"}网页选择已有机器人或创建新机器人`);
|
|
451
495
|
if (commentSubscription === "application") {
|
|
452
496
|
say(`! 已显式选择 ${commentSubscription} 维度评论订阅:一旦平台状态验证为已订阅,Bot 可见文档中实际送达的每条支持评论都会进入 Inbox 并唤醒 Agent,不要求 @Bot。`);
|
|
453
497
|
say("! setup 将通过官方 lark-cli 的结构化 API 请求创建该订阅,并以只读 subscription_status 二次核验;不会从意图或事件配置推断订阅已生效。");
|
|
@@ -483,6 +527,8 @@ export async function main() {
|
|
|
483
527
|
let pollingCount = 0;
|
|
484
528
|
const result = await registerApp({
|
|
485
529
|
source: "larkin",
|
|
530
|
+
domain: registerAppAccountsHost(selectedTenant),
|
|
531
|
+
larkDomain: registerAppAccountsHost("lark"),
|
|
486
532
|
addons: {
|
|
487
533
|
scopes: { tenant: TENANT_SCOPES },
|
|
488
534
|
events: { items: { tenant: TENANT_EVENTS } },
|
|
@@ -509,9 +555,9 @@ export async function main() {
|
|
|
509
555
|
die("授权完成但未取得有效凭证;未执行凭证同步、文件写入或 Agent 绑定");
|
|
510
556
|
const id = rawId;
|
|
511
557
|
const secret = rawSecret;
|
|
558
|
+
const tenant = persistTenant(selectedTenant, userInfo);
|
|
512
559
|
const botsDir = ensureSecureBotsDir();
|
|
513
560
|
say(`[setup 3/5] 正在按 App ID ${id} 刷新凭证并配置 Agent`);
|
|
514
|
-
const tenant = userInfo?.tenant_brand === "lark" ? "lark" : "feishu";
|
|
515
561
|
const botFile = path.join(botsDir, `${id}.json`);
|
|
516
562
|
const prior = (() => {
|
|
517
563
|
try {
|
|
@@ -8,6 +8,8 @@ import { registerApp as channelRegisterApp } from "@larksuite/channel";
|
|
|
8
8
|
import { markSetupCapabilitiesRequested } from "../platform/callback-capability.mjs";
|
|
9
9
|
import * as larkinConfig from "../platform/config.mjs";
|
|
10
10
|
import { managedOfficialLarkCli } from "../app/agent-lark-cli-workspace.mjs";
|
|
11
|
+
import { loadValidatedBotCredential } from "./run-credential-preflight.mjs";
|
|
12
|
+
import { parseLarkinTenant, registerAppAccountsHost } from "../feishu/platform-hosts.mjs";
|
|
11
13
|
// qrcode-terminal does not publish TypeScript declarations.
|
|
12
14
|
// @ts-expect-error bundled CommonJS dependency
|
|
13
15
|
import qrcodePackage from "qrcode-terminal";
|
|
@@ -41,7 +43,19 @@ if (explicitAppId && explicitAppId !== selected.feishuAppId)
|
|
|
41
43
|
if (!/^cli_[A-Za-z0-9]+$/.test(APP_ID))
|
|
42
44
|
throw new Error(`无效飞书(Lark) App ID:${APP_ID}`);
|
|
43
45
|
const SEND_TO = flag("--send-to", "");
|
|
44
|
-
const
|
|
46
|
+
const explicitTenant = argv.includes("--tenant") ? flag("--tenant", "") : "";
|
|
47
|
+
if (argv.includes("--tenant") && !parseLarkinTenant(explicitTenant)) {
|
|
48
|
+
throw new Error("--tenant 只支持 feishu 或 lark");
|
|
49
|
+
}
|
|
50
|
+
function storedCredentialTenant() {
|
|
51
|
+
try {
|
|
52
|
+
return loadValidatedBotCredential(path.join(config.larkinHome, "bots"), APP_ID).tenant;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return "feishu";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const TENANT = parseLarkinTenant(explicitTenant) ?? storedCredentialTenant();
|
|
45
59
|
const WAIT_MIN = Number(flag("--wait-min", "9"));
|
|
46
60
|
const URL_FILE = flag("--url-file", "");
|
|
47
61
|
const TENANT_SCOPES = [
|
|
@@ -108,9 +122,8 @@ export async function main() {
|
|
|
108
122
|
sendUrlToChat(info.url, minutes);
|
|
109
123
|
},
|
|
110
124
|
onStatusChange: (info) => log(`[grant] 状态: ${info.status}`),
|
|
125
|
+
domain: registerAppAccountsHost(TENANT),
|
|
111
126
|
};
|
|
112
|
-
if (TENANT === "lark")
|
|
113
|
-
options.domain = "lark";
|
|
114
127
|
let result;
|
|
115
128
|
try {
|
|
116
129
|
result = await registerApp(options);
|