@lark-project/openclaw-lark-project 2026.3.176 → 2026.3.178
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/src/core/project-token-store.js +11 -11
- package/dist/src/core/project-token-store.js.map +2 -2
- package/dist/src/core/token-store.js.map +2 -2
- package/dist/src/tools/mcp/project/tools.js +27 -3
- package/dist/src/tools/mcp/project/tools.js.map +2 -2
- package/dist/src/tools/oauth-cards.js +23 -4
- package/dist/src/tools/oauth-cards.js.map +2 -2
- package/dist/src/tools/project-oauth.js +55 -31
- package/dist/src/tools/project-oauth.js.map +2 -2
- package/package.json +1 -1
- package/skills/feishu-project/SKILL.md +31 -21
- package/skills/feishu-project/references/{moql-syntax.md → mql-syntax.md} +6 -6
|
@@ -131,13 +131,13 @@ async function setProjectStoredToken(token) {
|
|
|
131
131
|
const key = accountKey(token.appId, token.userOpenId);
|
|
132
132
|
const payload = JSON.stringify(token);
|
|
133
133
|
await backend.set(PROJECT_SERVICE, key, payload);
|
|
134
|
-
await saveClientIdIndex(token.userOpenId, token.appId);
|
|
135
|
-
log.info(`saved project UAT for ${token.userOpenId} (at:${maskToken(token.accessToken)})`);
|
|
134
|
+
await saveClientIdIndex(token.userOpenId, token.appId, token.domain);
|
|
135
|
+
log.info(`saved project UAT for ${token.userOpenId} domain=${token.domain} (at:${maskToken(token.accessToken)})`);
|
|
136
136
|
}
|
|
137
|
-
async function removeProjectStoredToken(appId, userOpenId) {
|
|
137
|
+
async function removeProjectStoredToken(appId, userOpenId, domain) {
|
|
138
138
|
await backend.remove(PROJECT_SERVICE, accountKey(appId, userOpenId));
|
|
139
|
-
await removeClientIdIndex(userOpenId);
|
|
140
|
-
log.info(`removed project UAT for ${userOpenId}`);
|
|
139
|
+
await removeClientIdIndex(userOpenId, domain);
|
|
140
|
+
log.info(`removed project UAT for ${userOpenId} domain=${domain}`);
|
|
141
141
|
}
|
|
142
142
|
function projectTokenStatus(token) {
|
|
143
143
|
const now = Date.now();
|
|
@@ -146,19 +146,19 @@ function projectTokenStatus(token) {
|
|
|
146
146
|
return "expired";
|
|
147
147
|
}
|
|
148
148
|
const CLIENT_ID_SERVICE = "openclaw-feishu-project-cid";
|
|
149
|
-
async function getProjectClientId(userOpenId) {
|
|
149
|
+
async function getProjectClientId(userOpenId, domain) {
|
|
150
150
|
try {
|
|
151
|
-
return await backend.get(CLIENT_ID_SERVICE, userOpenId);
|
|
151
|
+
return await backend.get(CLIENT_ID_SERVICE, `${domain}:${userOpenId}`);
|
|
152
152
|
} catch {
|
|
153
153
|
return null;
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
|
-
async function saveClientIdIndex(userOpenId, clientId) {
|
|
157
|
-
await backend.set(CLIENT_ID_SERVICE, userOpenId
|
|
156
|
+
async function saveClientIdIndex(userOpenId, clientId, domain) {
|
|
157
|
+
await backend.set(CLIENT_ID_SERVICE, `${domain}:${userOpenId}`, clientId);
|
|
158
158
|
}
|
|
159
|
-
async function removeClientIdIndex(userOpenId) {
|
|
159
|
+
async function removeClientIdIndex(userOpenId, domain) {
|
|
160
160
|
try {
|
|
161
|
-
await backend.remove(CLIENT_ID_SERVICE, userOpenId);
|
|
161
|
+
await backend.remove(CLIENT_ID_SERVICE, `${domain}:${userOpenId}`);
|
|
162
162
|
} catch {
|
|
163
163
|
}
|
|
164
164
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/core/project-token-store.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * \u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u4E13\u7528 Token \u5B58\u50A8\n *\n * \u590D\u7528 token-store \u7684 StoredUAToken \u7ED3\u6784\u548C\u8DE8\u5E73\u53F0\u540E\u7AEF\uFF0C\n * \u4F46\u4F7F\u7528\u72EC\u7ACB\u7684 service key\uFF0C\u4E0E\u98DE\u4E66 Open API \u7684 token \u5206\u5F00\u5B58\u50A8\u3002\n */\n\nimport { execFile as execFileCb } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { mkdir, unlink, readFile, writeFile, chmod } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { randomBytes, createCipheriv, createDecipheriv } from 'node:crypto';\nimport { larkLogger } from './lark-logger';\nimport { maskToken, type StoredUAToken } from './token-store';\n\nconst log = larkLogger('core/project-token-store');\nconst execFile = promisify(execFileCb);\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst PROJECT_SERVICE = 'openclaw-feishu-project-uat';\nconst REFRESH_AHEAD_MS = 5 * 60 * 1000;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction accountKey(appId: string, userOpenId: string): string {\n return `${appId}:${userOpenId}`;\n}\n\n// ---------------------------------------------------------------------------\n// Backend interface (same as token-store.ts)\n// ---------------------------------------------------------------------------\n\ninterface KeychainBackend {\n get(service: string, account: string): Promise<string | null>;\n set(service: string, account: string, data: string): Promise<void>;\n remove(service: string, account: string): Promise<void>;\n}\n\n// macOS\nconst darwinBackend: KeychainBackend = {\n async get(service, account) {\n try {\n const { stdout } = await execFile('security', ['find-generic-password', '-s', service, '-a', account, '-w']);\n return stdout.trim() || null;\n } catch {\n return null;\n }\n },\n async set(service, account, data) {\n try {\n await execFile('security', ['delete-generic-password', '-s', service, '-a', account]);\n } catch { /* Not found */ }\n await execFile('security', ['add-generic-password', '-s', service, '-a', account, '-w', data]);\n },\n async remove(service, account) {\n try {\n await execFile('security', ['delete-generic-password', '-s', service, '-a', account]);\n } catch { /* Already absent */ }\n },\n};\n\n// Linux / Windows \u2014 AES-256-GCM encrypted files\nconst MASTER_KEY_BYTES = 32;\nconst IV_BYTES = 12;\nconst TAG_BYTES = 16;\n\nfunction getStorageDir(): string {\n if (process.platform === 'win32') {\n return join(\n process.env.LOCALAPPDATA ?? join(process.env.USERPROFILE ?? homedir(), 'AppData', 'Local'),\n PROJECT_SERVICE,\n );\n }\n return join(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), PROJECT_SERVICE);\n}\n\nconst STORAGE_DIR = getStorageDir();\nconst MASTER_KEY_PATH = join(STORAGE_DIR, 'master.key');\n\nfunction safeFileName(account: string): string {\n return account.replace(/[^a-zA-Z0-9._-]/g, '_') + '.enc';\n}\n\nasync function ensureDir(): Promise<void> {\n await mkdir(STORAGE_DIR, { recursive: true, mode: 0o700 });\n}\n\nasync function getMasterKey(): Promise<Buffer> {\n try {\n const key = await readFile(MASTER_KEY_PATH);\n if (key.length === MASTER_KEY_BYTES) return key;\n } catch (err: unknown) {\n if (!(err instanceof Error) || (err as NodeJS.ErrnoException).code !== 'ENOENT') {\n log.warn(`failed to read project master key: ${err instanceof Error ? err.message : err}`);\n }\n }\n await ensureDir();\n const key = randomBytes(MASTER_KEY_BYTES);\n await writeFile(MASTER_KEY_PATH, key, { mode: 0o600 });\n await chmod(MASTER_KEY_PATH, 0o600);\n return key;\n}\n\nfunction encrypt(plaintext: string, key: Buffer): Buffer {\n const iv = randomBytes(IV_BYTES);\n const cipher = createCipheriv('aes-256-gcm', key, iv);\n const enc = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);\n return Buffer.concat([iv, cipher.getAuthTag(), enc]);\n}\n\nfunction decrypt(data: Buffer, key: Buffer): string | null {\n if (data.length < IV_BYTES + TAG_BYTES) return null;\n try {\n const iv = data.subarray(0, IV_BYTES);\n const tag = data.subarray(IV_BYTES, IV_BYTES + TAG_BYTES);\n const enc = data.subarray(IV_BYTES + TAG_BYTES);\n const decipher = createDecipheriv('aes-256-gcm', key, iv);\n decipher.setAuthTag(tag);\n return Buffer.concat([decipher.update(enc), decipher.final()]).toString('utf8');\n } catch {\n return null;\n }\n}\n\nconst fileBackend: KeychainBackend = {\n async get(_service, account) {\n try {\n const key = await getMasterKey();\n const data = await readFile(join(STORAGE_DIR, safeFileName(account)));\n return decrypt(data, key);\n } catch {\n return null;\n }\n },\n async set(_service, account, data) {\n const key = await getMasterKey();\n await ensureDir();\n const filePath = join(STORAGE_DIR, safeFileName(account));\n const encrypted = encrypt(data, key);\n await writeFile(filePath, encrypted, { mode: 0o600 });\n },\n async remove(_service, account) {\n try {\n await unlink(join(STORAGE_DIR, safeFileName(account)));\n } catch { /* Already absent */ }\n },\n};\n\nfunction createBackend(): KeychainBackend {\n return process.platform === 'darwin' ? darwinBackend : fileBackend;\n}\n\nconst backend = createBackend();\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport async function getProjectStoredToken(appId: string, userOpenId: string): Promise<StoredUAToken | null> {\n try {\n const json = await backend.get(PROJECT_SERVICE, accountKey(appId, userOpenId));\n if (!json) return null;\n return JSON.parse(json) as StoredUAToken;\n } catch {\n return null;\n }\n}\n\nexport async function setProjectStoredToken(token: StoredUAToken): Promise<void> {\n const key = accountKey(token.appId, token.userOpenId);\n const payload = JSON.stringify(token);\n await backend.set(PROJECT_SERVICE, key, payload);\n await saveClientIdIndex(token.userOpenId, token.appId);\n log.info(`saved project UAT for ${token.userOpenId} (at:${maskToken(token.accessToken)})`);\n}\n\nexport async function removeProjectStoredToken(appId: string, userOpenId: string): Promise<void> {\n await backend.remove(PROJECT_SERVICE, accountKey(appId, userOpenId));\n await removeClientIdIndex(userOpenId);\n log.info(`removed project UAT for ${userOpenId}`);\n}\n\nexport function projectTokenStatus(token: StoredUAToken): 'valid' | 'needs_refresh' | 'expired' {\n const now = Date.now();\n if (now < token.expiresAt - REFRESH_AHEAD_MS) return 'valid';\n if (now < token.refreshExpiresAt) return 'needs_refresh';\n return 'expired';\n}\n\n// ---------------------------------------------------------------------------\n// clientId \u7D22\u5F15 \u2014 \u4ECE userOpenId \u53CD\u67E5\u52A8\u6001\u6CE8\u518C\u7684 clientId\n// ---------------------------------------------------------------------------\n\nconst CLIENT_ID_SERVICE = 'openclaw-feishu-project-cid';\n\n/**\n * \u83B7\u53D6\u7528\u6237\u5BF9\u5E94\u7684\u98DE\u4E66\u9879\u76EE OAuth clientId\u3002\n * \u52A8\u6001\u6CE8\u518C\u7684 clientId \u5728\u6BCF\u6B21\u6CE8\u518C\u65F6\u53EF\u80FD\u4E0D\u540C\uFF0C\u6B64\u7D22\u5F15\u7528\u4E8E status/revoke \u7B49\n * \u4E0D\u77E5\u9053 clientId \u7684\u573A\u666F\u3002\n */\nexport async function getProjectClientId(userOpenId: string): Promise<string | null> {\n try {\n return await backend.get(CLIENT_ID_SERVICE, userOpenId);\n } catch {\n return null;\n }\n}\n\
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * \u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u4E13\u7528 Token \u5B58\u50A8\n *\n * \u590D\u7528 token-store \u7684 StoredUAToken \u7ED3\u6784\u548C\u8DE8\u5E73\u53F0\u540E\u7AEF\uFF0C\n * \u4F46\u4F7F\u7528\u72EC\u7ACB\u7684 service key\uFF0C\u4E0E\u98DE\u4E66 Open API \u7684 token \u5206\u5F00\u5B58\u50A8\u3002\n */\n\nimport { execFile as execFileCb } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { mkdir, unlink, readFile, writeFile, chmod } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { randomBytes, createCipheriv, createDecipheriv } from 'node:crypto';\nimport { larkLogger } from './lark-logger';\nimport { maskToken, type StoredUAToken } from './token-store';\n\nconst log = larkLogger('core/project-token-store');\nconst execFile = promisify(execFileCb);\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst PROJECT_SERVICE = 'openclaw-feishu-project-uat';\nconst REFRESH_AHEAD_MS = 5 * 60 * 1000;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction accountKey(appId: string, userOpenId: string): string {\n return `${appId}:${userOpenId}`;\n}\n\n// ---------------------------------------------------------------------------\n// Backend interface (same as token-store.ts)\n// ---------------------------------------------------------------------------\n\ninterface KeychainBackend {\n get(service: string, account: string): Promise<string | null>;\n set(service: string, account: string, data: string): Promise<void>;\n remove(service: string, account: string): Promise<void>;\n}\n\n// macOS\nconst darwinBackend: KeychainBackend = {\n async get(service, account) {\n try {\n const { stdout } = await execFile('security', ['find-generic-password', '-s', service, '-a', account, '-w']);\n return stdout.trim() || null;\n } catch {\n return null;\n }\n },\n async set(service, account, data) {\n try {\n await execFile('security', ['delete-generic-password', '-s', service, '-a', account]);\n } catch { /* Not found */ }\n await execFile('security', ['add-generic-password', '-s', service, '-a', account, '-w', data]);\n },\n async remove(service, account) {\n try {\n await execFile('security', ['delete-generic-password', '-s', service, '-a', account]);\n } catch { /* Already absent */ }\n },\n};\n\n// Linux / Windows \u2014 AES-256-GCM encrypted files\nconst MASTER_KEY_BYTES = 32;\nconst IV_BYTES = 12;\nconst TAG_BYTES = 16;\n\nfunction getStorageDir(): string {\n if (process.platform === 'win32') {\n return join(\n process.env.LOCALAPPDATA ?? join(process.env.USERPROFILE ?? homedir(), 'AppData', 'Local'),\n PROJECT_SERVICE,\n );\n }\n return join(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), PROJECT_SERVICE);\n}\n\nconst STORAGE_DIR = getStorageDir();\nconst MASTER_KEY_PATH = join(STORAGE_DIR, 'master.key');\n\nfunction safeFileName(account: string): string {\n return account.replace(/[^a-zA-Z0-9._-]/g, '_') + '.enc';\n}\n\nasync function ensureDir(): Promise<void> {\n await mkdir(STORAGE_DIR, { recursive: true, mode: 0o700 });\n}\n\nasync function getMasterKey(): Promise<Buffer> {\n try {\n const key = await readFile(MASTER_KEY_PATH);\n if (key.length === MASTER_KEY_BYTES) return key;\n } catch (err: unknown) {\n if (!(err instanceof Error) || (err as NodeJS.ErrnoException).code !== 'ENOENT') {\n log.warn(`failed to read project master key: ${err instanceof Error ? err.message : err}`);\n }\n }\n await ensureDir();\n const key = randomBytes(MASTER_KEY_BYTES);\n await writeFile(MASTER_KEY_PATH, key, { mode: 0o600 });\n await chmod(MASTER_KEY_PATH, 0o600);\n return key;\n}\n\nfunction encrypt(plaintext: string, key: Buffer): Buffer {\n const iv = randomBytes(IV_BYTES);\n const cipher = createCipheriv('aes-256-gcm', key, iv);\n const enc = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);\n return Buffer.concat([iv, cipher.getAuthTag(), enc]);\n}\n\nfunction decrypt(data: Buffer, key: Buffer): string | null {\n if (data.length < IV_BYTES + TAG_BYTES) return null;\n try {\n const iv = data.subarray(0, IV_BYTES);\n const tag = data.subarray(IV_BYTES, IV_BYTES + TAG_BYTES);\n const enc = data.subarray(IV_BYTES + TAG_BYTES);\n const decipher = createDecipheriv('aes-256-gcm', key, iv);\n decipher.setAuthTag(tag);\n return Buffer.concat([decipher.update(enc), decipher.final()]).toString('utf8');\n } catch {\n return null;\n }\n}\n\nconst fileBackend: KeychainBackend = {\n async get(_service, account) {\n try {\n const key = await getMasterKey();\n const data = await readFile(join(STORAGE_DIR, safeFileName(account)));\n return decrypt(data, key);\n } catch {\n return null;\n }\n },\n async set(_service, account, data) {\n const key = await getMasterKey();\n await ensureDir();\n const filePath = join(STORAGE_DIR, safeFileName(account));\n const encrypted = encrypt(data, key);\n await writeFile(filePath, encrypted, { mode: 0o600 });\n },\n async remove(_service, account) {\n try {\n await unlink(join(STORAGE_DIR, safeFileName(account)));\n } catch { /* Already absent */ }\n },\n};\n\nfunction createBackend(): KeychainBackend {\n return process.platform === 'darwin' ? darwinBackend : fileBackend;\n}\n\nconst backend = createBackend();\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport async function getProjectStoredToken(appId: string, userOpenId: string): Promise<StoredUAToken | null> {\n try {\n const json = await backend.get(PROJECT_SERVICE, accountKey(appId, userOpenId));\n if (!json) return null;\n return JSON.parse(json) as StoredUAToken;\n } catch {\n return null;\n }\n}\n\nexport async function setProjectStoredToken(token: StoredUAToken & { domain: string }): Promise<void> {\n const key = accountKey(token.appId, token.userOpenId);\n const payload = JSON.stringify(token);\n await backend.set(PROJECT_SERVICE, key, payload);\n await saveClientIdIndex(token.userOpenId, token.appId, token.domain);\n log.info(`saved project UAT for ${token.userOpenId} domain=${token.domain} (at:${maskToken(token.accessToken)})`);\n}\n\nexport async function removeProjectStoredToken(appId: string, userOpenId: string, domain: string): Promise<void> {\n await backend.remove(PROJECT_SERVICE, accountKey(appId, userOpenId));\n await removeClientIdIndex(userOpenId, domain);\n log.info(`removed project UAT for ${userOpenId} domain=${domain}`);\n}\n\nexport function projectTokenStatus(token: StoredUAToken): 'valid' | 'needs_refresh' | 'expired' {\n const now = Date.now();\n if (now < token.expiresAt - REFRESH_AHEAD_MS) return 'valid';\n if (now < token.refreshExpiresAt) return 'needs_refresh';\n return 'expired';\n}\n\n// ---------------------------------------------------------------------------\n// clientId \u7D22\u5F15 \u2014 \u4ECE userOpenId \u53CD\u67E5\u52A8\u6001\u6CE8\u518C\u7684 clientId\n// ---------------------------------------------------------------------------\n\nconst CLIENT_ID_SERVICE = 'openclaw-feishu-project-cid';\n\n/**\n * \u83B7\u53D6\u7528\u6237\u5BF9\u5E94\u7684\u98DE\u4E66\u9879\u76EE OAuth clientId\u3002\n * \u52A8\u6001\u6CE8\u518C\u7684 clientId \u5728\u6BCF\u6B21\u6CE8\u518C\u65F6\u53EF\u80FD\u4E0D\u540C\uFF0C\u6B64\u7D22\u5F15\u7528\u4E8E status/revoke \u7B49\n * \u4E0D\u77E5\u9053 clientId \u7684\u573A\u666F\u3002\n */\nexport async function getProjectClientId(userOpenId: string, domain: string): Promise<string | null> {\n try {\n return await backend.get(CLIENT_ID_SERVICE, `${domain}:${userOpenId}`);\n } catch {\n return null;\n }\n}\n\nasync function saveClientIdIndex(userOpenId: string, clientId: string, domain: string): Promise<void> {\n await backend.set(CLIENT_ID_SERVICE, `${domain}:${userOpenId}`, clientId);\n}\n\nasync function removeClientIdIndex(userOpenId: string, domain: string): Promise<void> {\n try {\n await backend.remove(CLIENT_ID_SERVICE, `${domain}:${userOpenId}`);\n } catch { /* Already absent */ }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["key"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/core/token-store.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * UAT (User Access Token) persistent storage with cross-platform support.\n *\n * Stores OAuth token data using OS-native credential services so that tokens\n * survive process restarts without introducing plain-text local files.\n *\n * Platform backends:\n * macOS \u2013 Keychain Access via `security` CLI\n * Linux \u2013 AES-256-GCM encrypted files (XDG_DATA_HOME)\n * Windows \u2013 AES-256-GCM encrypted files (%LOCALAPPDATA%)\n *\n * Storage layout:\n * Service = \"openclaw-feishu-uat\"\n * Account = \"{appId}:{userOpenId}\"\n * Password = JSON-serialised StoredUAToken\n */\n\nimport { execFile as execFileCb } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { mkdir, unlink, readFile, writeFile, chmod } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { randomBytes, createCipheriv, createDecipheriv } from 'node:crypto';\nimport { larkLogger } from './lark-logger';\n\nconst log = larkLogger('core/token-store');\n\nconst execFile = promisify(execFileCb);\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface StoredUAToken {\n userOpenId: string;\n appId: string;\n accessToken: string;\n refreshToken: string;\n expiresAt: number; // Unix ms \u2013 access_token expiry\n refreshExpiresAt: number; // Unix ms \u2013 refresh_token expiry\n scope: string;\n grantedAt: number; // Unix ms \u2013 original grant time\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst KEYCHAIN_SERVICE = 'openclaw-feishu-uat';\n\n/** Refresh proactively when access_token expires within this window. */\nconst REFRESH_AHEAD_MS = 5 * 60 * 1000; // 5 minutes\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction accountKey(appId: string, userOpenId: string): string {\n return `${appId}:${userOpenId}`;\n}\n\n/** Mask a token for safe logging: only the last 4 chars are visible. */\nexport function maskToken(token: string): string {\n if (token.length <= 8) return '****';\n return `****${token.slice(-4)}`;\n}\n\n// ---------------------------------------------------------------------------\n// Backend interface\n// ---------------------------------------------------------------------------\n\ninterface KeychainBackend {\n get(service: string, account: string): Promise<string | null>;\n set(service: string, account: string, data: string): Promise<void>;\n remove(service: string, account: string): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// macOS backend \u2013 Keychain Access via `security` CLI\n// ---------------------------------------------------------------------------\n\nconst darwinBackend: KeychainBackend = {\n async get(service, account) {\n try {\n const { stdout } = await execFile('security', ['find-generic-password', '-s', service, '-a', account, '-w']);\n return stdout.trim() || null;\n } catch {\n return null;\n }\n },\n\n async set(service, account, data) {\n // Delete first \u2013 `add-generic-password` fails if the item already exists.\n try {\n await execFile('security', ['delete-generic-password', '-s', service, '-a', account]);\n } catch {\n // Not found \u2013 fine.\n }\n await execFile('security', ['add-generic-password', '-s', service, '-a', account, '-w', data]);\n },\n\n async remove(service, account) {\n try {\n await execFile('security', ['delete-generic-password', '-s', service, '-a', account]);\n } catch {\n // Already absent \u2013 fine.\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// Linux backend \u2013 AES-256-GCM encrypted files (XDG Base Directory)\n//\n// Headless Linux servers typically lack D-Bus / GNOME Keyring, so we store\n// tokens as AES-256-GCM encrypted files instead of using `secret-tool`.\n//\n// Storage path: ${XDG_DATA_HOME:-~/.local/share}/openclaw-feishu-uat/\n// ---------------------------------------------------------------------------\n\nconst LINUX_UAT_DIR = join(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), 'openclaw-feishu-uat');\nconst MASTER_KEY_PATH = join(LINUX_UAT_DIR, 'master.key');\nconst MASTER_KEY_BYTES = 32; // AES-256\nconst IV_BYTES = 12; // GCM recommended\nconst TAG_BYTES = 16; // GCM auth tag\n\n/** Convert account key to a filesystem-safe filename. */\nfunction linuxSafeFileName(account: string): string {\n return account.replace(/[^a-zA-Z0-9._-]/g, '_') + '.enc';\n}\n\n/** Ensure the credentials directory exists with mode 0700. */\nasync function ensureLinuxCredDir(): Promise<void> {\n await mkdir(LINUX_UAT_DIR, { recursive: true, mode: 0o700 });\n}\n\n/**\n * Load or create the 32-byte master key.\n *\n * On first run, generates a random key and writes it to disk (mode 0600).\n * On subsequent runs, reads the existing key file.\n */\nasync function getMasterKey(): Promise<Buffer> {\n try {\n const key = await readFile(MASTER_KEY_PATH);\n if (key.length === MASTER_KEY_BYTES) return key;\n log.warn('master key has unexpected length, regenerating');\n } catch (err: unknown) {\n if (!(err instanceof Error) || (err as NodeJS.ErrnoException).code !== 'ENOENT') {\n log.warn(`failed to read master key: ${err instanceof Error ? err.message : err}`);\n }\n }\n\n await ensureLinuxCredDir();\n const key = randomBytes(MASTER_KEY_BYTES);\n await writeFile(MASTER_KEY_PATH, key, { mode: 0o600 });\n await chmod(MASTER_KEY_PATH, 0o600);\n log.info('generated new master key for encrypted file storage');\n return key;\n}\n\n/** AES-256-GCM encrypt. Returns [12-byte IV][16-byte tag][ciphertext]. */\nfunction encryptData(plaintext: string, key: Buffer): Buffer {\n const iv = randomBytes(IV_BYTES);\n const cipher = createCipheriv('aes-256-gcm', key, iv);\n const enc = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);\n return Buffer.concat([iv, cipher.getAuthTag(), enc]);\n}\n\n/** AES-256-GCM decrypt. Returns plaintext or `null` on failure. */\nfunction decryptData(data: Buffer, key: Buffer): string | null {\n if (data.length < IV_BYTES + TAG_BYTES) return null;\n try {\n const iv = data.subarray(0, IV_BYTES);\n const tag = data.subarray(IV_BYTES, IV_BYTES + TAG_BYTES);\n const enc = data.subarray(IV_BYTES + TAG_BYTES);\n const decipher = createDecipheriv('aes-256-gcm', key, iv);\n decipher.setAuthTag(tag);\n return Buffer.concat([decipher.update(enc), decipher.final()]).toString('utf8');\n } catch {\n return null;\n }\n}\n\nconst linuxBackend: KeychainBackend = {\n async get(_service, account) {\n try {\n const key = await getMasterKey();\n const data = await readFile(join(LINUX_UAT_DIR, linuxSafeFileName(account)));\n return decryptData(data, key);\n } catch {\n return null;\n }\n },\n\n async set(_service, account, data) {\n const key = await getMasterKey();\n await ensureLinuxCredDir();\n const filePath = join(LINUX_UAT_DIR, linuxSafeFileName(account));\n const encrypted = encryptData(data, key);\n await writeFile(filePath, encrypted, { mode: 0o600 });\n await chmod(filePath, 0o600);\n },\n\n async remove(_service, account) {\n try {\n await unlink(join(LINUX_UAT_DIR, linuxSafeFileName(account)));\n } catch {\n // Already absent \u2013 fine.\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// Windows backend \u2013 AES-256-GCM encrypted files\n//\n// Replaces the previous DPAPI-via-PowerShell approach which was unreliable\n// (PowerShell cold-start latency, execution policy restrictions, cmd.exe\n// command-line length limits, and unavailability in containers).\n//\n// Uses the same AES-256-GCM scheme as the Linux backend with its own\n// independent storage directory and master key.\n//\n// Storage path: %LOCALAPPDATA%\\openclaw-feishu-uat\\\n// ---------------------------------------------------------------------------\n\nconst WIN32_UAT_DIR = join(\n process.env.LOCALAPPDATA ?? join(process.env.USERPROFILE ?? homedir(), 'AppData', 'Local'),\n KEYCHAIN_SERVICE,\n);\nconst WIN32_MASTER_KEY_PATH = join(WIN32_UAT_DIR, 'master.key');\n\n/** Convert account key to a filesystem-safe filename (whitelist approach). */\nfunction win32SafeFileName(account: string): string {\n return account.replace(/[^a-zA-Z0-9._-]/g, '_') + '.enc';\n}\n\nasync function ensureWin32CredDir(): Promise<void> {\n await mkdir(WIN32_UAT_DIR, { recursive: true });\n}\n\nasync function getWin32MasterKey(): Promise<Buffer> {\n try {\n const key = await readFile(WIN32_MASTER_KEY_PATH);\n if (key.length === MASTER_KEY_BYTES) return key;\n log.warn('win32 master key has unexpected length, regenerating');\n } catch (err: unknown) {\n if (!(err instanceof Error) || (err as NodeJS.ErrnoException).code !== 'ENOENT') {\n log.warn(`failed to read win32 master key: ${err instanceof Error ? err.message : err}`);\n }\n }\n\n await ensureWin32CredDir();\n const key = randomBytes(MASTER_KEY_BYTES);\n await writeFile(WIN32_MASTER_KEY_PATH, key);\n log.info('generated new master key for win32 encrypted file storage');\n return key;\n}\n\nconst win32Backend: KeychainBackend = {\n async get(_service, account) {\n try {\n const key = await getWin32MasterKey();\n const data = await readFile(join(WIN32_UAT_DIR, win32SafeFileName(account)));\n return decryptData(data, key);\n } catch {\n return null;\n }\n },\n\n async set(_service, account, data) {\n const key = await getWin32MasterKey();\n await ensureWin32CredDir();\n const filePath = join(WIN32_UAT_DIR, win32SafeFileName(account));\n const encrypted = encryptData(data, key);\n await writeFile(filePath, encrypted);\n },\n\n async remove(_service, account) {\n try {\n await unlink(join(WIN32_UAT_DIR, win32SafeFileName(account)));\n } catch {\n // Already absent \u2013 fine.\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// Platform selection\n// ---------------------------------------------------------------------------\n\nfunction createBackend(): KeychainBackend {\n switch (process.platform) {\n case 'darwin':\n return darwinBackend;\n case 'linux':\n return linuxBackend;\n case 'win32':\n return win32Backend;\n default:\n log.warn(`unsupported platform \"${process.platform}\", falling back to macOS backend`);\n return darwinBackend;\n }\n}\n\nconst backend = createBackend();\n\n// ---------------------------------------------------------------------------\n// Public API \u2013 Credential operations\n// ---------------------------------------------------------------------------\n\n/**\n * Read the stored UAT for a given (appId, userOpenId) pair.\n * Returns `null` when no entry exists or the payload is unparseable.\n */\nexport async function getStoredToken(appId: string, userOpenId: string): Promise<StoredUAToken | null> {\n try {\n const json = await backend.get(KEYCHAIN_SERVICE, accountKey(appId, userOpenId));\n if (!json) return null;\n return JSON.parse(json) as StoredUAToken;\n } catch {\n return null;\n }\n}\n\n/**\n * Persist a UAT using the platform credential store.\n *\n * Overwrites any existing entry for the same (appId, userOpenId).\n */\nexport async function setStoredToken(token: StoredUAToken): Promise<void> {\n const key = accountKey(token.appId, token.userOpenId);\n const payload = JSON.stringify(token);\n await backend.set(KEYCHAIN_SERVICE, key, payload);\n log.info(`saved UAT for ${token.userOpenId} (at:${maskToken(token.accessToken)})`);\n}\n\n/**\n * Remove a stored UAT from the credential store.\n */\nexport async function removeStoredToken(appId: string, userOpenId: string): Promise<void> {\n await backend.remove(KEYCHAIN_SERVICE, accountKey(appId, userOpenId));\n log.info(`removed UAT for ${userOpenId}`);\n}\n\n// ---------------------------------------------------------------------------\n// Token validity check\n// ---------------------------------------------------------------------------\n\n/**\n * Determine the freshness of a stored token.\n *\n * - `\"valid\"` \u2013 access_token is still good (expires > 5 min from now)\n * - `\"needs_refresh\"` \u2013 access_token expired/expiring but refresh_token is valid\n * - `\"expired\"` \u2013 both tokens are expired; re-authorization required\n */\nexport function tokenStatus(token: StoredUAToken): 'valid' | 'needs_refresh' | 'expired' {\n const now = Date.now();\n if (now < token.expiresAt - REFRESH_AHEAD_MS) {\n return 'valid';\n }\n if (now < token.refreshExpiresAt) {\n return 'needs_refresh';\n }\n return 'expired';\n}\n"],
|
|
5
|
-
"mappings": "AAoBA,SAAS,YAAY,kBAAkB;AACvC,SAAS,iBAAiB;AAC1B,SAAS,OAAO,QAAQ,UAAU,WAAW,aAAa;AAC1D,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,aAAa,gBAAgB,wBAAwB;AAC9D,SAAS,kBAAkB;AAE3B,MAAM,MAAM,WAAW,kBAAkB;AAEzC,MAAM,WAAW,UAAU,UAAU;
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * UAT (User Access Token) persistent storage with cross-platform support.\n *\n * Stores OAuth token data using OS-native credential services so that tokens\n * survive process restarts without introducing plain-text local files.\n *\n * Platform backends:\n * macOS \u2013 Keychain Access via `security` CLI\n * Linux \u2013 AES-256-GCM encrypted files (XDG_DATA_HOME)\n * Windows \u2013 AES-256-GCM encrypted files (%LOCALAPPDATA%)\n *\n * Storage layout:\n * Service = \"openclaw-feishu-uat\"\n * Account = \"{appId}:{userOpenId}\"\n * Password = JSON-serialised StoredUAToken\n */\n\nimport { execFile as execFileCb } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { mkdir, unlink, readFile, writeFile, chmod } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { randomBytes, createCipheriv, createDecipheriv } from 'node:crypto';\nimport { larkLogger } from './lark-logger';\n\nconst log = larkLogger('core/token-store');\n\nconst execFile = promisify(execFileCb);\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface StoredUAToken {\n userOpenId: string;\n appId: string;\n accessToken: string;\n refreshToken: string;\n expiresAt: number; // Unix ms \u2013 access_token expiry\n refreshExpiresAt: number; // Unix ms \u2013 refresh_token expiry\n scope: string;\n grantedAt: number; // Unix ms \u2013 original grant time\n domain?: string; // \u98DE\u4E66\u9879\u76EE\u73AF\u5883\u6807\u8BC6\uFF08feishu/meegle/\u81EA\u5B9A\u4E49URL\uFF09\uFF0C\u98DE\u4E66 Open API token \u4E0D\u4F7F\u7528\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst KEYCHAIN_SERVICE = 'openclaw-feishu-uat';\n\n/** Refresh proactively when access_token expires within this window. */\nconst REFRESH_AHEAD_MS = 5 * 60 * 1000; // 5 minutes\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction accountKey(appId: string, userOpenId: string): string {\n return `${appId}:${userOpenId}`;\n}\n\n/** Mask a token for safe logging: only the last 4 chars are visible. */\nexport function maskToken(token: string): string {\n if (token.length <= 8) return '****';\n return `****${token.slice(-4)}`;\n}\n\n// ---------------------------------------------------------------------------\n// Backend interface\n// ---------------------------------------------------------------------------\n\ninterface KeychainBackend {\n get(service: string, account: string): Promise<string | null>;\n set(service: string, account: string, data: string): Promise<void>;\n remove(service: string, account: string): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// macOS backend \u2013 Keychain Access via `security` CLI\n// ---------------------------------------------------------------------------\n\nconst darwinBackend: KeychainBackend = {\n async get(service, account) {\n try {\n const { stdout } = await execFile('security', ['find-generic-password', '-s', service, '-a', account, '-w']);\n return stdout.trim() || null;\n } catch {\n return null;\n }\n },\n\n async set(service, account, data) {\n // Delete first \u2013 `add-generic-password` fails if the item already exists.\n try {\n await execFile('security', ['delete-generic-password', '-s', service, '-a', account]);\n } catch {\n // Not found \u2013 fine.\n }\n await execFile('security', ['add-generic-password', '-s', service, '-a', account, '-w', data]);\n },\n\n async remove(service, account) {\n try {\n await execFile('security', ['delete-generic-password', '-s', service, '-a', account]);\n } catch {\n // Already absent \u2013 fine.\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// Linux backend \u2013 AES-256-GCM encrypted files (XDG Base Directory)\n//\n// Headless Linux servers typically lack D-Bus / GNOME Keyring, so we store\n// tokens as AES-256-GCM encrypted files instead of using `secret-tool`.\n//\n// Storage path: ${XDG_DATA_HOME:-~/.local/share}/openclaw-feishu-uat/\n// ---------------------------------------------------------------------------\n\nconst LINUX_UAT_DIR = join(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), 'openclaw-feishu-uat');\nconst MASTER_KEY_PATH = join(LINUX_UAT_DIR, 'master.key');\nconst MASTER_KEY_BYTES = 32; // AES-256\nconst IV_BYTES = 12; // GCM recommended\nconst TAG_BYTES = 16; // GCM auth tag\n\n/** Convert account key to a filesystem-safe filename. */\nfunction linuxSafeFileName(account: string): string {\n return account.replace(/[^a-zA-Z0-9._-]/g, '_') + '.enc';\n}\n\n/** Ensure the credentials directory exists with mode 0700. */\nasync function ensureLinuxCredDir(): Promise<void> {\n await mkdir(LINUX_UAT_DIR, { recursive: true, mode: 0o700 });\n}\n\n/**\n * Load or create the 32-byte master key.\n *\n * On first run, generates a random key and writes it to disk (mode 0600).\n * On subsequent runs, reads the existing key file.\n */\nasync function getMasterKey(): Promise<Buffer> {\n try {\n const key = await readFile(MASTER_KEY_PATH);\n if (key.length === MASTER_KEY_BYTES) return key;\n log.warn('master key has unexpected length, regenerating');\n } catch (err: unknown) {\n if (!(err instanceof Error) || (err as NodeJS.ErrnoException).code !== 'ENOENT') {\n log.warn(`failed to read master key: ${err instanceof Error ? err.message : err}`);\n }\n }\n\n await ensureLinuxCredDir();\n const key = randomBytes(MASTER_KEY_BYTES);\n await writeFile(MASTER_KEY_PATH, key, { mode: 0o600 });\n await chmod(MASTER_KEY_PATH, 0o600);\n log.info('generated new master key for encrypted file storage');\n return key;\n}\n\n/** AES-256-GCM encrypt. Returns [12-byte IV][16-byte tag][ciphertext]. */\nfunction encryptData(plaintext: string, key: Buffer): Buffer {\n const iv = randomBytes(IV_BYTES);\n const cipher = createCipheriv('aes-256-gcm', key, iv);\n const enc = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);\n return Buffer.concat([iv, cipher.getAuthTag(), enc]);\n}\n\n/** AES-256-GCM decrypt. Returns plaintext or `null` on failure. */\nfunction decryptData(data: Buffer, key: Buffer): string | null {\n if (data.length < IV_BYTES + TAG_BYTES) return null;\n try {\n const iv = data.subarray(0, IV_BYTES);\n const tag = data.subarray(IV_BYTES, IV_BYTES + TAG_BYTES);\n const enc = data.subarray(IV_BYTES + TAG_BYTES);\n const decipher = createDecipheriv('aes-256-gcm', key, iv);\n decipher.setAuthTag(tag);\n return Buffer.concat([decipher.update(enc), decipher.final()]).toString('utf8');\n } catch {\n return null;\n }\n}\n\nconst linuxBackend: KeychainBackend = {\n async get(_service, account) {\n try {\n const key = await getMasterKey();\n const data = await readFile(join(LINUX_UAT_DIR, linuxSafeFileName(account)));\n return decryptData(data, key);\n } catch {\n return null;\n }\n },\n\n async set(_service, account, data) {\n const key = await getMasterKey();\n await ensureLinuxCredDir();\n const filePath = join(LINUX_UAT_DIR, linuxSafeFileName(account));\n const encrypted = encryptData(data, key);\n await writeFile(filePath, encrypted, { mode: 0o600 });\n await chmod(filePath, 0o600);\n },\n\n async remove(_service, account) {\n try {\n await unlink(join(LINUX_UAT_DIR, linuxSafeFileName(account)));\n } catch {\n // Already absent \u2013 fine.\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// Windows backend \u2013 AES-256-GCM encrypted files\n//\n// Replaces the previous DPAPI-via-PowerShell approach which was unreliable\n// (PowerShell cold-start latency, execution policy restrictions, cmd.exe\n// command-line length limits, and unavailability in containers).\n//\n// Uses the same AES-256-GCM scheme as the Linux backend with its own\n// independent storage directory and master key.\n//\n// Storage path: %LOCALAPPDATA%\\openclaw-feishu-uat\\\n// ---------------------------------------------------------------------------\n\nconst WIN32_UAT_DIR = join(\n process.env.LOCALAPPDATA ?? join(process.env.USERPROFILE ?? homedir(), 'AppData', 'Local'),\n KEYCHAIN_SERVICE,\n);\nconst WIN32_MASTER_KEY_PATH = join(WIN32_UAT_DIR, 'master.key');\n\n/** Convert account key to a filesystem-safe filename (whitelist approach). */\nfunction win32SafeFileName(account: string): string {\n return account.replace(/[^a-zA-Z0-9._-]/g, '_') + '.enc';\n}\n\nasync function ensureWin32CredDir(): Promise<void> {\n await mkdir(WIN32_UAT_DIR, { recursive: true });\n}\n\nasync function getWin32MasterKey(): Promise<Buffer> {\n try {\n const key = await readFile(WIN32_MASTER_KEY_PATH);\n if (key.length === MASTER_KEY_BYTES) return key;\n log.warn('win32 master key has unexpected length, regenerating');\n } catch (err: unknown) {\n if (!(err instanceof Error) || (err as NodeJS.ErrnoException).code !== 'ENOENT') {\n log.warn(`failed to read win32 master key: ${err instanceof Error ? err.message : err}`);\n }\n }\n\n await ensureWin32CredDir();\n const key = randomBytes(MASTER_KEY_BYTES);\n await writeFile(WIN32_MASTER_KEY_PATH, key);\n log.info('generated new master key for win32 encrypted file storage');\n return key;\n}\n\nconst win32Backend: KeychainBackend = {\n async get(_service, account) {\n try {\n const key = await getWin32MasterKey();\n const data = await readFile(join(WIN32_UAT_DIR, win32SafeFileName(account)));\n return decryptData(data, key);\n } catch {\n return null;\n }\n },\n\n async set(_service, account, data) {\n const key = await getWin32MasterKey();\n await ensureWin32CredDir();\n const filePath = join(WIN32_UAT_DIR, win32SafeFileName(account));\n const encrypted = encryptData(data, key);\n await writeFile(filePath, encrypted);\n },\n\n async remove(_service, account) {\n try {\n await unlink(join(WIN32_UAT_DIR, win32SafeFileName(account)));\n } catch {\n // Already absent \u2013 fine.\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// Platform selection\n// ---------------------------------------------------------------------------\n\nfunction createBackend(): KeychainBackend {\n switch (process.platform) {\n case 'darwin':\n return darwinBackend;\n case 'linux':\n return linuxBackend;\n case 'win32':\n return win32Backend;\n default:\n log.warn(`unsupported platform \"${process.platform}\", falling back to macOS backend`);\n return darwinBackend;\n }\n}\n\nconst backend = createBackend();\n\n// ---------------------------------------------------------------------------\n// Public API \u2013 Credential operations\n// ---------------------------------------------------------------------------\n\n/**\n * Read the stored UAT for a given (appId, userOpenId) pair.\n * Returns `null` when no entry exists or the payload is unparseable.\n */\nexport async function getStoredToken(appId: string, userOpenId: string): Promise<StoredUAToken | null> {\n try {\n const json = await backend.get(KEYCHAIN_SERVICE, accountKey(appId, userOpenId));\n if (!json) return null;\n return JSON.parse(json) as StoredUAToken;\n } catch {\n return null;\n }\n}\n\n/**\n * Persist a UAT using the platform credential store.\n *\n * Overwrites any existing entry for the same (appId, userOpenId).\n */\nexport async function setStoredToken(token: StoredUAToken): Promise<void> {\n const key = accountKey(token.appId, token.userOpenId);\n const payload = JSON.stringify(token);\n await backend.set(KEYCHAIN_SERVICE, key, payload);\n log.info(`saved UAT for ${token.userOpenId} (at:${maskToken(token.accessToken)})`);\n}\n\n/**\n * Remove a stored UAT from the credential store.\n */\nexport async function removeStoredToken(appId: string, userOpenId: string): Promise<void> {\n await backend.remove(KEYCHAIN_SERVICE, accountKey(appId, userOpenId));\n log.info(`removed UAT for ${userOpenId}`);\n}\n\n// ---------------------------------------------------------------------------\n// Token validity check\n// ---------------------------------------------------------------------------\n\n/**\n * Determine the freshness of a stored token.\n *\n * - `\"valid\"` \u2013 access_token is still good (expires > 5 min from now)\n * - `\"needs_refresh\"` \u2013 access_token expired/expiring but refresh_token is valid\n * - `\"expired\"` \u2013 both tokens are expired; re-authorization required\n */\nexport function tokenStatus(token: StoredUAToken): 'valid' | 'needs_refresh' | 'expired' {\n const now = Date.now();\n if (now < token.expiresAt - REFRESH_AHEAD_MS) {\n return 'valid';\n }\n if (now < token.refreshExpiresAt) {\n return 'needs_refresh';\n }\n return 'expired';\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["key"]
|
|
7
7
|
}
|
|
@@ -3,8 +3,10 @@ import { isRecord, unwrapJsonRpcResult } from "../shared.js";
|
|
|
3
3
|
import { createToolContext, formatToolResult } from "../../helpers.js";
|
|
4
4
|
import { getProjectStoredToken, setProjectStoredToken, projectTokenStatus, getProjectClientId } from "../../../core/project-token-store.js";
|
|
5
5
|
import { refreshProjectToken } from "../../../core/project-auth.js";
|
|
6
|
+
import { getProjectDomainFromConfig } from "./endpoint.js";
|
|
6
7
|
import { getTicket } from "../../../core/lark-ticket.js";
|
|
7
8
|
import { larkLogger } from "../../../core/lark-logger.js";
|
|
9
|
+
import { LarkClient } from "../../../core/lark-client.js";
|
|
8
10
|
const log = larkLogger("tools/mcp/project");
|
|
9
11
|
async function callProjectMcpTool(name, args, toolCallId, accessToken, endpoint) {
|
|
10
12
|
const body = {
|
|
@@ -55,7 +57,14 @@ function registerProjectMcpTool(api, config) {
|
|
|
55
57
|
error: "\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u7528\u6237\u8EAB\u4EFD\uFF0C\u8BF7\u5728\u98DE\u4E66\u5BF9\u8BDD\u4E2D\u4F7F\u7528\u6B64\u5DE5\u5177\u3002"
|
|
56
58
|
});
|
|
57
59
|
}
|
|
58
|
-
const
|
|
60
|
+
const currentDomain = getProjectDomainFromConfig(LarkClient.runtime.config.loadConfig());
|
|
61
|
+
if (!currentDomain) {
|
|
62
|
+
return formatToolResult({
|
|
63
|
+
error: "project_auth_required",
|
|
64
|
+
message: "\u98DE\u4E66\u9879\u76EE\u73AF\u5883\u672A\u914D\u7F6E\u3002\u8BF7\u5148\u8C03\u7528 feishu_project_oauth \u5DE5\u5177\u7684 authorize \u64CD\u4F5C\u9009\u62E9\u73AF\u5883\u5E76\u5B8C\u6210\u6388\u6743\u3002"
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const clientId = await getProjectClientId(senderOpenId, currentDomain);
|
|
59
68
|
if (!clientId) {
|
|
60
69
|
return formatToolResult({
|
|
61
70
|
error: "project_auth_required",
|
|
@@ -69,6 +78,12 @@ function registerProjectMcpTool(api, config) {
|
|
|
69
78
|
message: "\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u5148\u8C03\u7528 feishu_project_oauth \u5DE5\u5177\u5B8C\u6210\u6388\u6743\u3002"
|
|
70
79
|
});
|
|
71
80
|
}
|
|
81
|
+
if (token.domain && currentDomain && token.domain !== currentDomain) {
|
|
82
|
+
return formatToolResult({
|
|
83
|
+
error: "project_auth_required",
|
|
84
|
+
message: `\u5F53\u524D\u98DE\u4E66\u9879\u76EE\u73AF\u5883\u5DF2\u5207\u6362\u4E3A\u300C${currentDomain}\u300D\uFF0C\u4E0E\u6388\u6743\u65F6\u7684\u73AF\u5883\u300C${token.domain}\u300D\u4E0D\u5339\u914D\u3002\u8BF7\u8C03\u7528 feishu_project_oauth \u5DE5\u5177\u7684 authorize \u64CD\u4F5C\u91CD\u65B0\u6388\u6743\u3002`
|
|
85
|
+
});
|
|
86
|
+
}
|
|
72
87
|
const status = projectTokenStatus(token);
|
|
73
88
|
if (status === "expired") {
|
|
74
89
|
return formatToolResult({
|
|
@@ -85,7 +100,8 @@ function registerProjectMcpTool(api, config) {
|
|
|
85
100
|
accessToken: refreshed.access_token,
|
|
86
101
|
refreshToken: refreshed.refresh_token ?? token.refreshToken,
|
|
87
102
|
expiresAt: Date.now() + (refreshed.expires_in ?? 7200) * 1e3,
|
|
88
|
-
scope: refreshed.scope ?? token.scope
|
|
103
|
+
scope: refreshed.scope ?? token.scope,
|
|
104
|
+
domain: currentDomain
|
|
89
105
|
};
|
|
90
106
|
await setProjectStoredToken(token);
|
|
91
107
|
toolLog.debug?.("project token refreshed");
|
|
@@ -98,9 +114,10 @@ function registerProjectMcpTool(api, config) {
|
|
|
98
114
|
}
|
|
99
115
|
}
|
|
100
116
|
const resolvedEndpoint = typeof config.endpoint === "function" ? config.endpoint() : config.endpoint;
|
|
117
|
+
const finalArgs = config.transformArgs ? config.transformArgs(p) : p;
|
|
101
118
|
const result = await callProjectMcpTool(
|
|
102
119
|
config.mcpToolName,
|
|
103
|
-
|
|
120
|
+
finalArgs,
|
|
104
121
|
toolCallId,
|
|
105
122
|
token.accessToken,
|
|
106
123
|
resolvedEndpoint
|
|
@@ -190,6 +207,13 @@ function registerProjectTools(api, endpoint) {
|
|
|
190
207
|
)
|
|
191
208
|
)
|
|
192
209
|
}),
|
|
210
|
+
transformArgs: (args) => {
|
|
211
|
+
const { mql, ...rest } = args;
|
|
212
|
+
if (mql !== void 0) {
|
|
213
|
+
return { ...rest, moql: mql };
|
|
214
|
+
}
|
|
215
|
+
return args;
|
|
216
|
+
},
|
|
193
217
|
endpoint
|
|
194
218
|
});
|
|
195
219
|
count++;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/tools/mcp/project/tools.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * \u98DE\u4E66\u9879\u76EE MCP \u5DE5\u5177\u6CE8\u518C\n *\n * \u6BCF\u4E2A\u5DE5\u5177\u4F7F\u7528\u98DE\u4E66\u9879\u76EE\u4E13\u5C5E\u7684 MCP \u7AEF\u70B9\u548C\u72EC\u7ACB\u7684 OAuth token\u3002\n * \u4E0D\u901A\u8FC7 registerMcpTool\uFF08\u4F7F\u7528\u98DE\u4E66 Open API UAT\uFF09\uFF0C\u800C\u662F\u4F7F\u7528\n * registerProjectMcpTool \u5305\u88C5\u51FD\u6570\u3002\n *\n * \u5DE5\u5177\u53C2\u6570\u7B7E\u540D\u53C2\u7167\u98DE\u4E66\u9879\u76EE MCP Server \u7684\u5B9E\u9645\u5B9A\u4E49\u3002\n */\n\nimport type { OpenClawPluginApi } from 'openclaw/plugin-sdk';\nimport type { TSchema } from '@sinclair/typebox';\nimport { Type } from '@sinclair/typebox';\nimport type { McpRpcResponse } from '../shared';\nimport { isRecord, unwrapJsonRpcResult } from '../shared';\nimport { createToolContext, formatToolResult } from '../../helpers';\nimport { getProjectStoredToken, setProjectStoredToken, projectTokenStatus, getProjectClientId } from '../../../core/project-token-store';\nimport { refreshProjectToken } from '../../../core/project-auth';\nimport { getProjectMcpEndpoint } from './endpoint';\nimport { getTicket } from '../../../core/lark-ticket';\nimport { larkLogger } from '../../../core/lark-logger';\n\nconst log = larkLogger('tools/mcp/project');\n\n// ---------------------------------------------------------------------------\n// \u98DE\u4E66\u9879\u76EE MCP \u4E13\u7528 JSON-RPC \u5BA2\u6237\u7AEF\uFF08\u4F7F\u7528\u6807\u51C6 OAuth Bearer \u8BA4\u8BC1\uFF09\n// ---------------------------------------------------------------------------\n\nasync function callProjectMcpTool(\n name: string,\n args: Record<string, unknown>,\n toolCallId: string,\n accessToken: string,\n endpoint: string,\n): Promise<unknown> {\n const body = {\n jsonrpc: '2.0',\n id: toolCallId,\n method: 'tools/call',\n params: { name, arguments: args },\n };\n\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify(body),\n });\n\n const text = await res.text();\n if (!res.ok) {\n throw new Error(`MCP HTTP ${res.status} ${res.statusText}: ${text.slice(0, 4000)}`);\n }\n\n let data: McpRpcResponse;\n try {\n data = JSON.parse(text) as McpRpcResponse;\n } catch {\n throw new Error(`MCP \u8FD4\u56DE\u975E JSON\uFF1A${text.slice(0, 4000)}`);\n }\n\n if ('error' in data) {\n throw new Error(`MCP error ${data.error.code}: ${data.error.message}`);\n }\n\n return unwrapJsonRpcResult(data.result);\n}\n\n// ---------------------------------------------------------------------------\n// \u98DE\u4E66\u9879\u76EE MCP \u5DE5\u5177\u901A\u7528\u6CE8\u518C\u51FD\u6570\n// ---------------------------------------------------------------------------\n\ninterface ProjectMcpToolConfig {\n name: string;\n mcpToolName: string;\n label: string;\n description: string;\n schema: TSchema;\n endpoint: string | (() => string);\n}\n\nexport function registerProjectMcpTool(\n api: OpenClawPluginApi,\n config: ProjectMcpToolConfig,\n): void {\n const { log: toolLog } = createToolContext(api, config.name);\n\n api.registerTool(\n {\n name: config.name,\n label: config.label,\n description: config.description,\n parameters: config.schema,\n async execute(toolCallId, params) {\n const p = params as Record<string, unknown>;\n try {\n const startTime = Date.now();\n\n const ticket = getTicket();\n const senderOpenId = ticket?.senderOpenId;\n if (!senderOpenId) {\n return formatToolResult({\n error: '\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u7528\u6237\u8EAB\u4EFD\uFF0C\u8BF7\u5728\u98DE\u4E66\u5BF9\u8BDD\u4E2D\u4F7F\u7528\u6B64\u5DE5\u5177\u3002',\n });\n }\n\n const clientId = await getProjectClientId(senderOpenId);\n if (!clientId) {\n return formatToolResult({\n error: 'project_auth_required',\n message: '\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u5148\u8C03\u7528 feishu_project_oauth \u5DE5\u5177\u5B8C\u6210\u6388\u6743\u3002',\n });\n }\n\n let token = await getProjectStoredToken(clientId, senderOpenId);\n if (!token) {\n return formatToolResult({\n error: 'project_auth_required',\n message: '\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u5148\u8C03\u7528 feishu_project_oauth \u5DE5\u5177\u5B8C\u6210\u6388\u6743\u3002',\n });\n }\n\n const status = projectTokenStatus(token);\n if (status === 'expired') {\n return formatToolResult({\n error: 'project_auth_required',\n message: '\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5DF2\u8FC7\u671F\u3002\u8BF7\u8C03\u7528 feishu_project_oauth \u5DE5\u5177\u7684 authorize \u64CD\u4F5C\u91CD\u65B0\u6388\u6743\u3002',\n });\n }\n\n // Token \u5373\u5C06\u8FC7\u671F\u6216\u5DF2\u8FC7\u671F\u4F46 refresh_token \u4ECD\u6709\u6548 \u2192 \u81EA\u52A8\u5237\u65B0\n if (status === 'needs_refresh') {\n try {\n const resolvedEp = typeof config.endpoint === 'function' ? config.endpoint() : config.endpoint;\n const refreshed = await refreshProjectToken(resolvedEp, token.refreshToken, clientId);\n token = {\n ...token,\n accessToken: refreshed.access_token,\n refreshToken: refreshed.refresh_token ?? token.refreshToken,\n expiresAt: Date.now() + (refreshed.expires_in ?? 7200) * 1000,\n scope: refreshed.scope ?? token.scope,\n };\n await setProjectStoredToken(token);\n toolLog.debug?.('project token refreshed');\n } catch (refreshErr) {\n toolLog.error(`project token refresh failed: ${refreshErr}`);\n return formatToolResult({\n error: 'project_auth_required',\n message: '\u98DE\u4E66\u9879\u76EE token \u5237\u65B0\u5931\u8D25\uFF0C\u8BF7\u8C03\u7528 feishu_project_oauth \u5DE5\u5177\u7684 authorize \u64CD\u4F5C\u91CD\u65B0\u6388\u6743\u3002',\n });\n }\n }\n\n const resolvedEndpoint = typeof config.endpoint === 'function' ? config.endpoint() : config.endpoint;\n\n const result = await callProjectMcpTool(\n config.mcpToolName,\n p,\n toolCallId,\n token.accessToken,\n resolvedEndpoint,\n );\n\n const duration = Date.now() - startTime;\n toolLog.debug?.(`${config.mcpToolName} succeeded in ${duration}ms`);\n\n // MCP \u8FD4\u56DE\u503C\u5904\u7406\uFF08\u540C registerMcpTool \u903B\u8F91\uFF09\n if (isRecord(result) && Array.isArray((result as Record<string, unknown>).content)) {\n const mcpContent = (result as Record<string, unknown>).content as Array<{\n type: string;\n text: string;\n }>;\n let details: unknown = result;\n if (mcpContent.length === 1 && mcpContent[0]?.type === 'text') {\n try {\n details = JSON.parse(mcpContent[0].text);\n } catch {\n // text is not JSON\n }\n }\n return {\n content: mcpContent.map((c) => ({\n type: 'text' as const,\n text: c.text,\n })),\n details,\n };\n }\n return formatToolResult(result);\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n toolLog.error(`${config.mcpToolName} failed: ${errMsg}`);\n return formatToolResult({\n error: errMsg,\n hint: '\u5982\u679C\u662F\u6388\u6743\u95EE\u9898\uFF0C\u8BF7\u8C03\u7528 feishu_project_oauth \u5DE5\u5177\u91CD\u65B0\u6388\u6743\u3002',\n });\n }\n },\n },\n { name: config.name },\n );\n}\n\n// ---------------------------------------------------------------------------\n// \u5177\u4F53\u5DE5\u5177\u6CE8\u518C\uFF08\u53C2\u6570\u7B7E\u540D\u5339\u914D\u98DE\u4E66\u9879\u76EE MCP Server \u5B9E\u9645\u5B9A\u4E49\uFF09\n// ---------------------------------------------------------------------------\n\nexport function registerProjectTools(api: OpenClawPluginApi, endpoint: string | (() => string)) {\n let count = 0;\n\n // list_todo \u2014 \u5F85\u529E/\u5DF2\u529E/\u8D85\u671F/\u672C\u5468\u5230\u671F\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_todo',\n mcpToolName: 'list_todo',\n label: 'Feishu Project: \u5F85\u529E\u5217\u8868',\n description:\n '\u83B7\u53D6\u5F53\u524D\u7528\u6237\u7684\u98DE\u4E66\u9879\u76EE\u5DE5\u4F5C\u9879\u5217\u8868\u3002\u652F\u6301\u67E5\u8BE2\u7C7B\u578B\uFF1A' +\n 'todo(\u5F85\u529E)\u3001done(\u5DF2\u529E)\u3001overdue(\u5DF2\u8D85\u671F)\u3001this_week(\u672C\u5468\u5230\u671F)\u3002',\n schema: Type.Object({\n action: Type.String({\n description: '\u67E5\u8BE2\u7C7B\u578B\u3002todo \u4E3A\u5F85\u529E, done \u4E3A\u5DF2\u529E, overdue \u4E3A\u5DF2\u8D85\u671F, this_week \u4E3A\u672C\u5468\u5230\u671F',\n }),\n asset_key: Type.Optional(\n Type.String({ description: '\u5DE5\u4F5C\u533A key\uFF0C\u683C\u5F0F\u4E3A Asset_\uFF0C\u4EC5\u5728\u63A5\u53E3\u62A5\u9519\u9700\u8981\u9009\u62E9\u65F6\u4F20\u9012' }),\n ),\n page_num: Type.Optional(\n Type.Number({ description: '\u9875\u7801\uFF0C\u4ECE 1 \u5F00\u59CB\u9012\u589E\uFF0C\u6BCF\u9875 50 \u6761' }),\n ),\n }),\n endpoint,\n });\n count++;\n\n // search_by_mql \u2014 MQL \u67E5\u8BE2\n registerProjectMcpTool(api, {\n name: 'feishu_project_search_by_mql',\n mcpToolName: 'search_by_mql',\n label: 'Feishu Project: MQL \u641C\u7D22',\n description:\n '\u4F7F\u7528 MQL \u641C\u7D22\u98DE\u4E66\u9879\u76EE\u5DE5\u4F5C\u9879\u3002MQL \u57FA\u4E8E SQL \u8BED\u6CD5\u6269\u5C55\u3002' +\n '\u4F7F\u7528\u524D\u9700\u5148\u7528 get_workitem_info \u786E\u8BA4\u7A7A\u95F4\u548C\u5DE5\u4F5C\u9879\u7C7B\u578B\u3002' +\n 'select \u540E\u4F7F\u7528\u53EF\u8BFB\u6027\u5F3A\u7684\u5B57\u6BB5\u540D\u79F0\uFF08\u5982\"\u4EFB\u52A1\u540D\u79F0\"\u800C\u975E\"name\"\uFF09\u3002',\n schema: Type.Object({\n project_key: Type.String({\n description: '\u7A7A\u95F4 projectKey\u3001simpleName \u6216\u7A7A\u95F4\u540D',\n }),\n mql: Type.Optional(\n Type.String({\n description:\n 'MQL \u8BED\u53E5\u3002\u8BED\u6CD5\uFF1Aselect `\u5B57\u6BB5\u540D` from `\u7A7A\u95F4\u540D`.`\u5DE5\u4F5C\u9879\u540D` where `\u5B57\u6BB5\u540D` = \\'\u5B57\u6BB5\u503C\\'',\n }),\n ),\n helper: Type.Optional(\n Type.String({\n description: '\u5E2E\u52A9\u5DE5\u5177\u3002\u8F93\u5165 \"helper\" \u83B7\u53D6\u6240\u6709\u547D\u4EE4\u548C\u7528\u6CD5\u793A\u4F8B',\n }),\n ),\n session_id: Type.Optional(\n Type.String({ description: 'sessionID\uFF0C\u7528\u4E8E\u5206\u9875\u67E5\u8BE2\uFF0C\u4F20\u5165\u540E\u4E0D\u89E3\u6790 mql' }),\n ),\n group_pagination_list: Type.Optional(\n Type.Array(\n Type.Object({\n group_id: Type.Optional(Type.String({ description: '\u5206\u7EC4 ID' })),\n page_num: Type.Optional(Type.Number({ description: '\u9875\u7801\uFF0C\u4ECE 1 \u5F00\u59CB' })),\n }),\n ),\n ),\n }),\n endpoint,\n });\n count++;\n\n // get_workitem_info \u2014 \u83B7\u53D6\u5DE5\u4F5C\u9879\u7C7B\u578B\u7684\u5B57\u6BB5\u4E0E\u89D2\u8272\u4FE1\u606F\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_workitem_info',\n mcpToolName: 'get_workitem_info',\n label: 'Feishu Project: \u5DE5\u4F5C\u9879\u7C7B\u578B\u4FE1\u606F',\n description: '\u83B7\u53D6\u4E00\u4E2A\u5DE5\u4F5C\u9879\u7C7B\u578B\u5177\u5907\u7684\u53EF\u7528\u5B57\u6BB5\u4E0E\u89D2\u8272\u4FE1\u606F\u3002',\n schema: Type.Object({\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n ),\n work_item_type: Type.String({\n description: '\u5DE5\u4F5C\u9879\u7C7B\u578B\u7684\u7CFB\u7EDF\u6807\u8BC6\u6216\u540D\u79F0\uFF0C\u5982 story\u3001\u9700\u6C42\u3001issue\u3001\u7F3A\u9677\u7B49',\n }),\n }),\n endpoint,\n });\n count++;\n\n // get_workitem_brief \u2014 \u83B7\u53D6\u5DE5\u4F5C\u9879\u6982\u51B5\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_workitem_brief',\n mcpToolName: 'get_workitem_brief',\n label: 'Feishu Project: \u5DE5\u4F5C\u9879\u6982\u51B5',\n description: '\u83B7\u53D6\u4E00\u4E2A\u5DE5\u4F5C\u9879\u5B9E\u4F8B\u7684\u6982\u51B5\u4FE1\u606F\u3002',\n schema: Type.Object({\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey \u6216 simpleName' }),\n ),\n work_item_id: Type.String({\n description: '\u5DE5\u4F5C\u9879 ID \u6216\u540D\u79F0',\n }),\n fields: Type.Optional(\n Type.Array(Type.String({ description: '\u8981\u67E5\u8BE2\u7684 field_key \u6216 field_name' })),\n ),\n }),\n endpoint,\n });\n count++;\n\n // get_workitem_field_meta \u2014 \u83B7\u53D6\u521B\u5EFA\u5DE5\u4F5C\u9879\u5143\u4FE1\u606F\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_workitem_field_meta',\n mcpToolName: 'get_workitem_field_meta',\n label: 'Feishu Project: \u5B57\u6BB5\u5143\u4FE1\u606F',\n description: '\u83B7\u53D6\u521B\u5EFA\u5DE5\u4F5C\u9879\u65F6\u7684\u5B57\u6BB5\u5143\u4FE1\u606F\uFF08\u5B57\u6BB5\u540D\u3001\u7C7B\u578B\u3001\u9009\u9879\u7B49\uFF09\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n work_item_type_key: Type.String({ description: '\u5DE5\u4F5C\u9879\u7C7B\u578B key' }),\n }),\n endpoint,\n });\n count++;\n\n // create_workitem \u2014 \u521B\u5EFA\u5DE5\u4F5C\u9879\n registerProjectMcpTool(api, {\n name: 'feishu_project_create_workitem',\n mcpToolName: 'create_workitem',\n label: 'Feishu Project: \u521B\u5EFA\u5DE5\u4F5C\u9879',\n description: '\u521B\u5EFA\u4E00\u6761\u5DE5\u4F5C\u9879\u5B9E\u4F8B\u3002\u521B\u5EFA\u6210\u529F\u540E\u83B7\u5F97\u8BE6\u60C5\u9875 URL\u3002',\n schema: Type.Object({\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey \u6216 simpleName' }),\n ),\n work_item_type: Type.String({\n description: '\u5DE5\u4F5C\u9879\u7C7B\u578B',\n }),\n fields: Type.Optional(\n Type.Array(\n Type.Object({\n field_key: Type.String({ description: '\u5B57\u6BB5 key' }),\n field_value: Type.String({\n description: '\u5B57\u6BB5\u503C\u3002\u65F6\u95F4\u7C7B\u9700\u4F20 16 \u4F4D unix \u6BEB\u79D2\u65F6\u95F4\u6233\uFF0C\u4EBA\u5458\u7C7B\u9700\u7528\u82F1\u6587\u9017\u53F7\u533A\u9694',\n }),\n }),\n ),\n ),\n }),\n endpoint,\n });\n count++;\n\n // update_field \u2014 \u4FEE\u6539\u5DE5\u4F5C\u9879\u5B57\u6BB5\n registerProjectMcpTool(api, {\n name: 'feishu_project_update_field',\n mcpToolName: 'update_field',\n label: 'Feishu Project: \u4FEE\u6539\u5B57\u6BB5',\n description: '\u4FEE\u6539\u6307\u5B9A\u5DE5\u4F5C\u9879\u5B9E\u4F8B\u7684\u5B57\u6BB5\u503C\uFF0C\u652F\u6301\u4E00\u6B21\u4FEE\u6539\u591A\u4E2A\u5B57\u6BB5\u3002',\n schema: Type.Object({\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n ),\n work_item_id: Type.String({\n description: '\u5DE5\u4F5C\u9879 ID \u6216\u540D\u79F0',\n }),\n fields: Type.Optional(\n Type.Array(\n Type.Object({\n field_key: Type.String({ description: '\u5B57\u6BB5 key' }),\n field_value: Type.String({\n description: '\u5B57\u6BB5\u503C\u3002\u65F6\u95F4\u7C7B\u9700\u4F20 16 \u4F4D unix \u6BEB\u79D2\u65F6\u95F4\u6233\uFF0C\u4EBA\u5458\u7C7B\u9700\u7528\u82F1\u6587\u9017\u53F7\u533A\u9694',\n }),\n }),\n ),\n ),\n }),\n endpoint,\n });\n count++;\n\n // add_comment \u2014 \u6DFB\u52A0\u8BC4\u8BBA\n registerProjectMcpTool(api, {\n name: 'feishu_project_add_comment',\n mcpToolName: 'add_comment',\n label: 'Feishu Project: \u6DFB\u52A0\u8BC4\u8BBA',\n description: '\u5728\u6307\u5B9A\u5DE5\u4F5C\u9879\u7684\u8BC4\u8BBA/\u5907\u6CE8\u9875\u6DFB\u52A0\u4E00\u6761\u8BC4\u8BBA\uFF0C\u652F\u6301 markdown \u683C\u5F0F\u3002',\n schema: Type.Object({\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n ),\n work_item_id: Type.String({\n description: '\u5DE5\u4F5C\u9879 ID \u6216\u540D\u79F0',\n }),\n work_item_type: Type.Optional(\n Type.String({ description: '\u5DE5\u4F5C\u9879\u7C7B\u578B' }),\n ),\n comment_content: Type.String({\n description: '\u8BC4\u8BBA\u5185\u5BB9\uFF0C\u652F\u6301 markdown \u683C\u5F0F',\n }),\n url: Type.Optional(\n Type.String({ description: '\u5DE5\u4F5C\u9879 URL\uFF0C\u53EF\u4ECE\u4E2D\u89E3\u6790 project_key \u7B49\u4FE1\u606F' }),\n ),\n }),\n endpoint,\n });\n count++;\n\n // finish_node \u2014 \u5B8C\u6210\u8282\u70B9/\u6D41\u8F6C\u72B6\u6001\n registerProjectMcpTool(api, {\n name: 'feishu_project_finish_node',\n mcpToolName: 'finish_node',\n label: 'Feishu Project: \u6D41\u8F6C\u72B6\u6001',\n description: '\u5B8C\u6210\u67D0\u4E2A\u8282\u70B9\uFF0C\u6D41\u8F6C\u5DE5\u4F5C\u9879\u5230\u4E0B\u4E00\u4E2A\u72B6\u6001\u3002',\n schema: Type.Object({\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n ),\n work_item_id: Type.String({\n description: '\u5DE5\u4F5C\u9879 ID \u6216\u540D\u79F0',\n }),\n node_id: Type.String({\n description: '\u8981\u5B8C\u6210\u6D41\u8F6C\u7684\u8282\u70B9 ID \u6216\u8282\u70B9\u540D\u79F0',\n }),\n }),\n endpoint,\n });\n count++;\n\n // get_node_detail \u2014 \u83B7\u53D6\u8282\u70B9\u8BE6\u60C5\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_node_detail',\n mcpToolName: 'get_node_detail',\n label: 'Feishu Project: \u8282\u70B9\u8BE6\u60C5',\n description: '\u83B7\u53D6\u6307\u5B9A\u8282\u70B9\u7684\u5173\u952E\u4FE1\u606F\uFF0C\u5305\u62EC\u8282\u70B9\u4FE1\u606F\u3001\u5B50\u9879\u4FE1\u606F\u3001\u81EA\u5B9A\u4E49\u5B57\u6BB5\u4FE1\u606F\u3002',\n schema: Type.Object({\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n ),\n work_item_id: Type.String({ description: '\u5DE5\u4F5C\u9879 ID' }),\n node_id: Type.String({ description: '\u8282\u70B9\u540D\u79F0\u6216\u8282\u70B9 ID' }),\n node_ids: Type.Optional(\n Type.Array(Type.String({ description: '\u8282\u70B9\u540D\u79F0\u6216\u8282\u70B9 ID \u5217\u8868' })),\n ),\n }),\n endpoint,\n });\n count++;\n\n // get_transitable_statuses \u2014 \u83B7\u53D6\u53EF\u6D41\u8F6C\u72B6\u6001\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_transitable_statuses',\n mcpToolName: 'get_transitable_statuses',\n label: 'Feishu Project: \u53EF\u6D41\u8F6C\u72B6\u6001',\n description: '\u83B7\u53D6\u72B6\u6001\u6D41\u5DE5\u4F5C\u9879\u7684\u53EF\u6D41\u8F6C\u72B6\u6001\u5217\u8868\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n work_item_id: Type.String({ description: '\u5DE5\u4F5C\u9879 ID' }),\n work_item_type_key: Type.String({ description: '\u5DE5\u4F5C\u9879\u7C7B\u578B key' }),\n user_key: Type.String({ description: '\u7528\u6237 userKey' }),\n }),\n endpoint,\n });\n count++;\n\n // list_workitem_types \u2014 \u83B7\u53D6\u5DE5\u4F5C\u9879\u7C7B\u578B\u5217\u8868\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_workitem_types',\n mcpToolName: 'list_workitem_types',\n label: 'Feishu Project: \u5DE5\u4F5C\u9879\u7C7B\u578B\u5217\u8868',\n description: '\u83B7\u53D6\u7A7A\u95F4\u4E0B\u7684\u6240\u6709\u5DE5\u4F5C\u9879\u7C7B\u578B\u5217\u8868\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n }),\n endpoint,\n });\n count++;\n\n // search_project_info \u2014 \u67E5\u8BE2\u7A7A\u95F4\u4FE1\u606F\n registerProjectMcpTool(api, {\n name: 'feishu_project_search_project_info',\n mcpToolName: 'search_project_info',\n label: 'Feishu Project: \u67E5\u8BE2\u7A7A\u95F4',\n description: '\u67E5\u8BE2\u98DE\u4E66\u9879\u76EE\u7A7A\u95F4\u57FA\u7840\u4FE1\u606F\u3002',\n schema: Type.Object({\n project_key: Type.String({\n description: '\u7A7A\u95F4 projectKey\u3001simpleName \u6216\u7A7A\u95F4\u540D\u79F0',\n }),\n }),\n endpoint,\n });\n count++;\n\n // search_user_info \u2014 \u67E5\u8BE2\u7528\u6237\u4FE1\u606F\n registerProjectMcpTool(api, {\n name: 'feishu_project_search_user_info',\n mcpToolName: 'search_user_info',\n label: 'Feishu Project: \u67E5\u8BE2\u7528\u6237',\n description: '\u6279\u91CF\u67E5\u8BE2\u7528\u6237\u57FA\u7840\u4FE1\u606F\uFF0C\u6700\u591A 20 \u4E2A\u3002',\n schema: Type.Object({\n user_keys: Type.Array(\n Type.String({ description: 'userKey\u3001Email \u6216\u540D\u5B57' }),\n ),\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n ),\n }),\n endpoint,\n });\n count++;\n\n // list_workitem_comments \u2014 \u67E5\u8BE2\u8BC4\u8BBA\u5217\u8868\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_workitem_comments',\n mcpToolName: 'list_workitem_comments',\n label: 'Feishu Project: \u8BC4\u8BBA\u5217\u8868',\n description: '\u67E5\u8BE2\u5DE5\u4F5C\u9879\u8BC4\u8BBA\u5217\u8868\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n work_item_id: Type.String({ description: '\u5DE5\u4F5C\u9879 ID \u6216\u540D\u79F0' }),\n page_num: Type.Optional(Type.Number({ description: '\u9875\u7801\uFF0C\u9ED8\u8BA4 1' })),\n start_time: Type.Optional(Type.Number({ description: '\u5F00\u59CB\u65F6\u95F4\uFF0CUnix \u6BEB\u79D2\u65F6\u95F4\u6233' })),\n end_time: Type.Optional(Type.Number({ description: '\u7ED3\u675F\u65F6\u95F4\uFF0CUnix \u6BEB\u79D2\u65F6\u95F4\u6233' })),\n }),\n endpoint,\n });\n count++;\n\n // search_view_by_title \u2014 \u641C\u7D22\u89C6\u56FE\n registerProjectMcpTool(api, {\n name: 'feishu_project_search_view_by_title',\n mcpToolName: 'search_view_by_title',\n label: 'Feishu Project: \u641C\u7D22\u89C6\u56FE',\n description: '\u901A\u8FC7\u89C6\u56FE\u540D\u79F0\u6A21\u7CCA\u67E5\u8BE2\u89C6\u56FE ID\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n view_scope: Type.String({\n description: '\u5DE5\u4F5C\u9879\u7C7B\u578B\uFF1A\u9700\u6C42\u4E3A storyView\uFF0C\u7F3A\u9677\u4E3A issueView\uFF0C\u7248\u672C\u4E3A version\uFF0C\u81EA\u5B9A\u4E49\u7C7B\u578B\u4E3A\u5BF9\u5E94 key',\n }),\n key_word: Type.String({ description: '\u89C6\u56FE\u540D\u79F0\u5173\u952E\u5B57' }),\n }),\n endpoint,\n });\n count++;\n\n // get_view_detail \u2014 \u83B7\u53D6\u89C6\u56FE\u8BE6\u60C5\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_view_detail',\n mcpToolName: 'get_view_detail',\n label: 'Feishu Project: \u89C6\u56FE\u8BE6\u60C5',\n description: '\u83B7\u53D6\u6307\u5B9A\u89C6\u56FE\u7684\u5DE5\u4F5C\u9879\u5217\u8868\u3002',\n schema: Type.Object({\n view_id: Type.String({ description: '\u89C6\u56FE ID' }),\n project_key: Type.Optional(Type.String({ description: '\u7A7A\u95F4 projectKey' })),\n fields: Type.Optional(\n Type.Array(Type.String({ description: '\u8981\u67E5\u8BE2\u7684 field_key \u6216 field_name' })),\n ),\n page_num: Type.Optional(Type.Number({ description: '\u5206\u9875\u9875\u7801' })),\n }),\n endpoint,\n });\n count++;\n\n // get_workitem_man_hour_records \u2014 \u5DE5\u65F6\u8BB0\u5F55\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_workitem_man_hour_records',\n mcpToolName: 'get_workitem_man_hour_records',\n label: 'Feishu Project: \u5DE5\u65F6\u8BB0\u5F55',\n description: '\u83B7\u53D6\u5DE5\u4F5C\u9879\u7684\u5DE5\u65F6\u767B\u8BB0\u8BB0\u5F55\u5217\u8868\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n work_item_type_key: Type.String({ description: '\u5DE5\u4F5C\u9879\u7C7B\u578B key' }),\n work_item_id: Type.Number({ description: '\u5DE5\u4F5C\u9879\u5B9E\u4F8B ID' }),\n page_num: Type.Optional(Type.Number({ description: '\u5206\u9875\u9875\u7801\uFF0C\u6BCF\u9875\u9ED8\u8BA4 20' })),\n }),\n endpoint,\n });\n count++;\n\n // get_workitem_op_record \u2014 \u64CD\u4F5C\u8BB0\u5F55\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_workitem_op_record',\n mcpToolName: 'get_workitem_op_record',\n label: 'Feishu Project: \u64CD\u4F5C\u8BB0\u5F55',\n description: '\u83B7\u53D6\u6307\u5B9A\u7A7A\u95F4\u4E0B\u4E00\u4E2A\u5DE5\u4F5C\u9879\u5B9E\u4F8B\u7684\u64CD\u4F5C\u8BB0\u5F55\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n work_item_id: Type.Number({ description: '\u5DE5\u4F5C\u9879 ID' }),\n start: Type.Optional(Type.Number({ description: '\u5F00\u59CB\u65F6\u95F4\uFF0C\u6BEB\u79D2\u7EA7\u65F6\u95F4\u6233' })),\n end: Type.Optional(Type.Number({ description: '\u7ED3\u675F\u65F6\u95F4\uFF0C\u6BEB\u79D2\u7EA7\u65F6\u95F4\u6233\uFF0C\u4E0E start \u914D\u5408\u6700\u957F 7 \u5929' })),\n operation_type: Type.Optional(\n Type.Array(Type.String({ description: '\u64CD\u4F5C\u7C7B\u578B\uFF1Amodify/create/delete/terminate/restore/complete/rollback/add/remove' })),\n ),\n start_from: Type.Optional(Type.String({ description: '\u5206\u9875\u53C2\u6570\uFF0C\u4F7F\u7528\u4E0A\u6B21\u8FD4\u56DE\u7684 start_from' })),\n }),\n endpoint,\n });\n count++;\n\n // list_schedule \u2014 \u6392\u671F\u67E5\u8BE2\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_schedule',\n mcpToolName: 'list_schedule',\n label: 'Feishu Project: \u6392\u671F\u67E5\u8BE2',\n description: '\u6309\u7A7A\u95F4\u548C\u7528\u6237\u67E5\u8BE2\u6307\u5B9A\u65F6\u95F4\u8303\u56F4\u5185\u7684\u6392\u671F\u660E\u7EC6\uFF0C\u7528\u4E8E\u4EBA\u529B\u8D1F\u8377\u4E0E\u6392\u671F\u5206\u6790\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey\u3001\u540D\u79F0\u6216 simple_name' }),\n user_keys: Type.Array(\n Type.String({ description: '\u7528\u6237\u540D\u79F0\u3001\u90AE\u7BB1\u6216 userkey\uFF0C\u6700\u591A 20 \u4E2A' }),\n ),\n start_time: Type.String({ description: '\u5F00\u59CB\u65F6\u95F4\uFF0C\u683C\u5F0F 2006-01-01' }),\n end_time: Type.String({ description: '\u7ED3\u675F\u65F6\u95F4\uFF0C\u683C\u5F0F 2006-01-01\uFF0C\u6700\u5927\u4E0D\u8D85\u8FC7 3 \u4E2A\u6708' }),\n work_item_type_keys: Type.Optional(\n Type.Array(Type.String({ description: '\u5DE5\u4F5C\u9879\u7C7B\u578B key\uFF0C\u4F20 _all \u67E5\u8BE2\u6240\u6709\u7C7B\u578B' })),\n ),\n }),\n endpoint,\n });\n count++;\n\n // list_related_workitems \u2014 \u5173\u8054\u5DE5\u4F5C\u9879\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_related_workitems',\n mcpToolName: 'list_related_workitems',\n label: 'Feishu Project: \u5173\u8054\u5DE5\u4F5C\u9879',\n description: '\u67E5\u8BE2\u67D0\u4E2A\u5173\u8054\u5DE5\u4F5C\u9879\u5B57\u6BB5\u6240\u5173\u8054\u7684\u5DE5\u4F5C\u9879\u5B9E\u4F8B\u7B80\u8981\u4FE1\u606F\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n work_item_type_key: Type.String({ description: '\u6E90\u5DE5\u4F5C\u9879\u7C7B\u578B' }),\n work_item_id: Type.Number({ description: '\u6E90\u5DE5\u4F5C\u9879\u5B9E\u4F8B ID' }),\n relation_work_item_type_key: Type.String({ description: '\u5173\u8054\u7684\u76EE\u6807\u5DE5\u4F5C\u9879\u7C7B\u578B' }),\n relation_key: Type.String({ description: '\u5173\u8054\u5173\u7CFB key \u6216\u5BF9\u63A5\u6807\u8BC6' }),\n relation_type: Type.Optional(Type.Number({ description: '\u5173\u8054\u6807\u8BC6\u65B9\u5F0F\uFF1A0=\u5B57\u6BB5ID\uFF0C1=\u5BF9\u63A5\u6807\u8BC6\u3002\u9ED8\u8BA4 0' })),\n page_num: Type.Optional(Type.Number({ description: '\u9875\u7801\uFF0C\u4ECE 1 \u5F00\u59CB' })),\n page_size: Type.Optional(Type.Number({ description: '\u6BCF\u9875\u6570\u91CF\uFF0C\u6700\u5927 50' })),\n }),\n endpoint,\n });\n count++;\n\n // list_workitem_relations \u2014 \u7A7A\u95F4\u5173\u8054\u5173\u7CFB\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_workitem_relations',\n mcpToolName: 'list_workitem_relations',\n label: 'Feishu Project: \u5173\u8054\u5173\u7CFB\u5217\u8868',\n description: '\u67E5\u8BE2\u67D0\u4E2A\u7A7A\u95F4\u4E0B\u7684\u6240\u6709\u5DE5\u4F5C\u9879\u5173\u8054\u5173\u7CFB\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n }),\n endpoint,\n });\n count++;\n\n // list_team_members \u2014 \u56E2\u961F\u6210\u5458\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_team_members',\n mcpToolName: 'list_team_members',\n label: 'Feishu Project: \u56E2\u961F\u6210\u5458',\n description: '\u67E5\u8BE2\u56E2\u961F\u4FE1\u606F\u548C\u6210\u5458\u5217\u8868\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n team_id: Type.String({ description: '\u56E2\u961F ID' }),\n page_token: Type.Optional(Type.String({ description: '\u5206\u9875 token\uFF0C\u9996\u9875\u4E0D\u4F20' })),\n }),\n endpoint,\n });\n count++;\n\n // list_charts \u2014 \u5EA6\u91CF\u56FE\u8868\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_charts',\n mcpToolName: 'list_charts',\n label: 'Feishu Project: \u5EA6\u91CF\u56FE\u8868',\n description: '\u67E5\u8BE2\u6307\u5B9A\u7A7A\u95F4\u89C6\u56FE\u4E0B\u7684\u6240\u6709\u5EA6\u91CF\u56FE\u8868\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n view_id: Type.String({ description: '\u89C6\u56FE ID' }),\n page_num: Type.Optional(Type.Number({ description: '\u9875\u7801\uFF0C\u9ED8\u8BA4 1' })),\n page_size: Type.Optional(Type.Number({ description: '\u6BCF\u9875\u6761\u6570\uFF0C\u9ED8\u8BA4 50\uFF0C\u6700\u5927 200' })),\n }),\n endpoint,\n });\n count++;\n\n // create_fixed_view \u2014 \u521B\u5EFA\u56FA\u5B9A\u89C6\u56FE\n registerProjectMcpTool(api, {\n name: 'feishu_project_create_fixed_view',\n mcpToolName: 'create_fixed_view',\n label: 'Feishu Project: \u521B\u5EFA\u56FA\u5B9A\u89C6\u56FE',\n description: '\u5728\u6307\u5B9A\u7A7A\u95F4\u548C\u5DE5\u4F5C\u9879\u7C7B\u578B\u4E0B\u65B0\u589E\u4E00\u4E2A\u56FA\u5B9A\u89C6\u56FE\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n name: Type.String({ description: '\u56FA\u5B9A\u89C6\u56FE\u540D\u79F0' }),\n work_item_type_key: Type.String({ description: '\u5DE5\u4F5C\u9879\u7C7B\u578B key' }),\n work_item_id_list: Type.Array(\n Type.Number({ description: '\u5DE5\u4F5C\u9879 ID' }),\n { description: '\u9700\u8981\u6DFB\u52A0\u5165\u89C6\u56FE\u7684\u5DE5\u4F5C\u9879 ID \u5217\u8868\uFF0C\u4E0A\u9650 200 \u4E2A' },\n ),\n cooperation_mode: Type.Optional(\n Type.Number({ description: '\u534F\u4F5C\u6A21\u5F0F\uFF1A1=\u6307\u5B9A\u4EBA\u5458/\u56E2\u961F, 2=\u5168\u90E8\u7BA1\u7406\u5458, 3=\u5168\u90E8\u6210\u5458\u3002\u9ED8\u8BA4 1' }),\n ),\n }),\n endpoint,\n });\n count++;\n\n // update_fixed_view \u2014 \u66F4\u65B0\u56FA\u5B9A\u89C6\u56FE\n registerProjectMcpTool(api, {\n name: 'feishu_project_update_fixed_view',\n mcpToolName: 'update_fixed_view',\n label: 'Feishu Project: \u66F4\u65B0\u56FA\u5B9A\u89C6\u56FE',\n description: '\u66F4\u65B0\u56FA\u5B9A\u89C6\u56FE\u7684\u5DE5\u4F5C\u9879\u5217\u8868\uFF08\u65B0\u589E\u6216\u5220\u9664\uFF0C\u4E0D\u80FD\u540C\u65F6\u64CD\u4F5C\uFF09\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n view_id: Type.String({ description: '\u56FA\u5B9A\u89C6\u56FE ID' }),\n work_item_type_key: Type.String({ description: '\u5DE5\u4F5C\u9879\u7C7B\u578B key' }),\n add_work_item_ids: Type.Optional(\n Type.Array(Type.Number(), { description: '\u9700\u8981\u6DFB\u52A0\u7684\u5DE5\u4F5C\u9879 ID \u5217\u8868\uFF0C\u4E0A\u9650 200' }),\n ),\n remove_work_item_ids: Type.Optional(\n Type.Array(Type.Number(), { description: '\u9700\u8981\u5220\u9664\u7684\u5DE5\u4F5C\u9879 ID \u5217\u8868\uFF0C\u4E0A\u9650 200' }),\n ),\n }),\n endpoint,\n });\n count++;\n\n log.info(`registered ${count} project MCP tools with endpoint=${endpoint}`);\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * \u98DE\u4E66\u9879\u76EE MCP \u5DE5\u5177\u6CE8\u518C\n *\n * \u6BCF\u4E2A\u5DE5\u5177\u4F7F\u7528\u98DE\u4E66\u9879\u76EE\u4E13\u5C5E\u7684 MCP \u7AEF\u70B9\u548C\u72EC\u7ACB\u7684 OAuth token\u3002\n * \u4E0D\u901A\u8FC7 registerMcpTool\uFF08\u4F7F\u7528\u98DE\u4E66 Open API UAT\uFF09\uFF0C\u800C\u662F\u4F7F\u7528\n * registerProjectMcpTool \u5305\u88C5\u51FD\u6570\u3002\n *\n * \u5DE5\u5177\u53C2\u6570\u7B7E\u540D\u53C2\u7167\u98DE\u4E66\u9879\u76EE MCP Server \u7684\u5B9E\u9645\u5B9A\u4E49\u3002\n */\n\nimport type { OpenClawPluginApi } from 'openclaw/plugin-sdk';\nimport type { TSchema } from '@sinclair/typebox';\nimport { Type } from '@sinclair/typebox';\nimport type { McpRpcResponse } from '../shared';\nimport { isRecord, unwrapJsonRpcResult } from '../shared';\nimport { createToolContext, formatToolResult } from '../../helpers';\nimport { getProjectStoredToken, setProjectStoredToken, projectTokenStatus, getProjectClientId } from '../../../core/project-token-store';\nimport type { StoredUAToken } from '../../../core/token-store';\nimport { refreshProjectToken } from '../../../core/project-auth';\nimport { getProjectDomainFromConfig } from './endpoint';\nimport { getTicket } from '../../../core/lark-ticket';\nimport { larkLogger } from '../../../core/lark-logger';\nimport { LarkClient } from '../../../core/lark-client';\n\nconst log = larkLogger('tools/mcp/project');\n\n// ---------------------------------------------------------------------------\n// \u98DE\u4E66\u9879\u76EE MCP \u4E13\u7528 JSON-RPC \u5BA2\u6237\u7AEF\uFF08\u4F7F\u7528\u6807\u51C6 OAuth Bearer \u8BA4\u8BC1\uFF09\n// ---------------------------------------------------------------------------\n\nasync function callProjectMcpTool(\n name: string,\n args: Record<string, unknown>,\n toolCallId: string,\n accessToken: string,\n endpoint: string,\n): Promise<unknown> {\n const body = {\n jsonrpc: '2.0',\n id: toolCallId,\n method: 'tools/call',\n params: { name, arguments: args },\n };\n\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify(body),\n });\n\n const text = await res.text();\n if (!res.ok) {\n throw new Error(`MCP HTTP ${res.status} ${res.statusText}: ${text.slice(0, 4000)}`);\n }\n\n let data: McpRpcResponse;\n try {\n data = JSON.parse(text) as McpRpcResponse;\n } catch {\n throw new Error(`MCP \u8FD4\u56DE\u975E JSON\uFF1A${text.slice(0, 4000)}`);\n }\n\n if ('error' in data) {\n throw new Error(`MCP error ${data.error.code}: ${data.error.message}`);\n }\n\n return unwrapJsonRpcResult(data.result);\n}\n\n// ---------------------------------------------------------------------------\n// \u98DE\u4E66\u9879\u76EE MCP \u5DE5\u5177\u901A\u7528\u6CE8\u518C\u51FD\u6570\n// ---------------------------------------------------------------------------\n\ninterface ProjectMcpToolConfig {\n name: string;\n mcpToolName: string;\n label: string;\n description: string;\n schema: TSchema;\n endpoint: string | (() => string);\n transformArgs?: (args: Record<string, unknown>) => Record<string, unknown>;\n}\n\nexport function registerProjectMcpTool(\n api: OpenClawPluginApi,\n config: ProjectMcpToolConfig,\n): void {\n const { log: toolLog } = createToolContext(api, config.name);\n\n api.registerTool(\n {\n name: config.name,\n label: config.label,\n description: config.description,\n parameters: config.schema,\n async execute(toolCallId, params) {\n const p = params as Record<string, unknown>;\n try {\n const startTime = Date.now();\n\n const ticket = getTicket();\n const senderOpenId = ticket?.senderOpenId;\n if (!senderOpenId) {\n return formatToolResult({\n error: '\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u7528\u6237\u8EAB\u4EFD\uFF0C\u8BF7\u5728\u98DE\u4E66\u5BF9\u8BDD\u4E2D\u4F7F\u7528\u6B64\u5DE5\u5177\u3002',\n });\n }\n\n const currentDomain = getProjectDomainFromConfig(LarkClient.runtime.config.loadConfig());\n if (!currentDomain) {\n return formatToolResult({\n error: 'project_auth_required',\n message: '\u98DE\u4E66\u9879\u76EE\u73AF\u5883\u672A\u914D\u7F6E\u3002\u8BF7\u5148\u8C03\u7528 feishu_project_oauth \u5DE5\u5177\u7684 authorize \u64CD\u4F5C\u9009\u62E9\u73AF\u5883\u5E76\u5B8C\u6210\u6388\u6743\u3002',\n });\n }\n\n const clientId = await getProjectClientId(senderOpenId, currentDomain);\n if (!clientId) {\n return formatToolResult({\n error: 'project_auth_required',\n message: '\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u5148\u8C03\u7528 feishu_project_oauth \u5DE5\u5177\u5B8C\u6210\u6388\u6743\u3002',\n });\n }\n\n let token = await getProjectStoredToken(clientId, senderOpenId);\n if (!token) {\n return formatToolResult({\n error: 'project_auth_required',\n message: '\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u5148\u8C03\u7528 feishu_project_oauth \u5DE5\u5177\u5B8C\u6210\u6388\u6743\u3002',\n });\n }\n\n // \u6821\u9A8C token \u73AF\u5883\u4E0E\u5F53\u524D\u914D\u7F6E\u7684\u73AF\u5883\u662F\u5426\u5339\u914D\n if (token.domain && currentDomain && token.domain !== currentDomain) {\n return formatToolResult({\n error: 'project_auth_required',\n message: `\u5F53\u524D\u98DE\u4E66\u9879\u76EE\u73AF\u5883\u5DF2\u5207\u6362\u4E3A\u300C${currentDomain}\u300D\uFF0C\u4E0E\u6388\u6743\u65F6\u7684\u73AF\u5883\u300C${token.domain}\u300D\u4E0D\u5339\u914D\u3002\u8BF7\u8C03\u7528 feishu_project_oauth \u5DE5\u5177\u7684 authorize \u64CD\u4F5C\u91CD\u65B0\u6388\u6743\u3002`,\n });\n }\n\n const status = projectTokenStatus(token);\n if (status === 'expired') {\n return formatToolResult({\n error: 'project_auth_required',\n message: '\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5DF2\u8FC7\u671F\u3002\u8BF7\u8C03\u7528 feishu_project_oauth \u5DE5\u5177\u7684 authorize \u64CD\u4F5C\u91CD\u65B0\u6388\u6743\u3002',\n });\n }\n\n // Token \u5373\u5C06\u8FC7\u671F\u6216\u5DF2\u8FC7\u671F\u4F46 refresh_token \u4ECD\u6709\u6548 \u2192 \u81EA\u52A8\u5237\u65B0\n if (status === 'needs_refresh') {\n try {\n const resolvedEp = typeof config.endpoint === 'function' ? config.endpoint() : config.endpoint;\n const refreshed = await refreshProjectToken(resolvedEp, token.refreshToken, clientId);\n token = {\n ...token,\n accessToken: refreshed.access_token,\n refreshToken: refreshed.refresh_token ?? token.refreshToken,\n expiresAt: Date.now() + (refreshed.expires_in ?? 7200) * 1000,\n scope: refreshed.scope ?? token.scope,\n domain: currentDomain,\n };\n await setProjectStoredToken(token as StoredUAToken & { domain: string });\n toolLog.debug?.('project token refreshed');\n } catch (refreshErr) {\n toolLog.error(`project token refresh failed: ${refreshErr}`);\n return formatToolResult({\n error: 'project_auth_required',\n message: '\u98DE\u4E66\u9879\u76EE token \u5237\u65B0\u5931\u8D25\uFF0C\u8BF7\u8C03\u7528 feishu_project_oauth \u5DE5\u5177\u7684 authorize \u64CD\u4F5C\u91CD\u65B0\u6388\u6743\u3002',\n });\n }\n }\n\n const resolvedEndpoint = typeof config.endpoint === 'function' ? config.endpoint() : config.endpoint;\n\n const finalArgs = config.transformArgs ? config.transformArgs(p) : p;\n\n const result = await callProjectMcpTool(\n config.mcpToolName,\n finalArgs,\n toolCallId,\n token.accessToken,\n resolvedEndpoint,\n );\n\n const duration = Date.now() - startTime;\n toolLog.debug?.(`${config.mcpToolName} succeeded in ${duration}ms`);\n\n // MCP \u8FD4\u56DE\u503C\u5904\u7406\uFF08\u540C registerMcpTool \u903B\u8F91\uFF09\n if (isRecord(result) && Array.isArray((result as Record<string, unknown>).content)) {\n const mcpContent = (result as Record<string, unknown>).content as Array<{\n type: string;\n text: string;\n }>;\n let details: unknown = result;\n if (mcpContent.length === 1 && mcpContent[0]?.type === 'text') {\n try {\n details = JSON.parse(mcpContent[0].text);\n } catch {\n // text is not JSON\n }\n }\n return {\n content: mcpContent.map((c) => ({\n type: 'text' as const,\n text: c.text,\n })),\n details,\n };\n }\n return formatToolResult(result);\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n toolLog.error(`${config.mcpToolName} failed: ${errMsg}`);\n return formatToolResult({\n error: errMsg,\n hint: '\u5982\u679C\u662F\u6388\u6743\u95EE\u9898\uFF0C\u8BF7\u8C03\u7528 feishu_project_oauth \u5DE5\u5177\u91CD\u65B0\u6388\u6743\u3002',\n });\n }\n },\n },\n { name: config.name },\n );\n}\n\n// ---------------------------------------------------------------------------\n// \u5177\u4F53\u5DE5\u5177\u6CE8\u518C\uFF08\u53C2\u6570\u7B7E\u540D\u5339\u914D\u98DE\u4E66\u9879\u76EE MCP Server \u5B9E\u9645\u5B9A\u4E49\uFF09\n// ---------------------------------------------------------------------------\n\nexport function registerProjectTools(api: OpenClawPluginApi, endpoint: string | (() => string)) {\n let count = 0;\n\n // list_todo \u2014 \u5F85\u529E/\u5DF2\u529E/\u8D85\u671F/\u672C\u5468\u5230\u671F\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_todo',\n mcpToolName: 'list_todo',\n label: 'Feishu Project: \u5F85\u529E\u5217\u8868',\n description:\n '\u83B7\u53D6\u5F53\u524D\u7528\u6237\u7684\u98DE\u4E66\u9879\u76EE\u5DE5\u4F5C\u9879\u5217\u8868\u3002\u652F\u6301\u67E5\u8BE2\u7C7B\u578B\uFF1A' +\n 'todo(\u5F85\u529E)\u3001done(\u5DF2\u529E)\u3001overdue(\u5DF2\u8D85\u671F)\u3001this_week(\u672C\u5468\u5230\u671F)\u3002',\n schema: Type.Object({\n action: Type.String({\n description: '\u67E5\u8BE2\u7C7B\u578B\u3002todo \u4E3A\u5F85\u529E, done \u4E3A\u5DF2\u529E, overdue \u4E3A\u5DF2\u8D85\u671F, this_week \u4E3A\u672C\u5468\u5230\u671F',\n }),\n asset_key: Type.Optional(\n Type.String({ description: '\u5DE5\u4F5C\u533A key\uFF0C\u683C\u5F0F\u4E3A Asset_\uFF0C\u4EC5\u5728\u63A5\u53E3\u62A5\u9519\u9700\u8981\u9009\u62E9\u65F6\u4F20\u9012' }),\n ),\n page_num: Type.Optional(\n Type.Number({ description: '\u9875\u7801\uFF0C\u4ECE 1 \u5F00\u59CB\u9012\u589E\uFF0C\u6BCF\u9875 50 \u6761' }),\n ),\n }),\n endpoint,\n });\n count++;\n\n // search_by_mql \u2014 MQL \u67E5\u8BE2\n registerProjectMcpTool(api, {\n name: 'feishu_project_search_by_mql',\n mcpToolName: 'search_by_mql',\n label: 'Feishu Project: MQL \u641C\u7D22',\n description:\n '\u4F7F\u7528 MQL \u641C\u7D22\u98DE\u4E66\u9879\u76EE\u5DE5\u4F5C\u9879\u3002MQL \u57FA\u4E8E SQL \u8BED\u6CD5\u6269\u5C55\u3002' +\n '\u4F7F\u7528\u524D\u9700\u5148\u7528 get_workitem_info \u786E\u8BA4\u7A7A\u95F4\u548C\u5DE5\u4F5C\u9879\u7C7B\u578B\u3002' +\n 'select \u540E\u4F7F\u7528\u53EF\u8BFB\u6027\u5F3A\u7684\u5B57\u6BB5\u540D\u79F0\uFF08\u5982\"\u4EFB\u52A1\u540D\u79F0\"\u800C\u975E\"name\"\uFF09\u3002',\n schema: Type.Object({\n project_key: Type.String({\n description: '\u7A7A\u95F4 projectKey\u3001simpleName \u6216\u7A7A\u95F4\u540D',\n }),\n mql: Type.Optional(\n Type.String({\n description:\n 'MQL \u8BED\u53E5\u3002\u8BED\u6CD5\uFF1Aselect `\u5B57\u6BB5\u540D` from `\u7A7A\u95F4\u540D`.`\u5DE5\u4F5C\u9879\u540D` where `\u5B57\u6BB5\u540D` = \\'\u5B57\u6BB5\u503C\\'',\n }),\n ),\n helper: Type.Optional(\n Type.String({\n description: '\u5E2E\u52A9\u5DE5\u5177\u3002\u8F93\u5165 \"helper\" \u83B7\u53D6\u6240\u6709\u547D\u4EE4\u548C\u7528\u6CD5\u793A\u4F8B',\n }),\n ),\n session_id: Type.Optional(\n Type.String({ description: 'sessionID\uFF0C\u7528\u4E8E\u5206\u9875\u67E5\u8BE2\uFF0C\u4F20\u5165\u540E\u4E0D\u89E3\u6790 mql' }),\n ),\n group_pagination_list: Type.Optional(\n Type.Array(\n Type.Object({\n group_id: Type.Optional(Type.String({ description: '\u5206\u7EC4 ID' })),\n page_num: Type.Optional(Type.Number({ description: '\u9875\u7801\uFF0C\u4ECE 1 \u5F00\u59CB' })),\n }),\n ),\n ),\n }),\n transformArgs: (args) => {\n const { mql, ...rest } = args;\n if (mql !== undefined) {\n return { ...rest, moql: mql };\n }\n return args;\n },\n endpoint,\n });\n count++;\n\n // get_workitem_info \u2014 \u83B7\u53D6\u5DE5\u4F5C\u9879\u7C7B\u578B\u7684\u5B57\u6BB5\u4E0E\u89D2\u8272\u4FE1\u606F\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_workitem_info',\n mcpToolName: 'get_workitem_info',\n label: 'Feishu Project: \u5DE5\u4F5C\u9879\u7C7B\u578B\u4FE1\u606F',\n description: '\u83B7\u53D6\u4E00\u4E2A\u5DE5\u4F5C\u9879\u7C7B\u578B\u5177\u5907\u7684\u53EF\u7528\u5B57\u6BB5\u4E0E\u89D2\u8272\u4FE1\u606F\u3002',\n schema: Type.Object({\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n ),\n work_item_type: Type.String({\n description: '\u5DE5\u4F5C\u9879\u7C7B\u578B\u7684\u7CFB\u7EDF\u6807\u8BC6\u6216\u540D\u79F0\uFF0C\u5982 story\u3001\u9700\u6C42\u3001issue\u3001\u7F3A\u9677\u7B49',\n }),\n }),\n endpoint,\n });\n count++;\n\n // get_workitem_brief \u2014 \u83B7\u53D6\u5DE5\u4F5C\u9879\u6982\u51B5\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_workitem_brief',\n mcpToolName: 'get_workitem_brief',\n label: 'Feishu Project: \u5DE5\u4F5C\u9879\u6982\u51B5',\n description: '\u83B7\u53D6\u4E00\u4E2A\u5DE5\u4F5C\u9879\u5B9E\u4F8B\u7684\u6982\u51B5\u4FE1\u606F\u3002',\n schema: Type.Object({\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey \u6216 simpleName' }),\n ),\n work_item_id: Type.String({\n description: '\u5DE5\u4F5C\u9879 ID \u6216\u540D\u79F0',\n }),\n fields: Type.Optional(\n Type.Array(Type.String({ description: '\u8981\u67E5\u8BE2\u7684 field_key \u6216 field_name' })),\n ),\n }),\n endpoint,\n });\n count++;\n\n // get_workitem_field_meta \u2014 \u83B7\u53D6\u521B\u5EFA\u5DE5\u4F5C\u9879\u5143\u4FE1\u606F\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_workitem_field_meta',\n mcpToolName: 'get_workitem_field_meta',\n label: 'Feishu Project: \u5B57\u6BB5\u5143\u4FE1\u606F',\n description: '\u83B7\u53D6\u521B\u5EFA\u5DE5\u4F5C\u9879\u65F6\u7684\u5B57\u6BB5\u5143\u4FE1\u606F\uFF08\u5B57\u6BB5\u540D\u3001\u7C7B\u578B\u3001\u9009\u9879\u7B49\uFF09\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n work_item_type_key: Type.String({ description: '\u5DE5\u4F5C\u9879\u7C7B\u578B key' }),\n }),\n endpoint,\n });\n count++;\n\n // create_workitem \u2014 \u521B\u5EFA\u5DE5\u4F5C\u9879\n registerProjectMcpTool(api, {\n name: 'feishu_project_create_workitem',\n mcpToolName: 'create_workitem',\n label: 'Feishu Project: \u521B\u5EFA\u5DE5\u4F5C\u9879',\n description: '\u521B\u5EFA\u4E00\u6761\u5DE5\u4F5C\u9879\u5B9E\u4F8B\u3002\u521B\u5EFA\u6210\u529F\u540E\u83B7\u5F97\u8BE6\u60C5\u9875 URL\u3002',\n schema: Type.Object({\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey \u6216 simpleName' }),\n ),\n work_item_type: Type.String({\n description: '\u5DE5\u4F5C\u9879\u7C7B\u578B',\n }),\n fields: Type.Optional(\n Type.Array(\n Type.Object({\n field_key: Type.String({ description: '\u5B57\u6BB5 key' }),\n field_value: Type.String({\n description: '\u5B57\u6BB5\u503C\u3002\u65F6\u95F4\u7C7B\u9700\u4F20 16 \u4F4D unix \u6BEB\u79D2\u65F6\u95F4\u6233\uFF0C\u4EBA\u5458\u7C7B\u9700\u7528\u82F1\u6587\u9017\u53F7\u533A\u9694',\n }),\n }),\n ),\n ),\n }),\n endpoint,\n });\n count++;\n\n // update_field \u2014 \u4FEE\u6539\u5DE5\u4F5C\u9879\u5B57\u6BB5\n registerProjectMcpTool(api, {\n name: 'feishu_project_update_field',\n mcpToolName: 'update_field',\n label: 'Feishu Project: \u4FEE\u6539\u5B57\u6BB5',\n description: '\u4FEE\u6539\u6307\u5B9A\u5DE5\u4F5C\u9879\u5B9E\u4F8B\u7684\u5B57\u6BB5\u503C\uFF0C\u652F\u6301\u4E00\u6B21\u4FEE\u6539\u591A\u4E2A\u5B57\u6BB5\u3002',\n schema: Type.Object({\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n ),\n work_item_id: Type.String({\n description: '\u5DE5\u4F5C\u9879 ID \u6216\u540D\u79F0',\n }),\n fields: Type.Optional(\n Type.Array(\n Type.Object({\n field_key: Type.String({ description: '\u5B57\u6BB5 key' }),\n field_value: Type.String({\n description: '\u5B57\u6BB5\u503C\u3002\u65F6\u95F4\u7C7B\u9700\u4F20 16 \u4F4D unix \u6BEB\u79D2\u65F6\u95F4\u6233\uFF0C\u4EBA\u5458\u7C7B\u9700\u7528\u82F1\u6587\u9017\u53F7\u533A\u9694',\n }),\n }),\n ),\n ),\n }),\n endpoint,\n });\n count++;\n\n // add_comment \u2014 \u6DFB\u52A0\u8BC4\u8BBA\n registerProjectMcpTool(api, {\n name: 'feishu_project_add_comment',\n mcpToolName: 'add_comment',\n label: 'Feishu Project: \u6DFB\u52A0\u8BC4\u8BBA',\n description: '\u5728\u6307\u5B9A\u5DE5\u4F5C\u9879\u7684\u8BC4\u8BBA/\u5907\u6CE8\u9875\u6DFB\u52A0\u4E00\u6761\u8BC4\u8BBA\uFF0C\u652F\u6301 markdown \u683C\u5F0F\u3002',\n schema: Type.Object({\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n ),\n work_item_id: Type.String({\n description: '\u5DE5\u4F5C\u9879 ID \u6216\u540D\u79F0',\n }),\n work_item_type: Type.Optional(\n Type.String({ description: '\u5DE5\u4F5C\u9879\u7C7B\u578B' }),\n ),\n comment_content: Type.String({\n description: '\u8BC4\u8BBA\u5185\u5BB9\uFF0C\u652F\u6301 markdown \u683C\u5F0F',\n }),\n url: Type.Optional(\n Type.String({ description: '\u5DE5\u4F5C\u9879 URL\uFF0C\u53EF\u4ECE\u4E2D\u89E3\u6790 project_key \u7B49\u4FE1\u606F' }),\n ),\n }),\n endpoint,\n });\n count++;\n\n // finish_node \u2014 \u5B8C\u6210\u8282\u70B9/\u6D41\u8F6C\u72B6\u6001\n registerProjectMcpTool(api, {\n name: 'feishu_project_finish_node',\n mcpToolName: 'finish_node',\n label: 'Feishu Project: \u6D41\u8F6C\u72B6\u6001',\n description: '\u5B8C\u6210\u67D0\u4E2A\u8282\u70B9\uFF0C\u6D41\u8F6C\u5DE5\u4F5C\u9879\u5230\u4E0B\u4E00\u4E2A\u72B6\u6001\u3002',\n schema: Type.Object({\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n ),\n work_item_id: Type.String({\n description: '\u5DE5\u4F5C\u9879 ID \u6216\u540D\u79F0',\n }),\n node_id: Type.String({\n description: '\u8981\u5B8C\u6210\u6D41\u8F6C\u7684\u8282\u70B9 ID \u6216\u8282\u70B9\u540D\u79F0',\n }),\n }),\n endpoint,\n });\n count++;\n\n // get_node_detail \u2014 \u83B7\u53D6\u8282\u70B9\u8BE6\u60C5\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_node_detail',\n mcpToolName: 'get_node_detail',\n label: 'Feishu Project: \u8282\u70B9\u8BE6\u60C5',\n description: '\u83B7\u53D6\u6307\u5B9A\u8282\u70B9\u7684\u5173\u952E\u4FE1\u606F\uFF0C\u5305\u62EC\u8282\u70B9\u4FE1\u606F\u3001\u5B50\u9879\u4FE1\u606F\u3001\u81EA\u5B9A\u4E49\u5B57\u6BB5\u4FE1\u606F\u3002',\n schema: Type.Object({\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n ),\n work_item_id: Type.String({ description: '\u5DE5\u4F5C\u9879 ID' }),\n node_id: Type.String({ description: '\u8282\u70B9\u540D\u79F0\u6216\u8282\u70B9 ID' }),\n node_ids: Type.Optional(\n Type.Array(Type.String({ description: '\u8282\u70B9\u540D\u79F0\u6216\u8282\u70B9 ID \u5217\u8868' })),\n ),\n }),\n endpoint,\n });\n count++;\n\n // get_transitable_statuses \u2014 \u83B7\u53D6\u53EF\u6D41\u8F6C\u72B6\u6001\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_transitable_statuses',\n mcpToolName: 'get_transitable_statuses',\n label: 'Feishu Project: \u53EF\u6D41\u8F6C\u72B6\u6001',\n description: '\u83B7\u53D6\u72B6\u6001\u6D41\u5DE5\u4F5C\u9879\u7684\u53EF\u6D41\u8F6C\u72B6\u6001\u5217\u8868\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n work_item_id: Type.String({ description: '\u5DE5\u4F5C\u9879 ID' }),\n work_item_type_key: Type.String({ description: '\u5DE5\u4F5C\u9879\u7C7B\u578B key' }),\n user_key: Type.String({ description: '\u7528\u6237 userKey' }),\n }),\n endpoint,\n });\n count++;\n\n // list_workitem_types \u2014 \u83B7\u53D6\u5DE5\u4F5C\u9879\u7C7B\u578B\u5217\u8868\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_workitem_types',\n mcpToolName: 'list_workitem_types',\n label: 'Feishu Project: \u5DE5\u4F5C\u9879\u7C7B\u578B\u5217\u8868',\n description: '\u83B7\u53D6\u7A7A\u95F4\u4E0B\u7684\u6240\u6709\u5DE5\u4F5C\u9879\u7C7B\u578B\u5217\u8868\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n }),\n endpoint,\n });\n count++;\n\n // search_project_info \u2014 \u67E5\u8BE2\u7A7A\u95F4\u4FE1\u606F\n registerProjectMcpTool(api, {\n name: 'feishu_project_search_project_info',\n mcpToolName: 'search_project_info',\n label: 'Feishu Project: \u67E5\u8BE2\u7A7A\u95F4',\n description: '\u67E5\u8BE2\u98DE\u4E66\u9879\u76EE\u7A7A\u95F4\u57FA\u7840\u4FE1\u606F\u3002',\n schema: Type.Object({\n project_key: Type.String({\n description: '\u7A7A\u95F4 projectKey\u3001simpleName \u6216\u7A7A\u95F4\u540D\u79F0',\n }),\n }),\n endpoint,\n });\n count++;\n\n // search_user_info \u2014 \u67E5\u8BE2\u7528\u6237\u4FE1\u606F\n registerProjectMcpTool(api, {\n name: 'feishu_project_search_user_info',\n mcpToolName: 'search_user_info',\n label: 'Feishu Project: \u67E5\u8BE2\u7528\u6237',\n description: '\u6279\u91CF\u67E5\u8BE2\u7528\u6237\u57FA\u7840\u4FE1\u606F\uFF0C\u6700\u591A 20 \u4E2A\u3002',\n schema: Type.Object({\n user_keys: Type.Array(\n Type.String({ description: 'userKey\u3001Email \u6216\u540D\u5B57' }),\n ),\n project_key: Type.Optional(\n Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n ),\n }),\n endpoint,\n });\n count++;\n\n // list_workitem_comments \u2014 \u67E5\u8BE2\u8BC4\u8BBA\u5217\u8868\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_workitem_comments',\n mcpToolName: 'list_workitem_comments',\n label: 'Feishu Project: \u8BC4\u8BBA\u5217\u8868',\n description: '\u67E5\u8BE2\u5DE5\u4F5C\u9879\u8BC4\u8BBA\u5217\u8868\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n work_item_id: Type.String({ description: '\u5DE5\u4F5C\u9879 ID \u6216\u540D\u79F0' }),\n page_num: Type.Optional(Type.Number({ description: '\u9875\u7801\uFF0C\u9ED8\u8BA4 1' })),\n start_time: Type.Optional(Type.Number({ description: '\u5F00\u59CB\u65F6\u95F4\uFF0CUnix \u6BEB\u79D2\u65F6\u95F4\u6233' })),\n end_time: Type.Optional(Type.Number({ description: '\u7ED3\u675F\u65F6\u95F4\uFF0CUnix \u6BEB\u79D2\u65F6\u95F4\u6233' })),\n }),\n endpoint,\n });\n count++;\n\n // search_view_by_title \u2014 \u641C\u7D22\u89C6\u56FE\n registerProjectMcpTool(api, {\n name: 'feishu_project_search_view_by_title',\n mcpToolName: 'search_view_by_title',\n label: 'Feishu Project: \u641C\u7D22\u89C6\u56FE',\n description: '\u901A\u8FC7\u89C6\u56FE\u540D\u79F0\u6A21\u7CCA\u67E5\u8BE2\u89C6\u56FE ID\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n view_scope: Type.String({\n description: '\u5DE5\u4F5C\u9879\u7C7B\u578B\uFF1A\u9700\u6C42\u4E3A storyView\uFF0C\u7F3A\u9677\u4E3A issueView\uFF0C\u7248\u672C\u4E3A version\uFF0C\u81EA\u5B9A\u4E49\u7C7B\u578B\u4E3A\u5BF9\u5E94 key',\n }),\n key_word: Type.String({ description: '\u89C6\u56FE\u540D\u79F0\u5173\u952E\u5B57' }),\n }),\n endpoint,\n });\n count++;\n\n // get_view_detail \u2014 \u83B7\u53D6\u89C6\u56FE\u8BE6\u60C5\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_view_detail',\n mcpToolName: 'get_view_detail',\n label: 'Feishu Project: \u89C6\u56FE\u8BE6\u60C5',\n description: '\u83B7\u53D6\u6307\u5B9A\u89C6\u56FE\u7684\u5DE5\u4F5C\u9879\u5217\u8868\u3002',\n schema: Type.Object({\n view_id: Type.String({ description: '\u89C6\u56FE ID' }),\n project_key: Type.Optional(Type.String({ description: '\u7A7A\u95F4 projectKey' })),\n fields: Type.Optional(\n Type.Array(Type.String({ description: '\u8981\u67E5\u8BE2\u7684 field_key \u6216 field_name' })),\n ),\n page_num: Type.Optional(Type.Number({ description: '\u5206\u9875\u9875\u7801' })),\n }),\n endpoint,\n });\n count++;\n\n // get_workitem_man_hour_records \u2014 \u5DE5\u65F6\u8BB0\u5F55\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_workitem_man_hour_records',\n mcpToolName: 'get_workitem_man_hour_records',\n label: 'Feishu Project: \u5DE5\u65F6\u8BB0\u5F55',\n description: '\u83B7\u53D6\u5DE5\u4F5C\u9879\u7684\u5DE5\u65F6\u767B\u8BB0\u8BB0\u5F55\u5217\u8868\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n work_item_type_key: Type.String({ description: '\u5DE5\u4F5C\u9879\u7C7B\u578B key' }),\n work_item_id: Type.Number({ description: '\u5DE5\u4F5C\u9879\u5B9E\u4F8B ID' }),\n page_num: Type.Optional(Type.Number({ description: '\u5206\u9875\u9875\u7801\uFF0C\u6BCF\u9875\u9ED8\u8BA4 20' })),\n }),\n endpoint,\n });\n count++;\n\n // get_workitem_op_record \u2014 \u64CD\u4F5C\u8BB0\u5F55\n registerProjectMcpTool(api, {\n name: 'feishu_project_get_workitem_op_record',\n mcpToolName: 'get_workitem_op_record',\n label: 'Feishu Project: \u64CD\u4F5C\u8BB0\u5F55',\n description: '\u83B7\u53D6\u6307\u5B9A\u7A7A\u95F4\u4E0B\u4E00\u4E2A\u5DE5\u4F5C\u9879\u5B9E\u4F8B\u7684\u64CD\u4F5C\u8BB0\u5F55\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n work_item_id: Type.Number({ description: '\u5DE5\u4F5C\u9879 ID' }),\n start: Type.Optional(Type.Number({ description: '\u5F00\u59CB\u65F6\u95F4\uFF0C\u6BEB\u79D2\u7EA7\u65F6\u95F4\u6233' })),\n end: Type.Optional(Type.Number({ description: '\u7ED3\u675F\u65F6\u95F4\uFF0C\u6BEB\u79D2\u7EA7\u65F6\u95F4\u6233\uFF0C\u4E0E start \u914D\u5408\u6700\u957F 7 \u5929' })),\n operation_type: Type.Optional(\n Type.Array(Type.String({ description: '\u64CD\u4F5C\u7C7B\u578B\uFF1Amodify/create/delete/terminate/restore/complete/rollback/add/remove' })),\n ),\n start_from: Type.Optional(Type.String({ description: '\u5206\u9875\u53C2\u6570\uFF0C\u4F7F\u7528\u4E0A\u6B21\u8FD4\u56DE\u7684 start_from' })),\n }),\n endpoint,\n });\n count++;\n\n // list_schedule \u2014 \u6392\u671F\u67E5\u8BE2\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_schedule',\n mcpToolName: 'list_schedule',\n label: 'Feishu Project: \u6392\u671F\u67E5\u8BE2',\n description: '\u6309\u7A7A\u95F4\u548C\u7528\u6237\u67E5\u8BE2\u6307\u5B9A\u65F6\u95F4\u8303\u56F4\u5185\u7684\u6392\u671F\u660E\u7EC6\uFF0C\u7528\u4E8E\u4EBA\u529B\u8D1F\u8377\u4E0E\u6392\u671F\u5206\u6790\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey\u3001\u540D\u79F0\u6216 simple_name' }),\n user_keys: Type.Array(\n Type.String({ description: '\u7528\u6237\u540D\u79F0\u3001\u90AE\u7BB1\u6216 userkey\uFF0C\u6700\u591A 20 \u4E2A' }),\n ),\n start_time: Type.String({ description: '\u5F00\u59CB\u65F6\u95F4\uFF0C\u683C\u5F0F 2006-01-01' }),\n end_time: Type.String({ description: '\u7ED3\u675F\u65F6\u95F4\uFF0C\u683C\u5F0F 2006-01-01\uFF0C\u6700\u5927\u4E0D\u8D85\u8FC7 3 \u4E2A\u6708' }),\n work_item_type_keys: Type.Optional(\n Type.Array(Type.String({ description: '\u5DE5\u4F5C\u9879\u7C7B\u578B key\uFF0C\u4F20 _all \u67E5\u8BE2\u6240\u6709\u7C7B\u578B' })),\n ),\n }),\n endpoint,\n });\n count++;\n\n // list_related_workitems \u2014 \u5173\u8054\u5DE5\u4F5C\u9879\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_related_workitems',\n mcpToolName: 'list_related_workitems',\n label: 'Feishu Project: \u5173\u8054\u5DE5\u4F5C\u9879',\n description: '\u67E5\u8BE2\u67D0\u4E2A\u5173\u8054\u5DE5\u4F5C\u9879\u5B57\u6BB5\u6240\u5173\u8054\u7684\u5DE5\u4F5C\u9879\u5B9E\u4F8B\u7B80\u8981\u4FE1\u606F\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n work_item_type_key: Type.String({ description: '\u6E90\u5DE5\u4F5C\u9879\u7C7B\u578B' }),\n work_item_id: Type.Number({ description: '\u6E90\u5DE5\u4F5C\u9879\u5B9E\u4F8B ID' }),\n relation_work_item_type_key: Type.String({ description: '\u5173\u8054\u7684\u76EE\u6807\u5DE5\u4F5C\u9879\u7C7B\u578B' }),\n relation_key: Type.String({ description: '\u5173\u8054\u5173\u7CFB key \u6216\u5BF9\u63A5\u6807\u8BC6' }),\n relation_type: Type.Optional(Type.Number({ description: '\u5173\u8054\u6807\u8BC6\u65B9\u5F0F\uFF1A0=\u5B57\u6BB5ID\uFF0C1=\u5BF9\u63A5\u6807\u8BC6\u3002\u9ED8\u8BA4 0' })),\n page_num: Type.Optional(Type.Number({ description: '\u9875\u7801\uFF0C\u4ECE 1 \u5F00\u59CB' })),\n page_size: Type.Optional(Type.Number({ description: '\u6BCF\u9875\u6570\u91CF\uFF0C\u6700\u5927 50' })),\n }),\n endpoint,\n });\n count++;\n\n // list_workitem_relations \u2014 \u7A7A\u95F4\u5173\u8054\u5173\u7CFB\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_workitem_relations',\n mcpToolName: 'list_workitem_relations',\n label: 'Feishu Project: \u5173\u8054\u5173\u7CFB\u5217\u8868',\n description: '\u67E5\u8BE2\u67D0\u4E2A\u7A7A\u95F4\u4E0B\u7684\u6240\u6709\u5DE5\u4F5C\u9879\u5173\u8054\u5173\u7CFB\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n }),\n endpoint,\n });\n count++;\n\n // list_team_members \u2014 \u56E2\u961F\u6210\u5458\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_team_members',\n mcpToolName: 'list_team_members',\n label: 'Feishu Project: \u56E2\u961F\u6210\u5458',\n description: '\u67E5\u8BE2\u56E2\u961F\u4FE1\u606F\u548C\u6210\u5458\u5217\u8868\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n team_id: Type.String({ description: '\u56E2\u961F ID' }),\n page_token: Type.Optional(Type.String({ description: '\u5206\u9875 token\uFF0C\u9996\u9875\u4E0D\u4F20' })),\n }),\n endpoint,\n });\n count++;\n\n // list_charts \u2014 \u5EA6\u91CF\u56FE\u8868\n registerProjectMcpTool(api, {\n name: 'feishu_project_list_charts',\n mcpToolName: 'list_charts',\n label: 'Feishu Project: \u5EA6\u91CF\u56FE\u8868',\n description: '\u67E5\u8BE2\u6307\u5B9A\u7A7A\u95F4\u89C6\u56FE\u4E0B\u7684\u6240\u6709\u5EA6\u91CF\u56FE\u8868\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n view_id: Type.String({ description: '\u89C6\u56FE ID' }),\n page_num: Type.Optional(Type.Number({ description: '\u9875\u7801\uFF0C\u9ED8\u8BA4 1' })),\n page_size: Type.Optional(Type.Number({ description: '\u6BCF\u9875\u6761\u6570\uFF0C\u9ED8\u8BA4 50\uFF0C\u6700\u5927 200' })),\n }),\n endpoint,\n });\n count++;\n\n // create_fixed_view \u2014 \u521B\u5EFA\u56FA\u5B9A\u89C6\u56FE\n registerProjectMcpTool(api, {\n name: 'feishu_project_create_fixed_view',\n mcpToolName: 'create_fixed_view',\n label: 'Feishu Project: \u521B\u5EFA\u56FA\u5B9A\u89C6\u56FE',\n description: '\u5728\u6307\u5B9A\u7A7A\u95F4\u548C\u5DE5\u4F5C\u9879\u7C7B\u578B\u4E0B\u65B0\u589E\u4E00\u4E2A\u56FA\u5B9A\u89C6\u56FE\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n name: Type.String({ description: '\u56FA\u5B9A\u89C6\u56FE\u540D\u79F0' }),\n work_item_type_key: Type.String({ description: '\u5DE5\u4F5C\u9879\u7C7B\u578B key' }),\n work_item_id_list: Type.Array(\n Type.Number({ description: '\u5DE5\u4F5C\u9879 ID' }),\n { description: '\u9700\u8981\u6DFB\u52A0\u5165\u89C6\u56FE\u7684\u5DE5\u4F5C\u9879 ID \u5217\u8868\uFF0C\u4E0A\u9650 200 \u4E2A' },\n ),\n cooperation_mode: Type.Optional(\n Type.Number({ description: '\u534F\u4F5C\u6A21\u5F0F\uFF1A1=\u6307\u5B9A\u4EBA\u5458/\u56E2\u961F, 2=\u5168\u90E8\u7BA1\u7406\u5458, 3=\u5168\u90E8\u6210\u5458\u3002\u9ED8\u8BA4 1' }),\n ),\n }),\n endpoint,\n });\n count++;\n\n // update_fixed_view \u2014 \u66F4\u65B0\u56FA\u5B9A\u89C6\u56FE\n registerProjectMcpTool(api, {\n name: 'feishu_project_update_fixed_view',\n mcpToolName: 'update_fixed_view',\n label: 'Feishu Project: \u66F4\u65B0\u56FA\u5B9A\u89C6\u56FE',\n description: '\u66F4\u65B0\u56FA\u5B9A\u89C6\u56FE\u7684\u5DE5\u4F5C\u9879\u5217\u8868\uFF08\u65B0\u589E\u6216\u5220\u9664\uFF0C\u4E0D\u80FD\u540C\u65F6\u64CD\u4F5C\uFF09\u3002',\n schema: Type.Object({\n project_key: Type.String({ description: '\u7A7A\u95F4 projectKey' }),\n view_id: Type.String({ description: '\u56FA\u5B9A\u89C6\u56FE ID' }),\n work_item_type_key: Type.String({ description: '\u5DE5\u4F5C\u9879\u7C7B\u578B key' }),\n add_work_item_ids: Type.Optional(\n Type.Array(Type.Number(), { description: '\u9700\u8981\u6DFB\u52A0\u7684\u5DE5\u4F5C\u9879 ID \u5217\u8868\uFF0C\u4E0A\u9650 200' }),\n ),\n remove_work_item_ids: Type.Optional(\n Type.Array(Type.Number(), { description: '\u9700\u8981\u5220\u9664\u7684\u5DE5\u4F5C\u9879 ID \u5217\u8868\uFF0C\u4E0A\u9650 200' }),\n ),\n }),\n endpoint,\n });\n count++;\n\n log.info(`registered ${count} project MCP tools with endpoint=${endpoint}`);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -388,13 +388,32 @@ function buildProjectDomainCard() {
|
|
|
388
388
|
function buildProjectDomainConfirmedCard(envLabel) {
|
|
389
389
|
return {
|
|
390
390
|
schema: "2.0",
|
|
391
|
-
config: {
|
|
391
|
+
config: {
|
|
392
|
+
wide_screen_mode: false,
|
|
393
|
+
style: {
|
|
394
|
+
color: {
|
|
395
|
+
"light-green-bg": {
|
|
396
|
+
light_mode: "rgba(52, 199, 89, 0.12)",
|
|
397
|
+
dark_mode: "rgba(52, 199, 89, 0.08)"
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
},
|
|
392
402
|
header: {
|
|
393
|
-
title: {
|
|
394
|
-
|
|
403
|
+
title: {
|
|
404
|
+
tag: "plain_text",
|
|
405
|
+
content: "\u73AF\u5883\u5DF2\u786E\u8BA4"
|
|
406
|
+
},
|
|
407
|
+
subtitle: {
|
|
408
|
+
tag: "plain_text",
|
|
409
|
+
content: ""
|
|
410
|
+
},
|
|
395
411
|
template: "green",
|
|
396
412
|
padding: "12px 12px 12px 12px",
|
|
397
|
-
icon: {
|
|
413
|
+
icon: {
|
|
414
|
+
tag: "standard_icon",
|
|
415
|
+
token: "yes_filled"
|
|
416
|
+
}
|
|
398
417
|
},
|
|
399
418
|
body: {
|
|
400
419
|
elements: [
|