Install
openclaw skills install resource-2-nasUse when a user asks to search for movie, TV, animation, or other media resources; provides a Quark/Baidu share link to save; wants to verify saved resources through OpenList; wants to view/cancel OpenList transfer task progress; or wants to copy saved resources to NAS/SMB storage.
openclaw skills install resource-2-nasUse this skill when the user enters any stage of a media-to-NAS workflow:
The default search upstream is the PanSou instance at https://so.252035.xyz/, backed by the fish2018/pansou API.
Use scripts/quark-save.mjs when the user wants to save a Quark share link into their own Quark cloud drive folder. Use scripts/baidu-save.mjs when the user wants to save a Baidu Netdisk share link into their own Baidu cloud drive path. These workflows transfer the resource into the user's cloud drive only; they do not download files to the local filesystem.
For OpenClaw, Hermes, or any delegated sub Agent, read SUBAGENT.md first. Sub Agents should prefer --format json, follow the preview-confirm-save protocol, and never parse Markdown when a JSON result is available.
| Task | Sub-Agent command |
|---|---|
| Search | node scripts/search-rrdynb.mjs "$KW" --format json --max-candidates 50 |
| Full readiness check | npm run check-ready |
| ENV check | npm run check-env -- --json |
| Cookie check | npm run check-cookies |
| Quark preview | node scripts/quark-save.mjs "$SHARE_URL" "$DEST_URL" --dry-run --format json |
| Baidu preview | node scripts/baidu-save.mjs "$SHARE_URL" "$DEST_PATH_OR_URL" --dry-run --format json |
| OpenList task progress | npm run openlist-tasks -- list copy undone --format json |
| OpenList cancel preview | npm run openlist-tasks -- cancel copy undone --provider baidu --format json |
| OpenList copy preview | npm run openlist-copy -- "$SRC_DIR" "$DST_DIR" "$NAME" --format json |
| Confirmed save | Re-run the preview command with --yes --format json after user/supervisor confirmation. |
| Confirmed OpenList copy/cancel | Re-run the preview command with --yes --format json after user/supervisor confirmation. |
Before editing .env, point the user to the setup guide if they need help collecting Cookies, OpenList tokens, or save/copy paths: https://guantou.site/archives/N2CmhISt
Before the first operation that needs Quark saving, Baidu saving, OpenList verification, or NAS/SMB backup copying, run the full read-only readiness check:
npm run check-ready
check-ready is Agent-oriented and outputs JSON by default. It checks:
.env shape and required core keys.refresh:true.Use nextAction to decide what to do:
ready: configuration and read-only connectivity are usable; save/copy flows can continue.configure_core: ask for OpenList URL, OpenList Token, or NAS backup path.configure_provider: ask the user to configure at least one complete provider: Quark or Baidu.configure_core_and_provider: both core and provider config are incomplete.fix_openlist_target: OpenList/NAS backup target cannot be listed.fix_provider_target: the configured Quark/Baidu save directory cannot be reached.refresh_invalid_cookies: ask the user to log in again and copy fresh Cookies.retry_network_or_check_access: the current machine cannot reach the provider or the request timed out.For focused checks, use npm run check-env -- --json or npm run check-cookies. For a human-readable view, run npm run check-ready -- --format text. These scripts mask Cookie/Token values and never print raw secrets.
If .env is missing, tell the user to copy .env.example to .env and fill in the required values. Never ask the user to paste secrets into docs or commit them.
Required .env values:
| Key | Required | Meaning |
|---|---|---|
OPENLIST_TOKEN | yes | Fixed OpenList API token used for fs/list, fs/get, fs/copy, and task APIs. |
OPENLIST_BASE_URL | yes | OpenList service base URL, e.g. http://127.0.0.1:5244. |
OPENLIST_DEFAULT_COPY_DST_PATH | yes | Default OpenList path backed by SMB/NAS storage for backup copies. |
QUARK_COOKIE + QUARK_DEFAULT_SAVE_URL | conditional | Quark provider config. Required only if using Quark. QUARK_DEFAULT_SAVE_URL may be /备份资源 or a full Quark folder URL. |
BAIDU_COOKIE + BAIDU_DEFAULT_SAVE_PATH | conditional | Baidu provider config. Required only if using Baidu. BAIDU_DEFAULT_SAVE_PATH may be /NAS资源下载 or a Baidu folder URL. |
Security rules:
.env contains full credentials. It is ignored by git and must not be committed.scripts/check-env.mjs masks QUARK_COOKIE, BAIDU_COOKIE, and OPENLIST_TOKEN.QUARK_DEFAULT_SAVE_URL may be a Quark cloud-drive path such as /备份资源, or a full Quark folder URL such as https://pan.quark.cn/list#/list/all/<fid>-<folder-name>. It is not a local filesystem path.BAIDU_DEFAULT_SAVE_PATH must be a Baidu cloud-drive path such as /NAS资源下载, or a Baidu folder URL copied from the address bar such as https://pan.baidu.com/disk/main#/index?category=all&path=%2FNAS%E8%B5%84%E6%BA%90%E4%B8%8B%E8%BD%BD. It is not a local filesystem path.OPENLIST_DEFAULT_COPY_DST_PATH must be an OpenList path such as /影视资源备份/影视, not an OS path such as /mnt/nas/movies.Use these values as defaults:
QUARK_DEFAULT_SAVE_URL.BAIDU_DEFAULT_SAVE_PATH.npm run check-ready and require nextAction: "ready".OPENLIST_BASE_URL and OPENLIST_TOKEN.OPENLIST_DEFAULT_COPY_DST_PATH.fs/copy.https://so.252035.xyz/https://so.252035.xyz/apiGET /api/healthGET /api/search or POST /api/searchPOST /api/check/linkshealth.auth_enabled is true:
POST /api/auth/loginPOST /api/auth/verifyPOST /api/auth/logoutFor ordinary user searches, use:
node scripts/search-rrdynb.mjs "蜘蛛侠"
Default behavior: search Baidu Netdisk and Quark Netdisk results first, then return a Markdown table with only the top 50 PanSou-ranked results after local disk-type filtering. Do not show hundreds of raw upstream matches to the user unless they explicitly ask for a larger export. For programmatic JSON output, use --format json or --json.
The helper calls GET /api/search with these parameters:
| Parameter | GET type | POST type | Required | Meaning |
|---|---|---|---|---|
kw | string | string | yes | Search keyword/title. |
channels | comma string | string[] | no | Telegram channels to search. Omit for server defaults. |
plugins | comma string | string[] | no | Plugin names to search. Omit for all enabled plugins. |
conc | number | number | no | Search concurrency. Omit for server auto setting. |
refresh | "true" | boolean | no | Force refresh and bypass cache. |
res | string | string | no | merge default, all, or results. |
src | string | string | no | all default, tg, or plugin. |
cloud_types | comma string | string[] | no | Limit returned disk types. |
ext | JSON string | object | no | Plugin extension parameters, e.g. {"title_en":"Spider-Man","is_all":true}. |
filter | JSON string | object | no | Include/exclude filter, e.g. {"include":["4K"],"exclude":["预告"]}. |
Supported cloud_types: baidu, aliyun, quark, guangya, tianyi, uc, mobile, 115, pikpak, xunlei, 123, magnet, ed2k, others.
When cloud_types is requested, the helper also filters normalized results[] locally, because the public instance may still include other disk types inside ranked result messages.
Useful helper options:
node scripts/search-rrdynb.mjs "蜘蛛侠" \
--cloud-types baidu,quark \
--res all \
--src all \
--max-candidates 50
--channels tgsearchers4,Aliyun_4K_Movies--plugins wanou,zhizhen--include 4K,合集--exclude 预告,花絮--refresh--ext-json '{"title_en":"Spider-Man"}'--filter-json '{"include":["4K"],"exclude":["预告"]}'--api-base https://so.252035.xyz/api--format markdown|jsonPanSou may return either direct data or a wrapper:
{
"code": 0,
"message": "success",
"data": {
"total": 15,
"results": [],
"merged_by_type": {}
}
}
Prefer data.merged_by_type for user-facing output because it is already grouped by disk type. Each merged link has:
url: cloud disk, magnet, or ed2k link.password: extraction code/password.note: resource note/title.datetime: resource update time.source: tg:<channel>, plugin:<name>, or unknown.images: optional images from Telegram messages.The helper normalizes this into:
candidates[]: ranked resource rows, each with one downloadLinks[] entry.downloadLinks[]: flat list containing provider, diskType, url, extractionCode, note, datetime, and source.availableTotal: upstream total count, for reference only.returnedCount / total: number of results actually returned to the user, capped by --max-candidates and defaulting to 50.providerCounts: counts by disk type among returned results only.When answering a search request, output a Markdown table sorted by PanSou relevance. Include clickable links directly in the table so the user can open and download without digging through JSON.
Use these columns:
| # | 资源 | 网盘 | 链接 | 提取码 | 来源 | 时间 |
|---|---|---|---|---|---|---|
| 1 | 示例资源 | 夸克网盘 | 打开 | - | plugin:example | 2026-01-01 |
Rules:
[打开](url) for the link cell.- when extraction code, source, or datetime is absent.按 PanSou 相关度排序,返回前 N 条。上游可用结果约 M 条。Use link checks only when the user asks to verify whether returned links are alive, or when checking results would materially improve the answer:
node scripts/search-rrdynb.mjs "蜘蛛侠" --check-links --max-candidates 5
POST /api/check/links body:
| Parameter | Type | Required | Meaning |
|---|---|---|---|
items | object[] | yes | Links to check. |
items[].disk_type | string | yes | Disk type. |
items[].url | string | yes | Full share URL. |
items[].password | string | no | Extraction code if not already in URL. |
view_token | string | no | View/batch token for frontend-style checks. |
proxy_url | string | no | Per-request proxy. Supports http://, https://, socks5://, socks5h://. |
proxy | string | no | Alias for proxy_url; proxy_url wins if both exist. |
Checkable disk types: baidu, aliyun, quark, tianyi, uc, mobile, 115, xunlei, 123. Magnet and ed2k are search results, but not link-check targets.
The PanSou project documents /api/check/links, and the frontend API panel also references it. If the public https://so.252035.xyz/api/check/links instance returns 404, keep the search results and report that link checking is unavailable on the current public instance instead of treating the whole search as failed.
Check states:
ok: link valid.bad: link invalid.locked: extraction code required or wrong.unsupported: platform not supported by checker.uncertain: check failed or result uncertain.When the user provides a Quark share URL and a destination Quark cloud-drive path or folder URL, first preview the share contents:
node scripts/quark-save.mjs \
"https://pan.quark.cn/s/bcbd9d24fe5a#/list/share" \
"/备份资源" \
--dry-run
The preview reads the public share. If the destination is a path like /备份资源, actual saving resolves that path to a Quark fid with the user's Cookie. Actual saving requires the user's Quark Cookie through an environment variable:
QUARK_COOKIE='...' node scripts/quark-save.mjs \
"https://pan.quark.cn/s/bcbd9d24fe5a#/list/share" \
"/备份资源" \
--context-name "你的友好邻居蜘蛛侠 第一季" \
--resource-type series
Security rules:
QUARK_COOKIE as a full login credential. Never print it, commit it, or put it in docs.--cookie-env QUARK_COOKIE; if the user uses another env var, pass that name with --cookie-env.--yes only when the user explicitly asked for non-interactive execution or has already confirmed the selected rows.Agent responsibility before saving:
--resource-type series.--context-name.--rename-plan-json. The script applies this plan after Quark returns the saved top-level fids.Example Agent rename plan:
node scripts/quark-save.mjs "$SHARE_URL" "$DEST_URL" \
--context-name "你的友好邻居蜘蛛侠 第一季" \
--resource-type series \
--rename-plan-json '[{"rank":1,"name":"你的友好邻居蜘蛛侠 第一季","reason":"Agent 根据搜索上下文修正规避字符和季名"}]'
Useful options:
--select all|1,3|2-5: choose which rows to save./夸克目录 or a full Quark folder URL.--yes: skip the confirmation prompt and save the selected rows immediately.--dry-run: preview only; no Cookie needed and no save happens.--format json / --json: output a single structured JSON object for sub Agents.--no-rename: save without post-save rename.--resource-type auto|series|movie|collection: pass the Agent's resource classification.--rename-plan-json '[{"rank":1,"name":"...","reason":"..."}]': pass Agent-decided final names. rank refers to the row number in the preview table.Quark API flow used by the helper:
POST /1/clouddrive/share/sharepage/token: obtain stoken for the share URL.GET /1/clouddrive/share/sharepage/detail: list share rows for user confirmation.POST /1/clouddrive/share/sharepage/save: save selected fid_list + fid_token_list to to_pdir_fid.GET /1/clouddrive/task: poll the async save task until completion.POST /1/clouddrive/file/rename: apply the Agent-approved rename plan to saved top-level files/folders.When the user provides a Baidu Netdisk share URL and a destination Baidu cloud-drive path, first preview the share contents:
node scripts/baidu-save.mjs \
"https://pan.baidu.com/s/1abcDEF?pwd=8888" \
"/NAS资源下载" \
--dry-run
If the user omits the destination path, use BAIDU_DEFAULT_SAVE_PATH from .env. This value may be either a direct Baidu path like /NAS资源下载 or a Baidu folder URL like https://pan.baidu.com/disk/main#/index?category=all&path=%2FNAS%E8%B5%84%E6%BA%90%E4%B8%8B%E8%BD%BD; the helper decodes the URL to /NAS资源下载 before calling share/transfer. Links in /share/init?surl=...&pwd=... form are also supported. If the extraction code is not in the URL, pass it with --passcode:
node scripts/baidu-save.mjs "$SHARE_URL" "$BAIDU_DEFAULT_SAVE_PATH" \
--passcode 8888 \
--context-name "你的友好邻居蜘蛛侠 第一季" \
--resource-type series
Actual saving requires the user's Baidu Netdisk Cookie through BAIDU_COOKIE:
BAIDU_COOKIE='...' node scripts/baidu-save.mjs \
"https://pan.baidu.com/s/1abcDEF?pwd=8888" \
"/NAS资源下载" \
--context-name "蜘蛛侠:平行宇宙" \
--resource-type movie
Security rules:
BAIDU_COOKIE as a full login credential. Never print it, commit it, or put it in docs.--cookie-env BAIDU_COOKIE; if the user uses another env var, pass that name with --cookie-env.--yes only when the user explicitly asked for non-interactive execution or has already confirmed the selected rows.Agent responsibility before saving:
pan.baidu.com; Quark links use pan.quark.cn.GET /share/list using that row's path before deciding --resource-type.--resource-type series.1080p, 4K, SDR, 彩色版, or 黑白版, treat it as collection, not series.--context-name.--rename-plan-json. The script attempts a post-transfer Baidu rename through /api/filemanager./NAS资源下载 is still a Baidu folder unless the user explicitly asks for an OpenList/NAS copy after cloud save.Example Agent rename plan:
node scripts/baidu-save.mjs "$SHARE_URL" "$BAIDU_DEFAULT_SAVE_PATH" \
--context-name "你的友好邻居蜘蛛侠 第一季" \
--resource-type series \
--rename-plan-json '[{"rank":1,"name":"你的友好邻居蜘蛛侠 第一季","reason":"Agent 根据搜索上下文修正规避字符和季名"}]'
Useful options:
--select all|1,3|2-5: choose which rows to save.--yes: skip the confirmation prompt and save the selected rows immediately.--dry-run: preview only; no save happens.--format json / --json: output a single structured JSON object for sub Agents.--no-rename: save without post-save rename.--passcode 8888: provide a Baidu extraction code if the URL does not include pwd.--resource-type auto|series|movie|collection: pass the Agent's resource classification.--rename-plan-json '[{"rank":1,"name":"...","reason":"..."}]': pass Agent-decided final names. rank refers to the row number in the preview table.--save-path / positional destination accepts either /百度目录 or a Baidu folder URL containing a path parameter.Baidu API flow used by the helper:
GET /s/<share-id>: read the share page and extract bdstoken, shareid, share_uk, and root files.window.yunData.setData({...}), or as the current two-part shape window.yunData={...}; locals.mset({...}). The helper supports both. If parsing fails with 无法解析百度分享页中的分享上下文, inspect the page for these fields and add a parser regression test before changing the script.POST /share/verify: verify extraction code and collect randsk/share cookies when needed.GET /share/list: list share files when the share page does not include complete file rows.POST /share/transfer: save selected fsidlist to the target Baidu cloud-drive path.GET /api/list: list the target path after transfer when post-save rename or save verification is needed.POST /api/filemanager?opera=rename: apply the Agent-approved rename plan to saved top-level files/folders.After Baidu save:
share/transfer response errno: 0 as a successful cloud-drive save.GET /api/list and match the Agent-approved resource name.Use OpenList when the user wants to verify that a just-saved Quark or Baidu resource is visible through their NAS/OpenList mount, or when they want to download a mounted resource through OpenList APIs.
Authentication:
Authorization header.POST /api/fs/get immediately before downloading.After Quark save:
refresh: true.scripts/quark-save.mjs.POST /api/fs/list on the OpenList target path with refresh: true.备份资源 was visible through OpenList path /pan/quark/备份资源 within the immediate refreshed list request.OpenList list request:
curl "$OPENLIST_URL/api/fs/list" \
-H "Authorization: $OPENLIST_TOKEN" \
-H "Content-Type: application/json" \
--data '{"path":"/pan/quark/备份资源","password":"","page":1,"per_page":100,"refresh":true}'
OpenList file download:
curl "$OPENLIST_URL/api/fs/get" \
-H "Authorization: $OPENLIST_TOKEN" \
-H "Content-Type: application/json" \
--data '{"path":"/pan/quark/备份资源/example.srt","password":""}'
If raw_url is returned, download it immediately:
curl -L "$RAW_URL" -o "./example.srt"
Server/NAS-side download rules:
/影视资源备份/影视, then use POST /api/fs/copy from the cloud-drive mount into that NAS-backed path.POST /api/fs/get, read the fresh raw_url, then curl -L "$RAW_URL" -o "/mounted/nas/path/file.ext".OpenList copy to NAS backup:
/pan/quark/备份资源.names[] item that will be copied, for example 钢铁侠与美国队长:英雄集结 (2014)./影视资源备份/影视, and the final path/name that will appear there.copy over move. Use move only if the user explicitly asks to remove the source after backup.refresh: true before and after copy.POST /api/fs/copy, polls /api/task/copy/undone and /api/task/copy/done, and refreshes the target directory until the copied folder/file appears.undone as failure by itself. First check done tasks and the refreshed destination directory.destination.existingTargetNames, tell the user the target already contains same-named items and require explicit confirmation before --yes.Copy preview:
npm run openlist-copy -- \
"/pan/quark/备份资源" \
"/影视资源备份/影视" \
"钢铁侠与美国队长:英雄集结 (2014)" \
--format json
Confirmed copy after the user/supervisor approves source, object, destination, and final naming:
npm run openlist-copy -- \
"/pan/quark/备份资源" \
"/影视资源备份/影视" \
"钢铁侠与美国队长:英雄集结 (2014)" \
--yes \
--format json
OpenList task progress and cancellation:
copy, offline_download, offline_download_transfer, upload, decompress, decompress_upload.undone, done.openlist-tasks defaults to list copy undone and outputs Agent JSON.--yes is passed.Task examples:
npm run openlist-tasks -- list copy undone --format json
npm run openlist-tasks -- list offline_download undone --format json
npm run openlist-tasks -- cancel copy undone --provider baidu --format json
npm run openlist-tasks -- cancel copy undone --provider baidu --yes --format json
SimpleHttp, aria2, and qBittorrent tools. For API use, call:curl "$OPENLIST_URL/api/fs/add_offline_download" \
-H "Authorization: $OPENLIST_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"path":"/nas/movies",
"urls":["https://example.com/file.mkv"],
"tool":"SimpleHttp",
"delete_policy":"delete_on_upload_succeed"
}'
Notes:
path is an OpenList path, not an arbitrary OS path. If the user wants /mnt/nas/movies, first mount that directory in OpenList and use its OpenList path.POST /api/fs/copy is usually more reliable than offline download because OpenList handles cloud-to-mounted-storage transfer as a copy task.POST /api/fs/get to obtain a fresh raw_url, then pass that URL to POST /api/fs/add_offline_download targeting the NAS-backed OpenList path.aria2 or qBittorrent, configure the tool in OpenList settings first. For Docker, make sure OpenList and the downloader share the documented temp directory mounts./api/task/offline_download/* and /api/task/offline_download_transfer/* when the user needs progress or completion status.npm run check-ready first in a fresh install or whenever .env may be missing/stale. Continue only when nextAction is ready.res=all and src=all so the helper can rank by PanSou results[] order; use cloud_types, plugins, channels, include, or exclude only when the user asks or the result set needs narrowing.--check-links or call /api/check/links on the visible links and include each state/summary./api/health reports auth_enabled: true, authenticate first or ask the user for credentials/token.scripts/quark-save.mjs --dry-run, tell the user what resource rows were found and whether the Agent judges it to be a series, then save only after confirmation/Cookie availability. Pass the Agent's canonical name and resource type to the script. Use QUARK_DEFAULT_SAVE_URL when the user does not specify a save folder.scripts/baidu-save.mjs --dry-run, tell the user what resource rows were found and whether the Agent judges it to be a series, then save only after confirmation/Cookie availability. Pass the Agent's canonical name and resource type to the script. Use BAIDU_DEFAULT_SAVE_PATH when the user does not specify a save folder.POST /api/fs/list with refresh: true every time, then report whether the Agent-approved resource name was found.npm run openlist-tasks -- list copy undone --format json or the matching task group; report task ids, names, progress, and errors. If they ask to cancel, preview first and require confirmation before adding --yes.npm run openlist-copy -- "$SRC_DIR" "$DST_DIR" "$NAME" --yes --format json after confirmation. Use OPENLIST_DEFAULT_COPY_DST_PATH when the user does not specify a target.raw_url from POST /api/fs/get.