Skip to content

feat(drive): add +member-add shortcut for Drive collaborator permissions#1204

Open
zhaojiaxing-coding wants to merge 1 commit into
mainfrom
feat/drive-member-add-shortcut
Open

feat(drive): add +member-add shortcut for Drive collaborator permissions#1204
zhaojiaxing-coding wants to merge 1 commit into
mainfrom
feat/drive-member-add-shortcut

Conversation

@zhaojiaxing-coding
Copy link
Copy Markdown
Collaborator

@zhaojiaxing-coding zhaojiaxing-coding commented Jun 1, 2026

Add drive +member-add shortcut that grants view/edit/full_access permissions to collaborators on Drive documents, files, folders, and wiki nodes. Supports single and batch member add (up to 10).

Key features:

  • Auto-infer resource type from Feishu/Lark/Doubao URL or token prefix
  • Required --member-type with prefix conflict detection (appid aliased to userid)
  • Reject --perm-type for non-wiki resource types
  • Trusted host validation for URL inputs

Also extends shortcuts/common/resource_url with minutes type support and InferResourceTypeFromToken for bare token prefix mapping.

Summary

Changes

  • Change 1
  • Change 2

Test Plan

  • Unit tests pass
  • Manual local verification confirms the lark-cli <domain> <command> flow works as expected

Related Issues

  • None

Summary by CodeRabbit

  • New Features

    • Added drive +member-add shortcut to add collaborators to Drive resources (docs, folders, spaces, wikis) with batch support, member type inference, and notification options.
    • Extended resource URL support for minutes resource type.
  • Documentation

    • Added comprehensive documentation for drive +member-add command with usage examples and parameter specifications.
    • Updated lark-drive skill documentation with guidance for the new shortcut.
  • Tests

    • Added extensive test coverage for drive +member-add command validation, behavior, and end-to-end CLI scenarios.

Add drive +member-add shortcut that grants view/edit/full_access
permissions to collaborators on Drive documents, files, folders,
and wiki nodes. Supports single and batch member add (up to 10).

Key features:
- Auto-infer resource type from Feishu/Lark/Doubao URL or token prefix
- Required --member-type with prefix conflict detection (appid aliased to userid)
- Reject --perm-type for non-wiki resource types
- Trusted host validation for URL inputs

Also extends shortcuts/common/resource_url with minutes type support
and InferResourceTypeFromToken for bare token prefix mapping.
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces the drive +member-add shortcut for adding collaborators to Drive resources (docs, folders, spaces, wiki nodes), extends resource URL infrastructure to support minutes resource type with token-prefix inference, and provides comprehensive test coverage and documentation for the new feature.

Changes

Drive +member-add shortcut and resource type inference

Layer / File(s) Summary
Minutes resource type support
shortcuts/common/resource_url.go, shortcuts/common/resource_url_test.go
BuildResourceURL builds /minutes/{token} URLs, ParseResourceURL recognizes /minutes/ paths, and new InferResourceTypeFromToken infers minutes type from mincn token prefixes via a token-prefix mapping. Tests validate parsing, round-trip building, inference with empty/unrecognized prefixes, and output formatting.
Member-add shortcut structure and validation
shortcuts/drive/drive_member_add.go (lines 1–346)
DriveMemberAdd shortcut defines flags and control flow; driveMemberAddSpec centralizes request parsing with member-type resolution (from explicit flag or ID prefix), permission defaulting, wiki-specific perm-type constraints, and bot-notification gating. Helpers parse trusted URLs vs bare tokens, infer resource type and member kind, and normalize appid↔userid aliasing.
Member-add execution and output
shortcuts/drive/drive_member_add.go (lines 347–482)
Dry-run rendering generates preview request bodies for single or batch collaborator creation. Single-member and batch execution paths call Drive API endpoints, handle responses, and shape output by backfilling missing fields (member_id, member_type, perm, perm_type, member_kind) and aliasing userid to appid for display.
Member-add tests
shortcuts/drive/drive_member_add_test.go
Unit tests for target parsing, member-type inference, ID splitting, and spec construction. Integration tests validate permission defaults, notification constraints, batch limits, wiki-specific behavior, dry-run correctness, API response flattening, bot-specific query behavior, and high-risk write confirmation requirements.
Shortcut registration and e2e tests
shortcuts/drive/shortcuts.go, shortcuts/drive/shortcuts_test.go, tests/cli_e2e/drive/drive_member_add_dryrun_test.go
Shortcuts() includes DriveMemberAdd. E2E CLI tests validate dry-run request structure (method, URL, resource type, query fields, body shape) and error handling for invalid inputs across multiple member-type/token/permission scenarios.
Skill documentation
skills/lark-drive/SKILL.md, skills/lark-drive/references/lark-drive-member-add.md
SKILL.md adds wiki-token guidance and updates bot permission flow to use drive +member-add with bot open_id. New reference page documents command usage, parameters (--token, --member-id, --member-type, --perm, --perm-type, --need-notification, --dry-run, --yes), output shapes, and identity/scope behavior.

