@vibe-lark/larkpal 0.1.85 → 0.1.87
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/account-id-BM1T6029-BzQ_cMoi.mjs +1 -0
- package/dist/activation-context--5yu1dPF-XV0Y0pTy.mjs +1 -0
- package/dist/anthropic-CFZUCb1w.mjs +39 -0
- package/dist/assistant-visible-text-DuFWokAt-BOJV_zwc.mjs +9 -0
- package/dist/azure-openai-responses-BEuiMaF-.mjs +1 -0
- package/dist/boundary-file-read-BSd9MXPy-gnNVhPKT.mjs +1 -0
- package/dist/browser-lifecycle-cleanup-BdNsMhMD-BM97qA_M.mjs +1 -0
- package/dist/build-Bkj0hOS2.mjs +3 -0
- package/dist/bundled-compat-BpaDaAAu-DJtrIX6i.mjs +27 -0
- package/dist/bundled-dir-U738ay4K-BxbzrJOo.mjs +1 -0
- package/dist/call-DimWbpgJ-B4pv55RH.mjs +20 -0
- package/dist/channel-bootstrap.runtime-BVclcGv1-CEZ0-JP9.mjs +1 -0
- package/dist/channel-bootstrap.runtime-Cwcr38m2-Dt6QlQkX.mjs +1 -0
- package/dist/chunk-DHbaKqs-.mjs +1 -0
- package/dist/cli.mjs +7 -92
- package/dist/command-format-LSnUCVVF-Dn4h9IOh.mjs +16 -0
- package/dist/config-Bim2pi8a-03N-28sw.mjs +1 -0
- package/dist/delivery-context.shared-j5hC5qb1-BSLQG47q.mjs +1 -0
- package/dist/diagnostic-ClMwNnUA-BMKvPQJT.mjs +1 -0
- package/dist/dist-J5UB83Ws.mjs +601 -0
- package/dist/dist-coestTs9.mjs +5 -0
- package/dist/env-api-keys-Cuz5irWW.mjs +1 -0
- package/dist/event-stream-B7Ei41Bu.mjs +1 -0
- package/dist/facade-loader-DFzIYYt_-4bzFvy-b.mjs +1 -0
- package/dist/from-Ba69_5cR.mjs +14 -0
- package/dist/gateway-method-policy-C7rf0SeV-CTJcMQCE.mjs +1 -0
- package/dist/github-copilot-headers-BfhryVuU.mjs +1 -0
- package/dist/google-ABo-1guT.mjs +1 -0
- package/dist/google-gemini-cli-CGlN1zUA.mjs +2 -0
- package/dist/google-shared-C60sNhwd.mjs +301 -0
- package/dist/google-vertex-De0nAItu.mjs +1 -0
- package/dist/hash-Dw-N8Y9l.mjs +1 -0
- package/dist/headers-Bpsp70B9.mjs +1 -0
- package/dist/home-dir-C44RaPME-B5iLWwCq.mjs +1 -0
- package/dist/ids-BPsM98Uu-CJBJAeVC.mjs +1 -0
- package/dist/image-2w3cApg7-CrpUMCvm.mjs +3 -0
- package/dist/interactive-init-C5MFL5MW.mjs +8 -0
- package/dist/io-CH2g3vsv-JJtUIa_U.mjs +10 -0
- package/dist/jiti-loader-cache-Bat69wam-DNIteTtO.mjs +9 -0
- package/dist/json-file-BQRNuxmK-hRjIgOXI.mjs +1 -0
- package/dist/json-parse-BESd8UAM.mjs +3 -0
- package/dist/jws-YaZ38F_i.mjs +12 -0
- package/dist/lark-cli-provider-BLMxH6CN.mjs +4 -0
- package/dist/lark-logger-xpMKNNtY.mjs +1 -0
- package/dist/live-smoke-DpURW676.mjs +33 -0
- package/dist/load-context-mb7uvLSn-3xYwSnOZ.mjs +1 -0
- package/dist/loader-708jrx4Y-BbJRXNlQ.mjs +4 -0
- package/dist/main.mjs +480 -18553
- package/dist/manifest-registry-_hxKCS2K-OL8KK_o4.mjs +15 -0
- package/dist/mcp-server.mjs +4 -527
- package/dist/message-channel-Bk_YHfQg-n2Hw2Y0L.mjs +1 -0
- package/dist/message-channel-core-DTA--Gjh-CIh10vfo.mjs +1 -0
- package/dist/message.config.runtime-K-k4XOyq-F_h0Cgkl.mjs +1 -0
- package/dist/message.gateway.runtime-DqHOHRTQ-ge4c5p4s.mjs +1 -0
- package/dist/mistral-DyL3Zo1b.mjs +7 -0
- package/dist/model-auth-env-oyg2Hexc-DJbt9J3o.mjs +1 -0
- package/dist/models-config-BmpSQJQF-BL6S5qAo.mjs +1 -0
- package/dist/models-config.runtime-SOhLEw-f.mjs +1 -0
- package/dist/multipart-parser-B9dwXBqu.mjs +2 -0
- package/dist/oauth-25y2wc7K.mjs +75 -0
- package/dist/openai-C-rgGb7X.mjs +16 -0
- package/dist/openai-codex-responses-BbyAPsP1.mjs +7 -0
- package/dist/openai-completions-Bu5qvQzb.mjs +5 -0
- package/dist/openai-responses-pIdwQBc4.mjs +1 -0
- package/dist/openai-responses-shared-DqW1JSux.mjs +10 -0
- package/dist/paths-D6msg0S1-BO4XgM1u.mjs +1 -0
- package/dist/paths-D92DjaZ--DdtJ-zYp.mjs +1 -0
- package/dist/pi-model-discovery-Bd0TWzo8-DvSM0eWu.mjs +1 -0
- package/dist/pi-model-discovery-runtime-PrjWOmsV.mjs +1 -0
- package/dist/pi-tools.before-tool-call.runtime-Bn7uqKie-WPxU5oNX.mjs +2 -0
- package/dist/plugin-auto-enable-DdH3n9V8-Dr_bUXkG.mjs +1 -0
- package/dist/process-scoped-map-B2cWkYET-Co-i-MXA.mjs +1 -0
- package/dist/provider-discovery.runtime-DY0VWQ81.mjs +1 -0
- package/dist/provider-model-compat-CFqcq0it-BUwZENrz.mjs +1 -0
- package/dist/provider-runtime-C8rljsNs-BDvu6FW3.mjs +1 -0
- package/dist/provider-runtime-CT50mNIX-DImw7ZZu.mjs +79 -0
- package/dist/provider-stream-COLujAAo-Dpq1DSIk.mjs +248 -0
- package/dist/providers.runtime-Ba7tOlq7-B4t6ywCr.mjs +1 -0
- package/dist/public-surface-loader-BVl0NQAE-TfJ3izj1.mjs +1 -0
- package/dist/resolve-C33B7eeu-DZM_z6Wr.mjs +1 -0
- package/dist/runtime-Cym-fqI8-1GOIuRRQ.mjs +1 -0
- package/dist/runtime-channel-state-CohMybAh-BBzhl07Q.mjs +1 -0
- package/dist/runtime-snapshot-C54O1-gI-DxusscPf.mjs +1 -0
- package/dist/runtime-state-IBOIXKOx-Cw1GA1td.mjs +1 -0
- package/dist/runtime-web-tools-fallback.runtime-Ca-qn1mj-BsYE2gOd.mjs +1 -0
- package/dist/runtime-web-tools-manifest.runtime-BgxQTjmq-P6MKP9gQ.mjs +1 -0
- package/dist/runtime-web-tools-public-artifacts.runtime-D9qcoWIX-5VoyjouO.mjs +1 -0
- package/dist/session-archive.runtime-B52_5vs4-cU2tYbBz.mjs +1 -0
- package/dist/session-key-Cn2QoOyX-QZeYmbxd.mjs +1 -0
- package/dist/session-transcript-files.fs-DEeFXruQ-CXIAFSsr.mjs +1 -0
- package/dist/src-CCDx_C-U.mjs +3 -0
- package/dist/src-DACmA9V6.mjs +5 -0
- package/dist/subagent-announce-L5WsQatW--rMn240x.mjs +2 -0
- package/dist/subagent-announce.registry.runtime-DiqZyeTs-DNfNHsls.mjs +1 -0
- package/dist/subagent-registry-steer-runtime-DFgZFC0j-XYS1_qwl.mjs +1 -0
- package/dist/subagent-system-prompt-C1qnN--K-CsU4f97T.mjs +23 -0
- package/dist/target-parsing-loaded-DKkb6jJY-B830OA1a.mjs +12 -0
- package/dist/task-registry-delivery-runtime-Dd1CUkZF-B2Hj2k8C.mjs +16 -0
- package/dist/task-registry-delivery-runtime-ragbeVx5-Bdvo2Q5u.mjs +1 -0
- package/dist/thinking-Bh-7MqXz-uApNUe6g.mjs +1 -0
- package/dist/transcript-E_-ISOkD-VTwb782D.mjs +5 -0
- package/dist/transcript.runtime-BqhvZZdl-B4E0TiX5.mjs +1 -0
- package/dist/transform-messages-BTA3xbAI.mjs +1 -0
- package/dist/web-provider-public-artifacts-BlpRwIUS-DbVhDOrY.mjs +1 -0
- package/dist/web-search-providers.runtime--0aUtC3b-Bjh4Bmrz.mjs +1 -0
- package/package.json +1 -2
- package/dist/interactive-init-B6j8j_Ap.mjs +0 -450
- package/dist/lark-cli-provider-7LvtOIF_.mjs +0 -554
- package/dist/lark-logger-D7_pEVQc.mjs +0 -143
- package/dist/live-smoke-nvTUPtrv.mjs +0 -7135
- package/dist/rolldown-runtime-wcPFST8Q.mjs +0 -13
package/dist/mcp-server.mjs
CHANGED
|
@@ -1,527 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
//#region src/mcp-server/host-bridge.ts
|
|
6
|
-
/**
|
|
7
|
-
* Host Bridge — 与宿主层的 HTTP 通信桥
|
|
8
|
-
*
|
|
9
|
-
* 负责通过 HTTP 回调与 LarkPal 宿主层通信:
|
|
10
|
-
* - 发送 tool 调用回调到宿主层
|
|
11
|
-
* - 轮询等待宿主层 resolve 结果(用于阻塞式 tool)
|
|
12
|
-
* - fire-and-forget 通知(用于非阻塞式 tool)
|
|
13
|
-
*/
|
|
14
|
-
/** 日志输出到 stderr(stdout 被 MCP 协议占用) */
|
|
15
|
-
function log$2(level, message, meta) {
|
|
16
|
-
const entry = {
|
|
17
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18
|
-
level,
|
|
19
|
-
component: "mcp-server/host-bridge",
|
|
20
|
-
message,
|
|
21
|
-
...meta
|
|
22
|
-
};
|
|
23
|
-
process.stderr.write(JSON.stringify(entry) + "\n");
|
|
24
|
-
}
|
|
25
|
-
/** 轮询配置 */
|
|
26
|
-
const POLL_INTERVAL_MS = 2e3;
|
|
27
|
-
const POLL_TIMEOUT_MS = 1800 * 1e3;
|
|
28
|
-
/** 获取宿主层基础 URL */
|
|
29
|
-
function getGatewayBaseUrl() {
|
|
30
|
-
return `http://localhost:${process.env["LARKPAL_GATEWAY_PORT"] || "3000"}`;
|
|
31
|
-
}
|
|
32
|
-
/** 获取当前 session ID */
|
|
33
|
-
function getSessionId() {
|
|
34
|
-
const sessionId = process.env["LARKPAL_MCP_SESSION_ID"];
|
|
35
|
-
if (!sessionId) throw new Error("环境变量 LARKPAL_MCP_SESSION_ID 未设置");
|
|
36
|
-
return sessionId;
|
|
37
|
-
}
|
|
38
|
-
function getCwd() {
|
|
39
|
-
return process.env["LARKPAL_MCP_CWD"];
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* 发送回调请求到宿主层
|
|
43
|
-
*
|
|
44
|
-
* @param tool - tool 名称
|
|
45
|
-
* @param params - tool 参数
|
|
46
|
-
* @returns requestId 用于后续轮询
|
|
47
|
-
*/
|
|
48
|
-
async function sendCallback(tool, params) {
|
|
49
|
-
const requestId = randomUUID();
|
|
50
|
-
const sessionId = getSessionId();
|
|
51
|
-
const url = `${getGatewayBaseUrl()}/api/mcp/callback`;
|
|
52
|
-
const body = {
|
|
53
|
-
requestId,
|
|
54
|
-
tool,
|
|
55
|
-
params,
|
|
56
|
-
sessionId,
|
|
57
|
-
cwd: getCwd()
|
|
58
|
-
};
|
|
59
|
-
log$2("info", `发送回调请求到宿主层`, {
|
|
60
|
-
url,
|
|
61
|
-
requestId,
|
|
62
|
-
tool,
|
|
63
|
-
sessionId
|
|
64
|
-
});
|
|
65
|
-
const response = await fetch(url, {
|
|
66
|
-
method: "POST",
|
|
67
|
-
headers: { "Content-Type": "application/json" },
|
|
68
|
-
body: JSON.stringify(body)
|
|
69
|
-
});
|
|
70
|
-
if (!response.ok) {
|
|
71
|
-
const text = await response.text().catch(() => "<读取失败>");
|
|
72
|
-
log$2("error", `回调请求失败`, {
|
|
73
|
-
requestId,
|
|
74
|
-
status: response.status,
|
|
75
|
-
responseBody: text
|
|
76
|
-
});
|
|
77
|
-
throw new Error(`宿主层回调请求失败: HTTP ${response.status} - ${text}`);
|
|
78
|
-
}
|
|
79
|
-
log$2("info", `回调请求成功`, {
|
|
80
|
-
requestId,
|
|
81
|
-
status: response.status
|
|
82
|
-
});
|
|
83
|
-
return requestId;
|
|
84
|
-
}
|
|
85
|
-
async function sendSyncRequest(tool, params) {
|
|
86
|
-
const requestId = randomUUID();
|
|
87
|
-
const sessionId = getSessionId();
|
|
88
|
-
const url = `${getGatewayBaseUrl()}/api/mcp/sync`;
|
|
89
|
-
const body = {
|
|
90
|
-
requestId,
|
|
91
|
-
tool,
|
|
92
|
-
params,
|
|
93
|
-
sessionId,
|
|
94
|
-
cwd: getCwd()
|
|
95
|
-
};
|
|
96
|
-
log$2("info", `发送同步请求到宿主层`, {
|
|
97
|
-
url,
|
|
98
|
-
requestId,
|
|
99
|
-
tool,
|
|
100
|
-
sessionId
|
|
101
|
-
});
|
|
102
|
-
const response = await fetch(url, {
|
|
103
|
-
method: "POST",
|
|
104
|
-
headers: { "Content-Type": "application/json" },
|
|
105
|
-
body: JSON.stringify(body)
|
|
106
|
-
});
|
|
107
|
-
if (!response.ok) {
|
|
108
|
-
const text = await response.text().catch(() => "<读取失败>");
|
|
109
|
-
throw new Error(`宿主层同步请求失败: HTTP ${response.status} - ${text}`);
|
|
110
|
-
}
|
|
111
|
-
return await response.json();
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* 轮询等待宿主层 resolve 结果
|
|
115
|
-
*
|
|
116
|
-
* @param requestId - 请求 ID
|
|
117
|
-
* @returns resolve 后的结果
|
|
118
|
-
*/
|
|
119
|
-
async function pollForResult(requestId) {
|
|
120
|
-
const url = `${getGatewayBaseUrl()}/api/mcp/resolve/${requestId}`;
|
|
121
|
-
const startTime = Date.now();
|
|
122
|
-
log$2("info", `开始轮询等待结果`, {
|
|
123
|
-
requestId,
|
|
124
|
-
url,
|
|
125
|
-
timeoutMs: POLL_TIMEOUT_MS
|
|
126
|
-
});
|
|
127
|
-
while (true) {
|
|
128
|
-
const elapsed = Date.now() - startTime;
|
|
129
|
-
if (elapsed >= POLL_TIMEOUT_MS) {
|
|
130
|
-
log$2("error", `轮询超时`, {
|
|
131
|
-
requestId,
|
|
132
|
-
elapsedMs: elapsed
|
|
133
|
-
});
|
|
134
|
-
throw new Error(`等待宿主层响应超时 (${POLL_TIMEOUT_MS / 1e3}s): requestId=${requestId}`);
|
|
135
|
-
}
|
|
136
|
-
try {
|
|
137
|
-
const response = await fetch(url, { method: "GET" });
|
|
138
|
-
if (!response.ok) {
|
|
139
|
-
const text = await response.text().catch(() => "<读取失败>");
|
|
140
|
-
log$2("warn", `轮询请求返回非 2xx`, {
|
|
141
|
-
requestId,
|
|
142
|
-
status: response.status,
|
|
143
|
-
responseBody: text
|
|
144
|
-
});
|
|
145
|
-
await sleep(POLL_INTERVAL_MS);
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
const data = await response.json();
|
|
149
|
-
if (data.status === "resolved") {
|
|
150
|
-
log$2("info", `轮询获得结果`, {
|
|
151
|
-
requestId,
|
|
152
|
-
status: data.status
|
|
153
|
-
});
|
|
154
|
-
return data.result;
|
|
155
|
-
}
|
|
156
|
-
if (data.status === "rejected") {
|
|
157
|
-
log$2("error", `请求被拒绝`, {
|
|
158
|
-
requestId,
|
|
159
|
-
error: data.error
|
|
160
|
-
});
|
|
161
|
-
throw new Error(`宿主层拒绝请求: ${data.error || "未知原因"}`);
|
|
162
|
-
}
|
|
163
|
-
} catch (err) {
|
|
164
|
-
if (err instanceof Error && err.message.startsWith("宿主层拒绝请求")) throw err;
|
|
165
|
-
log$2("warn", `轮询发生网络错误,将重试`, {
|
|
166
|
-
requestId,
|
|
167
|
-
error: err instanceof Error ? err.message : String(err)
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
await sleep(POLL_INTERVAL_MS);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* 发送 fire-and-forget 通知(不等待响应结果)
|
|
175
|
-
*
|
|
176
|
-
* @param tool - tool 名称
|
|
177
|
-
* @param params - tool 参数
|
|
178
|
-
*/
|
|
179
|
-
async function sendNotification(tool, params) {
|
|
180
|
-
const requestId = randomUUID();
|
|
181
|
-
const sessionId = getSessionId();
|
|
182
|
-
const url = `${getGatewayBaseUrl()}/api/mcp/callback`;
|
|
183
|
-
const body = {
|
|
184
|
-
requestId,
|
|
185
|
-
tool,
|
|
186
|
-
params,
|
|
187
|
-
sessionId,
|
|
188
|
-
cwd: getCwd()
|
|
189
|
-
};
|
|
190
|
-
log$2("info", `发送 fire-and-forget 通知`, {
|
|
191
|
-
url,
|
|
192
|
-
requestId,
|
|
193
|
-
tool,
|
|
194
|
-
sessionId
|
|
195
|
-
});
|
|
196
|
-
try {
|
|
197
|
-
log$2("info", `通知发送完成`, {
|
|
198
|
-
requestId,
|
|
199
|
-
status: (await fetch(url, {
|
|
200
|
-
method: "POST",
|
|
201
|
-
headers: { "Content-Type": "application/json" },
|
|
202
|
-
body: JSON.stringify(body)
|
|
203
|
-
})).status
|
|
204
|
-
});
|
|
205
|
-
} catch (err) {
|
|
206
|
-
log$2("warn", `通知发送失败(已忽略)`, {
|
|
207
|
-
requestId,
|
|
208
|
-
error: err instanceof Error ? err.message : String(err)
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
function sleep(ms) {
|
|
213
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
214
|
-
}
|
|
215
|
-
const hostBridge = {
|
|
216
|
-
sendCallback,
|
|
217
|
-
sendSyncRequest,
|
|
218
|
-
pollForResult,
|
|
219
|
-
sendNotification
|
|
220
|
-
};
|
|
221
|
-
//#endregion
|
|
222
|
-
//#region src/mcp-server/tools.ts
|
|
223
|
-
/** 日志输出到 stderr */
|
|
224
|
-
function log$1(level, message, meta) {
|
|
225
|
-
const entry = {
|
|
226
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
227
|
-
level,
|
|
228
|
-
component: "mcp-server/tools",
|
|
229
|
-
message,
|
|
230
|
-
...meta
|
|
231
|
-
};
|
|
232
|
-
process.stderr.write(JSON.stringify(entry) + "\n");
|
|
233
|
-
}
|
|
234
|
-
function readDisabledToolsFromEnv() {
|
|
235
|
-
return (process.env["LARKPAL_MCP_DISABLED_TOOLS"] ?? "").split(",").map((tool) => tool.trim()).filter(Boolean);
|
|
236
|
-
}
|
|
237
|
-
function isToolDisabled(disabledTools, tool) {
|
|
238
|
-
return disabledTools.has(tool) || disabledTools.has(`mcp__larkpal__${tool}`);
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* 注册所有 tool 到 MCP Server
|
|
242
|
-
*/
|
|
243
|
-
function registerTools(server, options = {}) {
|
|
244
|
-
const disabledTools = new Set(options.disabledTools ?? readDisabledToolsFromEnv());
|
|
245
|
-
if (!isToolDisabled(disabledTools, "ask_user")) server.tool("ask_user", "向用户提问,等待用户回答。支持多阶段提问,每个阶段可包含选项或自由文本输入;每个独立决策维度应拆成一个 phase,不要把多个问题塞进同一个选项列表。", {
|
|
246
|
-
question: z.string().describe("主问题描述"),
|
|
247
|
-
phases: z.array(z.object({
|
|
248
|
-
id: z.string().describe("阶段唯一标识"),
|
|
249
|
-
title: z.string().describe("自然语义化的阶段提问主题,例如\"要看的具体是什么?\"、\"按照什么视角来查?\";不要使用\"确认问题\"或\"请选择\"这类泛标题"),
|
|
250
|
-
description: z.string().optional().describe("阶段详细描述"),
|
|
251
|
-
options: z.array(z.object({
|
|
252
|
-
label: z.string().describe("选项显示文本"),
|
|
253
|
-
value: z.string().describe("选项值")
|
|
254
|
-
})).optional().describe("可选项列表"),
|
|
255
|
-
multiple: z.boolean().optional().describe("是否允许多选;默认 false"),
|
|
256
|
-
selectionMode: z.enum(["single", "multiple"]).optional().describe("选择模式;默认 single"),
|
|
257
|
-
allowFreeText: z.boolean().optional().describe("是否允许自由文本输入"),
|
|
258
|
-
required: z.boolean().optional().describe("是否必填")
|
|
259
|
-
})).describe("提问阶段列表")
|
|
260
|
-
}, async (params) => {
|
|
261
|
-
log$1("info", `ask_user 被调用`, {
|
|
262
|
-
question: params.question,
|
|
263
|
-
phaseCount: params.phases.length
|
|
264
|
-
});
|
|
265
|
-
try {
|
|
266
|
-
const toolParams = {
|
|
267
|
-
question: params.question,
|
|
268
|
-
phases: params.phases
|
|
269
|
-
};
|
|
270
|
-
const requestId = await hostBridge.sendCallback("ask_user", toolParams);
|
|
271
|
-
log$1("info", `等待用户回答`, { requestId });
|
|
272
|
-
const result = await hostBridge.pollForResult(requestId);
|
|
273
|
-
log$1("info", `用户已回答`, {
|
|
274
|
-
requestId,
|
|
275
|
-
answers: result
|
|
276
|
-
});
|
|
277
|
-
return { content: [{
|
|
278
|
-
type: "text",
|
|
279
|
-
text: JSON.stringify(result)
|
|
280
|
-
}] };
|
|
281
|
-
} catch (err) {
|
|
282
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
283
|
-
log$1("error", `ask_user 执行失败`, { error: errorMsg });
|
|
284
|
-
return {
|
|
285
|
-
content: [{
|
|
286
|
-
type: "text",
|
|
287
|
-
text: JSON.stringify({ error: errorMsg })
|
|
288
|
-
}],
|
|
289
|
-
isError: true
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
|
-
if (!isToolDisabled(disabledTools, "request_permission")) server.tool("request_permission", "请求飞书 API 权限。发送权限申请后等待用户授权。", {
|
|
294
|
-
scopes: z.array(z.string()).describe("需要的权限列表(如 \"contact:user.base:readonly\")"),
|
|
295
|
-
reason: z.string().optional().describe("申请权限的原因说明"),
|
|
296
|
-
grantUrl: z.string().optional().describe("可直接打开的权限申请链接"),
|
|
297
|
-
grant_url: z.string().optional().describe("可直接打开的权限申请链接(snake_case 兼容字段)"),
|
|
298
|
-
consoleUrl: z.string().optional().describe("开放平台权限配置页链接"),
|
|
299
|
-
console_url: z.string().optional().describe("开放平台权限配置页链接(snake_case 兼容字段)")
|
|
300
|
-
}, async (params) => {
|
|
301
|
-
log$1("info", `request_permission 被调用`, {
|
|
302
|
-
scopes: params.scopes,
|
|
303
|
-
reason: params.reason
|
|
304
|
-
});
|
|
305
|
-
try {
|
|
306
|
-
const toolParams = {
|
|
307
|
-
scopes: params.scopes,
|
|
308
|
-
reason: params.reason,
|
|
309
|
-
grantUrl: params.grantUrl,
|
|
310
|
-
grant_url: params.grant_url,
|
|
311
|
-
consoleUrl: params.consoleUrl,
|
|
312
|
-
console_url: params.console_url
|
|
313
|
-
};
|
|
314
|
-
const requestId = await hostBridge.sendCallback("request_permission", toolParams);
|
|
315
|
-
log$1("info", `等待权限审批`, { requestId });
|
|
316
|
-
const result = await hostBridge.pollForResult(requestId);
|
|
317
|
-
log$1("info", `权限审批完成`, {
|
|
318
|
-
requestId,
|
|
319
|
-
granted: result.granted
|
|
320
|
-
});
|
|
321
|
-
return { content: [{
|
|
322
|
-
type: "text",
|
|
323
|
-
text: JSON.stringify(result)
|
|
324
|
-
}] };
|
|
325
|
-
} catch (err) {
|
|
326
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
327
|
-
log$1("error", `request_permission 执行失败`, { error: errorMsg });
|
|
328
|
-
return {
|
|
329
|
-
content: [{
|
|
330
|
-
type: "text",
|
|
331
|
-
text: JSON.stringify({ error: errorMsg })
|
|
332
|
-
}],
|
|
333
|
-
isError: true
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
if (!isToolDisabled(disabledTools, "signal_no_reply")) server.tool("signal_no_reply", "标记本次不需要回复用户。仅用于通知宿主层,立即返回。", { reason: z.string().describe("不回复的原因(仅用于日志记录)") }, async (params) => {
|
|
338
|
-
log$1("info", `signal_no_reply 被调用`, { reason: params.reason });
|
|
339
|
-
hostBridge.sendNotification("signal_no_reply", { reason: params.reason }).catch((err) => {
|
|
340
|
-
log$1("warn", `signal_no_reply 通知发送失败(已忽略)`, { error: err instanceof Error ? err.message : String(err) });
|
|
341
|
-
});
|
|
342
|
-
return { content: [{
|
|
343
|
-
type: "text",
|
|
344
|
-
text: JSON.stringify({ acknowledged: true })
|
|
345
|
-
}] };
|
|
346
|
-
});
|
|
347
|
-
server.tool("memory_propose", "提交一个长期记忆候选。宿主层会校验、去重、限额后决定是否接受。", {
|
|
348
|
-
kind: z.enum([
|
|
349
|
-
"fact",
|
|
350
|
-
"preference",
|
|
351
|
-
"decision",
|
|
352
|
-
"recurring_context"
|
|
353
|
-
]).describe("记忆类型"),
|
|
354
|
-
text: z.string().describe("需要长期记住的简短事实或偏好"),
|
|
355
|
-
confidence: z.number().min(0).max(1).optional().describe("置信度 0-1,默认 0.7"),
|
|
356
|
-
tags: z.array(z.string()).optional().describe("用于检索的短标签"),
|
|
357
|
-
ttlDays: z.number().positive().optional().describe("可选过期天数"),
|
|
358
|
-
sourceRefs: z.array(z.string()).optional().describe("来源消息、任务或文档引用"),
|
|
359
|
-
reason: z.string().optional().describe("为什么值得记住")
|
|
360
|
-
}, async (params) => {
|
|
361
|
-
try {
|
|
362
|
-
const result = await hostBridge.sendSyncRequest("memory_propose", params);
|
|
363
|
-
return { content: [{
|
|
364
|
-
type: "text",
|
|
365
|
-
text: JSON.stringify(result)
|
|
366
|
-
}] };
|
|
367
|
-
} catch (err) {
|
|
368
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
369
|
-
log$1("error", `memory_propose 执行失败`, { error: errorMsg });
|
|
370
|
-
return {
|
|
371
|
-
content: [{
|
|
372
|
-
type: "text",
|
|
373
|
-
text: JSON.stringify({
|
|
374
|
-
accepted: false,
|
|
375
|
-
reasonCode: "error",
|
|
376
|
-
error: errorMsg
|
|
377
|
-
})
|
|
378
|
-
}],
|
|
379
|
-
isError: true
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
});
|
|
383
|
-
server.tool("memory_search", "检索当前会话的长期记忆。", {
|
|
384
|
-
query: z.string().describe("检索查询"),
|
|
385
|
-
kinds: z.array(z.enum([
|
|
386
|
-
"fact",
|
|
387
|
-
"preference",
|
|
388
|
-
"decision",
|
|
389
|
-
"recurring_context"
|
|
390
|
-
])).optional().describe("可选记忆类型过滤"),
|
|
391
|
-
limit: z.number().int().positive().max(20).optional().describe("返回条数,默认 8")
|
|
392
|
-
}, async (params) => {
|
|
393
|
-
try {
|
|
394
|
-
const result = await hostBridge.sendSyncRequest("memory_search", params);
|
|
395
|
-
return { content: [{
|
|
396
|
-
type: "text",
|
|
397
|
-
text: JSON.stringify(result)
|
|
398
|
-
}] };
|
|
399
|
-
} catch (err) {
|
|
400
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
401
|
-
log$1("error", `memory_search 执行失败`, { error: errorMsg });
|
|
402
|
-
return {
|
|
403
|
-
content: [{
|
|
404
|
-
type: "text",
|
|
405
|
-
text: JSON.stringify({
|
|
406
|
-
records: [],
|
|
407
|
-
error: errorMsg
|
|
408
|
-
})
|
|
409
|
-
}],
|
|
410
|
-
isError: true
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
server.tool("scheduled_task_list", "列出当前 Gateway 中已配置的定时 Agent Run。返回脱敏后的紧凑 JSON。", {}, async () => runScheduledTaskTool("scheduled_task_list", {}));
|
|
415
|
-
server.tool("scheduled_task_get", "按 ID 查看一个定时 Agent Run 的脱敏详情。", { id: z.string().describe("定时任务 ID") }, async (params) => runScheduledTaskTool("scheduled_task_get", params));
|
|
416
|
-
server.tool("scheduled_task_create", "创建定时 Agent Run。使用 Gateway scheduler 的标准运行语义,需要 name、cron、session_id、cwd、prompt 和 owner identity。", {
|
|
417
|
-
name: z.string().describe("定时任务名称"),
|
|
418
|
-
cron: z.string().describe("node-cron 表达式,如 \"0 9 * * *\""),
|
|
419
|
-
session_id: z.string().describe("运行会话 ID"),
|
|
420
|
-
conversation_id: z.string().optional().describe("对话 ID;默认可与 session_id 相同"),
|
|
421
|
-
cwd: z.string().describe("运行工作目录"),
|
|
422
|
-
prompt: z.string().describe("定时触发时执行的 prompt"),
|
|
423
|
-
identity: z.record(z.string(), z.unknown()).optional().describe("owner identity,至少包含 tenantKey 和 userId/openId"),
|
|
424
|
-
auth_headers: z.record(z.string(), z.string()).optional().describe("运行时认证头;宿主层会在返回时脱敏"),
|
|
425
|
-
runtime_instructions: z.unknown().optional().describe("运行时指令注入"),
|
|
426
|
-
runtime_skills: z.unknown().optional().describe("运行时技能注入"),
|
|
427
|
-
policy: z.record(z.string(), z.unknown()).optional().describe("执行策略"),
|
|
428
|
-
scenario_id: z.string().optional().describe("场景 ID"),
|
|
429
|
-
metadata: z.record(z.string(), z.unknown()).optional().describe("元数据")
|
|
430
|
-
}, async (params) => runScheduledTaskTool("scheduled_task_create", params));
|
|
431
|
-
server.tool("scheduled_task_update", "更新定时 Agent Run。updates 支持 Gateway scheduler 的可更新字段,如 name、cron、prompt、enabled、policy。", {
|
|
432
|
-
id: z.string().describe("定时任务 ID"),
|
|
433
|
-
updates: z.record(z.string(), z.unknown()).describe("要更新的字段")
|
|
434
|
-
}, async (params) => runScheduledTaskTool("scheduled_task_update", params));
|
|
435
|
-
server.tool("scheduled_task_delete", "删除定时 Agent Run。", { id: z.string().describe("定时任务 ID") }, async (params) => runScheduledTaskTool("scheduled_task_delete", params));
|
|
436
|
-
server.tool("scheduled_task_run_now", "立即执行一个定时 Agent Run,用于测试或手动触发。执行仍使用标准 scheduled-task runtime entrypoint。", { id: z.string().describe("定时任务 ID") }, async (params) => runScheduledTaskTool("scheduled_task_run_now", params));
|
|
437
|
-
}
|
|
438
|
-
async function runScheduledTaskTool(tool, params) {
|
|
439
|
-
try {
|
|
440
|
-
const result = await hostBridge.sendSyncRequest(tool, params);
|
|
441
|
-
return { content: [{
|
|
442
|
-
type: "text",
|
|
443
|
-
text: JSON.stringify(result)
|
|
444
|
-
}] };
|
|
445
|
-
} catch (err) {
|
|
446
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
447
|
-
log$1("error", `${tool} 执行失败`, { error: errorMsg });
|
|
448
|
-
return {
|
|
449
|
-
content: [{
|
|
450
|
-
type: "text",
|
|
451
|
-
text: JSON.stringify({ error: errorMsg })
|
|
452
|
-
}],
|
|
453
|
-
isError: true
|
|
454
|
-
};
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
//#endregion
|
|
458
|
-
//#region src/mcp-server/index.ts
|
|
459
|
-
/**
|
|
460
|
-
* MCP Server 入口 — stdio transport
|
|
461
|
-
*
|
|
462
|
-
* LarkPal MCP Server 通过 stdio 与 Claude Code 通信,
|
|
463
|
-
* 提供 ask_user、request_permission、signal_no_reply 三个 tool。
|
|
464
|
-
*
|
|
465
|
-
* 启动方式:由 CC 通过 --mcp-config 参数 spawn 本进程。
|
|
466
|
-
* 环境变量:
|
|
467
|
-
* - LARKPAL_GATEWAY_PORT: 宿主层 HTTP 端口(默认 3000)
|
|
468
|
-
* - LARKPAL_MCP_SESSION_ID: 当前 CC session 的 ID
|
|
469
|
-
*/
|
|
470
|
-
/** 日志输出到 stderr(stdout 被 MCP 协议占用) */
|
|
471
|
-
function log(level, message, meta) {
|
|
472
|
-
const entry = {
|
|
473
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
474
|
-
level,
|
|
475
|
-
component: "mcp-server",
|
|
476
|
-
message,
|
|
477
|
-
...meta
|
|
478
|
-
};
|
|
479
|
-
process.stderr.write(JSON.stringify(entry) + "\n");
|
|
480
|
-
}
|
|
481
|
-
async function main() {
|
|
482
|
-
log("info", "MCP Server 启动", {
|
|
483
|
-
sessionId: process.env["LARKPAL_MCP_SESSION_ID"] || "<未设置>",
|
|
484
|
-
gatewayPort: process.env["LARKPAL_GATEWAY_PORT"] || "3000",
|
|
485
|
-
pid: process.pid
|
|
486
|
-
});
|
|
487
|
-
const server = new McpServer({
|
|
488
|
-
name: "larkpal",
|
|
489
|
-
version: "1.0.0"
|
|
490
|
-
});
|
|
491
|
-
registerTools(server);
|
|
492
|
-
const transport = new StdioServerTransport();
|
|
493
|
-
await server.connect(transport);
|
|
494
|
-
log("info", "MCP Server 已连接,等待 tool 调用");
|
|
495
|
-
process.stdin.on("close", () => {
|
|
496
|
-
log("info", "stdin 已关闭,MCP Server 准备退出");
|
|
497
|
-
process.exit(0);
|
|
498
|
-
});
|
|
499
|
-
process.on("SIGTERM", () => {
|
|
500
|
-
log("info", "收到 SIGTERM,MCP Server 退出");
|
|
501
|
-
process.exit(0);
|
|
502
|
-
});
|
|
503
|
-
process.on("SIGINT", () => {
|
|
504
|
-
log("info", "收到 SIGINT,MCP Server 退出");
|
|
505
|
-
process.exit(0);
|
|
506
|
-
});
|
|
507
|
-
process.on("uncaughtException", (err) => {
|
|
508
|
-
log("error", "未捕获的异常", {
|
|
509
|
-
error: err.message,
|
|
510
|
-
stack: err.stack
|
|
511
|
-
});
|
|
512
|
-
process.exit(1);
|
|
513
|
-
});
|
|
514
|
-
process.on("unhandledRejection", (reason) => {
|
|
515
|
-
log("error", "未处理的 Promise rejection", { reason: reason instanceof Error ? reason.message : String(reason) });
|
|
516
|
-
process.exit(1);
|
|
517
|
-
});
|
|
518
|
-
}
|
|
519
|
-
main().catch((err) => {
|
|
520
|
-
log("error", "MCP Server 启动失败", {
|
|
521
|
-
error: err instanceof Error ? err.message : String(err),
|
|
522
|
-
stack: err instanceof Error ? err.stack : void 0
|
|
523
|
-
});
|
|
524
|
-
process.exit(1);
|
|
525
|
-
});
|
|
526
|
-
//#endregion
|
|
527
|
-
export {};
|
|
1
|
+
import{randomUUID as e}from"node:crypto";import{z as t}from"zod";import{McpServer as n}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as r}from"@modelcontextprotocol/sdk/server/stdio.js";function i(e,t,n){let r={timestamp:new Date().toISOString(),level:e,component:`mcp-server/host-bridge`,message:t,...n};process.stderr.write(JSON.stringify(r)+`
|
|
2
|
+
`)}const a=2e3,o=1800*1e3;function s(){return`http://localhost:${process.env.LARKPAL_GATEWAY_PORT||`3000`}`}function c(){let e=process.env.LARKPAL_MCP_SESSION_ID;if(!e)throw Error(`环境变量 LARKPAL_MCP_SESSION_ID 未设置`);return e}function l(){return process.env.LARKPAL_MCP_CWD}async function u(t,n){let r=e(),a=c(),o=`${s()}/api/mcp/callback`,u={requestId:r,tool:t,params:n,sessionId:a,cwd:l()};i(`info`,`发送回调请求到宿主层`,{url:o,requestId:r,tool:t,sessionId:a});let d=await fetch(o,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(u)});if(!d.ok){let e=await d.text().catch(()=>`<读取失败>`);throw i(`error`,`回调请求失败`,{requestId:r,status:d.status,responseBody:e}),Error(`宿主层回调请求失败: HTTP ${d.status} - ${e}`)}return i(`info`,`回调请求成功`,{requestId:r,status:d.status}),r}async function d(t,n){let r=e(),a=c(),o=`${s()}/api/mcp/sync`,u={requestId:r,tool:t,params:n,sessionId:a,cwd:l()};i(`info`,`发送同步请求到宿主层`,{url:o,requestId:r,tool:t,sessionId:a});let d=await fetch(o,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(u)});if(!d.ok){let e=await d.text().catch(()=>`<读取失败>`);throw Error(`宿主层同步请求失败: HTTP ${d.status} - ${e}`)}return await d.json()}async function f(e){let t=`${s()}/api/mcp/resolve/${e}`,n=Date.now();for(i(`info`,`开始轮询等待结果`,{requestId:e,url:t,timeoutMs:o});;){let r=Date.now()-n;if(r>=o)throw i(`error`,`轮询超时`,{requestId:e,elapsedMs:r}),Error(`等待宿主层响应超时 (${o/1e3}s): requestId=${e}`);try{let n=await fetch(t,{method:`GET`});if(!n.ok){let t=await n.text().catch(()=>`<读取失败>`);i(`warn`,`轮询请求返回非 2xx`,{requestId:e,status:n.status,responseBody:t}),await m(a);continue}let r=await n.json();if(r.status===`resolved`)return i(`info`,`轮询获得结果`,{requestId:e,status:r.status}),r.result;if(r.status===`rejected`)throw i(`error`,`请求被拒绝`,{requestId:e,error:r.error}),Error(`宿主层拒绝请求: ${r.error||`未知原因`}`)}catch(t){if(t instanceof Error&&t.message.startsWith(`宿主层拒绝请求`))throw t;i(`warn`,`轮询发生网络错误,将重试`,{requestId:e,error:t instanceof Error?t.message:String(t)})}await m(a)}}async function p(t,n){let r=e(),a=c(),o=`${s()}/api/mcp/callback`,u={requestId:r,tool:t,params:n,sessionId:a,cwd:l()};i(`info`,`发送 fire-and-forget 通知`,{url:o,requestId:r,tool:t,sessionId:a});try{i(`info`,`通知发送完成`,{requestId:r,status:(await fetch(o,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(u)})).status})}catch(e){i(`warn`,`通知发送失败(已忽略)`,{requestId:r,error:e instanceof Error?e.message:String(e)})}}function m(e){return new Promise(t=>setTimeout(t,e))}const h={sendCallback:u,sendSyncRequest:d,pollForResult:f,sendNotification:p};function g(e,t,n){let r={timestamp:new Date().toISOString(),level:e,component:`mcp-server/tools`,message:t,...n};process.stderr.write(JSON.stringify(r)+`
|
|
3
|
+
`)}function _(){return(process.env.LARKPAL_MCP_DISABLED_TOOLS??``).split(`,`).map(e=>e.trim()).filter(Boolean)}function v(e,t){return e.has(t)||e.has(`mcp__larkpal__${t}`)}function y(e,n={}){let r=new Set(n.disabledTools??_());v(r,`ask_user`)||e.tool(`ask_user`,`向用户提问,等待用户回答。支持多阶段提问,每个阶段可包含选项或自由文本输入;每个独立决策维度应拆成一个 phase,不要把多个问题塞进同一个选项列表。`,{question:t.string().describe(`主问题描述`),phases:t.array(t.object({id:t.string().describe(`阶段唯一标识`),title:t.string().describe(`自然语义化的阶段提问主题,例如"要看的具体是什么?"、"按照什么视角来查?";不要使用"确认问题"或"请选择"这类泛标题`),description:t.string().optional().describe(`阶段详细描述`),options:t.array(t.object({label:t.string().describe(`选项显示文本`),value:t.string().describe(`选项值`)})).optional().describe(`可选项列表`),multiple:t.boolean().optional().describe(`是否允许多选;默认 false`),selectionMode:t.enum([`single`,`multiple`]).optional().describe(`选择模式;默认 single`),allowFreeText:t.boolean().optional().describe(`是否允许自由文本输入`),required:t.boolean().optional().describe(`是否必填`)})).describe(`提问阶段列表`)},async e=>{g(`info`,`ask_user 被调用`,{question:e.question,phaseCount:e.phases.length});try{let t={question:e.question,phases:e.phases},n=await h.sendCallback(`ask_user`,t);g(`info`,`等待用户回答`,{requestId:n});let r=await h.pollForResult(n);return g(`info`,`用户已回答`,{requestId:n,answers:r}),{content:[{type:`text`,text:JSON.stringify(r)}]}}catch(e){let t=e instanceof Error?e.message:String(e);return g(`error`,`ask_user 执行失败`,{error:t}),{content:[{type:`text`,text:JSON.stringify({error:t})}],isError:!0}}}),v(r,`request_permission`)||e.tool(`request_permission`,`请求飞书 API 权限。发送权限申请后等待用户授权。`,{scopes:t.array(t.string()).describe(`需要的权限列表(如 "contact:user.base:readonly")`),reason:t.string().optional().describe(`申请权限的原因说明`),grantUrl:t.string().optional().describe(`可直接打开的权限申请链接`),grant_url:t.string().optional().describe(`可直接打开的权限申请链接(snake_case 兼容字段)`),consoleUrl:t.string().optional().describe(`开放平台权限配置页链接`),console_url:t.string().optional().describe(`开放平台权限配置页链接(snake_case 兼容字段)`)},async e=>{g(`info`,`request_permission 被调用`,{scopes:e.scopes,reason:e.reason});try{let t={scopes:e.scopes,reason:e.reason,grantUrl:e.grantUrl,grant_url:e.grant_url,consoleUrl:e.consoleUrl,console_url:e.console_url},n=await h.sendCallback(`request_permission`,t);g(`info`,`等待权限审批`,{requestId:n});let r=await h.pollForResult(n);return g(`info`,`权限审批完成`,{requestId:n,granted:r.granted}),{content:[{type:`text`,text:JSON.stringify(r)}]}}catch(e){let t=e instanceof Error?e.message:String(e);return g(`error`,`request_permission 执行失败`,{error:t}),{content:[{type:`text`,text:JSON.stringify({error:t})}],isError:!0}}}),v(r,`signal_no_reply`)||e.tool(`signal_no_reply`,`标记本次不需要回复用户。仅用于通知宿主层,立即返回。`,{reason:t.string().describe(`不回复的原因(仅用于日志记录)`)},async e=>(g(`info`,`signal_no_reply 被调用`,{reason:e.reason}),h.sendNotification(`signal_no_reply`,{reason:e.reason}).catch(e=>{g(`warn`,`signal_no_reply 通知发送失败(已忽略)`,{error:e instanceof Error?e.message:String(e)})}),{content:[{type:`text`,text:JSON.stringify({acknowledged:!0})}]})),e.tool(`memory_propose`,`提交一个长期记忆候选。宿主层会校验、去重、限额后决定是否接受。`,{kind:t.enum([`fact`,`preference`,`decision`,`recurring_context`]).describe(`记忆类型`),text:t.string().describe(`需要长期记住的简短事实或偏好`),confidence:t.number().min(0).max(1).optional().describe(`置信度 0-1,默认 0.7`),tags:t.array(t.string()).optional().describe(`用于检索的短标签`),ttlDays:t.number().positive().optional().describe(`可选过期天数`),sourceRefs:t.array(t.string()).optional().describe(`来源消息、任务或文档引用`),reason:t.string().optional().describe(`为什么值得记住`)},async e=>{try{let t=await h.sendSyncRequest(`memory_propose`,e);return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){let t=e instanceof Error?e.message:String(e);return g(`error`,`memory_propose 执行失败`,{error:t}),{content:[{type:`text`,text:JSON.stringify({accepted:!1,reasonCode:`error`,error:t})}],isError:!0}}}),e.tool(`memory_search`,`检索当前会话的长期记忆。`,{query:t.string().describe(`检索查询`),kinds:t.array(t.enum([`fact`,`preference`,`decision`,`recurring_context`])).optional().describe(`可选记忆类型过滤`),limit:t.number().int().positive().max(20).optional().describe(`返回条数,默认 8`)},async e=>{try{let t=await h.sendSyncRequest(`memory_search`,e);return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){let t=e instanceof Error?e.message:String(e);return g(`error`,`memory_search 执行失败`,{error:t}),{content:[{type:`text`,text:JSON.stringify({records:[],error:t})}],isError:!0}}}),e.tool(`scheduled_task_list`,`列出当前 Gateway 中已配置的定时 Agent Run。返回脱敏后的紧凑 JSON。`,{},async()=>b(`scheduled_task_list`,{})),e.tool(`scheduled_task_get`,`按 ID 查看一个定时 Agent Run 的脱敏详情。`,{id:t.string().describe(`定时任务 ID`)},async e=>b(`scheduled_task_get`,e)),e.tool(`scheduled_task_create`,`创建定时 Agent Run。使用 Gateway scheduler 的标准运行语义,需要 name、cron、session_id、cwd、prompt 和 owner identity。`,{name:t.string().describe(`定时任务名称`),cron:t.string().describe(`node-cron 表达式,如 "0 9 * * *"`),session_id:t.string().describe(`运行会话 ID`),conversation_id:t.string().optional().describe(`对话 ID;默认可与 session_id 相同`),cwd:t.string().describe(`运行工作目录`),prompt:t.string().describe(`定时触发时执行的 prompt`),identity:t.record(t.string(),t.unknown()).optional().describe(`owner identity,至少包含 tenantKey 和 userId/openId`),auth_headers:t.record(t.string(),t.string()).optional().describe(`运行时认证头;宿主层会在返回时脱敏`),runtime_instructions:t.unknown().optional().describe(`运行时指令注入`),runtime_skills:t.unknown().optional().describe(`运行时技能注入`),policy:t.record(t.string(),t.unknown()).optional().describe(`执行策略`),scenario_id:t.string().optional().describe(`场景 ID`),metadata:t.record(t.string(),t.unknown()).optional().describe(`元数据`)},async e=>b(`scheduled_task_create`,e)),e.tool(`scheduled_task_update`,`更新定时 Agent Run。updates 支持 Gateway scheduler 的可更新字段,如 name、cron、prompt、enabled、policy。`,{id:t.string().describe(`定时任务 ID`),updates:t.record(t.string(),t.unknown()).describe(`要更新的字段`)},async e=>b(`scheduled_task_update`,e)),e.tool(`scheduled_task_delete`,`删除定时 Agent Run。`,{id:t.string().describe(`定时任务 ID`)},async e=>b(`scheduled_task_delete`,e)),e.tool(`scheduled_task_run_now`,`立即执行一个定时 Agent Run,用于测试或手动触发。执行仍使用标准 scheduled-task runtime entrypoint。`,{id:t.string().describe(`定时任务 ID`)},async e=>b(`scheduled_task_run_now`,e))}async function b(e,t){try{let n=await h.sendSyncRequest(e,t);return{content:[{type:`text`,text:JSON.stringify(n)}]}}catch(t){let n=t instanceof Error?t.message:String(t);return g(`error`,`${e} 执行失败`,{error:n}),{content:[{type:`text`,text:JSON.stringify({error:n})}],isError:!0}}}function x(e,t,n){let r={timestamp:new Date().toISOString(),level:e,component:`mcp-server`,message:t,...n};process.stderr.write(JSON.stringify(r)+`
|
|
4
|
+
`)}async function S(){x(`info`,`MCP Server 启动`,{sessionId:process.env.LARKPAL_MCP_SESSION_ID||`<未设置>`,gatewayPort:process.env.LARKPAL_GATEWAY_PORT||`3000`,pid:process.pid});let e=new n({name:`larkpal`,version:`1.0.0`});y(e);let t=new r;await e.connect(t),x(`info`,`MCP Server 已连接,等待 tool 调用`),process.stdin.on(`close`,()=>{x(`info`,`stdin 已关闭,MCP Server 准备退出`),process.exit(0)}),process.on(`SIGTERM`,()=>{x(`info`,`收到 SIGTERM,MCP Server 退出`),process.exit(0)}),process.on(`SIGINT`,()=>{x(`info`,`收到 SIGINT,MCP Server 退出`),process.exit(0)}),process.on(`uncaughtException`,e=>{x(`error`,`未捕获的异常`,{error:e.message,stack:e.stack}),process.exit(1)}),process.on(`unhandledRejection`,e=>{x(`error`,`未处理的 Promise rejection`,{reason:e instanceof Error?e.message:String(e)}),process.exit(1)})}S().catch(e=>{x(`error`,`MCP Server 启动失败`,{error:e instanceof Error?e.message:String(e),stack:e instanceof Error?e.stack:void 0}),process.exit(1)});export{};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as e}from"./ids-BPsM98Uu-CJBJAeVC.mjs";import{Lt as t}from"./bundled-compat-BpaDaAAu-DJtrIX6i.mjs";import{n,t as r}from"./message-channel-core-DTA--Gjh-CIh10vfo.mjs";const i={WEBCHAT_UI:`webchat-ui`,CONTROL_UI:`openclaw-control-ui`,TUI:`openclaw-tui`,WEBCHAT:`webchat`,CLI:`cli`,GATEWAY_CLIENT:`gateway-client`,MACOS_APP:`openclaw-macos`,IOS_APP:`openclaw-ios`,ANDROID_APP:`openclaw-android`,NODE_HOST:`node-host`,TEST:`test`,FINGERPRINT:`fingerprint`,PROBE:`openclaw-probe`},a=i,o={WEBCHAT:`webchat`,CLI:`cli`,UI:`ui`,BACKEND:`backend`,NODE:`node`,PROBE:`probe`,TEST:`test`};new Set(Object.values(i)),new Set(Object.values(o));function s(e){return n(e)}const c=()=>t(),l=()=>Array.from(new Set([...e,...c()])),u=()=>[...l(),r];function d(e){return u().includes(e)}function f(e){return l().includes(e)}function p(e){return s(e)===r}export{d as a,s as c,f as i,o as n,p as o,a as r,l as s,i as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{s as e}from"./home-dir-C44RaPME-B5iLWwCq.mjs";import{i as t}from"./ids-BPsM98Uu-CJBJAeVC.mjs";import{t as n}from"./runtime-channel-state-CohMybAh-BBzhl07Q.mjs";const r=`webchat`;function i(){let e=n();return e?.channels&&e.channels.length>0?e.channels:[]}function a(t){let n=e(t);return n?i().find(t=>{let r=e(t.plugin.id??``)??``;return r&&r===n?!0:(t.plugin.meta?.aliases??[]).some(t=>e(t)===n)})?.plugin.id??null:null}function o(n){let i=e(n);return i?i===`webchat`?r:t(i)||(a(i)??i):void 0}export{o as n,r as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e}from"./io-CH2g3vsv-JJtUIa_U.mjs";export{e as loadConfig};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e,r as t}from"./call-DimWbpgJ-B4pv55RH.mjs";export{e as callGatewayLeastPrivilege,t as randomIdempotencyKey};
|