feishu-docs-cli 0.1.0-beta.9 → 1.0.1

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.
Files changed (83) hide show
  1. package/README.md +72 -30
  2. package/README.zh.md +55 -30
  3. package/dist/auth.js +31 -17
  4. package/dist/auth.js.map +1 -1
  5. package/dist/cli.js +5 -4
  6. package/dist/cli.js.map +1 -1
  7. package/dist/client.d.ts +4 -2
  8. package/dist/client.js +177 -61
  9. package/dist/client.js.map +1 -1
  10. package/dist/commands/authorize.d.ts +3 -0
  11. package/dist/commands/authorize.js +16 -34
  12. package/dist/commands/authorize.js.map +1 -1
  13. package/dist/commands/cp.js +44 -30
  14. package/dist/commands/cp.js.map +1 -1
  15. package/dist/commands/create.js +21 -7
  16. package/dist/commands/create.js.map +1 -1
  17. package/dist/commands/delete.js +53 -54
  18. package/dist/commands/delete.js.map +1 -1
  19. package/dist/commands/login.js +6 -2
  20. package/dist/commands/login.js.map +1 -1
  21. package/dist/commands/ls.js +38 -38
  22. package/dist/commands/ls.js.map +1 -1
  23. package/dist/commands/mkdir.js +25 -25
  24. package/dist/commands/mkdir.js.map +1 -1
  25. package/dist/commands/mv.d.ts +2 -2
  26. package/dist/commands/mv.js +45 -40
  27. package/dist/commands/mv.js.map +1 -1
  28. package/dist/commands/read.d.ts +1 -1
  29. package/dist/commands/read.js +17 -354
  30. package/dist/commands/read.js.map +1 -1
  31. package/dist/commands/search.js +57 -55
  32. package/dist/commands/search.js.map +1 -1
  33. package/dist/commands/share.js +135 -133
  34. package/dist/commands/share.js.map +1 -1
  35. package/dist/commands/update.js +43 -60
  36. package/dist/commands/update.js.map +1 -1
  37. package/dist/commands/wiki.js +8 -20
  38. package/dist/commands/wiki.js.map +1 -1
  39. package/dist/parser/block-types.d.ts +0 -1
  40. package/dist/parser/block-types.js +0 -22
  41. package/dist/parser/block-types.js.map +1 -1
  42. package/dist/parser/blocks-to-md.d.ts +10 -18
  43. package/dist/parser/blocks-to-md.js +341 -450
  44. package/dist/parser/blocks-to-md.js.map +1 -1
  45. package/dist/scopes.d.ts +5 -47
  46. package/dist/scopes.js +9 -54
  47. package/dist/scopes.js.map +1 -1
  48. package/dist/services/block-writer.d.ts +3 -2
  49. package/dist/services/block-writer.js +29 -13
  50. package/dist/services/block-writer.js.map +1 -1
  51. package/dist/services/doc-blocks.d.ts +1 -1
  52. package/dist/services/doc-blocks.js +1 -1
  53. package/dist/services/doc-blocks.js.map +1 -1
  54. package/dist/services/doc-enrichment.d.ts +64 -0
  55. package/dist/services/doc-enrichment.js +397 -0
  56. package/dist/services/doc-enrichment.js.map +1 -0
  57. package/dist/services/image-download.d.ts +31 -0
  58. package/dist/services/image-download.js +127 -0
  59. package/dist/services/image-download.js.map +1 -0
  60. package/dist/services/markdown-convert.d.ts +20 -0
  61. package/dist/services/markdown-convert.js +55 -1
  62. package/dist/services/markdown-convert.js.map +1 -1
  63. package/dist/services/wiki-nodes.d.ts +1 -1
  64. package/dist/services/wiki-nodes.js +2 -3
  65. package/dist/services/wiki-nodes.js.map +1 -1
  66. package/dist/types/api-responses.d.ts +34 -0
  67. package/dist/types/api-responses.js +8 -0
  68. package/dist/types/api-responses.js.map +1 -0
  69. package/dist/types/index.d.ts +9 -17
  70. package/dist/utils/concurrency.d.ts +12 -0
  71. package/dist/utils/concurrency.js +37 -0
  72. package/dist/utils/concurrency.js.map +1 -0
  73. package/dist/utils/errors.d.ts +3 -1
  74. package/dist/utils/errors.js +11 -7
  75. package/dist/utils/errors.js.map +1 -1
  76. package/dist/utils/retry.d.ts +49 -0
  77. package/dist/utils/retry.js +70 -0
  78. package/dist/utils/retry.js.map +1 -0
  79. package/dist/utils/scope-prompt.d.ts +23 -18
  80. package/dist/utils/scope-prompt.js +62 -51
  81. package/dist/utils/scope-prompt.js.map +1 -1
  82. package/package.json +3 -1
  83. package/skills/feishu-docs/SKILL.md +24 -3