Sequence Diagram

sequenceDiagram
  participant User
  participant CLI as drive +member-add
  participant Parser as readDriveMemberAddSpec
  participant Inference as Token/Type Inference
  participant DriveAPI as Drive API
  participant Output as driveMemberAddOutput
  User->>CLI: --token URL/token --member-id ID --perm view
  CLI->>Parser: parse flags
  Parser->>Inference: infer resource type from token
  Inference->>Parser: resource type (doc/wiki/etc)
  Parser->>Parser: validate member-type, resolve perm defaults
  CLI->>DriveAPI: single/batch collaborator.create
  DriveAPI->>Output: API response
  Output->>Output: backfill missing fields, alias types
  Output->>User: flattened JSON + status
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

The PR introduces a substantial new feature with multiple interacting components: resource type inference, command-line parsing with cross-field validation, nested execution logic for single vs batch operations, API response reshaping, and comprehensive test coverage spanning unit, integration, and e2e scenarios. The changes are spread across multiple files with varied complexity, including permission/notification constraints, bot-specific behavior, and wiki-specific rules that require careful review.

Possibly related PRs

  • larksuite/cli#947: Modifies shortcuts/common/resource_url.go URL/type parsing that this PR extends with the minutes resource type
  • larksuite/cli#680: Updates BuildResourceURL URL-kind mapping in the same file, directly related at the URL-building infrastructure level
  • larksuite/cli#414: Updates skills/lark-drive/SKILL.md with the same bot permission procedure now using the new drive +member-add shortcut

Suggested labels

size/XL, domain/ccm

Suggested reviewers

  • wittam-01
  • fangshuyu-768

Poem

🐰 A shortcut to befriend,
Minutes and members now blend,
Types inferred from token's start,
Collaborators join with art,
Wiki and doc, in batches we send!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description includes concrete feature details and lists the main changes, but the template sections (Summary, Changes, Test Plan, Related Issues) are largely unfilled or duplicated, lacking clarity on how the change was verified. Fill out or remove the template sections properly: provide a concise summary under '## Summary', list specific changes under '## Changes', confirm test verification status under '## Test Plan', and clarify '## Related Issues'.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a +member-add shortcut to the Drive module for managing collaborator permissions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/drive-member-add-shortcut

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added domain/ccm PR touches the ccm domain size/L Large or sensitive change across domains or core paths labels Jun 1, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 1, 2026

Codecov Report

❌ Patch coverage is 76.24521% with 62 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.17%. Comparing base (bbef3cb) to head (737045a).
⚠️ Report is 22 commits behind head on main.

Files with missing lines Patch % Lines
shortcuts/drive/drive_member_add.go 75.10% 52 Missing and 10 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1204      +/-   ##
==========================================
+ Coverage   68.61%   69.17%   +0.56%     
==========================================
  Files         625      631       +6     
  Lines       58348    59571    +1223     
==========================================
+ Hits        40035    41210    +1175     
- Misses      15029    15040      +11     
- Partials     3284     3321      +37     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@737045a42620977aabec9b06eef8626eee2231cc

🧩 Skill update

