@vibe-lark/larkpal 0.1.83 → 0.1.85
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 +68 -1
- package/dist/cli.mjs +30 -4
- package/dist/{interactive-init-BYF2-otj.mjs → interactive-init-B6j8j_Ap.mjs} +191 -33
- package/dist/{lark-cli-provider-CdgwmqSz.mjs → lark-cli-provider-7LvtOIF_.mjs} +11 -19
- package/dist/live-smoke-nvTUPtrv.mjs +7135 -0
- package/dist/main.mjs +2953 -5004
- package/dist/mcp-server.mjs +64 -7
- package/dist/rolldown-runtime-wcPFST8Q.mjs +13 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,7 +1,59 @@
|
|
|
1
|
+
# LarkPal
|
|
2
|
+
|
|
3
|
+
LarkPal connects Lark/Feishu conversations to agent runtimes. The first setup path is now organized around Feishu's Agent integration layers:
|
|
4
|
+
|
|
5
|
+
1. **凭据层**:用飞书官方扫码创建应用能力获取 App ID 和 App Secret。
|
|
6
|
+
2. **交互层**:LarkPal 的 Feishu channel 接收消息、评论、卡片交互并流式回复。
|
|
7
|
+
3. **执行层**:飞书 CLI 作为 Agent 操作文档、日历、表格、邮件、任务等业务对象的工具来源。
|
|
8
|
+
|
|
9
|
+
> Agent 协作入口:先读 [AGENTS.md](./AGENTS.md),再按 [agent-workspace/INDEX.md](./agent-workspace/INDEX.md) 选择相关领域文档。
|
|
10
|
+
|
|
11
|
+
## 快速开始
|
|
12
|
+
|
|
13
|
+
### 1. 安装依赖
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm install
|
|
17
|
+
pnpm -s build
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
运行服务时仍需要一个 agent runtime:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g @anthropic-ai/claude-code
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
如果使用 Codex app-server runtime,请安装并配置 Codex CLI,并设置 `LARKPAL_RUNTIME=codex-app-server`。
|
|
27
|
+
|
|
28
|
+
### 2. 连接飞书
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm start -- init
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
初始化向导默认使用飞书官方一键创建应用能力。用户打开或扫码向导中的链接后,LarkPal 会收到并保存 App ID 和 App Secret;不需要去飞书开发者后台手动复制 App Secret。
|
|
35
|
+
|
|
36
|
+
也可以选择手动输入 App ID/App Secret,或使用旧的 `lark-cli config init` 兼容流程。
|
|
37
|
+
|
|
38
|
+
### 3. 按需授权执行层
|
|
39
|
+
|
|
40
|
+
基础飞书 bot 会话只需要应用凭据。只有当 Agent 需要以用户身份访问个人日历、私信、云文档、邮箱等数据时,才需要飞书 CLI 用户授权:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npx @larksuite/cli@latest install
|
|
44
|
+
lark-cli auth login
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
这一步属于执行层,不是应用凭据配置的一部分。
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
1
51
|
# LarkPal 外部接入规范
|
|
2
52
|
|
|
3
53
|
本文档描述外部业务方接入 LarkPal 平台时需遵循的协议和配置规范。
|
|
4
54
|
|
|
55
|
+
> Agent 协作入口:先读 [AGENTS.md](./AGENTS.md),再按 [agent-workspace/INDEX.md](./agent-workspace/INDEX.md) 选择相关领域文档。
|
|
56
|
+
|
|
5
57
|
---
|
|
6
58
|
|
|
7
59
|
## 目录
|
|
@@ -225,6 +277,12 @@ interface RuntimeAdapter {
|
|
|
225
277
|
// 查询会话进程信息
|
|
226
278
|
getProcessInfo(sessionId: string): RuntimeProcessInfo | undefined;
|
|
227
279
|
getAllProcessInfo(): RuntimeProcessInfo[];
|
|
280
|
+
|
|
281
|
+
// 可选能力契约:上层通过能力判断是否展示交互入口
|
|
282
|
+
getCapabilities?(): RuntimeCapabilities;
|
|
283
|
+
getLastUserMessageId?(sessionId: string): string | undefined;
|
|
284
|
+
previewRewind?(params: RuntimeRewindParams): Promise<RuntimeRewindPreview>;
|
|
285
|
+
confirmRewind?(params: RuntimeRewindParams): Promise<RuntimeRewindResult>;
|
|
228
286
|
}
|
|
229
287
|
```
|
|
230
288
|
|
|
@@ -263,9 +321,18 @@ interface RuntimeStreamCallbacks {
|
|
|
263
321
|
|
|
264
322
|
```bash
|
|
265
323
|
LARKPAL_RUNTIME=claude-code # 默认,使用 Claude Code 子进程
|
|
324
|
+
LARKPAL_RUNTIME=codex-app-server # 使用 OpenAI Codex app-server
|
|
266
325
|
LARKPAL_RUNTIME=larkpal-agent # 使用 larkpal-agent 引擎(火山方舟 LLM)
|
|
267
326
|
```
|
|
268
327
|
|
|
328
|
+
Runtime 能力矩阵:
|
|
329
|
+
|
|
330
|
+
| Runtime | 流式回复 | 多模态输入 | 停止当前任务 | 交互卡片续跑 | 文件撤销 |
|
|
331
|
+
|---------|----------|------------|--------------|--------------|----------|
|
|
332
|
+
| `claude-code` | 支持 | 支持 | 支持 | 支持 | 支持 |
|
|
333
|
+
| `codex-app-server` | 支持 | 支持 | 支持 | 支持 | 支持 |
|
|
334
|
+
| `larkpal-agent` | 支持 | 由 agent/provider 决定 | 支持 | 支持 | 暂不支持 |
|
|
335
|
+
|
|
269
336
|
使用 `larkpal-agent` 时的完整配置示例:
|
|
270
337
|
|
|
271
338
|
```bash
|
|
@@ -278,7 +345,7 @@ LARKPAL_AGENT_MAX_TURNS=25
|
|
|
278
345
|
|
|
279
346
|
**工具发现**:由 larkpal-agent 自身的 MCP Client 负责。larkpal-agent 启动时自动连接配置的 MCP Server,发现并注册外部工具。larkpal 平台层不涉及工具层。
|
|
280
347
|
|
|
281
|
-
|
|
348
|
+
**人设 / system instructions 来源**:LarkPal 以飞书应用信息和人设文档为唯一来源,同步到 runtime 各自能读取的指令文件。Claude Code 读取 `~/.claude/CLAUDE.md` 和会话目录 `CLAUDE.md`;Codex 读取 workspace / 会话目录中的 `AGENTS.md`,内容由 LarkPal 从同一份人设自动镜像生成。
|
|
282
349
|
|
|
283
350
|
---
|
|
284
351
|
|
package/dist/cli.mjs
CHANGED
|
@@ -34,12 +34,22 @@ ${BOLD}LarkPal${RESET} ${DIM}v${version}${RESET} — 飞书 AI Bot 服务
|
|
|
34
34
|
${BOLD}用法:${RESET}
|
|
35
35
|
larkpal 启动服务
|
|
36
36
|
larkpal init 交互式初始化向导
|
|
37
|
+
larkpal meeting-live-smoke --meeting-number <number>
|
|
38
|
+
实验:固定文本进入飞书会议语音链路
|
|
39
|
+
可加 --listen-ms <ms> --raw-output <path> --summary-output <path>
|
|
40
|
+
larkpal meeting-live-teammate --meeting-number <number> --owner-user-id <id>
|
|
41
|
+
实验:实时听会转写后用 teammate 策略语音参与
|
|
42
|
+
可加 --summary-output <path> 留存脱敏运行摘要
|
|
43
|
+
larkpal meeting-live-check
|
|
44
|
+
实验:检查会议语音链路 TTS/ASR 配置,不入会
|
|
45
|
+
可加 --summary-output <path> 留存脱敏检查结果
|
|
37
46
|
larkpal --help 显示帮助信息
|
|
38
47
|
larkpal --version 显示版本号
|
|
39
48
|
|
|
40
49
|
${BOLD}环境变量:${RESET}
|
|
41
50
|
LARK_APP_ID 飞书应用 App ID(可选,覆盖 lark-cli 配置)
|
|
42
51
|
LARK_APP_SECRET 飞书应用 App Secret(可选,覆盖 lark-cli 配置)
|
|
52
|
+
LARK_USER_ACCESS_TOKEN 飞书用户访问令牌(会议实验能力需要)
|
|
43
53
|
LARKPAL_WORKSPACE 工作目录根路径(默认 ~/.larkpal/workspace)
|
|
44
54
|
CLAUDE_MODEL Claude 模型名称(如 ark-code-latest)
|
|
45
55
|
|
|
@@ -47,7 +57,8 @@ ${DIM}首次使用?运行 ${CYAN}larkpal init${DIM} 开始配置。${RESET}
|
|
|
47
57
|
`);
|
|
48
58
|
}
|
|
49
59
|
async function cli() {
|
|
50
|
-
const
|
|
60
|
+
const args = process.argv.slice(2);
|
|
61
|
+
const command = args[0];
|
|
51
62
|
if (command === "--version" || command === "-v") {
|
|
52
63
|
console.log(getVersion());
|
|
53
64
|
return;
|
|
@@ -57,21 +68,36 @@ async function cli() {
|
|
|
57
68
|
return;
|
|
58
69
|
}
|
|
59
70
|
if (command === "init") {
|
|
60
|
-
const { runInteractiveInit } = await import("./interactive-init-
|
|
71
|
+
const { runInteractiveInit } = await import("./interactive-init-B6j8j_Ap.mjs");
|
|
61
72
|
const exitCode = await runInteractiveInit();
|
|
62
73
|
process.exit(exitCode);
|
|
63
74
|
}
|
|
75
|
+
if (command === "meeting-live-smoke") {
|
|
76
|
+
const { runMeetingLiveSmokeCli } = await import("./live-smoke-nvTUPtrv.mjs").then((n) => n.t);
|
|
77
|
+
await runMeetingLiveSmokeCli(args.slice(1));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (command === "meeting-live-teammate") {
|
|
81
|
+
const { runMeetingLiveTeammateCli } = await import("./live-smoke-nvTUPtrv.mjs").then((n) => n.t);
|
|
82
|
+
await runMeetingLiveTeammateCli(args.slice(1));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (command === "meeting-live-check") {
|
|
86
|
+
const { runMeetingLiveCheckCli } = await import("./live-smoke-nvTUPtrv.mjs").then((n) => n.t);
|
|
87
|
+
const exitCode = await runMeetingLiveCheckCli(args.slice(1));
|
|
88
|
+
process.exit(exitCode);
|
|
89
|
+
}
|
|
64
90
|
if (command && !command.startsWith("-")) {
|
|
65
91
|
console.log(`${YELLOW}未知命令: ${command}${RESET}\n`);
|
|
66
92
|
printHelp();
|
|
67
93
|
process.exit(1);
|
|
68
94
|
}
|
|
69
95
|
if (!(process.env.LARKPAL_HEADLESS === "1" || process.env.LARKPAL_HEADLESS === "true")) {
|
|
70
|
-
const { LarkCliCredentialProvider } = await import("./lark-cli-provider-
|
|
96
|
+
const { LarkCliCredentialProvider } = await import("./lark-cli-provider-7LvtOIF_.mjs").then((n) => n.n);
|
|
71
97
|
if (!LarkCliCredentialProvider.isConfigured()) {
|
|
72
98
|
console.log(`\n${YELLOW}⚠️ 飞书应用凭证未配置${RESET}\n`);
|
|
73
99
|
console.log(` LarkPal 需要飞书应用凭证才能启动。正在进入初始化向导...\n`);
|
|
74
|
-
const { runInteractiveInit } = await import("./interactive-init-
|
|
100
|
+
const { runInteractiveInit } = await import("./interactive-init-B6j8j_Ap.mjs");
|
|
75
101
|
const exitCode = await runInteractiveInit();
|
|
76
102
|
if (exitCode !== 0) process.exit(exitCode);
|
|
77
103
|
console.log(`${DIM}凭证配置完成,正在启动服务...${RESET}\n`);
|
|
@@ -4,6 +4,124 @@ import { join } from "node:path";
|
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { execSync, spawn } from "node:child_process";
|
|
6
6
|
import { createInterface } from "node:readline";
|
|
7
|
+
import * as Lark from "@larksuiteoapi/node-sdk";
|
|
8
|
+
import qrcodeTerminal from "qrcode-terminal";
|
|
9
|
+
//#region src/credentials/app-registration.ts
|
|
10
|
+
async function registerFeishuAppCredentials(options) {
|
|
11
|
+
const result = await (options.registerApp ?? defaultRegisterApp)({
|
|
12
|
+
source: options.source ?? "larkpal",
|
|
13
|
+
signal: options.signal,
|
|
14
|
+
onQRCodeReady: options.onQRCodeReady,
|
|
15
|
+
onStatusChange: options.onStatusChange
|
|
16
|
+
});
|
|
17
|
+
if (!result.client_id || !result.client_secret) throw new Error("飞书应用创建完成但未返回 App ID 或 App Secret");
|
|
18
|
+
await options.saveCredentials({
|
|
19
|
+
appId: result.client_id,
|
|
20
|
+
appSecret: result.client_secret
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
appId: result.client_id,
|
|
24
|
+
appSecret: result.client_secret,
|
|
25
|
+
creatorOpenId: result.user_info?.open_id,
|
|
26
|
+
tenantBrand: result.user_info?.tenant_brand
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
async function defaultRegisterApp(options) {
|
|
30
|
+
return Lark.registerApp(options);
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/init/landing-presentation.ts
|
|
34
|
+
function renderFeishuAppCreationQRCode(input) {
|
|
35
|
+
return [
|
|
36
|
+
" 请扫码创建飞书智能体应用,或打开下面的链接:",
|
|
37
|
+
"",
|
|
38
|
+
renderQRCodeOrFallback(input.url, input.createQRCode ?? createQRCode),
|
|
39
|
+
` ${input.url}`,
|
|
40
|
+
` 链接有效期 ${input.expireIn} 秒。`
|
|
41
|
+
].join("\n");
|
|
42
|
+
}
|
|
43
|
+
function formatRuntimeSelectionSummary(runtimeName) {
|
|
44
|
+
switch (runtimeName) {
|
|
45
|
+
case "codex-app-server": return [
|
|
46
|
+
"已选择 Codex app-server 作为 agent runtime。",
|
|
47
|
+
"启动前请确保 Codex CLI 可用:codex",
|
|
48
|
+
"启动配置:LARKPAL_RUNTIME=codex-app-server"
|
|
49
|
+
].join("\n");
|
|
50
|
+
case "larkpal-agent": return [
|
|
51
|
+
"已选择 larkpal-agent 作为 agent runtime。",
|
|
52
|
+
"启动前请配置 larkpal-agent 所需的 LLM 和 scenario provider 环境变量。",
|
|
53
|
+
"启动配置:LARKPAL_RUNTIME=larkpal-agent"
|
|
54
|
+
].join("\n");
|
|
55
|
+
case "claude-code": return [
|
|
56
|
+
"已选择 Claude Code 作为 agent runtime。",
|
|
57
|
+
"启动前请确保 Claude Code CLI 可用:claude",
|
|
58
|
+
"启动配置:LARKPAL_RUNTIME=claude-code"
|
|
59
|
+
].join("\n");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function renderRuntimeSelectionPrompt(currentRuntime) {
|
|
63
|
+
return [
|
|
64
|
+
"Agent runtime 选择",
|
|
65
|
+
"",
|
|
66
|
+
` [1] Claude Code (claude-code)${currentRuntime === "claude-code" ? "(当前)" : ""}`,
|
|
67
|
+
` [2] Codex app-server (codex-app-server)${currentRuntime === "codex-app-server" ? "(当前)" : ""}`,
|
|
68
|
+
` [3] larkpal-agent${currentRuntime === "larkpal-agent" ? "(当前)" : ""}`
|
|
69
|
+
].join("\n");
|
|
70
|
+
}
|
|
71
|
+
function resolveRuntimeSelection(choice, currentRuntime) {
|
|
72
|
+
const normalized = choice.trim().toLowerCase();
|
|
73
|
+
if (!normalized) return currentRuntime;
|
|
74
|
+
if (normalized === "1" || normalized === "claude" || normalized === "claude-code") return "claude-code";
|
|
75
|
+
if (normalized === "2" || normalized === "codex" || normalized === "codex-app-server") return "codex-app-server";
|
|
76
|
+
if (normalized === "3" || normalized === "larkpal-agent") return "larkpal-agent";
|
|
77
|
+
return currentRuntime;
|
|
78
|
+
}
|
|
79
|
+
function createQRCode(url) {
|
|
80
|
+
let qrCode = "";
|
|
81
|
+
qrcodeTerminal.setErrorLevel("L");
|
|
82
|
+
qrcodeTerminal.generate(url, { small: true }, (code) => {
|
|
83
|
+
qrCode = code;
|
|
84
|
+
});
|
|
85
|
+
return qrCode.trimEnd();
|
|
86
|
+
}
|
|
87
|
+
function renderQRCodeOrFallback(url, renderer) {
|
|
88
|
+
try {
|
|
89
|
+
return renderer(url).trimEnd() || " [二维码渲染失败,请使用下方链接]";
|
|
90
|
+
} catch {
|
|
91
|
+
return " [二维码渲染失败,请使用下方链接]";
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region src/init/prerequisites.ts
|
|
96
|
+
function assessInitPrerequisites(deps) {
|
|
97
|
+
const larkCli = deps.detectCli("lark-cli");
|
|
98
|
+
const runtimeCliName = resolveRuntimeCliName(deps.runtimeName);
|
|
99
|
+
const runtimeCli = runtimeCliName ? deps.detectCli(runtimeCliName) : { installed: true };
|
|
100
|
+
const warnings = [];
|
|
101
|
+
if (runtimeCliName && !runtimeCli.installed) warnings.push(runtimeCliName === "codex" ? "OpenAI Codex CLI is not installed. Feishu app credentials can still be configured now, but service startup with LARKPAL_RUNTIME=codex-app-server will require codex later." : "Claude Code is not installed. Feishu app credentials can still be configured now, but service startup with the default runtime will require Claude Code later.");
|
|
102
|
+
if (!larkCli.installed) warnings.push("lark-cli is not installed. Basic Feishu bot chat can be configured with app credentials, but execution-layer tools for documents, calendars, mail, tasks, and personal data will require lark-cli later.");
|
|
103
|
+
return {
|
|
104
|
+
canConfigureFeishuCredentials: true,
|
|
105
|
+
larkCli: {
|
|
106
|
+
...larkCli,
|
|
107
|
+
ready: larkCli.installed,
|
|
108
|
+
requiredForBasicChat: false
|
|
109
|
+
},
|
|
110
|
+
runtime: {
|
|
111
|
+
...runtimeCli,
|
|
112
|
+
ready: runtimeCli.installed,
|
|
113
|
+
name: deps.runtimeName,
|
|
114
|
+
cliName: runtimeCliName ?? deps.runtimeName
|
|
115
|
+
},
|
|
116
|
+
warnings
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function resolveRuntimeCliName(runtimeName) {
|
|
120
|
+
if (runtimeName === "codex-app-server") return "codex";
|
|
121
|
+
if (runtimeName === "larkpal-agent") return void 0;
|
|
122
|
+
return "claude";
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
7
125
|
//#region src/init/interactive-init.ts
|
|
8
126
|
/**
|
|
9
127
|
* 交互式初始化流程
|
|
@@ -88,6 +206,9 @@ function writeCredentials(appId, appSecret) {
|
|
|
88
206
|
return false;
|
|
89
207
|
}
|
|
90
208
|
}
|
|
209
|
+
async function saveCredentialsOrThrow(credentials) {
|
|
210
|
+
if (!writeCredentials(credentials.appId, credentials.appSecret)) throw new Error("凭证保存失败");
|
|
211
|
+
}
|
|
91
212
|
/** 检查 LarkPal 凭证文件是否已有有效凭证 */
|
|
92
213
|
function hasLarkPalCredentials() {
|
|
93
214
|
if (!existsSync(LARKPAL_CREDENTIALS_PATH)) return null;
|
|
@@ -101,30 +222,29 @@ function hasLarkPalCredentials() {
|
|
|
101
222
|
} catch {}
|
|
102
223
|
return null;
|
|
103
224
|
}
|
|
104
|
-
function checkPrerequisites() {
|
|
225
|
+
function checkPrerequisites(runtimeName) {
|
|
105
226
|
console.log(`\n${BOLD}🔍 检查前置依赖${RESET}\n`);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
if (isCliInstalled("lark-cli")) console.log(` ${GREEN}✅ lark-cli 已安装${RESET}`);
|
|
116
|
-
else {
|
|
117
|
-
console.log(` ${RED}❌ lark-cli 未安装${RESET}`);
|
|
118
|
-
console.log(`\n lark-cli 提供飞书原生能力和凭证管理,必须安装。`);
|
|
119
|
-
console.log(` ${DIM}安装命令:${RESET}`);
|
|
120
|
-
console.log(` npm install -g @larksuite/cli`);
|
|
121
|
-
console.log(` npx skills add larksuite/cli -y -g`);
|
|
122
|
-
console.log(` ${DIM}文档:${RESET} https://github.com/larksuite/cli\n`);
|
|
123
|
-
log.error("前置依赖检查失败: lark-cli 未安装");
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
227
|
+
const assessment = assessInitPrerequisites({
|
|
228
|
+
runtimeName,
|
|
229
|
+
detectCli: (name) => ({ installed: isCliInstalled(name) })
|
|
230
|
+
});
|
|
231
|
+
if (assessment.runtime.ready) console.log(` ${GREEN}✅ ${assessment.runtime.cliName} 已安装${RESET}`);
|
|
232
|
+
else console.log(` ${YELLOW}⚠️ ${assessment.runtime.cliName} 未安装${RESET}`);
|
|
233
|
+
if (assessment.larkCli.ready) console.log(` ${GREEN}✅ lark-cli 已安装${RESET}`);
|
|
234
|
+
else console.log(` ${YELLOW}⚠️ lark-cli 未安装(基础飞书连接不阻塞)${RESET}`);
|
|
235
|
+
for (const warning of assessment.warnings) console.log(` ${DIM}${warning}${RESET}`);
|
|
126
236
|
console.log();
|
|
127
|
-
|
|
237
|
+
}
|
|
238
|
+
async function configureRuntime(rl) {
|
|
239
|
+
const currentRuntime = resolveConfiguredRuntime(process.env.LARKPAL_RUNTIME);
|
|
240
|
+
console.log(`\n${BOLD}🤖 Agent runtime 配置${RESET}\n`);
|
|
241
|
+
console.log(renderRuntimeSelectionPrompt(currentRuntime));
|
|
242
|
+
console.log();
|
|
243
|
+
const selectedRuntime = resolveRuntimeSelection(await ask(rl, ` 请选择 [1/2/3](默认当前): `), currentRuntime);
|
|
244
|
+
process.env.LARKPAL_RUNTIME = selectedRuntime;
|
|
245
|
+
console.log(`\n${formatRuntimeSelectionSummary(selectedRuntime)}\n`);
|
|
246
|
+
log.info("用户选择 agent runtime", { runtimeName: selectedRuntime });
|
|
247
|
+
return selectedRuntime;
|
|
128
248
|
}
|
|
129
249
|
/**
|
|
130
250
|
* 通过 lark-cli config init 子进程配置凭证。
|
|
@@ -219,6 +339,34 @@ async function manualCredentialInput(rl) {
|
|
|
219
339
|
console.log(`\n${RED}凭证保存失败${RESET}`);
|
|
220
340
|
return false;
|
|
221
341
|
}
|
|
342
|
+
async function scanCreateAppCredentials() {
|
|
343
|
+
console.log(`\n${BOLD}📱 扫码创建飞书智能体应用${RESET}\n`);
|
|
344
|
+
console.log(` LarkPal 会通过飞书官方一键创建应用能力获取 App ID 和 App Secret。`);
|
|
345
|
+
console.log(` ${DIM}这一步只配置应用凭据;个人日历、文档、邮件等能力后续通过 lark-cli auth login 授权。${RESET}\n`);
|
|
346
|
+
try {
|
|
347
|
+
const result = await registerFeishuAppCredentials({
|
|
348
|
+
saveCredentials: saveCredentialsOrThrow,
|
|
349
|
+
onQRCodeReady: ({ url, expireIn }) => {
|
|
350
|
+
console.log(renderFeishuAppCreationQRCode({
|
|
351
|
+
url,
|
|
352
|
+
expireIn
|
|
353
|
+
}));
|
|
354
|
+
console.log();
|
|
355
|
+
},
|
|
356
|
+
onStatusChange: ({ status }) => {
|
|
357
|
+
if (status === "polling") console.log(` ${DIM}等待飞书确认中...${RESET}`);
|
|
358
|
+
else if (status === "slow_down") console.log(` ${DIM}飞书要求降低轮询频率,继续等待...${RESET}`);
|
|
359
|
+
else if (status === "domain_switched") console.log(` ${DIM}已根据租户切换认证域名,继续等待...${RESET}`);
|
|
360
|
+
},
|
|
361
|
+
source: "larkpal-init"
|
|
362
|
+
});
|
|
363
|
+
console.log(`\n${GREEN}✅ 飞书应用创建并保存成功${RESET} ${DIM}(App ID: ${result.appId})${RESET}`);
|
|
364
|
+
return true;
|
|
365
|
+
} catch (err) {
|
|
366
|
+
console.log(`\n${RED}扫码创建飞书应用失败: ${err instanceof Error ? err.message : String(err)}${RESET}`);
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
222
370
|
async function configureCredentials(rl) {
|
|
223
371
|
if (process.env.LARK_APP_ID && process.env.LARK_APP_SECRET) {
|
|
224
372
|
console.log(`${GREEN}✅ 飞书应用凭证已通过环境变量配置${RESET}`);
|
|
@@ -238,14 +386,20 @@ async function configureCredentials(rl) {
|
|
|
238
386
|
return promptAppSecret(rl, existingAppId);
|
|
239
387
|
}
|
|
240
388
|
console.log(`${BOLD}📋 飞书应用配置${RESET}\n`);
|
|
241
|
-
console.log(` LarkPal
|
|
242
|
-
console.log(`
|
|
243
|
-
console.log(`
|
|
244
|
-
console.log(` ${DIM}[
|
|
245
|
-
|
|
246
|
-
const option =
|
|
389
|
+
console.log(` LarkPal 需要飞书应用凭据才能进入飞书会话。推荐使用飞书官方扫码创建应用能力:\n`);
|
|
390
|
+
console.log(` ${DIM}[1] 扫码创建飞书智能体应用(推荐)${RESET}`);
|
|
391
|
+
console.log(` ${DIM}[2] 手动输入 App ID 和 App Secret${RESET}`);
|
|
392
|
+
console.log(` ${DIM}[3] 使用 lark-cli config init(兼容旧流程)${RESET}\n`);
|
|
393
|
+
const choice = await ask(rl, ` 请选择 [1/2/3](默认 1): `);
|
|
394
|
+
const option = choice === "2" ? 2 : choice === "3" ? 3 : 1;
|
|
247
395
|
log.info("用户选择凭证配置方式", { option });
|
|
248
396
|
if (option === 1) {
|
|
397
|
+
if (await scanCreateAppCredentials()) return true;
|
|
398
|
+
console.log(`\n ${YELLOW}扫码创建未完成,是否手动输入凭证?${RESET}`);
|
|
399
|
+
if ((await ask(rl, ` 手动输入? [y/N]: `)).toLowerCase() === "y") return manualCredentialInput(rl);
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
if (option === 3) {
|
|
249
403
|
const appId = await runLarkCliConfigInit();
|
|
250
404
|
if (appId) return promptAppSecret(rl, appId);
|
|
251
405
|
console.log(`\n ${YELLOW}lark-cli 配置未成功,是否手动输入凭证?${RESET}`);
|
|
@@ -264,16 +418,15 @@ async function runInteractiveInit() {
|
|
|
264
418
|
console.log(`${BOLD}${CYAN}║ LarkPal 初始化向导 ║${RESET}`);
|
|
265
419
|
console.log(`${BOLD}${CYAN}╚══════════════════════════════════════╝${RESET}\n`);
|
|
266
420
|
log.info("开始交互式初始化流程");
|
|
267
|
-
if (!checkPrerequisites()) {
|
|
268
|
-
console.log(`${RED}请安装缺失的依赖后重新运行 ${CYAN}larkpal init${RESET}\n`);
|
|
269
|
-
return 1;
|
|
270
|
-
}
|
|
271
421
|
const rl = createInterface({
|
|
272
422
|
input: process.stdin,
|
|
273
423
|
output: process.stdout
|
|
274
424
|
});
|
|
275
|
-
let credentialsOk
|
|
425
|
+
let credentialsOk;
|
|
426
|
+
let selectedRuntime;
|
|
276
427
|
try {
|
|
428
|
+
selectedRuntime = await configureRuntime(rl);
|
|
429
|
+
checkPrerequisites(selectedRuntime);
|
|
277
430
|
credentialsOk = await configureCredentials(rl);
|
|
278
431
|
} finally {
|
|
279
432
|
rl.close();
|
|
@@ -284,9 +437,14 @@ async function runInteractiveInit() {
|
|
|
284
437
|
}
|
|
285
438
|
console.log(`\n${BOLD}${GREEN}🎉 初始化完成!${RESET}\n`);
|
|
286
439
|
console.log(` ${DIM}下一步:${RESET}`);
|
|
440
|
+
console.log(` ${formatRuntimeSelectionSummary(selectedRuntime)}`);
|
|
287
441
|
console.log(` 运行 ${CYAN}larkpal${RESET} 启动服务\n`);
|
|
288
442
|
log.info("交互式初始化流程完成");
|
|
289
443
|
return 0;
|
|
290
444
|
}
|
|
445
|
+
function resolveConfiguredRuntime(value) {
|
|
446
|
+
if (value === "codex-app-server" || value === "larkpal-agent" || value === "claude-code") return value;
|
|
447
|
+
return "claude-code";
|
|
448
|
+
}
|
|
291
449
|
//#endregion
|
|
292
450
|
export { runInteractiveInit };
|
|
@@ -1,21 +1,10 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-wcPFST8Q.mjs";
|
|
1
2
|
import { t as larkLogger } from "./lark-logger-D7_pEVQc.mjs";
|
|
2
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
import { homedir } from "node:os";
|
|
5
6
|
import { execSync } from "node:child_process";
|
|
6
7
|
import { createHash } from "node:crypto";
|
|
7
|
-
//#region \0rolldown/runtime.js
|
|
8
|
-
var __defProp = Object.defineProperty;
|
|
9
|
-
var __exportAll = (all, no_symbols) => {
|
|
10
|
-
let target = {};
|
|
11
|
-
for (var name in all) __defProp(target, name, {
|
|
12
|
-
get: all[name],
|
|
13
|
-
enumerable: true
|
|
14
|
-
});
|
|
15
|
-
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
16
|
-
return target;
|
|
17
|
-
};
|
|
18
|
-
//#endregion
|
|
19
8
|
//#region src/credentials/lark-cli-provider.ts
|
|
20
9
|
/**
|
|
21
10
|
* 飞书应用凭证提供者
|
|
@@ -121,18 +110,21 @@ var LarkCliCredentialProvider = class LarkCliCredentialProvider {
|
|
|
121
110
|
else log.warn("未找到 lark-cli(应由 preflight 检查保证安装)");
|
|
122
111
|
const envAppId = process.env.LARK_APP_ID;
|
|
123
112
|
const envAppSecret = process.env.LARK_APP_SECRET;
|
|
113
|
+
const envUserAccessToken = process.env.LARK_USER_ACCESS_TOKEN;
|
|
124
114
|
let cliConfig = {};
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
115
|
+
if (!envAppId || !envAppSecret) {
|
|
116
|
+
const larkpalCreds = this.loadFromLarkPalCredentials();
|
|
117
|
+
if (larkpalCreds?.appId && larkpalCreds?.appSecret) cliConfig = larkpalCreds;
|
|
118
|
+
else if (this.larkCliPath) try {
|
|
119
|
+
cliConfig = this.loadFromCli();
|
|
120
|
+
} catch (err) {
|
|
121
|
+
log.warn("从 lark-cli 读取配置失败,将使用环境变量", { error: err instanceof Error ? err.message : String(err) });
|
|
122
|
+
}
|
|
131
123
|
}
|
|
132
124
|
this.appId = envAppId ?? cliConfig.appId ?? "";
|
|
133
125
|
this.appSecret = envAppSecret ?? cliConfig.appSecret ?? "";
|
|
134
126
|
this.tenantAccessToken = cliConfig.tenant_access_token ?? "";
|
|
135
|
-
this.userAccessToken = cliConfig.user_access_token ?? "";
|
|
127
|
+
this.userAccessToken = envUserAccessToken ?? cliConfig.user_access_token ?? "";
|
|
136
128
|
if (!this.appId) {
|
|
137
129
|
const msg = [
|
|
138
130
|
"无法获取飞书应用凭证 (app_id)",
|