@@ -1,24 +1,21 @@
1
1
  /**
2
- * Interactive scope authorization prompt and recovery utilities.
2
+ * Reactive scope authorization: prompt and recovery utilities.
3
3
  *
4
- * When a command discovers missing OAuth scopes, these helpers guide
5
- * the user through authorizing them — or gracefully skipping if the
6
- * user declines (or the session is non-interactive).
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.
7
14
  */
8
15
  import * as readline from "node:readline";
9
16
  import { oauthLogin, loadTokens } from "../auth.js";
10
- import { createClient } from "../client.js";
11
17
  import { CliError } from "./errors.js";
12
- import { BASE_SCOPES, mergeScopes, getMissingScopes, buildScopeHint, } from "../scopes.js";
13
- /**
14
- * Check whether an error is a permission / scope error.
15
- */
16
- export function isPermissionError(err) {
17
- if (err instanceof CliError) {
18
- return (err.errorType === "PERMISSION_DENIED" || err.errorType === "AUTH_REQUIRED");
19
- }
20
- return false;
21
- }
18
+ import { BASE_SCOPES, mergeScopes, buildScopeHint } from "../scopes.js";
22
19
  /**
23
20
  * Ask a yes/no question on stderr, read from stdin.
24
21
  * Returns true only if user answers "y" or "yes".
@@ -44,11 +41,8 @@ function askYesNo(question) {
44
41
  *
45
42
  * Returns `true` if authorization succeeded, `false` otherwise.
46
43
  * Always returns `false` in non-interactive contexts (JSON mode, non-TTY).
47
- *
48
- * @param storedScopeStr Pre-loaded stored scope string (avoids duplicate
49
- * loadTokens call when called from ensureScopes).
50
44
  */