npx skills add larksuite/cli#feat/drive-member-add-shortcut -y -g

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@shortcuts/drive/drive_member_add_test.go`:
- Around line 673-711: The test currently only asserts the POST body but must
also assert the bot request omitted the need_notification query param; update
the test that uses the httpmock.Stub (stub) in DriveMemberAdd/mountAndRunDrive
to capture the incoming request's URL.RawQuery (or use the stub's
CapturedRequest/URL field) after mountAndRunDrive runs, then assert that the raw
query string does not contain "need_notification" (or that parsed query has no
"need_notification" key); keep existing body assertions (captured["perm"] ==
"edit") and add a failing t.Fatalf message if the query contains
need_notification so the execute-path contract is enforced.

In `@shortcuts/drive/drive_member_add.go`:
- Around line 414-438: The loop currently only appends members when
rawMembers[i] is a valid map and falls back to backfilling only if results is
empty; change it to iterate over spec.MemberIDs (by index) and for each index
attempt to read rawMembers[i] as a map[string]interface{}—if present use
driveMemberAddOutput(spec, memberID, m), otherwise call
driveMemberAddOutput(spec, memberID, nil) so every requested member is
represented; ensure results is built in the same order as spec.MemberIDs and
that the final runtime.Out and the printed count reflect the full spec.MemberIDs
length.

In `@skills/lark-drive/SKILL.md`:
- Around line 259-261: The two adjacent blockquote paragraphs in SKILL.md need
to be merged into one contiguous blockquote to satisfy markdownlint MD028;
remove the blank line between the two blocks and combine their text into a
single blockquote so the note about member-type mapping and recommendation to
use bot open_id appears as one continuous block (look for the blockquote
starting with "注意:此方式仅适用于需要授权给**当前应用**的场景。" and the following "补充:" paragraph
and join them).

In `@tests/cli_e2e/drive/drive_member_add_dryrun_test.go`:
- Around line 319-320: The test currently asserts the dry-run validation error
only against result.Stderr; update the assertion so Validate-stage failures are
checked from the full CLI output by combining result.Stdout and result.Stderr.
Replace the require.Contains call that uses result.Stderr with one that checks
result.Stdout+result.Stderr (keeping the existing result.AssertExitCode(t, 2)
and tt.wantErr), so the test verifies the error message in the structured stdout
envelope as well as stderr.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e259d1a8-6b0f-4838-9192-0e4b19d3539b

📥 Commits

Reviewing files that changed from the base of the PR and between 0bdd7de and 737045a.

📒 Files selected for processing (9)
  • shortcuts/common/resource_url.go
  • shortcuts/common/resource_url_test.go
  • shortcuts/drive/drive_member_add.go
  • shortcuts/drive/drive_member_add_test.go
  • shortcuts/drive/shortcuts.go
  • shortcuts/drive/shortcuts_test.go
  • skills/lark-drive/SKILL.md
  • skills/lark-drive/references/lark-drive-member-add.md
  • tests/cli_e2e/drive/drive_member_add_dryrun_test.go

Comment on lines +673 to +711
stub := &httpmock.Stub{
Method: "POST",
URL: "/open-apis/drive/v1/permissions/wikcnBotTok/members",
Body: map[string]interface{}{
"code": 0,
"msg": "success",
"data": map[string]interface{}{
"member": map[string]interface{}{
"member_id": "ou_bot_target",
"member_type": "openid",
"perm": "edit",
"type": "user",
},
},
},
}
reg.Register(stub)

err := mountAndRunDrive(t, DriveMemberAdd, []string{
"+member-add",
"--token", "wikcnBotTok",
"--member-id", "ou_bot_target",
"--member-type", "openid",
"--perm", "edit",
"--as", "bot",
"--yes",
}, f, stdout)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

// Bot identity should NOT send need_notification in query.
var captured map[string]interface{}
if err := json.Unmarshal(stub.CapturedBody, &captured); err != nil {
t.Fatalf("decode captured body: %v", err)
}
if captured["perm"] != "edit" {
t.Fatalf("captured body perm = %v, want edit", captured["perm"])
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Assert the bot query omission you describe.

This test says bot execution must not send need_notification, but it only checks the captured body. Capture req.URL.RawQuery and assert the parameter is absent so the execute-path contract is actually pinned.

Suggested test update
 func TestDriveMemberAdd_ExecuteSuccessAsBot(t *testing.T) {
 	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())

 	f, stdout, _, reg := cmdutil.TestFactory(t, driveTestConfig())
+	var capturedQuery string

 	stub := &httpmock.Stub{
 		Method: "POST",
 		URL:    "/open-apis/drive/v1/permissions/wikcnBotTok/members",
+		OnMatch: func(req *http.Request) {
+			capturedQuery = req.URL.RawQuery
+		},
 		Body: map[string]interface{}{
 			"code": 0,
 			"msg":  "success",
 			"data": map[string]interface{}{
@@
 	if captured["perm"] != "edit" {
 		t.Fatalf("captured body perm = %v, want edit", captured["perm"])
 	}
+	if strings.Contains(capturedQuery, "need_notification=") {
+		t.Fatalf("captured query = %q, want need_notification omitted", capturedQuery)
+	}

 	data := decodeDriveEnvelope(t, stdout)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/drive/drive_member_add_test.go` around lines 673 - 711, The test
