feishu-docs-cli 0.1.0-beta.2
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/LICENSE +21 -0
- package/README.md +333 -0
- package/README.zh.md +332 -0
- package/bin/feishu-docs.js +8 -0
- package/dist/auth.d.ts +76 -0
- package/dist/auth.js +512 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +166 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +42 -0
- package/dist/client.js +269 -0
- package/dist/client.js.map +1 -0
- package/dist/commands/cat.d.ts +6 -0
- package/dist/commands/cat.js +151 -0
- package/dist/commands/cat.js.map +1 -0
- package/dist/commands/create.d.ts +6 -0
- package/dist/commands/create.js +120 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/delete.d.ts +6 -0
- package/dist/commands/delete.js +89 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/info.d.ts +6 -0
- package/dist/commands/info.js +69 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/login.d.ts +10 -0
- package/dist/commands/login.js +90 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/ls.d.ts +6 -0
- package/dist/commands/ls.js +76 -0
- package/dist/commands/ls.js.map +1 -0
- package/dist/commands/read.d.ts +6 -0
- package/dist/commands/read.js +404 -0
- package/dist/commands/read.js.map +1 -0
- package/dist/commands/search.d.ts +7 -0
- package/dist/commands/search.js +87 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/share.d.ts +13 -0
- package/dist/commands/share.js +210 -0
- package/dist/commands/share.js.map +1 -0
- package/dist/commands/spaces.d.ts +6 -0
- package/dist/commands/spaces.js +43 -0
- package/dist/commands/spaces.js.map +1 -0
- package/dist/commands/tree.d.ts +6 -0
- package/dist/commands/tree.js +101 -0
- package/dist/commands/tree.js.map +1 -0
- package/dist/commands/update.d.ts +9 -0
- package/dist/commands/update.js +211 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/wiki.d.ts +6 -0
- package/dist/commands/wiki.js +284 -0
- package/dist/commands/wiki.js.map +1 -0
- package/dist/parser/block-types.d.ts +141 -0
- package/dist/parser/block-types.js +167 -0
- package/dist/parser/block-types.js.map +1 -0
- package/dist/parser/blocks-to-md.d.ts +26 -0
- package/dist/parser/blocks-to-md.js +576 -0
- package/dist/parser/blocks-to-md.js.map +1 -0
- package/dist/parser/text-elements.d.ts +13 -0
- package/dist/parser/text-elements.js +91 -0
- package/dist/parser/text-elements.js.map +1 -0
- package/dist/services/block-writer.d.ts +30 -0
- package/dist/services/block-writer.js +131 -0
- package/dist/services/block-writer.js.map +1 -0
- package/dist/services/doc-blocks.d.ts +9 -0
- package/dist/services/doc-blocks.js +30 -0
- package/dist/services/doc-blocks.js.map +1 -0
- package/dist/services/markdown-convert.d.ts +51 -0
- package/dist/services/markdown-convert.js +109 -0
- package/dist/services/markdown-convert.js.map +1 -0
- package/dist/services/wiki-nodes.d.ts +21 -0
- package/dist/services/wiki-nodes.js +47 -0
- package/dist/services/wiki-nodes.js.map +1 -0
- package/dist/types/index.d.ts +234 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/document-resolver.d.ts +28 -0
- package/dist/utils/document-resolver.js +47 -0
- package/dist/utils/document-resolver.js.map +1 -0
- package/dist/utils/drive-types.d.ts +4 -0
- package/dist/utils/drive-types.js +16 -0
- package/dist/utils/drive-types.js.map +1 -0
- package/dist/utils/errors.d.ts +21 -0
- package/dist/utils/errors.js +110 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/member.d.ts +11 -0
- package/dist/utils/member.js +30 -0
- package/dist/utils/member.js.map +1 -0
- package/dist/utils/url-parser.d.ts +5 -0
- package/dist/utils/url-parser.js +55 -0
- package/dist/utils/url-parser.js.map +1 -0
- package/dist/utils/validate.d.ts +8 -0
- package/dist/utils/validate.js +15 -0
- package/dist/utils/validate.js.map +1 -0
- package/package.json +54 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI entry point: declarative command registration + routing.
|
|
3
|
+
* Supports both top-level commands and subcommands (e.g., share list).
|
|
4
|
+
*/
|
|
5
|
+
import { parseArgs } from "node:util";
|
|
6
|
+
import { loginMeta, logoutMeta, whoamiMeta } from "./commands/login.js";
|
|
7
|
+
import { meta as readMeta } from "./commands/read.js";
|
|
8
|
+
import { meta as spaceMeta } from "./commands/spaces.js";
|
|
9
|
+
import { meta as treeMeta } from "./commands/tree.js";
|
|
10
|
+
import { meta as catMeta } from "./commands/cat.js";
|
|
11
|
+
import { meta as searchMeta } from "./commands/search.js";
|
|
12
|
+
import { meta as createMeta } from "./commands/create.js";
|
|
13
|
+
import { meta as updateMeta } from "./commands/update.js";
|
|
14
|
+
import { meta as deleteMeta } from "./commands/delete.js";
|
|
15
|
+
import { meta as shareMeta } from "./commands/share.js";
|
|
16
|
+
import { meta as infoMeta } from "./commands/info.js";
|
|
17
|
+
import { meta as lsMeta } from "./commands/ls.js";
|
|
18
|
+
import { meta as wikiMeta } from "./commands/wiki.js";
|
|
19
|
+
import { handleError, CliError } from "./utils/errors.js";
|
|
20
|
+
const HELP_TEXT = `feishu-docs - AI Agent 飞书云文档 CLI 工具
|
|
21
|
+
|
|
22
|
+
用法: feishu-docs <command> [options]
|
|
23
|
+
|
|
24
|
+
认证:
|
|
25
|
+
login [--scope "..."] [--redirect-uri <url>] [--port <port>]
|
|
26
|
+
OAuth 登录获取 user_access_token
|
|
27
|
+
logout 清除已保存的凭证
|
|
28
|
+
whoami 查看当前认证身份和模式
|
|
29
|
+
|
|
30
|
+
文档:
|
|
31
|
+
read <url|token> 读取文档,输出 Markdown
|
|
32
|
+
create <title> [options] 创建文档
|
|
33
|
+
update <url|token> [options] 更新文档内容
|
|
34
|
+
delete <url|token> 删除文档
|
|
35
|
+
info <url|token> 查看文档元信息
|
|
36
|
+
|
|
37
|
+
知识库:
|
|
38
|
+
spaces 列出所有知识库
|
|
39
|
+
tree <space_id|url> [--node <token>] 展示知识库节点树
|
|
40
|
+
cat <space_id|url> [--node <token>] 递归读取节点下所有文档
|
|
41
|
+
wiki create-space <name> 创建知识库
|
|
42
|
+
wiki add-member <space_id> <member> 添加知识库成员
|
|
43
|
+
wiki remove-member <space_id> <member> 移除知识库成员
|
|
44
|
+
wiki rename <url> --title <new_title> 重命名节点
|
|
45
|
+
wiki move <url> --to <space_id> 移动节点
|
|
46
|
+
wiki copy <url> --to <space_id> 复制节点
|
|
47
|
+
|
|
48
|
+
搜索:
|
|
49
|
+
search <query> [options] 搜索文档
|
|
50
|
+
|
|
51
|
+
云空间:
|
|
52
|
+
ls [folder_token] 列出文件夹内容
|
|
53
|
+
|
|
54
|
+
权限:
|
|
55
|
+
share list <url> 查看协作者
|
|
56
|
+
share add <url> <member> --role <role> 添加协作者
|
|
57
|
+
share set <url> --public <mode> 修改分享设置
|
|
58
|
+
|
|
59
|
+
全局选项:
|
|
60
|
+
--auth <user|tenant|auto> 认证模式(默认 auto)
|
|
61
|
+
--json 输出 JSON 格式
|
|
62
|
+
--lark 使用海外 Lark 域名
|
|
63
|
+
--help 显示帮助信息
|
|
64
|
+
`;
|
|
65
|
+
const GLOBAL_OPTIONS = {
|
|
66
|
+
auth: { type: "string", default: "auto" },
|
|
67
|
+
json: { type: "boolean", default: false },
|
|
68
|
+
lark: { type: "boolean", default: false },
|
|
69
|
+
help: { type: "boolean", default: false },
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Command registry: name → meta definition.
|
|
73
|
+
* Each meta has: { handler, options, positionals } or { subcommands }.
|
|
74
|
+
*/
|
|
75
|
+
const COMMANDS = {
|
|
76
|
+
login: loginMeta,
|
|
77
|
+
logout: logoutMeta,
|
|
78
|
+
whoami: whoamiMeta,
|
|
79
|
+
read: readMeta,
|
|
80
|
+
spaces: spaceMeta,
|
|
81
|
+
tree: treeMeta,
|
|
82
|
+
cat: catMeta,
|
|
83
|
+
search: searchMeta,
|
|
84
|
+
create: createMeta,
|
|
85
|
+
update: updateMeta,
|
|
86
|
+
delete: deleteMeta,
|
|
87
|
+
share: shareMeta,
|
|
88
|
+
info: infoMeta,
|
|
89
|
+
ls: lsMeta,
|
|
90
|
+
wiki: wikiMeta,
|
|
91
|
+
};
|
|
92
|
+
function extractGlobalOpts(values) {
|
|
93
|
+
return {
|
|
94
|
+
auth: values.auth,
|
|
95
|
+
json: values.json,
|
|
96
|
+
lark: values.lark,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Remap hyphenated option keys to camelCase for args.
|
|
101
|
+
*/
|
|
102
|
+
function remapArgs(values, optionDefs) {
|
|
103
|
+
const args = {};
|
|
104
|
+
for (const key of Object.keys(optionDefs)) {
|
|
105
|
+
const camel = key.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
106
|
+
if (values[key] !== undefined) {
|
|
107
|
+
args[camel] = values[key];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return args;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Parse and dispatch a command.
|
|
114
|
+
*/
|
|
115
|
+
function parseAndRun(def, argv) {
|
|
116
|
+
const allOptions = { ...GLOBAL_OPTIONS, ...def.options };
|
|
117
|
+
const { values, positionals } = parseArgs({
|
|
118
|
+
args: argv,
|
|
119
|
+
options: allOptions,
|
|
120
|
+
allowPositionals: def.positionals ?? false,
|
|
121
|
+
strict: false,
|
|
122
|
+
});
|
|
123
|
+
const globalOpts = extractGlobalOpts(values);
|
|
124
|
+
const args = remapArgs(values, def.options);
|
|
125
|
+
if (def.positionals) {
|
|
126
|
+
args.positionals = positionals;
|
|
127
|
+
}
|
|
128
|
+
return { handler: def.handler, args, globalOpts };
|
|
129
|
+
}
|
|
130
|
+
export async function run(argv) {
|
|
131
|
+
if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
|
|
132
|
+
process.stdout.write(HELP_TEXT);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const command = argv[0];
|
|
136
|
+
const restArgs = argv.slice(1);
|
|
137
|
+
const def = COMMANDS[command];
|
|
138
|
+
if (!def) {
|
|
139
|
+
throw new CliError("INVALID_ARGS", `未知命令: ${command}。运行 feishu-docs --help 查看可用命令`);
|
|
140
|
+
}
|
|
141
|
+
let handler, args, globalOpts;
|
|
142
|
+
if (def.subcommands) {
|
|
143
|
+
const subDef = def;
|
|
144
|
+
const subName = restArgs[0];
|
|
145
|
+
if (!subName ||
|
|
146
|
+
subName === "--help" ||
|
|
147
|
+
subName === "-h" ||
|
|
148
|
+
!subDef.subcommands[subName]) {
|
|
149
|
+
const available = Object.keys(subDef.subcommands).join(", ");
|
|
150
|
+
throw new CliError("INVALID_ARGS", `用法: feishu-docs ${command} <${available}> [options]`);
|
|
151
|
+
}
|
|
152
|
+
const subCmd = subDef.subcommands[subName];
|
|
153
|
+
const subArgs = restArgs.slice(1);
|
|
154
|
+
({ handler, args, globalOpts } = parseAndRun(subCmd, subArgs));
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
({ handler, args, globalOpts } = parseAndRun(def, restArgs));
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
await handler(args, globalOpts);
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
handleError(err, globalOpts.json);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lark SDK client factory with auth mode support.
|
|
3
|
+
*/
|
|
4
|
+
import * as lark from "@larksuiteoapi/node-sdk";
|
|
5
|
+
import type { AuthInfo, GlobalOpts, FetchOptions, ApiResponse } from "./types/index.js";
|
|
6
|
+
/**
|
|
7
|
+
* Create a configured Lark SDK client.
|
|
8
|
+
* @param {object} options - { auth: 'user'|'tenant'|'auto', lark: boolean }
|
|
9
|
+
* @returns {{ client: lark.Client, authInfo: object }}
|
|
10
|
+
*/
|
|
11
|
+
export declare function createClient(options?: Partial<GlobalOpts>, _refreshAttempt?: number): Promise<{
|
|
12
|
+
client: lark.Client;
|
|
13
|
+
authInfo: AuthInfo;
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Build request options with appropriate auth header.
|
|
17
|
+
*/
|
|
18
|
+
export declare function withAuth(authInfo: AuthInfo): {
|
|
19
|
+
headers?: {
|
|
20
|
+
Authorization: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the API base URL based on whether we're using Lark or Feishu.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getApiBase(authInfo: AuthInfo): string;
|
|
27
|
+
/**
|
|
28
|
+
* Get tenant_access_token for tenant mode API calls.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getTenantToken(authInfo: AuthInfo): Promise<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Direct fetch wrapper that correctly passes user/tenant token.
|
|
33
|
+
* Use this instead of SDK methods for APIs where the SDK overrides the token.
|
|
34
|
+
*/
|
|
35
|
+
export declare function fetchWithAuth(authInfo: AuthInfo, path: string, options?: FetchOptions): Promise<ApiResponse>;
|
|
36
|
+
/**
|
|
37
|
+
* Execute an API call with retry for idempotent operations.
|
|
38
|
+
*/
|
|
39
|
+
export declare function apiCall<T>(fn: () => Promise<T>, { retries, idempotent, }?: {
|
|
40
|
+
retries?: number;
|
|
41
|
+
idempotent?: boolean;
|
|
42
|
+
}): Promise<T>;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lark SDK client factory with auth mode support.
|
|
3
|
+
*/
|
|
4
|
+
import * as lark from "@larksuiteoapi/node-sdk";
|
|
5
|
+
import { resolveAuth, refreshUserToken, acquireRefreshLock } from "./auth.js";
|
|
6
|
+
import { CliError, mapApiError } from "./utils/errors.js";
|
|
7
|
+
const silentLogger = {
|
|
8
|
+
fatal: () => { },
|
|
9
|
+
error: () => { },
|
|
10
|
+
warn: () => { },
|
|
11
|
+
info: () => { },
|
|
12
|
+
debug: () => { },
|
|
13
|
+
trace: () => { },
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Create a configured Lark SDK client.
|
|
17
|
+
* @param {object} options - { auth: 'user'|'tenant'|'auto', lark: boolean }
|
|
18
|
+
* @returns {{ client: lark.Client, authInfo: object }}
|
|
19
|
+
*/
|
|
20
|
+
export async function createClient(options = {}, _refreshAttempt = 0) {
|
|
21
|
+
const authMode = options.auth || "auto";
|
|
22
|
+
const useLark = options.lark || false;
|
|
23
|
+
const resolved = await resolveAuth(authMode);
|
|
24
|
+
const authInfo = { ...resolved, useLark };
|
|
25
|
+
const { appId, appSecret } = authInfo;
|
|
26
|
+
if (!appId || !appSecret) {
|
|
27
|
+
if (authInfo.mode === "user" && authInfo.userToken) {
|
|
28
|
+
// User token from env, no app credentials needed for some APIs
|
|
29
|
+
// Create a minimal client
|
|
30
|
+
const client = new lark.Client({
|
|
31
|
+
appId: "placeholder",
|
|
32
|
+
appSecret: "placeholder",
|
|
33
|
+
domain: useLark ? lark.Domain.Lark : lark.Domain.Feishu,
|
|
34
|
+
logger: silentLogger,
|
|
35
|
+
});
|
|
36
|
+
return { client, authInfo };
|
|
37
|
+
}
|
|
38
|
+
throw new CliError("AUTH_REQUIRED", "缺少 FEISHU_APP_ID 或 FEISHU_APP_SECRET");
|
|
39
|
+
}
|
|
40
|
+
const client = new lark.Client({
|
|
41
|
+
appId,
|
|
42
|
+
appSecret,
|
|
43
|
+
domain: useLark ? lark.Domain.Lark : lark.Domain.Feishu,
|
|
44
|
+
logger: silentLogger,
|
|
45
|
+
});
|
|
46
|
+
// Auto-refresh user token if expired
|
|
47
|
+
if (authInfo.mode === "user" && authInfo.expiresAt) {
|
|
48
|
+
if (Date.now() >= authInfo.expiresAt) {
|
|
49
|
+
if (authInfo.refreshToken) {
|
|
50
|
+
try {
|
|
51
|
+
process.stderr.write("feishu-docs: info: token 已过期,正在自动刷新...\n");
|
|
52
|
+
const releaseLock = await acquireRefreshLock();
|
|
53
|
+
if (!releaseLock) {
|
|
54
|
+
if (_refreshAttempt >= 3) {
|
|
55
|
+
throw new CliError("TOKEN_EXPIRED", "等待 token 刷新超时。如果问题持续,请手动删除 ~/.feishu-docs/.refresh.lock 后重试");
|
|
56
|
+
}
|
|
57
|
+
process.stderr.write("feishu-docs: info: 另一个进程正在刷新 token,等待中...\n");
|
|
58
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
59
|
+
return createClient(options, _refreshAttempt + 1);
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const newTokens = await refreshUserToken(appId, appSecret, authInfo.refreshToken, { useLark });
|
|
63
|
+
const refreshedAuthInfo = {
|
|
64
|
+
...authInfo,
|
|
65
|
+
userToken: newTokens.user_access_token,
|
|
66
|
+
expiresAt: newTokens.expires_at,
|
|
67
|
+
refreshToken: newTokens.refresh_token,
|
|
68
|
+
};
|
|
69
|
+
return { client, authInfo: refreshedAuthInfo };
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
await releaseLock();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (refreshErr) {
|
|
76
|
+
const refreshError = refreshErr;
|
|
77
|
+
// In auto mode, fall back to tenant auth instead of failing
|
|
78
|
+
if (authMode === "auto" && appId && appSecret) {
|
|
79
|
+
process.stderr.write(`feishu-docs: info: 自动刷新失败 (${refreshError.message}),回退到 tenant 模式\n`);
|
|
80
|
+
const tenantAuthInfo = {
|
|
81
|
+
...authInfo,
|
|
82
|
+
mode: "tenant",
|
|
83
|
+
userToken: undefined,
|
|
84
|
+
};
|
|
85
|
+
return { client, authInfo: tenantAuthInfo };
|
|
86
|
+
}
|
|
87
|
+
throw new CliError("TOKEN_EXPIRED", "自动刷新 token 失败,请重新运行 feishu-docs login", {
|
|
88
|
+
recovery: "运行 feishu-docs login 重新认证",
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
// In auto mode, fall back to tenant auth instead of failing
|
|
94
|
+
if (authMode === "auto" && appId && appSecret) {
|
|
95
|
+
process.stderr.write("feishu-docs: info: user token 已过期,回退到 tenant 模式\n");
|
|
96
|
+
const tenantAuthInfo = {
|
|
97
|
+
...authInfo,
|
|
98
|
+
mode: "tenant",
|
|
99
|
+
userToken: undefined,
|
|
100
|
+
};
|
|
101
|
+
return { client, authInfo: tenantAuthInfo };
|
|
102
|
+
}
|
|
103
|
+
throw new CliError("TOKEN_EXPIRED", "token 已过期,请重新运行 feishu-docs login", {
|
|
104
|
+
recovery: "运行 feishu-docs login 重新认证",
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return { client, authInfo };
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Build request options with appropriate auth header.
|
|
113
|
+
*/
|
|
114
|
+
export function withAuth(authInfo) {
|
|
115
|
+
if (authInfo.mode === "user" && authInfo.userToken) {
|
|
116
|
+
return {
|
|
117
|
+
headers: { Authorization: `Bearer ${authInfo.userToken}` },
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
// tenant mode uses SDK's built-in tenant_access_token
|
|
121
|
+
return {};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Resolve the API base URL based on whether we're using Lark or Feishu.
|
|
125
|
+
*/
|
|
126
|
+
export function getApiBase(authInfo) {
|
|
127
|
+
return authInfo.useLark
|
|
128
|
+
? "https://open.larksuite.com"
|
|
129
|
+
: "https://open.feishu.cn";
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get tenant_access_token for tenant mode API calls.
|
|
133
|
+
*/
|
|
134
|
+
export async function getTenantToken(authInfo) {
|
|
135
|
+
const res = await fetch(`${getApiBase(authInfo)}/open-apis/auth/v3/tenant_access_token/internal`, {
|
|
136
|
+
method: "POST",
|
|
137
|
+
headers: { "Content-Type": "application/json" },
|
|
138
|
+
body: JSON.stringify({
|
|
139
|
+
app_id: authInfo.appId,
|
|
140
|
+
app_secret: authInfo.appSecret,
|
|
141
|
+
}),
|
|
142
|
+
});
|
|
143
|
+
const body = (await res.json());
|
|
144
|
+
if (body.code !== 0) {
|
|
145
|
+
throw new CliError("AUTH_REQUIRED", `获取 tenant_access_token 失败: ${body.msg}`, {
|
|
146
|
+
apiCode: body.code,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (!body.tenant_access_token) {
|
|
150
|
+
throw new CliError("AUTH_REQUIRED", "获取 tenant_access_token 失败: API 返回空值");
|
|
151
|
+
}
|
|
152
|
+
return body.tenant_access_token;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Build Authorization header value for any auth mode.
|
|
156
|
+
*/
|
|
157
|
+
async function resolveBearer(authInfo) {
|
|
158
|
+
if (authInfo.mode === "user" && authInfo.userToken) {
|
|
159
|
+
return `Bearer ${authInfo.userToken}`;
|
|
160
|
+
}
|
|
161
|
+
if (authInfo.tenantToken) {
|
|
162
|
+
return `Bearer ${authInfo.tenantToken}`;
|
|
163
|
+
}
|
|
164
|
+
const tenantToken = await getTenantToken(authInfo);
|
|
165
|
+
return `Bearer ${tenantToken}`;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Direct fetch wrapper that correctly passes user/tenant token.
|
|
169
|
+
* Use this instead of SDK methods for APIs where the SDK overrides the token.
|
|
170
|
+
*/
|
|
171
|
+
export async function fetchWithAuth(authInfo, path, options = {}) {
|
|
172
|
+
const base = getApiBase(authInfo);
|
|
173
|
+
const bearer = await resolveBearer(authInfo);
|
|
174
|
+
const url = new URL(path, base);
|
|
175
|
+
if (options.params) {
|
|
176
|
+
for (const [key, value] of Object.entries(options.params)) {
|
|
177
|
+
if (value === undefined || value === null)
|
|
178
|
+
continue;
|
|
179
|
+
if (Array.isArray(value)) {
|
|
180
|
+
for (const item of value) {
|
|
181
|
+
url.searchParams.append(key, String(item));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
url.searchParams.set(key, String(value));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const fetchOpts = {
|
|
190
|
+
method: options.method || "GET",
|
|
191
|
+
headers: {
|
|
192
|
+
Authorization: bearer,
|
|
193
|
+
"Content-Type": "application/json",
|
|
194
|
+
...options.headers,
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
if (options.body) {
|
|
198
|
+
fetchOpts.body = JSON.stringify(options.body);
|
|
199
|
+
}
|
|
200
|
+
const controller = new AbortController();
|
|
201
|
+
const timeoutId = setTimeout(() => controller.abort(), 30_000);
|
|
202
|
+
let res;
|
|
203
|
+
try {
|
|
204
|
+
res = await fetch(url.toString(), {
|
|
205
|
+
...fetchOpts,
|
|
206
|
+
signal: controller.signal,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
const error = err;
|
|
211
|
+
if (error.name === "AbortError") {
|
|
212
|
+
throw new CliError("API_ERROR", "API 请求超时(30秒)", {
|
|
213
|
+
retryable: true,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
throw err;
|
|
217
|
+
}
|
|
218
|
+
finally {
|
|
219
|
+
clearTimeout(timeoutId);
|
|
220
|
+
}
|
|
221
|
+
const body = (await res.json());
|
|
222
|
+
if (body.code !== undefined && body.code !== 0) {
|
|
223
|
+
throw mapApiError({ code: body.code, msg: body.msg });
|
|
224
|
+
}
|
|
225
|
+
return body;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Extract Feishu API error code from various error shapes.
|
|
229
|
+
*/
|
|
230
|
+
function extractErrorCode(err) {
|
|
231
|
+
const e = err;
|
|
232
|
+
return e?.code ?? e?.response?.data?.code ?? e?.response?.code;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Execute an API call with retry for idempotent operations.
|
|
236
|
+
*/
|
|
237
|
+
export async function apiCall(fn, { retries = 3, idempotent = true, } = {}) {
|
|
238
|
+
let lastError;
|
|
239
|
+
for (let attempt = 0; attempt <= (idempotent ? retries : 0); attempt++) {
|
|
240
|
+
try {
|
|
241
|
+
const res = await fn();
|
|
242
|
+
// Check SDK response code
|
|
243
|
+
const resWithCode = res;
|
|
244
|
+
if (resWithCode?.code && resWithCode.code !== 0) {
|
|
245
|
+
throw mapApiError({ code: resWithCode.code, msg: resWithCode.msg });
|
|
246
|
+
}
|
|
247
|
+
return res;
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
lastError = err;
|
|
251
|
+
const code = extractErrorCode(err);
|
|
252
|
+
// Don't retry client errors (permission, not found, auth errors)
|
|
253
|
+
const NON_RETRYABLE = new Set([
|
|
254
|
+
131001, 131002, 131006, 131008, 99991400, 99991663,
|
|
255
|
+
]);
|
|
256
|
+
if (code && NON_RETRYABLE.has(code)) {
|
|
257
|
+
throw mapApiError(err);
|
|
258
|
+
}
|
|
259
|
+
// Retry on network/server errors
|
|
260
|
+
if (attempt < retries && idempotent) {
|
|
261
|
+
const delay = 1000 * Math.pow(2, attempt);
|
|
262
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
throw mapApiError(lastError);
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cat command: Recursively read all documents under a knowledge base node.
|
|
3
|
+
*/
|
|
4
|
+
import { CommandMeta, CommandArgs, GlobalOpts } from "../types/index.js";
|
|
5
|
+
export declare const meta: CommandMeta;
|
|
6
|
+
export declare function cat(args: CommandArgs, globalOpts: GlobalOpts): Promise<void>;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cat command: Recursively read all documents under a knowledge base node.
|
|
3
|
+
*/
|
|
4
|
+
import { createClient } from "../client.js";
|
|
5
|
+
import { blocksToMarkdown } from "../parser/blocks-to-md.js";
|
|
6
|
+
import { fetchChildren } from "../services/wiki-nodes.js";
|
|
7
|
+
import { fetchAllBlocks } from "../services/doc-blocks.js";
|
|
8
|
+
import { CliError } from "../utils/errors.js";
|
|
9
|
+
import { validateToken } from "../utils/validate.js";
|
|
10
|
+
/**
|
|
11
|
+
* Parse size string like "500k", "1m" to bytes.
|
|
12
|
+
*/
|
|
13
|
+
function parseSize(sizeStr) {
|
|
14
|
+
if (typeof sizeStr === "number")
|
|
15
|
+
return sizeStr;
|
|
16
|
+
const match = String(sizeStr).match(/^(\d+(?:\.\d+)?)\s*([km]?)$/i);
|
|
17
|
+
if (!match)
|
|
18
|
+
return Number(sizeStr) || Infinity;
|
|
19
|
+
const num = parseFloat(match[1]);
|
|
20
|
+
const unit = match[2].toLowerCase();
|
|
21
|
+
if (unit === "k")
|
|
22
|
+
return num * 1024;
|
|
23
|
+
if (unit === "m")
|
|
24
|
+
return num * 1024 * 1024;
|
|
25
|
+
return num;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Recursively collect and output documents.
|
|
29
|
+
*/
|
|
30
|
+
async function walkNodes(client, authInfo, spaceId, parentNodeToken, path, ctx) {
|
|
31
|
+
if (ctx.stopped)
|
|
32
|
+
return;
|
|
33
|
+
if (ctx.maxDepth !== undefined && ctx.currentDepth >= ctx.maxDepth)
|
|
34
|
+
return;
|
|
35
|
+
const children = await fetchChildren(authInfo, spaceId, parentNodeToken);
|
|
36
|
+
for (const node of children) {
|
|
37
|
+
if (ctx.stopped)
|
|
38
|
+
return;
|
|
39
|
+
const nodePath = path
|
|
40
|
+
? `${path}/${node.title || "(无标题)"}`
|
|
41
|
+
: node.title || "(无标题)";
|
|
42
|
+
const objType = node.obj_type || "unknown";
|
|
43
|
+
// Filter by type
|
|
44
|
+
if (ctx.typeFilter && objType !== ctx.typeFilter) {
|
|
45
|
+
// Still recurse into non-matching nodes that have children
|
|
46
|
+
if (node.has_child) {
|
|
47
|
+
ctx.currentDepth++;
|
|
48
|
+
await walkNodes(client, authInfo, spaceId, node.node_token, nodePath, ctx);
|
|
49
|
+
ctx.currentDepth--;
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// Check limits
|
|
54
|
+
if (ctx.docsRead >= ctx.maxDocs) {
|
|
55
|
+
ctx.skipped++;
|
|
56
|
+
ctx.stopped = true;
|
|
57
|
+
process.stderr.write(`feishu-docs: warning: 已达到 --max-docs 上限 (${ctx.maxDocs}),共跳过 ${ctx.skipped} 篇文档\n`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (ctx.bytesWritten >= ctx.maxBytes) {
|
|
61
|
+
ctx.skipped++;
|
|
62
|
+
ctx.stopped = true;
|
|
63
|
+
process.stderr.write(`feishu-docs: warning: 已达到 --max-bytes 上限,共跳过 ${ctx.skipped} 篇文档\n`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// Output document
|
|
67
|
+
const header = `---\npath: ${nodePath}\ntoken: ${node.node_token}\ntype: ${objType}\n---\n\n`;
|
|
68
|
+
if (ctx.titleOnly) {
|
|
69
|
+
process.stdout.write(header);
|
|
70
|
+
ctx.bytesWritten += Buffer.byteLength(header);
|
|
71
|
+
ctx.docsRead++;
|
|
72
|
+
}
|
|
73
|
+
else if (objType === "docx") {
|
|
74
|
+
try {
|
|
75
|
+
const blocks = await fetchAllBlocks(client, authInfo, node.obj_token);
|
|
76
|
+
const md = blocksToMarkdown(blocks);
|
|
77
|
+
const output = header + md + "\n";
|
|
78
|
+
// Check byte limit before writing
|
|
79
|
+
const outputBytes = Buffer.byteLength(output);
|
|
80
|
+
if (ctx.bytesWritten + outputBytes > ctx.maxBytes) {
|
|
81
|
+
ctx.skipped++;
|
|
82
|
+
ctx.stopped = true;
|
|
83
|
+
process.stderr.write(`feishu-docs: warning: 已达到 --max-bytes 上限,共跳过 ${ctx.skipped} 篇文档\n`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
process.stdout.write(output);
|
|
87
|
+
ctx.bytesWritten += outputBytes;
|
|
88
|
+
ctx.docsRead++;
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
process.stderr.write(`feishu-docs: warning: 读取 ${nodePath} 失败: ${err.message}\n`);
|
|
92
|
+
ctx.docsRead++;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Non-docx types: output header only with type info
|
|
97
|
+
const output = header + `[${objType}: ${node.obj_token}]\n\n`;
|
|
98
|
+
process.stdout.write(output);
|
|
99
|
+
ctx.bytesWritten += Buffer.byteLength(output);
|
|
100
|
+
ctx.docsRead++;
|
|
101
|
+
}
|
|
102
|
+
// Recurse into children
|
|
103
|
+
if (node.has_child) {
|
|
104
|
+
ctx.currentDepth++;
|
|
105
|
+
await walkNodes(client, authInfo, spaceId, node.node_token, nodePath, ctx);
|
|
106
|
+
ctx.currentDepth--;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export const meta = {
|
|
111
|
+
options: {
|
|
112
|
+
node: { type: "string" },
|
|
113
|
+
depth: { type: "string" },
|
|
114
|
+
"max-docs": { type: "string" },
|
|
115
|
+
"max-bytes": { type: "string" },
|
|
116
|
+
"title-only": { type: "boolean", default: false },
|
|
117
|
+
type: { type: "string" },
|
|
118
|
+
},
|
|
119
|
+
positionals: true,
|
|
120
|
+
handler: cat,
|
|
121
|
+
};
|
|
122
|
+
export async function cat(args, globalOpts) {
|
|
123
|
+
const spaceId = args.positionals[0];
|
|
124
|
+
if (!spaceId) {
|
|
125
|
+
throw new CliError("INVALID_ARGS", "缺少知识库 ID。用法: feishu-docs cat <space_id> [--node <token>]");
|
|
126
|
+
}
|
|
127
|
+
validateToken(spaceId, "space_id");
|
|
128
|
+
const { client, authInfo } = await createClient(globalOpts);
|
|
129
|
+
const parentNode = args.node || undefined;
|
|
130
|
+
if (parentNode)
|
|
131
|
+
validateToken(parentNode, "node_token");
|
|
132
|
+
const ctx = {
|
|
133
|
+
maxDocs: args.maxDocs !== undefined ? Number(args.maxDocs) : 50,
|
|
134
|
+
maxBytes: args.maxBytes !== undefined
|
|
135
|
+
? parseSize(args.maxBytes)
|
|
136
|
+
: parseSize("1m"),
|
|
137
|
+
maxDepth: args.depth !== undefined ? Number(args.depth) : undefined,
|
|
138
|
+
typeFilter: args.type || undefined,
|
|
139
|
+
titleOnly: args.titleOnly || false,
|
|
140
|
+
docsRead: 0,
|
|
141
|
+
bytesWritten: 0,
|
|
142
|
+
skipped: 0,
|
|
143
|
+
stopped: false,
|
|
144
|
+
currentDepth: 0,
|
|
145
|
+
};
|
|
146
|
+
await walkNodes(client, authInfo, spaceId, parentNode, "", ctx);
|
|
147
|
+
if (ctx.docsRead === 0) {
|
|
148
|
+
process.stderr.write("feishu-docs: info: 未找到任何文档\n");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=cat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cat.js","sourceRoot":"","sources":["../../src/commands/cat.ts"],"names":[],"mappings":"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"}
|