feishu-docs-cli 0.1.0-beta.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -13
- package/README.zh.md +53 -13
- package/dist/auth.js +31 -17
- package/dist/auth.js.map +1 -1
- package/dist/cli.js +16 -4
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +4 -2
- package/dist/client.js +177 -61
- package/dist/client.js.map +1 -1
- package/dist/commands/authorize.d.ts +3 -0
- package/dist/commands/authorize.js +16 -34
- package/dist/commands/authorize.js.map +1 -1
- package/dist/commands/cp.d.ts +9 -0
- package/dist/commands/cp.js +70 -0
- package/dist/commands/cp.js.map +1 -0
- package/dist/commands/create.js +21 -7
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/delete.js +53 -54
- package/dist/commands/delete.js.map +1 -1
- package/dist/commands/login.js +6 -2
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/ls.js +38 -38
- package/dist/commands/ls.js.map +1 -1
- package/dist/commands/mkdir.d.ts +6 -0
- package/dist/commands/mkdir.js +49 -0
- package/dist/commands/mkdir.js.map +1 -0
- package/dist/commands/mv.d.ts +9 -0
- package/dist/commands/mv.js +72 -0
- package/dist/commands/mv.js.map +1 -0
- package/dist/commands/read.d.ts +1 -1
- package/dist/commands/read.js +17 -354
- package/dist/commands/read.js.map +1 -1
- package/dist/commands/search.js +57 -55
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/share.d.ts +1 -1
- package/dist/commands/share.js +164 -91
- package/dist/commands/share.js.map +1 -1
- package/dist/commands/update.js +43 -60
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/wiki.js +8 -20
- package/dist/commands/wiki.js.map +1 -1
- package/dist/parser/block-types.d.ts +0 -1
- package/dist/parser/block-types.js +0 -22
- package/dist/parser/block-types.js.map +1 -1
- package/dist/parser/blocks-to-md.d.ts +10 -18
- package/dist/parser/blocks-to-md.js +341 -450
- package/dist/parser/blocks-to-md.js.map +1 -1
- package/dist/scopes.d.ts +5 -47
- package/dist/scopes.js +9 -54
- package/dist/scopes.js.map +1 -1
- package/dist/services/block-writer.d.ts +3 -2
- package/dist/services/block-writer.js +29 -13
- package/dist/services/block-writer.js.map +1 -1
- package/dist/services/doc-blocks.d.ts +1 -1
- package/dist/services/doc-blocks.js +1 -1
- package/dist/services/doc-blocks.js.map +1 -1
- package/dist/services/doc-enrichment.d.ts +64 -0
- package/dist/services/doc-enrichment.js +397 -0
- package/dist/services/doc-enrichment.js.map +1 -0
- package/dist/services/image-download.d.ts +31 -0
- package/dist/services/image-download.js +127 -0
- package/dist/services/image-download.js.map +1 -0
- package/dist/services/markdown-convert.d.ts +20 -0
- package/dist/services/markdown-convert.js +55 -1
- package/dist/services/markdown-convert.js.map +1 -1
- package/dist/services/wiki-nodes.d.ts +1 -1
- package/dist/services/wiki-nodes.js +2 -3
- package/dist/services/wiki-nodes.js.map +1 -1
- package/dist/types/api-responses.d.ts +34 -0
- package/dist/types/api-responses.js +8 -0
- package/dist/types/api-responses.js.map +1 -0
- package/dist/types/index.d.ts +9 -17
- package/dist/utils/concurrency.d.ts +12 -0
- package/dist/utils/concurrency.js +37 -0
- package/dist/utils/concurrency.js.map +1 -0
- package/dist/utils/errors.d.ts +3 -1
- package/dist/utils/errors.js +11 -7
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/retry.d.ts +49 -0
- package/dist/utils/retry.js +70 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/scope-prompt.d.ts +23 -18
- package/dist/utils/scope-prompt.js +62 -51
- package/dist/utils/scope-prompt.js.map +1 -1
- package/package.json +3 -1
- package/skills/feishu-docs/SKILL.md +37 -2
|
@@ -35,6 +35,57 @@ export function normalizeLangNames(markdown) {
|
|
|
35
35
|
return alias ? fence + alias : match;
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Extract the first top-level heading (# title) from markdown.
|
|
40
|
+
*
|
|
41
|
+
* Returns the title text and the remaining body with the heading line removed.
|
|
42
|
+
* Only matches `# heading` (H1), not `## heading` (H2+).
|
|
43
|
+
* Ignores leading blank lines before the heading.
|
|
44
|
+
* If no H1 heading is found, returns null title and the original markdown.
|
|
45
|
+
*/
|
|
46
|
+
export function extractMarkdownTitle(markdown) {
|
|
47
|
+
const lines = markdown.split("\n");
|
|
48
|
+
let headingIndex = -1;
|
|
49
|
+
for (let i = 0; i < lines.length; i++) {
|
|
50
|
+
const trimmed = lines[i].trim();
|
|
51
|
+
if (trimmed === "")
|
|
52
|
+
continue;
|
|
53
|
+
// Match exactly one # followed by space (H1 only, not ##)
|
|
54
|
+
const match = trimmed.match(/^#\s+(.+)$/);
|
|
55
|
+
if (match) {
|
|
56
|
+
headingIndex = i;
|
|
57
|
+
}
|
|
58
|
+
break; // Only check the first non-empty line
|
|
59
|
+
}
|
|
60
|
+
if (headingIndex === -1) {
|
|
61
|
+
return { title: null, body: markdown };
|
|
62
|
+
}
|
|
63
|
+
const title = lines[headingIndex].trim().replace(/^#\s+/, "");
|
|
64
|
+
const remaining = [
|
|
65
|
+
...lines.slice(0, headingIndex),
|
|
66
|
+
...lines.slice(headingIndex + 1),
|
|
67
|
+
];
|
|
68
|
+
// Remove leading blank lines left after title extraction
|
|
69
|
+
let startIdx = 0;
|
|
70
|
+
while (startIdx < remaining.length && remaining[startIdx].trim() === "") {
|
|
71
|
+
startIdx++;
|
|
72
|
+
}
|
|
73
|
+
const body = remaining.slice(startIdx).join("\n");
|
|
74
|
+
return { title, body };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Replace literal `\n` with `<br>` inside mermaid code blocks.
|
|
78
|
+
*
|
|
79
|
+
* Claude and other AI tools generate mermaid node labels with `\n` for
|
|
80
|
+
* line breaks (e.g. `A[Line 1\nLine 2]`), but standard mermaid syntax
|
|
81
|
+
* requires `<br>` (e.g. `A[Line 1<br>Line 2]`).
|
|
82
|
+
*/
|
|
83
|
+
export function normalizeMermaidLineBreaks(markdown) {
|
|
84
|
+
return markdown.replace(/^```mermaid\s*\n([\s\S]*?)^```/gm, (_match, block) => {
|
|
85
|
+
const fixed = block.replace(/\\n/g, "<br>");
|
|
86
|
+
return "```mermaid\n" + fixed + "```";
|
|
87
|
+
});
|
|
88
|
+
}
|
|
38
89
|
/**
|
|
39
90
|
* Convert markdown string to Feishu block array via Convert API.
|
|
40
91
|
* Requires scope: docx:document.block:convert
|
|
@@ -44,7 +95,10 @@ export function normalizeLangNames(markdown) {
|
|
|
44
95
|
export async function convertMarkdown(authInfo, markdown) {
|
|
45
96
|
const res = await fetchWithAuth(authInfo, "/open-apis/docx/v1/documents/blocks/convert", {
|
|
46
97
|
method: "POST",
|
|
47
|
-
body: {
|
|
98
|
+
body: {
|
|
99
|
+
content: normalizeMermaidLineBreaks(normalizeLangNames(markdown)),
|
|
100
|
+
content_type: "markdown",
|
|
101
|
+
},
|
|
48
102
|
});
|
|
49
103
|
const data = res?.data;
|
|
50
104
|
if (!data?.blocks || !data?.first_level_block_ids) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-convert.js","sourceRoot":"","sources":["../../src/services/markdown-convert.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAG9C,0DAA0D;AAC1D,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAEjC;;;GAGG;AACH,MAAM,YAAY,GAA2B;IAC3C,aAAa,EAAE,MAAM;IACrB,OAAO,EAAE,MAAM;CAChB,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,OAAO,QAAQ,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACpE,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QAC/C,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAkB,EAClB,QAAgB;IAEhB,MAAM,GAAG,GAAG,MAAM,aAAa,CAC7B,QAAQ,EACR,6CAA6C,EAC7C;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"markdown-convert.js","sourceRoot":"","sources":["../../src/services/markdown-convert.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,7 +16,7 @@ export async function fetchChildren(authInfo, spaceId, parentNodeToken) {
|
|
|
16
16
|
...(parentNodeToken && { parent_node_token: parentNodeToken }),
|
|
17
17
|
};
|
|
18
18
|
const res = await fetchWithAuth(authInfo, `/open-apis/wiki/v2/spaces/${encodeURIComponent(spaceId)}/nodes`, { params });
|
|
19
|
-
const data = res
|
|
19
|
+
const data = res.data;
|
|
20
20
|
if (data?.items) {
|
|
21
21
|
nodes.push(...data.items);
|
|
22
22
|
}
|
|
@@ -29,8 +29,7 @@ export async function fetchChildren(authInfo, spaceId, parentNodeToken) {
|
|
|
29
29
|
*/
|
|
30
30
|
export async function resolveWikiToken(authInfo, wikiToken) {
|
|
31
31
|
const res = await fetchWithAuth(authInfo, "/open-apis/wiki/v2/spaces/get_node", { params: { token: wikiToken, obj_type: "wiki" } });
|
|
32
|
-
const
|
|
33
|
-
const node = data?.node;
|
|
32
|
+
const node = res.data?.node;
|
|
34
33
|
if (!node) {
|
|
35
34
|
throw new CliError("NOT_FOUND", `知识库节点不存在: ${wikiToken}`);
|
|
36
35
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wiki-nodes.js","sourceRoot":"","sources":["../../src/services/wiki-nodes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"wiki-nodes.js","sourceRoot":"","sources":["../../src/services/wiki-nodes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAgB9C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,QAAkB,EAClB,OAAe,EACf,eAAwB;IAExB,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,IAAI,SAA6B,CAAC;IAElC,GAAG,CAAC;QACF,MAAM,MAAM,GAAgD;YAC1D,SAAS,EAAE,EAAE;YACb,GAAG,CAAC,SAAS,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;YAC3C,GAAG,CAAC,eAAe,IAAI,EAAE,iBAAiB,EAAE,eAAe,EAAE,CAAC;SAC/D,CAAC;QAEF,MAAM,GAAG,GAAG,MAAM,aAAa,CAC7B,QAAQ,EACR,6BAA6B,kBAAkB,CAAC,OAAO,CAAC,QAAQ,EAChE,EAAE,MAAM,EAAE,CACX,CAAC;QAEF,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QACtB,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;QACD,SAAS,GAAG,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,CAAC,QAAQ,SAAS,EAAE;IAEpB,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAkB,EAClB,SAAiB;IAEjB,MAAM,GAAG,GAAG,MAAM,aAAa,CAC7B,QAAQ,EACR,oCAAoC,EACpC,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,CACnD,CAAC;IAEF,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC;IAC5B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,QAAQ,CAAC,WAAW,EAAE,aAAa,SAAS,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,IAAI,CAAC,SAAS,IAAI,EAAE;QAC9B,OAAO,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE;QAC5B,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE;QACvB,SAAS,EAAE,IAAI,CAAC,UAAU,IAAI,EAAE;QAChC,OAAO,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE;QAC5B,QAAQ,EAAE,IAAI,CAAC,SAAS,IAAI,KAAK;KAClC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed API response data interfaces for Feishu Open API endpoints.
|
|
3
|
+
*
|
|
4
|
+
* Each interface describes the shape of the `data` field in ApiResponse<T>.
|
|
5
|
+
* Used with fetchWithAuth<T>() for compile-time type safety.
|
|
6
|
+
*/
|
|
7
|
+
import type { Block, WikiNode } from "./index.js";
|
|
8
|
+
/** GET /open-apis/docx/v1/documents/{document_id}/blocks */
|
|
9
|
+
export interface DocxBlocksResponse {
|
|
10
|
+
items?: Block[];
|
|
11
|
+
has_more?: boolean;
|
|
12
|
+
page_token?: string;
|
|
13
|
+
}
|
|
14
|
+
/** GET /open-apis/wiki/v2/spaces/{space_id}/nodes */
|
|
15
|
+
export interface WikiChildrenResponse {
|
|
16
|
+
items?: WikiNode[];
|
|
17
|
+
has_more?: boolean;
|
|
18
|
+
page_token?: string;
|
|
19
|
+
}
|
|
20
|
+
/** GET /open-apis/wiki/v2/spaces/get_node */
|
|
21
|
+
export interface WikiGetNodeResponse {
|
|
22
|
+
node?: {
|
|
23
|
+
obj_token: string;
|
|
24
|
+
obj_type: string;
|
|
25
|
+
title: string;
|
|
26
|
+
node_token: string;
|
|
27
|
+
space_id: string;
|
|
28
|
+
has_child: boolean;
|
|
29
|
+
parent_node_token?: string;
|
|
30
|
+
node_type?: string;
|
|
31
|
+
origin_node_token?: string;
|
|
32
|
+
origin_space_id?: string;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed API response data interfaces for Feishu Open API endpoints.
|
|
3
|
+
*
|
|
4
|
+
* Each interface describes the shape of the `data` field in ApiResponse<T>.
|
|
5
|
+
* Used with fetchWithAuth<T>() for compile-time type safety.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=api-responses.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-responses.js","sourceRoot":"","sources":["../../src/types/api-responses.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared type definitions for feishu-docs CLI.
|
|
3
3
|
*/
|
|
4
|
-
export type ErrorType = "INVALID_ARGS" | "FILE_NOT_FOUND" | "AUTH_REQUIRED" | "TOKEN_EXPIRED" | "PERMISSION_DENIED" | "NOT_FOUND" | "NOT_SUPPORTED" | "RATE_LIMITED" | "API_ERROR";
|
|
4
|
+
export type ErrorType = "INVALID_ARGS" | "FILE_NOT_FOUND" | "AUTH_REQUIRED" | "TOKEN_EXPIRED" | "PERMISSION_DENIED" | "SCOPE_MISSING" | "NOT_FOUND" | "NOT_SUPPORTED" | "RATE_LIMITED" | "API_ERROR";
|
|
5
5
|
export interface CliErrorOptions {
|
|
6
6
|
apiCode?: number;
|
|
7
7
|
retryable?: boolean;
|
|
8
8
|
recovery?: string;
|
|
9
|
+
/** Scope names extracted from API permission_violations (for SCOPE_MISSING errors). */
|
|
10
|
+
missingScopes?: string[];
|
|
9
11
|
}
|
|
10
12
|
export type AuthMode = "user" | "tenant" | "auto";
|
|
11
13
|
export interface AuthInfo {
|
|
@@ -51,26 +53,22 @@ export interface ParsedDoc {
|
|
|
51
53
|
type: DocType;
|
|
52
54
|
token: string;
|
|
53
55
|
}
|
|
54
|
-
export interface DocumentInfo {
|
|
55
|
-
documentId: string;
|
|
56
|
-
objToken: string;
|
|
57
|
-
objType: string;
|
|
58
|
-
title: string;
|
|
59
|
-
url?: string;
|
|
60
|
-
revisionId?: number;
|
|
61
|
-
nodeToken?: string;
|
|
62
|
-
spaceId?: string;
|
|
63
|
-
}
|
|
64
56
|
export interface ApiResponse<T = unknown> {
|
|
65
57
|
code?: number;
|
|
66
58
|
msg?: string;
|
|
67
59
|
data?: T;
|
|
68
60
|
}
|
|
61
|
+
export interface RetryConfig {
|
|
62
|
+
maxRetries?: number;
|
|
63
|
+
initialDelay?: number;
|
|
64
|
+
maxDelay?: number;
|
|
65
|
+
}
|
|
69
66
|
export interface FetchOptions {
|
|
70
67
|
method?: string;
|
|
71
68
|
params?: Record<string, string | number | string[] | undefined>;
|
|
72
69
|
body?: unknown;
|
|
73
70
|
headers?: Record<string, string>;
|
|
71
|
+
retry?: RetryConfig | false;
|
|
74
72
|
}
|
|
75
73
|
export interface TextElement {
|
|
76
74
|
text_run?: {
|
|
@@ -221,12 +219,6 @@ export interface WikiNode {
|
|
|
221
219
|
origin_node_token?: string;
|
|
222
220
|
origin_space_id?: string;
|
|
223
221
|
}
|
|
224
|
-
export interface WikiSpace {
|
|
225
|
-
space_id: string;
|
|
226
|
-
name: string;
|
|
227
|
-
description?: string;
|
|
228
|
-
visibility?: string;
|
|
229
|
-
}
|
|
230
222
|
export interface ConvertedBlocks {
|
|
231
223
|
blocks: Block[];
|
|
232
224
|
firstLevelBlockIds: string[];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-dependency concurrency limiter (pLimit-style).
|
|
3
|
+
* Limits the number of concurrently executing async functions.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Create a concurrency limiter that allows at most `concurrency`
|
|
7
|
+
* async functions to execute simultaneously.
|
|
8
|
+
*
|
|
9
|
+
* @param concurrency Maximum number of concurrent executions (positive integer)
|
|
10
|
+
* @returns A limit function that wraps async functions with concurrency control
|
|
11
|
+
*/
|
|
12
|
+
export declare function pLimit(concurrency: number): <T>(fn: () => Promise<T>) => Promise<T>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-dependency concurrency limiter (pLimit-style).
|
|
3
|
+
* Limits the number of concurrently executing async functions.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Create a concurrency limiter that allows at most `concurrency`
|
|
7
|
+
* async functions to execute simultaneously.
|
|
8
|
+
*
|
|
9
|
+
* @param concurrency Maximum number of concurrent executions (positive integer)
|
|
10
|
+
* @returns A limit function that wraps async functions with concurrency control
|
|
11
|
+
*/
|
|
12
|
+
export function pLimit(concurrency) {
|
|
13
|
+
if (!Number.isInteger(concurrency) || concurrency < 1) {
|
|
14
|
+
throw new TypeError("Expected concurrency to be a positive integer");
|
|
15
|
+
}
|
|
16
|
+
let active = 0;
|
|
17
|
+
const queue = [];
|
|
18
|
+
function next() {
|
|
19
|
+
if (queue.length > 0 && active < concurrency) {
|
|
20
|
+
active++;
|
|
21
|
+
const run = queue.shift();
|
|
22
|
+
run();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return (fn) => {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
queue.push(() => {
|
|
28
|
+
fn().then(resolve, reject).finally(() => {
|
|
29
|
+
active--;
|
|
30
|
+
next();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
next();
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=concurrency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrency.js","sourceRoot":"","sources":["../../src/utils/concurrency.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,MAAM,CAAC,WAAmB;IACxC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,SAAS,CAAC,+CAA+C,CAAC,CAAC;IACvE,CAAC;IAED,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,KAAK,GAAsB,EAAE,CAAC;IAEpC,SAAS,IAAI;QACX,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,WAAW,EAAE,CAAC;YAC7C,MAAM,EAAE,CAAC;YACT,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;YAC3B,GAAG,EAAE,CAAC;QACR,CAAC;IACH,CAAC;IAED,OAAO,CAAI,EAAoB,EAAc,EAAE;QAC7C,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACxC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE;gBACd,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE;oBACtC,MAAM,EAAE,CAAC;oBACT,IAAI,EAAE,CAAC;gBACT,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YACH,IAAI,EAAE,CAAC;QACT,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/utils/errors.d.ts
CHANGED
|
@@ -14,7 +14,9 @@ export declare class CliError extends Error {
|
|
|
14
14
|
apiCode?: number;
|
|
15
15
|
retryable: boolean;
|
|
16
16
|
recovery?: string;
|
|
17
|
-
|
|
17
|
+
/** Scope names from API permission_violations (only for SCOPE_MISSING). */
|
|
18
|
+
missingScopes?: string[];
|
|
19
|
+
constructor(type: ErrorType, message: string, { apiCode, retryable, recovery, missingScopes, }?: CliErrorOptions);
|
|
18
20
|
}
|
|
19
21
|
export declare function formatError(err: unknown, json?: boolean): string;
|
|
20
22
|
export declare function handleError(err: unknown, json?: boolean): never;
|
package/dist/utils/errors.js
CHANGED
|
@@ -13,6 +13,7 @@ const ERROR_MAP = {
|
|
|
13
13
|
AUTH_REQUIRED: { exit: 2, type: "AUTH_REQUIRED" },
|
|
14
14
|
TOKEN_EXPIRED: { exit: 2, type: "TOKEN_EXPIRED" },
|
|
15
15
|
PERMISSION_DENIED: { exit: 2, type: "PERMISSION_DENIED" },
|
|
16
|
+
SCOPE_MISSING: { exit: 2, type: "SCOPE_MISSING" },
|
|
16
17
|
NOT_FOUND: { exit: 3, type: "NOT_FOUND" },
|
|
17
18
|
NOT_SUPPORTED: { exit: 3, type: "NOT_SUPPORTED" },
|
|
18
19
|
RATE_LIMITED: { exit: 3, type: "RATE_LIMITED" },
|
|
@@ -24,7 +25,9 @@ export class CliError extends Error {
|
|
|
24
25
|
apiCode;
|
|
25
26
|
retryable;
|
|
26
27
|
recovery;
|
|
27
|
-
|
|
28
|
+
/** Scope names from API permission_violations (only for SCOPE_MISSING). */
|
|
29
|
+
missingScopes;
|
|
30
|
+
constructor(type, message, { apiCode, retryable = false, recovery, missingScopes, } = {}) {
|
|
28
31
|
super(message);
|
|
29
32
|
this.name = "CliError";
|
|
30
33
|
const info = ERROR_MAP[type] || ERROR_MAP.API_ERROR;
|
|
@@ -33,6 +36,7 @@ export class CliError extends Error {
|
|
|
33
36
|
this.apiCode = apiCode;
|
|
34
37
|
this.retryable = retryable;
|
|
35
38
|
this.recovery = recovery;
|
|
39
|
+
this.missingScopes = missingScopes;
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
42
|
export function formatError(err, json = false) {
|
|
@@ -46,6 +50,10 @@ export function formatError(err, json = false) {
|
|
|
46
50
|
api_code: err.apiCode,
|
|
47
51
|
retryable: err.retryable,
|
|
48
52
|
recovery: err.recovery,
|
|
53
|
+
...(err.missingScopes &&
|
|
54
|
+
err.missingScopes.length > 0 && {
|
|
55
|
+
missing_scopes: err.missingScopes,
|
|
56
|
+
}),
|
|
49
57
|
},
|
|
50
58
|
});
|
|
51
59
|
}
|
|
@@ -99,12 +107,8 @@ export function mapApiError(err) {
|
|
|
99
107
|
recovery: "运行 feishu-docs login 重新认证",
|
|
100
108
|
});
|
|
101
109
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
apiCode: code,
|
|
105
|
-
retryable: true,
|
|
106
|
-
});
|
|
107
|
-
}
|
|
110
|
+
// 99991672 / 99991679 are scope errors — handled directly in fetchWithAuth
|
|
111
|
+
// before mapApiError is called. If they reach here, treat as generic API error.
|
|
108
112
|
return new CliError("API_ERROR", msg || "未知 API 错误", { apiCode: code });
|
|
109
113
|
}
|
|
110
114
|
//# sourceMappingURL=errors.js.map
|
package/dist/utils/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,SAAS,GAAmD;IAChE,YAAY,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE;IAC/C,cAAc,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE;IACnD,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE;IACjD,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE;IACjD,iBAAiB,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE;IACzD,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE;IACzC,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE;IACjD,YAAY,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE;IAC/C,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE;CAC1C,CAAC;AAEF,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAS;IACjB,SAAS,CAAS;IAClB,OAAO,CAAU;IACjB,SAAS,CAAU;IACnB,QAAQ,CAAU;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Zero-dependency retry utilities for transient error handling. */
|
|
2
|
+
/** Configuration options for retry behavior. */
|
|
3
|
+
export interface RetryOptions {
|
|
4
|
+
maxRetries: number;
|
|
5
|
+
initialDelay: number;
|
|
6
|
+
maxDelay: number;
|
|
7
|
+
}
|
|
8
|
+
/** Default retry configuration: 2 retries, 1s initial, 10s max. */
|
|
9
|
+
export declare const DEFAULT_RETRY: RetryOptions;
|
|
10
|
+
/**
|
|
11
|
+
* Calculate delay for a retry attempt using exponential backoff with jitter.
|
|
12
|
+
*
|
|
13
|
+
* Computes base = min(initialDelay * 2^attempt, maxDelay), then applies
|
|
14
|
+
* +/-25% random jitter. Result is clamped to >= 0.
|
|
15
|
+
*
|
|
16
|
+
* @param attempt - Zero-based retry attempt number
|
|
17
|
+
* @param initialDelay - Base delay in milliseconds
|
|
18
|
+
* @param maxDelay - Maximum delay cap in milliseconds
|
|
19
|
+
* @returns Delay in milliseconds with jitter applied
|
|
20
|
+
*/
|
|
21
|
+
export declare function calculateDelay(attempt: number, initialDelay: number, maxDelay: number): number;
|
|
22
|
+
/**
|
|
23
|
+
* Parse the Retry-After HTTP header value (seconds format only).
|
|
24
|
+
*
|
|
25
|
+
* Returns the value in milliseconds, capped at 30 seconds.
|
|
26
|
+
* Returns null for null, empty, non-numeric, or non-positive values.
|
|
27
|
+
* Does NOT handle HTTP date format.
|
|
28
|
+
*
|
|
29
|
+
* @param headerValue - The Retry-After header value (string of seconds)
|
|
30
|
+
* @returns Delay in milliseconds, or null if unparseable
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseRetryAfter(headerValue: string | null): number | null;
|
|
33
|
+
/**
|
|
34
|
+
* Check whether a status code or error name indicates a retryable condition.
|
|
35
|
+
*
|
|
36
|
+
* Retryable statuses: 429 (rate limit), 502 (bad gateway), 503 (service unavailable).
|
|
37
|
+
* Retryable errors: AbortError (timeout).
|
|
38
|
+
*
|
|
39
|
+
* @param statusOrErrorName - HTTP status code or error name string
|
|
40
|
+
* @returns true if the condition is retryable
|
|
41
|
+
*/
|
|
42
|
+
export declare function isRetryable(statusOrErrorName: number | string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Sleep for the specified number of milliseconds.
|
|
45
|
+
*
|
|
46
|
+
* @param ms - Duration to sleep in milliseconds
|
|
47
|
+
* @returns Promise that resolves after the delay
|
|
48
|
+
*/
|
|
49
|
+
export declare function sleep(ms: number): Promise<void>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/** Zero-dependency retry utilities for transient error handling. */
|
|
2
|
+
/** Default retry configuration: 2 retries, 1s initial, 10s max. */
|
|
3
|
+
export const DEFAULT_RETRY = {
|
|
4
|
+
maxRetries: 2,
|
|
5
|
+
initialDelay: 1000,
|
|
6
|
+
maxDelay: 10_000,
|
|
7
|
+
};
|
|
8
|
+
/** Retryable HTTP status codes. */
|
|
9
|
+
const RETRYABLE_STATUSES = new Set([429, 502, 503]);
|
|
10
|
+
/** Maximum Retry-After value in milliseconds (30 seconds). */
|
|
11
|
+
const MAX_RETRY_AFTER_MS = 30_000;
|
|
12
|
+
/**
|
|
13
|
+
* Calculate delay for a retry attempt using exponential backoff with jitter.
|
|
14
|
+
*
|
|
15
|
+
* Computes base = min(initialDelay * 2^attempt, maxDelay), then applies
|
|
16
|
+
* +/-25% random jitter. Result is clamped to >= 0.
|
|
17
|
+
*
|
|
18
|
+
* @param attempt - Zero-based retry attempt number
|
|
19
|
+
* @param initialDelay - Base delay in milliseconds
|
|
20
|
+
* @param maxDelay - Maximum delay cap in milliseconds
|
|
21
|
+
* @returns Delay in milliseconds with jitter applied
|
|
22
|
+
*/
|
|
23
|
+
export function calculateDelay(attempt, initialDelay, maxDelay) {
|
|
24
|
+
const base = Math.min(initialDelay * Math.pow(2, attempt), maxDelay);
|
|
25
|
+
const jitter = base * 0.25 * (2 * Math.random() - 1);
|
|
26
|
+
return Math.max(0, base + jitter);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parse the Retry-After HTTP header value (seconds format only).
|
|
30
|
+
*
|
|
31
|
+
* Returns the value in milliseconds, capped at 30 seconds.
|
|
32
|
+
* Returns null for null, empty, non-numeric, or non-positive values.
|
|
33
|
+
* Does NOT handle HTTP date format.
|
|
34
|
+
*
|
|
35
|
+
* @param headerValue - The Retry-After header value (string of seconds)
|
|
36
|
+
* @returns Delay in milliseconds, or null if unparseable
|
|
37
|
+
*/
|
|
38
|
+
export function parseRetryAfter(headerValue) {
|
|
39
|
+
if (headerValue === null || headerValue === "")
|
|
40
|
+
return null;
|
|
41
|
+
const seconds = Number(headerValue);
|
|
42
|
+
if (!Number.isFinite(seconds) || seconds <= 0)
|
|
43
|
+
return null;
|
|
44
|
+
return Math.min(seconds * 1000, MAX_RETRY_AFTER_MS);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Check whether a status code or error name indicates a retryable condition.
|
|
48
|
+
*
|
|
49
|
+
* Retryable statuses: 429 (rate limit), 502 (bad gateway), 503 (service unavailable).
|
|
50
|
+
* Retryable errors: AbortError (timeout).
|
|
51
|
+
*
|
|
52
|
+
* @param statusOrErrorName - HTTP status code or error name string
|
|
53
|
+
* @returns true if the condition is retryable
|
|
54
|
+
*/
|
|
55
|
+
export function isRetryable(statusOrErrorName) {
|
|
56
|
+
if (typeof statusOrErrorName === "number") {
|
|
57
|
+
return RETRYABLE_STATUSES.has(statusOrErrorName);
|
|
58
|
+
}
|
|
59
|
+
return statusOrErrorName === "AbortError";
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Sleep for the specified number of milliseconds.
|
|
63
|
+
*
|
|
64
|
+
* @param ms - Duration to sleep in milliseconds
|
|
65
|
+
* @returns Promise that resolves after the delay
|
|
66
|
+
*/
|
|
67
|
+
export function sleep(ms) {
|
|
68
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"AAAA,oEAAoE;AASpE,mEAAmE;AACnE,MAAM,CAAC,MAAM,aAAa,GAAiB;IACzC,UAAU,EAAE,CAAC;IACb,YAAY,EAAE,IAAI;IAClB,QAAQ,EAAE,MAAM;CACjB,CAAC;AAEF,mCAAmC;AACnC,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAEpD,8DAA8D;AAC9D,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,YAAoB,EACpB,QAAgB;IAEhB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;IACrE,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IACrD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,WAA0B;IACxD,IAAI,WAAW,KAAK,IAAI,IAAI,WAAW,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC5D,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3D,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,EAAE,kBAAkB,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,iBAAkC;IAC5D,IAAI,OAAO,iBAAiB,KAAK,QAAQ,EAAE,CAAC;QAC1C,OAAO,kBAAkB,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,iBAAiB,KAAK,YAAY,CAAC;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,KAAK,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Reactive scope authorization: prompt and recovery utilities.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*
|
|
4
|
+
* Instead of pre-flight scope checks, this module provides:
|
|
5
|
+
*
|
|
6
|
+
* - `promptScopeAuth()` — Interactive OAuth prompt for missing scopes.
|
|
7
|
+
* - `withScopeRecovery()` — Wrapper that catches SCOPE_MISSING errors,
|
|
8
|
+
* prompts the user (if interactive), and retries the operation once.
|
|
9
|
+
*
|
|
10
|
+
* The Feishu API is the source of truth for required scopes. When an API
|
|
11
|
+
* call fails with 99991672 (app scope) or 99991679 (user scope), `fetchWithAuth`
|
|
12
|
+
* throws `CliError("SCOPE_MISSING", { missingScopes })`. This module handles
|
|
13
|
+
* the recovery.
|
|
11
14
|
*/
|
|
12
|
-
|
|
15
|
+
import type { GlobalOpts } from "../types/index.js";
|
|
13
16
|
/**
|
|
14
17
|
* Prompt the user to authorize missing scopes interactively.
|
|
15
18
|
*
|
|
@@ -19,16 +22,18 @@ export declare function isPermissionError(err: unknown): boolean;
|
|
|
19
22
|
*
|
|
20
23
|
* Returns `true` if authorization succeeded, `false` otherwise.
|
|
21
24
|
* Always returns `false` in non-interactive contexts (JSON mode, non-TTY).
|
|
22
|
-
*
|
|
23
|
-
* @param storedScopeStr Pre-loaded stored scope string (avoids duplicate
|
|
24
|
-
* loadTokens call when called from ensureScopes).
|
|
25
25
|
*/
|
|
26
|
-
export declare function promptScopeAuth(missingScopes: string[], globalOpts: GlobalOpts
|
|
26
|
+
export declare function promptScopeAuth(missingScopes: string[], globalOpts: GlobalOpts): Promise<boolean>;
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
28
|
+
* Wrap an async operation with reactive scope error recovery.
|
|
29
|
+
*
|
|
30
|
+
* When the operation throws `CliError("SCOPE_MISSING")`:
|
|
31
|
+
* - If interactive (TTY + non-JSON) and scopes are known: prompt → retry once.
|
|
32
|
+
* - Otherwise: re-throw with a clear recovery hint.
|
|
29
33
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
34
|
+
* @param fallbackScopes Scope names to use when the API doesn't include
|
|
35
|
+
* `permission_violations` (older APIs like search). When the error has
|
|
36
|
+
* empty `missingScopes` but `fallbackScopes` is provided, these are used
|
|
37
|
+
* for the authorization prompt.
|
|
33
38
|
*/
|
|
34
|
-
export declare function
|
|
39
|
+
export declare function withScopeRecovery<T>(fn: () => Promise<T>, globalOpts: GlobalOpts, fallbackScopes?: string[]): Promise<T>;
|