currently only asserts the POST body but must also assert the bot request
omitted the need_notification query param; update the test that uses the
httpmock.Stub (stub) in DriveMemberAdd/mountAndRunDrive to capture the incoming
request's URL.RawQuery (or use the stub's CapturedRequest/URL field) after
mountAndRunDrive runs, then assert that the raw query string does not contain
"need_notification" (or that parsed query has no "need_notification" key); keep
existing body assertions (captured["perm"] == "edit") and add a failing t.Fatalf
message if the query contains need_notification so the execute-path contract is
enforced.

Comment on lines +414 to +438
// Flatten batch response: data.members is an array of member objects.
rawMembers, _ := data["members"].([]interface{})
var results []map[string]interface{}
for i, raw := range rawMembers {
if m, ok := raw.(map[string]interface{}); ok {
memberID := ""
if i < len(spec.MemberIDs) {
memberID = spec.MemberIDs[i]
}
out := driveMemberAddOutput(spec, memberID, m)
results = append(results, out)
}
}
if len(results) == 0 {
// Fallback: API returned success but no member details; backfill from spec.
for _, mid := range spec.MemberIDs {
results = append(results, driveMemberAddOutput(spec, mid, nil))
}
}
fmt.Fprintf(runtime.IO().ErrOut, "Added %d Drive member(s)\n", len(results))
runtime.Out(map[string]interface{}{
"resource_token": spec.Token,
"resource_type": spec.ResourceType,
"members": results,
}, nil)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Backfill partially omitted batch results instead of only the all-or-nothing case.

If the API returns a shorter members array, or one element is missing/not an object, this loop silently drops that requested member and prints a smaller success count. For a permission-grant command, that makes the final envelope misleading even though you already have the original request list locally. Consider iterating over spec.MemberIDs and backfilling each slot individually when rawMembers[i] is absent or malformed, rather than only falling back when len(results) == 0.

