mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-05 09:31:27 +00:00
fix(plugins): refresh bundled channel skills (#119020)
This commit is contained in:
@@ -1,136 +1,43 @@
|
||||
---
|
||||
name: discord
|
||||
description: "Discord message-tool ops: send/read/edit/delete, react, poll, pin, thread, search, presence, media/components."
|
||||
description: "Discord messaging workflows through OpenClaw's message tool."
|
||||
metadata: { "openclaw": { "emoji": "🎮", "requires": { "config": ["channels.discord.token"] } } }
|
||||
allowed-tools: ["message"]
|
||||
---
|
||||
|
||||
# Discord
|
||||
|
||||
Use the `message` tool with `channel: "discord"`. No separate Discord tool.
|
||||
Use the `message` tool with `channel: "discord"`. The tool schema lists the actions enabled by the current account's `channels.discord.actions.*` gates; do not assume unavailable actions.
|
||||
|
||||
## Rules
|
||||
## Workflow
|
||||
|
||||
- Respect `channels.discord.actions.*` gates.
|
||||
- Prefer explicit `guildId`, `channelId`, `messageId`, `userId`.
|
||||
- Multi-account: pass `accountId` when needed.
|
||||
- Send targets: `to: "channel:<id>"` or `to: "user:<id>"`.
|
||||
- Mention users as `<@USER_ID>`.
|
||||
- Avoid Markdown tables in outbound Discord messages.
|
||||
- Prefer components v2 for rich UI; do not mix v2 `components` with legacy `embeds`.
|
||||
- Prefer stable `guildId`, `channelId`, `messageId`, and `userId` values from context. Pass `accountId` when more than one Discord account could apply.
|
||||
- Resolve the exact message before editing, deleting, pinning, moderating, or reacting when the user's reference is ambiguous.
|
||||
- Keep thread replies in their existing thread. A forum parent cannot receive components; send components to the created forum thread instead.
|
||||
- Confirm destructive or moderation actions unless the user already specified the exact target and action.
|
||||
|
||||
## Common actions
|
||||
## Interactive components
|
||||
|
||||
Send:
|
||||
|
||||
```json
|
||||
{ "action": "send", "channel": "discord", "to": "channel:123", "message": "hello", "silent": true }
|
||||
```
|
||||
|
||||
Send media:
|
||||
`components` must be a structured object or native component array, never a placeholder string. Do not combine components v2 with legacy `embeds`.
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "send",
|
||||
"channel": "discord",
|
||||
"to": "channel:123",
|
||||
"message": "see attachment",
|
||||
"media": "file:///tmp/example.png"
|
||||
"message": "Choose an option",
|
||||
"components": {
|
||||
"blocks": [
|
||||
{
|
||||
"type": "actions",
|
||||
"buttons": [
|
||||
{ "label": "Approve", "style": "success", "callbackData": "approve" },
|
||||
{ "label": "Decline", "style": "danger", "callbackData": "decline" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Components v2:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "send",
|
||||
"channel": "discord",
|
||||
"to": "channel:123",
|
||||
"message": "Status",
|
||||
"components": "[Carbon v2 components]"
|
||||
}
|
||||
```
|
||||
|
||||
React:
|
||||
|
||||
```json
|
||||
{ "action": "react", "channel": "discord", "channelId": "123", "messageId": "456", "emoji": "👍" }
|
||||
```
|
||||
|
||||
Read:
|
||||
|
||||
```json
|
||||
{ "action": "read", "channel": "discord", "to": "channel:123", "limit": 20 }
|
||||
```
|
||||
|
||||
Edit/delete:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "edit",
|
||||
"channel": "discord",
|
||||
"channelId": "123",
|
||||
"messageId": "456",
|
||||
"message": "fixed typo"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{ "action": "delete", "channel": "discord", "channelId": "123", "messageId": "456" }
|
||||
```
|
||||
|
||||
Poll:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "poll",
|
||||
"channel": "discord",
|
||||
"to": "channel:123",
|
||||
"pollQuestion": "Lunch?",
|
||||
"pollOption": ["Pizza", "Sushi"],
|
||||
"pollDurationHours": 24
|
||||
}
|
||||
```
|
||||
|
||||
Pin:
|
||||
|
||||
```json
|
||||
{ "action": "pin", "channel": "discord", "channelId": "123", "messageId": "456" }
|
||||
```
|
||||
|
||||
Thread:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "thread-create",
|
||||
"channel": "discord",
|
||||
"channelId": "123",
|
||||
"messageId": "456",
|
||||
"threadName": "bug triage"
|
||||
}
|
||||
```
|
||||
|
||||
Search:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "search",
|
||||
"channel": "discord",
|
||||
"guildId": "999",
|
||||
"query": "release notes",
|
||||
"channelIds": ["123"],
|
||||
"limit": 10
|
||||
}
|
||||
```
|
||||
|
||||
Presence, often gated:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "set-presence",
|
||||
"channel": "discord",
|
||||
"activityType": "playing",
|
||||
"activityName": "OpenClaw",
|
||||
"status": "online"
|
||||
}
|
||||
```
|
||||
Discord mention syntax, component availability, and form hints are injected automatically. Follow the current hints and tool schema rather than a duplicated action catalog.
|
||||
|
||||
@@ -1,215 +1,40 @@
|
||||
---
|
||||
name: feishu-doc
|
||||
description: |
|
||||
Feishu document read/write operations. Activate when user mentions Feishu docs, cloud docs, or docx links.
|
||||
Feishu document read/write workflows. Activate when the user mentions Feishu docs, cloud docs, or docx links.
|
||||
---
|
||||
|
||||
# Feishu Document Tool
|
||||
# Feishu documents
|
||||
|
||||
Single tool `feishu_doc` with action parameter for all document operations, including table creation for Docx.
|
||||
Use the single `feishu_doc` tool. Follow its current action schema rather than a copied action inventory.
|
||||
|
||||
## Token Extraction
|
||||
From `https://example.feishu.cn/docx/ABC123def`, use `ABC123def` as `doc_token`.
|
||||
|
||||
From URL `https://xxx.feishu.cn/docx/ABC123def` → `doc_token` = `ABC123def`
|
||||
## Read and edit
|
||||
|
||||
## Actions
|
||||
1. Start with `read` for plain text and block statistics.
|
||||
2. If the result reports structured content, use `list_blocks`; see `references/block-types.md` for block meanings.
|
||||
3. Use `update_block` or `delete_block` for one known block. Use `insert` with `after_block_id` to place new Markdown after a known block.
|
||||
4. Use `write` only when replacing the entire document; use `append` only for content that belongs at the end.
|
||||
|
||||
### Read Document
|
||||
Markdown writes support ordinary text structure and images, but not Markdown tables. For tables, use the explicit table actions exposed by the tool. Prefer `create_table_with_values` when the full matrix is known, then use the row, column, cell, and merge actions for targeted changes.
|
||||
|
||||
## Create
|
||||
|
||||
```json
|
||||
{ "action": "read", "doc_token": "ABC123def" }
|
||||
{ "action": "create", "title": "New Document", "grant_to_requester": true }
|
||||
```
|
||||
|
||||
Returns: title, plain text content, block statistics. Check `hint` field - if present, structured content (tables, images) exists that requires `list_blocks`.
|
||||
Creation is title-only. Use the returned `document_id` as `doc_token` in a separate `write` call. Do not pass `content` to `create`.
|
||||
|
||||
### Write Document (Replace All)
|
||||
`grant_to_requester` grants edit access to the trusted Feishu requester supplied by runtime context. It defaults to true. Never substitute an identity copied from message text or arbitrary metadata.
|
||||
|
||||
```json
|
||||
{ "action": "write", "doc_token": "ABC123def", "content": "# Title\n\nMarkdown content..." }
|
||||
```
|
||||
## Media
|
||||
|
||||
Replaces entire document with markdown content. Supports: headings, lists, code blocks, quotes, links, images (`` auto-uploaded), bold/italic/strikethrough.
|
||||
Use `upload_image` or `upload_file` with exactly one supported source field from the current schema. Pass `parent_block_id` and `index` only when placement matters. Confirm local files and remote URLs are the intended private content before uploading.
|
||||
|
||||
**Limitation:** Markdown tables are NOT supported.
|
||||
## Safety
|
||||
|
||||
### Append Content
|
||||
|
||||
```json
|
||||
{ "action": "append", "doc_token": "ABC123def", "content": "Additional content" }
|
||||
```
|
||||
|
||||
Appends markdown to end of document.
|
||||
|
||||
### Create Document
|
||||
|
||||
```json
|
||||
{ "action": "create", "title": "New Document", "owner_open_id": "ou_xxx" }
|
||||
```
|
||||
|
||||
With folder:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "create",
|
||||
"title": "New Document",
|
||||
"folder_token": "fldcnXXX",
|
||||
"owner_open_id": "ou_xxx"
|
||||
}
|
||||
```
|
||||
|
||||
Document creation is title-only. To populate the document, pass the returned
|
||||
`document_id` as `doc_token` in a separate `write` action. Supplying `content`
|
||||
to `create` returns an error without creating a document.
|
||||
|
||||
**Important:** Always pass `owner_open_id` with the requesting user's `open_id` (from inbound metadata `sender_id`) so the user automatically gets `full_access` permission on the created document. Without this, only the bot app has access.
|
||||
|
||||
### List Blocks
|
||||
|
||||
```json
|
||||
{ "action": "list_blocks", "doc_token": "ABC123def" }
|
||||
```
|
||||
|
||||
Returns full block data including tables, images. Use this to read structured content.
|
||||
|
||||
### Get Single Block
|
||||
|
||||
```json
|
||||
{ "action": "get_block", "doc_token": "ABC123def", "block_id": "doxcnXXX" }
|
||||
```
|
||||
|
||||
### Update Block Text
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "update_block",
|
||||
"doc_token": "ABC123def",
|
||||
"block_id": "doxcnXXX",
|
||||
"content": "New text"
|
||||
}
|
||||
```
|
||||
|
||||
### Delete Block
|
||||
|
||||
```json
|
||||
{ "action": "delete_block", "doc_token": "ABC123def", "block_id": "doxcnXXX" }
|
||||
```
|
||||
|
||||
### Create Table (Docx Table Block)
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "create_table",
|
||||
"doc_token": "ABC123def",
|
||||
"row_size": 2,
|
||||
"column_size": 2,
|
||||
"column_width": [200, 200]
|
||||
}
|
||||
```
|
||||
|
||||
Optional: `parent_block_id` to insert under a specific block.
|
||||
|
||||
### Write Table Cells
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "write_table_cells",
|
||||
"doc_token": "ABC123def",
|
||||
"table_block_id": "doxcnTABLE",
|
||||
"values": [
|
||||
["A1", "B1"],
|
||||
["A2", "B2"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Create Table With Values (One-step)
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "create_table_with_values",
|
||||
"doc_token": "ABC123def",
|
||||
"row_size": 2,
|
||||
"column_size": 2,
|
||||
"column_width": [200, 200],
|
||||
"values": [
|
||||
["A1", "B1"],
|
||||
["A2", "B2"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Optional: `parent_block_id` to insert under a specific block.
|
||||
|
||||
### Upload Image to Docx (from URL or local file)
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "upload_image",
|
||||
"doc_token": "ABC123def",
|
||||
"url": "https://example.com/image.png"
|
||||
}
|
||||
```
|
||||
|
||||
Or local path with position control:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "upload_image",
|
||||
"doc_token": "ABC123def",
|
||||
"file_path": "/tmp/image.png",
|
||||
"parent_block_id": "doxcnParent",
|
||||
"index": 5
|
||||
}
|
||||
```
|
||||
|
||||
Optional `index` (0-based) inserts the image at a specific position among sibling blocks. Omit to append at end.
|
||||
|
||||
**Note:** Image display size is determined by the uploaded image's pixel dimensions. For small images (e.g. 480x270 GIFs), scale to 800px+ width before uploading to ensure proper display.
|
||||
|
||||
### Upload File Attachment to Docx (from URL or local file)
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "upload_file",
|
||||
"doc_token": "ABC123def",
|
||||
"url": "https://example.com/report.pdf"
|
||||
}
|
||||
```
|
||||
|
||||
Or local path:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "upload_file",
|
||||
"doc_token": "ABC123def",
|
||||
"file_path": "/tmp/report.pdf",
|
||||
"filename": "Q1-report.pdf"
|
||||
}
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- exactly one of `url` / `file_path`
|
||||
- optional `filename` override
|
||||
- optional `parent_block_id`
|
||||
|
||||
## Reading Workflow
|
||||
|
||||
1. Start with `action: "read"` - get plain text + statistics
|
||||
2. Check `block_types` in response for Table, Image, Code, etc.
|
||||
3. If structured content exists, use `action: "list_blocks"` for full data
|
||||
|
||||
## Configuration
|
||||
|
||||
```yaml
|
||||
channels:
|
||||
feishu:
|
||||
tools:
|
||||
doc: true # default: true
|
||||
```
|
||||
|
||||
**Note:** `feishu_wiki` depends on this tool - wiki page content is read/written via `feishu_doc`.
|
||||
|
||||
## Permissions
|
||||
|
||||
Required: `docx:document`, `docx:document:readonly`, `docx:document.block:convert`, `drive:drive`
|
||||
- Resolve exact document and block IDs before destructive edits.
|
||||
- Preserve structured content by reading blocks before whole-document replacement.
|
||||
- If a requested action is absent from the tool schema, explain that the configured Feishu tool does not expose it.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Feishu Block Types Reference
|
||||
|
||||
Complete reference for Feishu document block types. Use with `feishu_doc_list_blocks`, `feishu_doc_update_block`, and `feishu_doc_delete_block`.
|
||||
Complete reference for Feishu document block types. Use the single `feishu_doc` tool with its `list_blocks`, `update_block`, and `delete_block` actions.
|
||||
|
||||
## Block Type Table
|
||||
|
||||
@@ -59,10 +59,11 @@ Complete reference for Feishu document block types. Use with `feishu_doc_list_bl
|
||||
|
||||
### Text-based blocks (2-17, 19)
|
||||
|
||||
Update text content using `feishu_doc_update_block`:
|
||||
Update text content with `feishu_doc`:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "update_block",
|
||||
"doc_token": "ABC123",
|
||||
"block_id": "block_xxx",
|
||||
"content": "New text content"
|
||||
@@ -71,13 +72,11 @@ Update text content using `feishu_doc_update_block`:
|
||||
|
||||
### Image blocks (27)
|
||||
|
||||
Images cannot be updated directly via `update_block`. Use `feishu_doc_write` or `feishu_doc_append` with markdown to add new images.
|
||||
Images cannot be updated directly via `update_block`. Use `upload_image` to add a replacement image, then delete the old block only after the new upload succeeds.
|
||||
|
||||
### Table blocks (31)
|
||||
|
||||
**Important:** Table blocks CANNOT be created via the `documentBlockChildren.create` API (error 1770029). This affects `feishu_doc_write` and `feishu_doc_append` - markdown tables will be skipped with a warning.
|
||||
|
||||
Tables can only be read (via `list_blocks`) and individual cells (type 32) can be updated, but new tables cannot be inserted programmatically via markdown.
|
||||
Markdown tables are not converted by `write`, `append`, or `insert`. Create tables with `create_table` or `create_table_with_values`; use the table row, column, cell, and merge actions exposed by the current schema for later edits.
|
||||
|
||||
### Container blocks (24, 25, 35)
|
||||
|
||||
@@ -87,17 +86,13 @@ Grid and QuoteContainer are layout containers. Edit their child blocks instead.
|
||||
|
||||
### Replace specific paragraph
|
||||
|
||||
1. `feishu_doc_list_blocks` - find the block_id
|
||||
2. `feishu_doc_update_block` - update its content
|
||||
1. Call `feishu_doc` with `action: "list_blocks"` to find the block ID.
|
||||
2. Call `feishu_doc` with `action: "update_block"` to replace its text.
|
||||
|
||||
### Insert content at specific location
|
||||
|
||||
Currently, the API only supports appending to document end. For insertion at specific positions, consider:
|
||||
|
||||
1. Read existing content
|
||||
2. Delete affected blocks
|
||||
3. Rewrite with new content in desired order
|
||||
Call `feishu_doc` with `action: "insert"`, the target `doc_token`, Markdown `content`, and the preceding block's ID as `after_block_id`.
|
||||
|
||||
### Delete multiple blocks
|
||||
|
||||
Blocks must be deleted one at a time. Delete child blocks before parent containers.
|
||||
Call `delete_block` for exact block IDs. Delete child blocks before parent containers and confirm broad deletions first.
|
||||
|
||||
@@ -1,114 +1,28 @@
|
||||
---
|
||||
name: feishu-drive
|
||||
description: |
|
||||
Feishu cloud storage file management. Activate when user mentions cloud space, folders, drive.
|
||||
Feishu cloud-storage and comment workflows. Activate when the user mentions cloud space, folders, Drive files, or document comments.
|
||||
---
|
||||
|
||||
# Feishu Drive Tool
|
||||
# Feishu Drive
|
||||
|
||||
Single tool `feishu_drive` for cloud storage operations.
|
||||
Use the single `feishu_drive` tool and its current action schema.
|
||||
|
||||
## Token Extraction
|
||||
From `https://example.feishu.cn/drive/folder/ABC123`, use `ABC123` as `folder_token`.
|
||||
|
||||
From URL `https://xxx.feishu.cn/drive/folder/ABC123` → `folder_token` = `ABC123`
|
||||
## Files and folders
|
||||
|
||||
## Actions
|
||||
- Start from a folder shared with the bot. Bot credentials normally have no usable personal root folder.
|
||||
- For paginated folder listings, keep the same `folder_token` and pass the returned `page_token` until no continuation token remains.
|
||||
- Use `info` with the exact file token and type returned by Drive or wiki discovery.
|
||||
- Resolve the exact source and destination before moving or deleting. Confirm destructive deletes when the user's intent or target is unclear.
|
||||
- Create subfolders inside a shared folder; creating at the account root normally fails for bots.
|
||||
|
||||
### List Folder Contents
|
||||
## Comments
|
||||
|
||||
```json
|
||||
{ "action": "list" }
|
||||
```
|
||||
- Use `list_comments`, then `list_comment_replies` with the exact `comment_id` to inspect a discussion.
|
||||
- Use `add_comment` for a document-level comment. Include `block_id` only when the user wants a comment on one known Docx block.
|
||||
- Use `reply_comment` for an existing comment thread.
|
||||
- Preserve the file type and pagination fields returned by the tool. The schema is authoritative for which file types each comment action accepts.
|
||||
|
||||
Requests the account root (no `folder_token`). Bot credentials normally have no root folder, so
|
||||
use a folder that has been shared with the bot instead.
|
||||
|
||||
```json
|
||||
{ "action": "list", "folder_token": "fldcnXXX", "page_size": 100 }
|
||||
```
|
||||
|
||||
Returns one page of files with token, name, type, url, timestamps, and `next_page_token` when
|
||||
another page is available. To continue, pass the returned token with the same folder token:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "list",
|
||||
"folder_token": "fldcnXXX",
|
||||
"page_size": 100,
|
||||
"page_token": "next-page-token"
|
||||
}
|
||||
```
|
||||
|
||||
`page_size` must be between 1 and 200. Pagination requires a concrete shared `folder_token`;
|
||||
root-list cursors are not forwarded.
|
||||
|
||||
### Get File Info
|
||||
|
||||
```json
|
||||
{ "action": "info", "file_token": "ABC123", "type": "docx" }
|
||||
```
|
||||
|
||||
Looks up file metadata directly by token and type, regardless of which shared folder contains it.
|
||||
Shortcuts are the exception: Feishu's metadata API does not support the `shortcut` type, so shortcut
|
||||
info retains the root-directory lookup behavior.
|
||||
|
||||
`type`: `doc`, `docx`, `sheet`, `bitable`, `folder`, `file`, `mindnote`, `shortcut`
|
||||
|
||||
### Create Folder
|
||||
|
||||
```json
|
||||
{ "action": "create_folder", "name": "New Folder" }
|
||||
```
|
||||
|
||||
In parent folder:
|
||||
|
||||
```json
|
||||
{ "action": "create_folder", "name": "New Folder", "folder_token": "fldcnXXX" }
|
||||
```
|
||||
|
||||
### Move File
|
||||
|
||||
```json
|
||||
{ "action": "move", "file_token": "ABC123", "type": "docx", "folder_token": "fldcnXXX" }
|
||||
```
|
||||
|
||||
### Delete File
|
||||
|
||||
```json
|
||||
{ "action": "delete", "file_token": "ABC123", "type": "docx" }
|
||||
```
|
||||
|
||||
## File Types
|
||||
|
||||
| Type | Description |
|
||||
| ---------- | ----------------------- |
|
||||
| `doc` | Old format document |
|
||||
| `docx` | New format document |
|
||||
| `sheet` | Spreadsheet |
|
||||
| `bitable` | Multi-dimensional table |
|
||||
| `folder` | Folder |
|
||||
| `file` | Uploaded file |
|
||||
| `mindnote` | Mind map |
|
||||
| `shortcut` | Shortcut |
|
||||
|
||||
## Configuration
|
||||
|
||||
```yaml
|
||||
channels:
|
||||
feishu:
|
||||
tools:
|
||||
drive: true # default: true
|
||||
```
|
||||
|
||||
## Permissions
|
||||
|
||||
- `drive:drive` - Full access (create, move, delete)
|
||||
- `drive:drive:readonly` - Read only (list and root-level info fallback)
|
||||
- `drive:drive.metadata:readonly` - Direct `info` lookup outside the root (not needed with `drive:drive`)
|
||||
|
||||
## Known Limitations
|
||||
|
||||
- **Bots have no root folder**: Feishu bots use `tenant_access_token` and don't have their own "My Space". The root folder concept only exists for user accounts. This means:
|
||||
- `create_folder` without `folder_token` will fail (400 error)
|
||||
- Bot can only access files/folders that have been **shared with it**
|
||||
- **Workaround**: User must first create a folder manually and share it with the bot, then bot can create subfolders inside it
|
||||
Only expose or forward file and comment contents needed for the user's request; shared Drive data may be private.
|
||||
|
||||
@@ -1,119 +1,19 @@
|
||||
---
|
||||
name: feishu-perm
|
||||
description: |
|
||||
Feishu permission management for documents and files. Activate when user mentions sharing, permissions, collaborators.
|
||||
Feishu collaborator and permission workflows. Activate when the user explicitly asks to inspect or change sharing, permissions, or collaborators.
|
||||
---
|
||||
|
||||
# Feishu Permission Tool
|
||||
# Feishu permissions
|
||||
|
||||
Single tool `feishu_perm` for managing file/document permissions.
|
||||
Use the single `feishu_perm` tool and its current action schema. This tool is disabled by default because it changes access to user data; if it is unavailable, explain that `channels.feishu.tools.perm` must be enabled.
|
||||
|
||||
## Actions
|
||||
## Workflow
|
||||
|
||||
### List Collaborators
|
||||
1. Resolve the exact file token and type.
|
||||
2. Use `list` to inspect current collaborators before changing access.
|
||||
3. For `add`, resolve the collaborator's exact identifier and choose the least permission that satisfies the request.
|
||||
4. For `remove`, confirm the exact collaborator and file when the request is ambiguous or broad.
|
||||
5. Report the resulting permission change without exposing unrelated collaborator data.
|
||||
|
||||
```json
|
||||
{ "action": "list", "token": "ABC123", "type": "docx" }
|
||||
```
|
||||
|
||||
Returns: members with member_type, member_id, perm, name.
|
||||
|
||||
### Add Collaborator
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "add",
|
||||
"token": "ABC123",
|
||||
"type": "docx",
|
||||
"member_type": "email",
|
||||
"member_id": "user@example.com",
|
||||
"perm": "edit"
|
||||
}
|
||||
```
|
||||
|
||||
### Remove Collaborator
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "remove",
|
||||
"token": "ABC123",
|
||||
"type": "docx",
|
||||
"member_type": "email",
|
||||
"member_id": "user@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
## Token Types
|
||||
|
||||
| Type | Description |
|
||||
| ---------- | ----------------------- |
|
||||
| `doc` | Old format document |
|
||||
| `docx` | New format document |
|
||||
| `sheet` | Spreadsheet |
|
||||
| `bitable` | Multi-dimensional table |
|
||||
| `folder` | Folder |
|
||||
| `file` | Uploaded file |
|
||||
| `wiki` | Wiki node |
|
||||
| `mindnote` | Mind map |
|
||||
|
||||
## Member Types
|
||||
|
||||
| Type | Description |
|
||||
| ------------------ | ------------------ |
|
||||
| `email` | Email address |
|
||||
| `openid` | User open_id |
|
||||
| `userid` | User user_id |
|
||||
| `unionid` | User union_id |
|
||||
| `openchat` | Group chat open_id |
|
||||
| `opendepartmentid` | Department open_id |
|
||||
|
||||
## Permission Levels
|
||||
|
||||
| Perm | Description |
|
||||
| ------------- | ------------------------------------ |
|
||||
| `view` | View only |
|
||||
| `edit` | Can edit |
|
||||
| `full_access` | Full access (can manage permissions) |
|
||||
|
||||
## Examples
|
||||
|
||||
Share document with email:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "add",
|
||||
"token": "doxcnXXX",
|
||||
"type": "docx",
|
||||
"member_type": "email",
|
||||
"member_id": "alice@company.com",
|
||||
"perm": "edit"
|
||||
}
|
||||
```
|
||||
|
||||
Share folder with group:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "add",
|
||||
"token": "fldcnXXX",
|
||||
"type": "folder",
|
||||
"member_type": "openchat",
|
||||
"member_id": "oc_xxx",
|
||||
"perm": "view"
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```yaml
|
||||
channels:
|
||||
feishu:
|
||||
tools:
|
||||
perm: true # default: false (disabled)
|
||||
```
|
||||
|
||||
**Note:** This tool is disabled by default because permission management is a sensitive operation. Enable explicitly if needed.
|
||||
|
||||
## Permissions
|
||||
|
||||
Required: `drive:permission`
|
||||
Never infer an email, user ID, department, or chat from a display name alone. Follow the current schema for supported member types, token types, and permission levels.
|
||||
|
||||
@@ -1,122 +1,26 @@
|
||||
---
|
||||
name: feishu-wiki
|
||||
description: |
|
||||
Feishu knowledge base navigation. Activate when user mentions knowledge base, wiki, or wiki links.
|
||||
Feishu knowledge-base navigation workflows. Activate when the user mentions a knowledge base, wiki, or wiki link.
|
||||
---
|
||||
|
||||
# Feishu Wiki Tool
|
||||
# Feishu wiki
|
||||
|
||||
Single tool `feishu_wiki` for knowledge base operations.
|
||||
Use the single `feishu_wiki` tool and its current action schema.
|
||||
|
||||
Wiki `space_id` values are opaque strings. Always keep them quoted in tool calls, even when they contain only digits; passing a long numeric-looking ID as a number can corrupt the suffix due to JavaScript number precision limits.
|
||||
From `https://example.feishu.cn/wiki/ABC123def`, use `ABC123def` as `token`. Treat every `space_id` as an opaque quoted string, even when it contains only digits.
|
||||
|
||||
## Token Extraction
|
||||
## Navigate
|
||||
|
||||
From URL `https://xxx.feishu.cn/wiki/ABC123def` → `token` = `ABC123def`
|
||||
- Use `spaces` to enumerate accessible knowledge spaces and `nodes` for a space or parent node.
|
||||
- Continue pagination with the returned `page_token` while `has_more` is true, keeping the same space and parent.
|
||||
- Use `search` when the user provides a query but not an exact node.
|
||||
- Use `get` to resolve a wiki token to its `node_token`, `obj_token`, and `obj_type`.
|
||||
|
||||
## Actions
|
||||
## Create and organize
|
||||
|
||||
### List Knowledge Spaces
|
||||
Wiki creation supports only `docx`, `sheet`, and `bitable`; `docx` is the default. Resolve an exact space and parent before create, move, or rename operations, and confirm ambiguous or destructive reorganizations.
|
||||
|
||||
```json
|
||||
{ "action": "spaces" }
|
||||
```
|
||||
## Wiki content workflow
|
||||
|
||||
Returns one page of accessible wiki spaces plus `has_more` and `page_token`.
|
||||
Continue with the returned `page_token` while `has_more` is true:
|
||||
|
||||
```json
|
||||
{ "action": "spaces", "page_token": "next-page-token" }
|
||||
```
|
||||
|
||||
### List Nodes
|
||||
|
||||
```json
|
||||
{ "action": "nodes", "space_id": "7xxx" }
|
||||
```
|
||||
|
||||
With parent:
|
||||
|
||||
```json
|
||||
{ "action": "nodes", "space_id": "7xxx", "parent_node_token": "wikcnXXX" }
|
||||
```
|
||||
|
||||
Returns one page of nodes plus `has_more` and `page_token`. Continue with the
|
||||
same `space_id` and `parent_node_token`, adding the returned `page_token`, while
|
||||
`has_more` is true. Both list actions accept optional `page_size` from 1 to 50.
|
||||
|
||||
### Get Node Details
|
||||
|
||||
```json
|
||||
{ "action": "get", "token": "ABC123def" }
|
||||
```
|
||||
|
||||
Returns: `node_token`, `obj_token`, `obj_type`, etc. Use `obj_token` with `feishu_doc` to read/write the document.
|
||||
|
||||
### Create Node
|
||||
|
||||
```json
|
||||
{ "action": "create", "space_id": "7xxx", "title": "New Page" }
|
||||
```
|
||||
|
||||
With type and parent:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "create",
|
||||
"space_id": "7xxx",
|
||||
"title": "Sheet",
|
||||
"obj_type": "sheet",
|
||||
"parent_node_token": "wikcnXXX"
|
||||
}
|
||||
```
|
||||
|
||||
`obj_type`: `docx` (default), `sheet`, `bitable`, `mindnote`, `file`, `doc`, `slides`
|
||||
|
||||
### Move Node
|
||||
|
||||
```json
|
||||
{ "action": "move", "space_id": "7xxx", "node_token": "wikcnXXX" }
|
||||
```
|
||||
|
||||
To different location:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "move",
|
||||
"space_id": "7xxx",
|
||||
"node_token": "wikcnXXX",
|
||||
"target_space_id": "7yyy",
|
||||
"target_parent_token": "wikcnYYY"
|
||||
}
|
||||
```
|
||||
|
||||
### Rename Node
|
||||
|
||||
```json
|
||||
{ "action": "rename", "space_id": "7xxx", "node_token": "wikcnXXX", "title": "New Title" }
|
||||
```
|
||||
|
||||
## Wiki-Doc Workflow
|
||||
|
||||
To edit a wiki page:
|
||||
|
||||
1. Get node: `{ "action": "get", "token": "wiki_token" }` → returns `obj_token`
|
||||
2. Read doc: `feishu_doc { "action": "read", "doc_token": "obj_token" }`
|
||||
3. Write doc: `feishu_doc { "action": "write", "doc_token": "obj_token", "content": "..." }`
|
||||
|
||||
## Configuration
|
||||
|
||||
```yaml
|
||||
channels:
|
||||
feishu:
|
||||
tools:
|
||||
wiki: true # default: true
|
||||
doc: true # required - wiki content uses feishu_doc
|
||||
```
|
||||
|
||||
**Dependency:** This tool requires `feishu_doc` to be enabled. Wiki pages are documents - use `feishu_wiki` to navigate, then `feishu_doc` to read/edit content.
|
||||
|
||||
## Permissions
|
||||
|
||||
Required: `wiki:wiki` or `wiki:wiki:readonly`
|
||||
Wiki navigation is independent of the document tool. When `get` returns a `docx` object, use its `obj_token` as `doc_token` with `feishu_doc` to read or edit the page. Other object types require a currently available tool that supports that type; do not treat them as documents.
|
||||
|
||||
@@ -25,7 +25,7 @@ metadata:
|
||||
|
||||
# imsg
|
||||
|
||||
Use `imsg` when an OpenClaw agent must act through the user's local macOS Messages.app account: inspect iMessage/SMS history, choose the correct DM or group, send messages/files, reply, react, vote in polls, or use private-API iMessage features.
|
||||
Use the generic `message` tool first for actions that its current iMessage schema exposes. Use `imsg` when the task needs local Messages.app history, target discovery, watching, or an administrative/private-API capability that is not exposed by `message`.
|
||||
|
||||
Do not use this skill for Telegram, Signal, WhatsApp, Discord, Slack, or for replying inside the current OpenClaw conversation when the configured channel already routes the reply.
|
||||
|
||||
@@ -33,7 +33,7 @@ Do not use this skill for Telegram, Signal, WhatsApp, Discord, Slack, or for rep
|
||||
|
||||
1. Resolve the conversation first.
|
||||
2. Choose DM, existing group, or new group.
|
||||
3. Pick the lowest-capability command that can do the requested action.
|
||||
3. Prefer an available `message` action; otherwise pick the lowest-capability `imsg` command that preserves the requested semantics.
|
||||
4. Confirm any send or visible state change unless the user already gave exact recipient, content, and action.
|
||||
5. Execute with stable identifiers: prefer `--chat-id` for normal sends/watch/history and `--chat` chat GUID for bridge actions.
|
||||
|
||||
@@ -74,7 +74,7 @@ Do not make `jq` a hard prerequisite for the skill; it is only a convenient form
|
||||
|
||||
## Capability Choice
|
||||
|
||||
Use standard commands for reads, target resolution, and plain sends:
|
||||
Use `imsg` standard commands when the current `message` schema does not cover the operation or when local history is needed:
|
||||
|
||||
- Read/list/search/watch: `chats`, `group`, `history`, `search`, `watch`
|
||||
- Basic text/file send: `send`
|
||||
|
||||
@@ -36,81 +36,7 @@ metadata: { "openclaw": { "emoji": "⏰", "requires": { "config": ["channels.qqb
|
||||
1. 调用 `qqbot_remind`:`{ "action": "add", "content": "喝水", "time": "5m" }`
|
||||
2. 工具返回成功后,回复用户:`⏰ 好的,5分钟后提醒你喝水~`
|
||||
|
||||
---
|
||||
|
||||
## 备用方案(直接使用 `cron` 工具)
|
||||
|
||||
> 仅当 `qqbot_remind` 工具不可用但 `cron` 工具可用时使用以下方式。
|
||||
|
||||
### 核心规则
|
||||
|
||||
> **payload.kind 必须是 `"agentTurn"`,绝对不能用 `"systemEvent"`!**
|
||||
> `systemEvent` 只在 AI 会话内部注入文本,用户收不到 QQ 消息。
|
||||
|
||||
**不可更改字段**:
|
||||
|
||||
| 字段 | 固定值 | 原因 |
|
||||
| -------------------- | ------------- | ---------------------------- |
|
||||
| `payload.kind` | `"agentTurn"` | `systemEvent` 不会发 QQ 消息 |
|
||||
| `delivery.mode` | `"announce"` | 主动投递模式 |
|
||||
| `delivery.channel` | `"qqbot"` | QQ 通道标识 |
|
||||
| `delivery.to` | 目标地址 | 从当前会话上下文获取 |
|
||||
| `delivery.accountId` | 当前账户 ID | 多账号场景下不可省略 |
|
||||
| `sessionTarget` | `"isolated"` | 隔离会话避免污染 |
|
||||
|
||||
> `schedule.at` 必须是**绝对 ISO 8601 时间戳**(如 `"2026-07-29T09:00:00.000Z"`),不支持 `"5m"` 等相对字符串。
|
||||
> 计算方式:`当前时间戳ms + 延迟毫秒`。
|
||||
|
||||
### 一次性提醒(schedule.kind = "at")
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "add",
|
||||
"job": {
|
||||
"name": "{任务名}",
|
||||
"schedule": { "kind": "at", "at": "{当前时间戳ms + N*60000 转换为 ISO 8601}" },
|
||||
"sessionTarget": "isolated",
|
||||
"wakeMode": "now",
|
||||
"deleteAfterRun": true,
|
||||
"payload": {
|
||||
"kind": "agentTurn",
|
||||
"message": "你是一个暖心的提醒助手。请用温暖、有趣的方式提醒用户:{提醒内容}。要求:(1) 不要回复HEARTBEAT_OK (2) 不要解释你是谁 (3) 直接输出一条暖心的提醒消息 (4) 可以加一句简短的鸡汤或关怀的话 (5) 控制在2-3句话以内 (6) 用emoji点缀"
|
||||
},
|
||||
"delivery": {
|
||||
"mode": "announce",
|
||||
"channel": "qqbot",
|
||||
"to": "qqbot:c2c:{openid}",
|
||||
"accountId": "{accountId}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 周期提醒(schedule.kind = "cron")
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "add",
|
||||
"job": {
|
||||
"name": "{任务名}",
|
||||
"schedule": { "kind": "cron", "expr": "0 8 * * *", "tz": "{用户确认的 IANA 时区}" },
|
||||
"sessionTarget": "isolated",
|
||||
"wakeMode": "now",
|
||||
"payload": {
|
||||
"kind": "agentTurn",
|
||||
"message": "你是一个暖心的提醒助手。请用温暖、有趣的方式提醒用户:{提醒内容}。要求:(1) 不要回复HEARTBEAT_OK (2) 不要解释你是谁 (3) 直接输出一条暖心的提醒消息 (4) 可以加一句简短的鸡汤或关怀的话 (5) 控制在2-3句话以内 (6) 用emoji点缀"
|
||||
},
|
||||
"delivery": {
|
||||
"mode": "announce",
|
||||
"channel": "qqbot",
|
||||
"to": "qqbot:c2c:{openid}",
|
||||
"accountId": "{accountId}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 周期任务**不加** `deleteAfterRun`。群聊 `delivery.to` 格式为 `"qqbot:group:{group_openid}"`。
|
||||
`qqbot_remind` 不可用时,不要绕过它直接创建 Gateway 任务。说明当前无法安全注册 QQ 提醒,并建议用户检查 QQBot 工具配置。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,78 +1,20 @@
|
||||
---
|
||||
name: slack
|
||||
description: "Slack tool actions: send/read/edit/delete messages, react, pin/unpin, list pins/reactions/emoji, member info."
|
||||
description: "Slack messaging workflows through OpenClaw's message tool."
|
||||
metadata: { "openclaw": { "emoji": "💬", "requires": { "config": ["channels.slack"] } } }
|
||||
allowed-tools: ["message"]
|
||||
---
|
||||
|
||||
# Slack
|
||||
|
||||
Use the `slack` tool. Reuse `channelId` and Slack timestamp message IDs from context when present.
|
||||
Use the `message` tool with `channel: "slack"`. The tool schema lists the actions enabled for the current Slack account; do not assume an action that is not present.
|
||||
|
||||
## Inputs
|
||||
## Workflow
|
||||
|
||||
- `channelId`: Slack channel ID.
|
||||
- `messageId`: Slack timestamp, e.g. `1712023032.1234`.
|
||||
- `to`: `channel:<id>` or `user:<id>` for sends.
|
||||
- `emoji`: Unicode or `:name:` for reactions.
|
||||
- Prefer stable Slack IDs from context. Sends outside the current conversation use `channel:<id>` or `user:<id>` targets.
|
||||
- Keep replies in the current thread unless the user asks for a top-level post. For another thread, pass its Slack timestamp as `threadId`; use the same timestamp as `messageId` for message-specific actions.
|
||||
- Before editing, deleting, pinning, or reacting to an ambiguous message, read the conversation and resolve its exact ID.
|
||||
- When multiple Slack accounts are configured, pass `accountId` rather than guessing.
|
||||
- Confirm destructive deletes when the target or intent is unclear.
|
||||
|
||||
## Actions
|
||||
|
||||
```json
|
||||
{ "action": "sendMessage", "to": "channel:C123", "content": "Hello" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "action": "readMessages", "channelId": "C123", "limit": 20 }
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "react",
|
||||
"channelId": "C123",
|
||||
"messageId": "1712023032.1234",
|
||||
"emoji": ":white_check_mark:"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{ "action": "reactions", "channelId": "C123", "messageId": "1712023032.1234" }
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "editMessage",
|
||||
"channelId": "C123",
|
||||
"messageId": "1712023032.1234",
|
||||
"content": "Updated text"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{ "action": "deleteMessage", "channelId": "C123", "messageId": "1712023032.1234" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "action": "pinMessage", "channelId": "C123", "messageId": "1712023032.1234" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "action": "unpinMessage", "channelId": "C123", "messageId": "1712023032.1234" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "action": "listPins", "channelId": "C123" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "action": "memberInfo", "userId": "U123" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "action": "emojiList" }
|
||||
```
|
||||
|
||||
## Safety
|
||||
|
||||
- Confirm destructive deletes when context is unclear.
|
||||
- Keep outbound messages short; avoid Markdown tables.
|
||||
- Prefer thread/message IDs over fuzzy channel names.
|
||||
Slack formatting, mentions, tables, charts, and interactive controls are described automatically by the current `message` tool hints. Follow those hints instead of maintaining a second action or formatting catalog here.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.1095.0",
|
||||
"@aws-sdk/s3-request-presigner": "3.1095.0",
|
||||
"@tloncorp/tlon-skill": "0.4.4",
|
||||
"@tloncorp/tlon-skill": "0.4.5",
|
||||
"@urbit/aura": "3.0.0",
|
||||
"zod": "4.4.3"
|
||||
},
|
||||
|
||||
@@ -20,7 +20,7 @@ metadata:
|
||||
{
|
||||
"id": "go",
|
||||
"kind": "go",
|
||||
"module": "github.com/steipete/wacli/cmd/wacli@latest",
|
||||
"module": "github.com/openclaw/wacli/cmd/wacli@latest",
|
||||
"bins": ["wacli"],
|
||||
"label": "Install wacli (go)",
|
||||
},
|
||||
|
||||
42
pnpm-lock.yaml
generated
42
pnpm-lock.yaml
generated
@@ -1907,8 +1907,8 @@ importers:
|
||||
specifier: 3.1095.0
|
||||
version: 3.1095.0
|
||||
'@tloncorp/tlon-skill':
|
||||
specifier: 0.4.4
|
||||
version: 0.4.4
|
||||
specifier: 0.4.5
|
||||
version: 0.4.5
|
||||
'@urbit/aura':
|
||||
specifier: 3.0.0
|
||||
version: 3.0.0
|
||||
@@ -5226,32 +5226,32 @@ packages:
|
||||
resolution: {integrity: sha512-5Kc5CM2Ysn3vTTArBs2vESUt0AQiWZA86yc1TI3B+lxXmtEq133C1nxXNOgnzhrivdPZIh3zLj5gDnZjoLL5GA==}
|
||||
engines: {node: '>=12.17.0'}
|
||||
|
||||
'@tloncorp/tlon-skill-darwin-arm64@0.4.4':
|
||||
resolution: {integrity: sha512-yr7Lg4KJPWInBFarXhJV+Z8LxFFLGJAgoBY6d6sc7zVW40oKUM+OQjKomKaNy8H4aYk0CnZkXlN+CTOn3/LD6g==}
|
||||
'@tloncorp/tlon-skill-darwin-arm64@0.4.5':
|
||||
resolution: {integrity: sha512-urM3HnsoQR/yqT8qxs4DZu/B6bWS0EdNKcJXme5FdTsuX4NfOhbMAdjl+aasu2G86GF3q/fgH00g75DNiIFB9w==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
hasBin: true
|
||||
|
||||
'@tloncorp/tlon-skill-darwin-x64@0.4.4':
|
||||
resolution: {integrity: sha512-h9yL4fwkLa2zXqROWV6QuqVB4FlQOA9iQfahST15MAjyQRBqJEt2zxjuXHbkvlZ9SKm4vS5J7YLXI6yXLvULGA==}
|
||||
'@tloncorp/tlon-skill-darwin-x64@0.4.5':
|
||||
resolution: {integrity: sha512-w+pr1NwiD6524WAIHwpQ0IjtqV1K7SUw2aqaG27lvluPukzolYWl3fcliJzY2AOeCwe2r3JkCq5JD1oiZGkLjQ==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
hasBin: true
|
||||
|
||||
'@tloncorp/tlon-skill-linux-arm64@0.4.4':
|
||||
resolution: {integrity: sha512-OBzY/ACHo9/wecN0XTkepK2mGwDnqcs+T7vEhjHPKr4aD+YGBvdgA0abeXcSsz9EXKVnRnaDMo9QDYcROy4DIw==}
|
||||
'@tloncorp/tlon-skill-linux-arm64@0.4.5':
|
||||
resolution: {integrity: sha512-t/N4nUF2abzQA22ONbjSdOEyQ0k6nWaA/wZ3OZ0Gzmsfa4caEbMwiHS/nY9po9p3Gb8IFXW7ZTMwMe7w2E3XlA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
hasBin: true
|
||||
|
||||
'@tloncorp/tlon-skill-linux-x64@0.4.4':
|
||||
resolution: {integrity: sha512-KCSfVFDBvR/oaDoa/C4Zyvw8eZTq0GDEcaWfn0tbLF3oWDNziSG1a/VFsQzDhHQWI4n0oq2402/HEbMMAI0Fvw==}
|
||||
'@tloncorp/tlon-skill-linux-x64@0.4.5':
|
||||
resolution: {integrity: sha512-1u5cdmnjSeYGhgTSMTDrpenPOxCu7B5Q62cuviBeYaS7lvvNTNwynIwuO9QR3Q98r7rMMyzWExhDHJ+sRlSbsw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
hasBin: true
|
||||
|
||||
'@tloncorp/tlon-skill@0.4.4':
|
||||
resolution: {integrity: sha512-kjFEqZNieQZR7aSgdM54GRWfMYtukuZlMzlW6ThNWKwrX2YDl8ieOZ7dA0gGcp2TpPHbtTpj83Ro1DMPlcGZ5A==}
|
||||
'@tloncorp/tlon-skill@0.4.5':
|
||||
resolution: {integrity: sha512-rsQ4Qqm1oCGLz7rW/TFyVc+8KANifGNrY8jTEu4AbIgFfGR9m3I8tdRjSdAkVW7ZCZ7VDI/RlZhCN8s/dNz+Kw==}
|
||||
hasBin: true
|
||||
|
||||
'@tokenizer/inflate@0.4.1':
|
||||
@@ -12387,24 +12387,24 @@ snapshots:
|
||||
'@tinyhttp/content-disposition@2.2.4':
|
||||
optional: true
|
||||
|
||||
'@tloncorp/tlon-skill-darwin-arm64@0.4.4':
|
||||
'@tloncorp/tlon-skill-darwin-arm64@0.4.5':
|
||||
optional: true
|
||||
|
||||
'@tloncorp/tlon-skill-darwin-x64@0.4.4':
|
||||
'@tloncorp/tlon-skill-darwin-x64@0.4.5':
|
||||
optional: true
|
||||
|
||||
'@tloncorp/tlon-skill-linux-arm64@0.4.4':
|
||||
'@tloncorp/tlon-skill-linux-arm64@0.4.5':
|
||||
optional: true
|
||||
|
||||
'@tloncorp/tlon-skill-linux-x64@0.4.4':
|
||||
'@tloncorp/tlon-skill-linux-x64@0.4.5':
|
||||
optional: true
|
||||
|
||||
'@tloncorp/tlon-skill@0.4.4':
|
||||
'@tloncorp/tlon-skill@0.4.5':
|
||||
optionalDependencies:
|
||||
'@tloncorp/tlon-skill-darwin-arm64': 0.4.4
|
||||
'@tloncorp/tlon-skill-darwin-x64': 0.4.4
|
||||
'@tloncorp/tlon-skill-linux-arm64': 0.4.4
|
||||
'@tloncorp/tlon-skill-linux-x64': 0.4.4
|
||||
'@tloncorp/tlon-skill-darwin-arm64': 0.4.5
|
||||
'@tloncorp/tlon-skill-darwin-x64': 0.4.5
|
||||
'@tloncorp/tlon-skill-linux-arm64': 0.4.5
|
||||
'@tloncorp/tlon-skill-linux-x64': 0.4.5
|
||||
|
||||
'@tokenizer/inflate@0.4.1(supports-color@10.2.2)':
|
||||
dependencies:
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, relative, resolve } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { listGitTrackedFiles } from "../../test-utils/repo-files.js";
|
||||
|
||||
type PluginManifest = {
|
||||
channels?: unknown;
|
||||
skills?: unknown;
|
||||
};
|
||||
|
||||
const RETIRED_SKILL_PATTERNS = [
|
||||
{
|
||||
pattern: /use\s+the\s+[`'"]slack[`'"]\s+tool/iu,
|
||||
replacement: "the `message` tool",
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
/["']action["']\s*:\s*["'](?:sendMessage|readMessages|editMessage|deleteMessage|pinMessage|unpinMessage|listPins|memberInfo|emojiList)["']/u,
|
||||
replacement: "a canonical `message` action",
|
||||
},
|
||||
{ pattern: /owner_open_id/u, replacement: "grant_to_requester" },
|
||||
{ pattern: /feishu_doc_list_blocks/u, replacement: "feishu_doc action=list_blocks" },
|
||||
{ pattern: /feishu_doc_update_block/u, replacement: "feishu_doc action=update_block" },
|
||||
{ pattern: /feishu_doc_delete_block/u, replacement: "feishu_doc action=delete_block" },
|
||||
{
|
||||
pattern: /["']components["']\s*:\s*["']\[Carbon v2 components\]["']/u,
|
||||
replacement: "structured components",
|
||||
},
|
||||
{ pattern: /## 备用方案(直接使用 `cron` 工具)/u, replacement: "qqbot_remind only" },
|
||||
{
|
||||
pattern: /github\.com\/steipete\/wacli\/cmd\/wacli@latest/u,
|
||||
replacement: "github.com/openclaw/wacli/cmd/wacli@latest",
|
||||
},
|
||||
] as const;
|
||||
|
||||
function listRepositoryOwnedChannelSkillFiles(): string[] {
|
||||
const trackedFiles = listGitTrackedFiles({ pathspecs: ["extensions"] }) ?? [];
|
||||
const trackedFileSet = new Set(trackedFiles);
|
||||
const skillFiles = new Set<string>();
|
||||
|
||||
for (const manifestPath of trackedFiles.filter((file) =>
|
||||
/^extensions\/[^/]+\/openclaw\.plugin\.json$/u.test(file),
|
||||
)) {
|
||||
const manifest = JSON.parse(
|
||||
readFileSync(resolve(process.cwd(), manifestPath), "utf8"),
|
||||
) as PluginManifest;
|
||||
if (!Array.isArray(manifest.channels) || !Array.isArray(manifest.skills)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const pluginDir = dirname(manifestPath);
|
||||
for (const skillRoot of manifest.skills) {
|
||||
if (typeof skillRoot !== "string" || skillRoot.includes("node_modules")) {
|
||||
continue;
|
||||
}
|
||||
const relativeRoot = relative(process.cwd(), resolve(pluginDir, skillRoot)).replaceAll(
|
||||
"\\",
|
||||
"/",
|
||||
);
|
||||
for (const file of trackedFileSet) {
|
||||
if (file.startsWith(`${relativeRoot}/`) && file.endsWith(".md")) {
|
||||
skillFiles.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...skillFiles].toSorted();
|
||||
}
|
||||
|
||||
describe("bundled channel-provider skill contracts", () => {
|
||||
it("does not teach retired tool, action, parameter, or install contracts", () => {
|
||||
const failures: string[] = [];
|
||||
const skillFiles = listRepositoryOwnedChannelSkillFiles();
|
||||
|
||||
expect(skillFiles.length).toBeGreaterThan(0);
|
||||
for (const file of skillFiles) {
|
||||
const source = readFileSync(resolve(process.cwd(), file), "utf8");
|
||||
for (const entry of RETIRED_SKILL_PATTERNS) {
|
||||
if (entry.pattern.test(source)) {
|
||||
failures.push(`${file}: replace ${entry.pattern.source} with ${entry.replacement}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect(failures).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user