51
- export async function promptScopeAuth(missingScopes, globalOpts, storedScopeStr) {
45
+ export async function promptScopeAuth(missingScopes, globalOpts) {
52
46
  // Non-interactive: skip prompt
53
47
  if (globalOpts.json || !process.stdin.isTTY) {
54
48
  return false;
@@ -57,6 +51,10 @@ export async function promptScopeAuth(missingScopes, globalOpts, storedScopeStr)
57
51
  const appSecret = process.env.FEISHU_APP_SECRET;
58
52
  if (!appId || !appSecret)
59
53
  return false;
54
+ // Skip interactive OAuth when using env-var token — re-auth would save to file
55
+ // but createClient still prefers the env token, so retry would fail again.
56
+ if (process.env.FEISHU_USER_TOKEN)
57
+ return false;
60
58
  const scopeList = missingScopes.join(", ");
61
59
  const scopeStr = missingScopes.join(" ");
62
60
  process.stderr.write(`\nfeishu-docs: 缺少以下权限: ${scopeList}\n` +
@@ -67,16 +65,10 @@ export async function promptScopeAuth(missingScopes, globalOpts, storedScopeStr)
67
65
  if (!yes)
68
66
  return false;
69
67
  // Merge missing scopes with current grants and re-run OAuth
70
- let currentScopes;
71
- if (storedScopeStr) {
72
- currentScopes = storedScopeStr.split(/\s+/).filter(Boolean);
73
- }
74
- else {
75
- const stored = await loadTokens();
76
- currentScopes = stored?.tokens?.scope
77
- ? stored.tokens.scope.split(/\s+/).filter(Boolean)
78
- : [...BASE_SCOPES];
79
- }
68
+ const stored = await loadTokens();
69
+ const currentScopes = stored?.tokens?.scope
70
+ ? stored.tokens.scope.split(/\s+/).filter(Boolean)
71
+ : [...BASE_SCOPES];
80
72
  const merged = mergeScopes(currentScopes, missingScopes);
81
73
  try {
82
74
  await oauthLogin(appId, {
@@ -94,30 +86,49 @@ export async function promptScopeAuth(missingScopes, globalOpts, storedScopeStr)
94
86
  }
95
87
  }
96
88
  /**
97
- * Pre-flight scope check with interactive recovery.
89
+ * Wrap an async operation with reactive scope error recovery.
90
+ *
91
+ * When the operation throws `CliError("SCOPE_MISSING")`:
92
+ * - If interactive (TTY + non-JSON) and scopes are known: prompt → retry once.
93
+ * - Otherwise: re-throw with a clear recovery hint.
98
94
  *
99
- * If scopes are missing and the user authorizes them, returns a fresh
100
- * `AuthInfo` with the new token. If no recovery is needed, returns the
101
- * original `authInfo` unchanged. If the user declines, throws.
95
+ * @param fallbackScopes Scope names to use when the API doesn't include
96
+ * `permission_violations` (older APIs like search). When the error has
97
+ * empty `missingScopes` but `fallbackScopes` is provided, these are used
98
+ * for the authorization prompt.
102
99
  */
103
- export async function ensureScopes(authInfo, requiredScopes, globalOpts) {
104
- if (authInfo.mode !== "user")
105
- return authInfo;
106
- // No stored file → env-var token or tenant mode; scope info unavailable,
107
- // skip pre-flight check (downstream API call will surface permission errors).
108
- const stored = await loadTokens();
109
- if (!stored)
110
- return authInfo;
111
- const missing = getMissingScopes(stored.tokens.scope, [...requiredScopes]);
112
- if (missing.length === 0)
113
- return authInfo;
114
- // Pass stored scope string to avoid a second loadTokens() call
115
- const authorized = await promptScopeAuth(missing, globalOpts, stored.tokens.scope);
116
- if (!authorized) {
117
- throw new CliError("AUTH_REQUIRED", buildScopeHint(missing));
100
+ export async function withScopeRecovery(fn, globalOpts, fallbackScopes) {
101
+ try {
102
+ return await fn();
103
+ }
104
+ catch (err) {
105
+ if (!(err instanceof CliError) || err.errorType !== "SCOPE_MISSING") {
106
+ throw err;
107
+ }
108
+ const apiScopes = err.missingScopes ?? [];
109
+ const scopes = apiScopes.length > 0 ? apiScopes : (fallbackScopes ?? []);
110
+ // No scopes from API or fallback → can't prompt meaningfully, just throw
111
+ if (scopes.length === 0) {
112
+ throw err;
113
+ }
114
+ // Tenant mode: OAuth produces a user token which tenant auth ignores,
115
+ // so interactive recovery would be pointless — just throw with a hint.
116
+ if (globalOpts.auth === "tenant") {
117
+ throw new CliError("AUTH_REQUIRED", buildScopeHint(scopes), {
118
+ apiCode: err.apiCode,
119
+ missingScopes: scopes,
120
+ });
121
+ }
122
+ // Try interactive recovery
123
+ const authorized = await promptScopeAuth(scopes, globalOpts);
124
+ if (!authorized) {
125
+ throw new CliError("AUTH_REQUIRED", buildScopeHint(scopes), {
126
+ apiCode: err.apiCode,
127
+ missingScopes: scopes,
128
+ });
129
+ }
130
+ // Retry once — if it fails again, let the error propagate
131
+ return await fn();
118
132
  }
119
- // Re-create client to pick up the fresh token
120
- const { authInfo: refreshed } = await createClient(globalOpts);
121
- return refreshed;
122
133
  }
123
134
  //# sourceMappingURL=scope-prompt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scope-prompt.js","sourceRoot":"","sources":["../../src/utils/scope-prompt.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scope-prompt.js","sourceRoot":"","sources":["../../src/utils/scope-prompt.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feishu-docs-cli",
3
- "version": "0.1.0-beta.9",
3
+ "version": "1.0.1",
4
4
  "description": "CLI tool for AI Agents to read/write Feishu docs via shell commands",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,6 +21,7 @@
21
21
  "build:check": "tsc --noEmit",
22
22
  "pretest": "npm run build:check",
23
23
  "test": "tsx --test test/*.test.ts",
24
+ "lint:dead-code": "knip",
24
25
  "prepublishOnly": "npm run build"
25
26
  },
26
27
  "repository": {
@@ -47,6 +48,7 @@
47
48
  },
48
49
  "devDependencies": {
49
50
  "@types/node": "^25.5.0",
51
+ "knip": "^6.0.6",
50
52
  "tsx": "^4.21.0",
51
53
  "typescript": "^5.9.3"
52
54
  }
@@ -9,7 +9,23 @@ description: Read, write, search, and manage Feishu (Lark) cloud documents and k
9
9
 
10
10
  ## Prerequisites
11
11
 
12
- Before using any command, verify the environment is ready:
12
+ Before using any command, check that the CLI is installed and authenticated. Run these two checks in order:
13
+
14
+ ### Step 1: Check installation
15
+
16
+ ```bash
17
+ command -v feishu-docs >/dev/null 2>&1 && feishu-docs --version || echo "NOT_INSTALLED"
18
+ ```
19
+
20
+ If the output is `NOT_INSTALLED`, install the CLI first:
21
+
22
+ ```bash
23
+ npm install -g feishu-docs-cli
24
+ ```
25
+
26
+ This installs the `feishu-docs` command globally. Requires Node.js 18+. After installation, verify with `feishu-docs --version`.
27
+
28
+ ### Step 2: Check authentication
13
29
 
14
30
  ```bash
15
31
  feishu-docs whoami
@@ -90,7 +106,7 @@ feishu-docs update <url> --body ./extra.md --append
90
106
  echo "## New Section" | feishu-docs update <url> --body - --append
91
107
  ```
92
108
 
93
- Overwrite mode automatically backs up the current document to `~/.feishu-docs/backups/` before writing. If the write fails, it auto-recovers from the backup.
109
+ Overwrite mode automatically backs up the current document to `~/.feishu-docs/backups/` before writing. If the write fails, it auto-recovers from the backup. Backups are kept for undo; old backups are rotated automatically (max 10 per document).
94
110
 
95
111
  To restore a previous version:
96
112
  ```bash
@@ -170,6 +186,7 @@ Every command accepts these flags:
170
186
  | `--auth tenant` | Force app token (CI/CD, shared docs) |
171
187
  | `--json` | Output structured JSON instead of text |
172
188
  | `--lark` | Use Lark (international) domain |
189
+ | `-v, --version` | Show version number |
173
190
 
174
191
  Default auth mode is `auto` — tries user token first, falls back to tenant.
175
192
 
@@ -189,6 +206,10 @@ Default auth mode is `auto` — tries user token first, falls back to tenant.
189
206
 
190
207
  - Only `docx` (new document format) is fully supported for read/write
191
208
  - Legacy `doc` format is not supported
192
- - Images cannot be written; read returns temporary URLs valid ~24 hours
209
+ - Embedded `sheet` and `bitable` are rendered as tables (lossy)
210
+ - Embedded `board`/`whiteboard` are exported as local PNG images
211
+ - `mindnote` renders as a link only
212
+ - Images are downloaded to `~/.feishu-docs/images/` with 30-day cache. Image write is not supported.
213
+ - Mermaid code blocks are preserved as-is (code block, not visual diagram) — the Open API does not support creating visual "text diagram" blocks
193
214
  - Markdown conversion is lossy — use `--blocks` for lossless JSON when precision matters
194
215
  - Search requires user-level auth (run `feishu-docs login` first)