💡 Suggested fix
-	rawMembers, _ := data["members"].([]interface{})
-	var results []map[string]interface{}
-	for i, raw := range rawMembers {
-		if m, ok := raw.(map[string]interface{}); ok {
-			memberID := ""
-			if i < len(spec.MemberIDs) {
-				memberID = spec.MemberIDs[i]
-			}
-			out := driveMemberAddOutput(spec, memberID, m)
-			results = append(results, out)
-		}
-	}
-	if len(results) == 0 {
-		// Fallback: API returned success but no member details; backfill from spec.
-		for _, mid := range spec.MemberIDs {
-			results = append(results, driveMemberAddOutput(spec, mid, nil))
-		}
-	}
+	rawMembers, _ := data["members"].([]interface{})
+	results := make([]map[string]interface{}, 0, len(spec.MemberIDs))
+	for i, mid := range spec.MemberIDs {
+		var rawMember map[string]interface{}
+		if i < len(rawMembers) {
+			if m, ok := rawMembers[i].(map[string]interface{}); ok {
+				rawMember = m
+			}
+		}
+		results = append(results, driveMemberAddOutput(spec, mid, rawMember))
+	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Flatten batch response: data.members is an array of member objects.
rawMembers, _ := data["members"].([]interface{})
var results []map[string]interface{}
for i, raw := range rawMembers {
if m, ok := raw.(map[string]interface{}); ok {
memberID := ""
if i < len(spec.MemberIDs) {
memberID = spec.MemberIDs[i]
}
out := driveMemberAddOutput(spec, memberID, m)
results = append(results, out)
}
}
if len(results) == 0 {
// Fallback: API returned success but no member details; backfill from spec.
for _, mid := range spec.MemberIDs {
results = append(results, driveMemberAddOutput(spec, mid, nil))
}
}
fmt.Fprintf(runtime.IO().ErrOut, "Added %d Drive member(s)\n", len(results))
runtime.Out(map[string]interface{}{
"resource_token": spec.Token,
"resource_type": spec.ResourceType,
"members": results,
}, nil)
// Flatten batch response: data.members is an array of member objects.
rawMembers, _ := data["members"].([]interface{})
results := make([]map[string]interface{}, 0, len(spec.MemberIDs))
for i, mid := range spec.MemberIDs {
var rawMember map[string]interface{}
if i < len(rawMembers) {
if m, ok := rawMembers[i].(map[string]interface{}); ok {
rawMember = m
}
}
results = append(results, driveMemberAddOutput(spec, mid, rawMember))
}
fmt.Fprintf(runtime.IO().ErrOut, "Added %d Drive member(s)\n", len(results))
runtime.Out(map[string]interface{}{
"resource_token": spec.Token,
"resource_type": spec.ResourceType,
"members": results,
}, nil)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/drive/drive_member_add.go` around lines 414 - 438, The loop
currently only appends members when rawMembers[i] is a valid map and falls back
to backfilling only if results is empty; change it to iterate over
spec.MemberIDs (by index) and for each index attempt to read rawMembers[i] as a
map[string]interface{}—if present use driveMemberAddOutput(spec, memberID, m),
otherwise call driveMemberAddOutput(spec, memberID, nil) so every requested
member is represented; ensure results is built in the same order as
spec.MemberIDs and that the final runtime.Out and the printed count reflect the
full spec.MemberIDs length.

Comment on lines +259 to +261
> **注意**:此方式仅适用于需要授权给**当前应用**的场景。授权给其他用户时,直接使用对方的 open_id 即可,无需调用 bot info 接口。`drive +member-add` 会从可信飞书/Lark/豆包 URL 或裸 token 前缀推断 resource type;`--member-type` 必须显式传入,如果 ID 前缀与 member-type 不一致会报错。

`<resource_type>` 可选值:`doc`、`docx`、`sheet`、`bitable`、`file`、`folder`、`wiki`、`slides`
> **补充**:如果你手里只有 app id,也可以把 `--member-type` 设为 `appid`;CLI 会在请求层兼容映射为 Drive API 的 `member_type=userid`。但在 skill 文档和实际操作里,仍然优先推荐使用 bot `open_id` + `--member-type openid`,因为这条路径语义更直接、和仓库里其他实现也更一致
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep this blockquote contiguous.

The blank line here triggers markdownlint MD028 (no-blanks-blockquote). Remove the empty line or merge the two notes into one blockquote block.

Suggested markdown fix
 > **注意**:此方式仅适用于需要授权给**当前应用**的场景。授权给其他用户时,直接使用对方的 open_id 即可,无需调用 bot info 接口。`drive +member-add` 会从可信飞书/Lark/豆包 URL 或裸 token 前缀推断 resource type;`--member-type` 必须显式传入,如果 ID 前缀与 member-type 不一致会报错。
-
 > **补充**:如果你手里只有 app id,也可以把 `--member-type` 设为 `appid`;CLI 会在请求层兼容映射为 Drive API 的 `member_type=userid`。但在 skill 文档和实际操作里,仍然优先推荐使用 bot `open_id` + `--member-type openid`,因为这条路径语义更直接、和仓库里其他实现也更一致。
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
> **注意**:此方式仅适用于需要授权给**当前应用**的场景。授权给其他用户时,直接使用对方的 open_id 即可,无需调用 bot info 接口。`drive +member-add` 会从可信飞书/Lark/豆包 URL 或裸 token 前缀推断 resource type;`--member-type` 必须显式传入,如果 ID 前缀与 member-type 不一致会报错。
`<resource_type>` 可选值:`doc``docx``sheet``bitable``file``folder``wiki``slides`
> **补充**:如果你手里只有 app id,也可以把 `--member-type` 设为 `appid`;CLI 会在请求层兼容映射为 Drive API 的 `member_type=userid`。但在 skill 文档和实际操作里,仍然优先推荐使用 bot `open_id` + `--member-type openid`,因为这条路径语义更直接、和仓库里其他实现也更一致。
> **注意**:此方式仅适用于需要授权给**当前应用**的场景。授权给其他用户时,直接使用对方的 open_id 即可,无需调用 bot info 接口。`drive +member-add` 会从可信飞书/Lark/豆包 URL 或裸 token 前缀推断 resource type;`--member-type` 必须显式传入,如果 ID 前缀与 member-type 不一致会报错。
> **补充**:如果你手里只有 app id,也可以把 `--member-type` 设为 `appid`;CLI 会在请求层兼容映射为 Drive API 的 `member_type=userid`。但在 skill 文档和实际操作里,仍然优先推荐使用 bot `open_id` + `--member-type openid`,因为这条路径语义更直接、和仓库里其他实现也更一致。
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 260-260: Blank line inside blockquote

(MD028, no-blanks-blockquote)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-drive/SKILL.md` around lines 259 - 261, The two adjacent
blockquote paragraphs in SKILL.md need to be merged into one contiguous
blockquote to satisfy markdownlint MD028; remove the blank line between the two
blocks and combine their text into a single blockquote so the note about
member-type mapping and recommendation to use bot open_id appears as one
continuous block (look for the blockquote starting with
"注意:此方式仅适用于需要授权给**当前应用**的场景。" and the following "补充:" paragraph and join them).

Comment on lines +319 to +320
result.AssertExitCode(t, 2)
require.Contains(t, result.Stderr, tt.wantErr, "stderr:\n%s", result.Stderr)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Read validate-stage dry-run errors from the full CLI output.

These cases are exercising Validate, so the failure message can come from the structured stdout envelope rather than stderr alone. Asserting only result.Stderr makes the test miss the intended dry-run contract.

Suggested assertion change
 			require.NoError(t, err)
 			result.AssertExitCode(t, 2)
-			require.Contains(t, result.Stderr, tt.wantErr, "stderr:\n%s", result.Stderr)
+			output := result.Stdout + result.Stderr
+			require.Contains(t, output, tt.wantErr, "stdout:\n%s\nstderr:\n%s", result.Stdout, result.Stderr)
 		})
 	}
 }

Based on learnings: in tests/cli_e2e dry-run tests, Validate-stage failures should exit with code 2 and be verified from result.Stdout + result.Stderr, not stderr alone.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
result.AssertExitCode(t, 2)
require.Contains(t, result.Stderr, tt.wantErr, "stderr:\n%s", result.Stderr)
result.AssertExitCode(t, 2)
output := result.Stdout + result.Stderr
require.Contains(t, output, tt.wantErr, "stdout:\n%s\nstderr:\n%s", result.Stdout, result.Stderr)
})
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/cli_e2e/drive/drive_member_add_dryrun_test.go` around lines 319 - 320,
The test currently asserts the dry-run validation error only against
result.Stderr; update the assertion so Validate-stage failures are checked from
the full CLI output by combining result.Stdout and result.Stderr. Replace the
require.Contains call that uses result.Stderr with one that checks
result.Stdout+result.Stderr (keeping the existing result.AssertExitCode(t, 2)
and tt.wantErr), so the test verifies the error message in the structured stdout
envelope as well as stderr.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/ccm PR touches the ccm domain size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants