Imported from gaozhangmin/boxplayer (
AGENTS.md). Install upstream withnpx skills add gaozhangmin/boxplayer. Copyright stays with the author.
AGENTS.md — BoxPlayer (aliyunpan)
Quick reference
pnpm install # install deps (pnpm only, never npm/yarn)
pnpm dev # hot-reload Electron dev server
pnpm run build # typecheck + bundle (vue-tsc --noEmit && vite build)
pnpm run test # focused Vitest suite (not full repo)
pnpm run test:clouddrive-cli # clouddrive-cli tests only
Package manager: pnpm only
Lockfile is pnpm-lock.yaml. package-lock.json and yarn.lock are in .gitignore.
Never use npm install or yarn.
Node version
engines.node >= 22.12.0 in package.json. Electron 40 requires Node 22.12+ during install/build.
Build pipeline order matters
pnpm run build runs version bump → typecheck → vite bundle. The version bump (version.mjs) auto-increments the patch version in package.json before every build. The build removes dist/ and release/ directories on start.
pnpm run build:electron calls build first, then runs electron-builder.
For platform-specific packaging:
pnpm run build:mac # unsigned macOS
pnpm run build:mac:signed # signed + notarized
pnpm run build:linux
pnpm run build:windows
pnpm run build:all # cross-platform, sequential
Sensitive config: generated secrets
Private client IDs, client secrets, API keys, and private API URLs live outside git:
pnpm run secrets:generate
Local development reads them from .env.local and generates src/secrets.generated.ts. GitHub Actions reads the same keys from GitHub Secrets and runs scripts/generate-secrets.mjs --mode=ci --strict before building.
.env.local and src/secrets.generated.ts are ignored and must never be committed. src/config.ts should import generated secrets or use placeholders, not contain real private values.
Architecture: Electron + Vue 3 + Vite
| Directory | Purpose |
|---|---|
electron/main/ |
Electron main process (entry: electron/main/index.ts) |
electron/preload/ |
Preload scripts |
src/ |
Vue 3 renderer (components, views, stores, API modules) |
shared/ |
Code shared between main + renderer |
scripts/ |
Build, CLI entry points, config management |
clouddrive-cli/ |
Standalone CLI package (published to npm as clouddrive-cli) |
Path aliases (set in tsconfig.json, vite.config.ts, vitest.config.ts):
@shared/*→shared/*@main/*→electron/main/*
Cloud provider API modules in src/: aliapi/, cloudbaidu/, cloud123/, cloud115/, pikpak/, onedrive/, box/, dropbox/, quark/, cloud139/, cloud189/.
GitHub Issue workflow
When the user asks to handle GitHub Issues, first fetch the complete list of unfinished/open Issues in the requested repository. Review each Issue individually, identify its likely cause and affected code, and report the triage before changing code. Then implement fixes and run focused regression tests for each applicable Issue. Do not jump straight into a code change based only on the newest Issue or close an Issue before its fix is verified. If the repository or Issue access is unavailable, state that limitation rather than assuming the list is complete.
Testing: Vitest, Node environment, selective includes
Default app inspection: attach to the existing dev app
- Unless the user explicitly asks to execute E2E tests, always connect to the app the user already started with
pnpm run devfor inspection, reproduction, debugging, and UI verification. - Use the existing Electron CDP endpoint (normally
http://127.0.0.1:9223; confirm the active endpoint). Playwright may attach withchromium.connectOverCDP; do not useelectron.launch, spawn another Electron process, or start anotherpnpm run devinstance. - Preserve the running app's real user-data directory, accounts, settings, and playback state. Do not substitute an isolated/temporary profile or seed test data into the user's app.
- Requests to review, investigate, reproduce, fix, or verify a bug are not explicit requests to run E2E tests. Do not automatically run
pretest:e2e,test:e2e, or Playwright specs whose fixtures launch a separate app. - If the existing app or relevant window is unavailable, report that and ask the user to start/reopen it. Do not silently launch a replacement. Request approval before restarting the user's dev app when a main-process or native-module change requires it.
- This rule takes precedence over the E2E workflow below. Record what was actually verified in the existing dev app; do not describe attached-session checks as production E2E acceptance. A pending E2E gate does not authorize launching another app.
Playwright acceptance gate when E2E execution is explicitly requested
When the user explicitly asks to execute E2E tests, verify bug fixes through Playwright against an actually launched Electron application using the workflow below. Unit tests, type checks, source inspection, and merely starting pnpm run dev cannot replace this E2E gate.
- Add or update a Playwright regression test that reproduces the reported failure through the user-visible application flow.
- Build the production Electron entry with
pnpm run pretest:e2e; do not test staledist/output or an installed older BoxPlayer build. - Run the focused Playwright regression test and record the exact command and result.
- For cloud-drive bugs, use the configured real account and exercise the real provider API. Do not skip the relevant real-account case or replace it with mocks.
- For platform-specific behavior that cannot execute on the current host, run Playwright on the target platform before calling the bug fixed. If that is unavailable, report the fix as unverified/blocked rather than complete.
- A failed, skipped, or unexecuted Playwright regression test blocks declaring the bug fixed and blocks release publication.
Keep the regression test after the fix so the same bug cannot silently return.
Tests run in Node environment (not jsdom). The vitest.config.ts lists explicit test directories — not a glob over **/*.test.ts. Tests outside these paths are not picked up:
electron/main/core/__tests__/,electron/main/aria/__tests__/shared/__tests__/,scripts/__tests__/src/down/motrix-integration/,src/media-server/__tests__/src/utils/__tests__/,src/aliapi/__tests__/src/pikpak/__tests__/,src/quark/__tests__/src/dropbox/__tests__/,src/onedrive/__tests__/,src/box/__tests__/clouddrive-cli/__tests__/
Default pnpm run test runs only a subset (aria2 tests, motrix integration, a couple utils files).
When adding tests in a new directory, update vitest.config.ts.
Pre-commit hooks
Do not rely on pre-commit cleanup for secrets. Secret safety is handled by keeping real values in ignored/generated files and GitHub Secrets.
Formatting conventions
From .prettierrc:
- Single quotes, no semicolons
- 260 printWidth (very wide)
- No trailing commas
- LF line endings
sortAttributes: trueon HTML/Vue
TypeScript
tsconfig.jsontargets ESNext, moduleResolution node, strict mode,@shared/*and@main/*path aliases- Includes
src/andshared/, excludesshared/__tests__ - References
tsconfig.node.jsonfor Electron-side code
Provider Integration Checklist
When adding a new cloud-drive provider, implement the following in order unless the user explicitly excludes a step. Do not stop after only wiring file listing. Sync/change-listening is not required by default.
1. Account And Auth
- Read the official OAuth/API documentation and prefer stable API versions.
- Add
src/<provider>/auth.tswith auth URL, authorization-code token exchange, and refresh token support. - Define
CLIENT_ID/CLIENT_SECRETplaceholder constants, defaulting to empty strings. - Add app key / secret fields to
.env.example,src/secrets.example.ts, andscripts/generate-secrets.mjs. - Add the provider to
tokenfrominsrc/user/userstore.ts. - Wire login, token refresh, and drive loading in
src/user/userdal.ts. - Add login entry points in
src/user/UserLogin.vueandsrc/user/UserInfo.vue.
2. Provider Detection And Drive Model
- Add
is<Provider>Userinsrc/aliapi/utils.ts. - Update
GetDriveIDandGetDriveType, modeling third-party drives as a single-root drive. - Filter unsupported virtual directories in
src/pan/PanLeft.vueandsrc/pan/pantreestore.ts.
3. File List And Detail
- Add
src/<provider>/dirfilelist.ts. - Implement root listing, child directory pagination, and file detail.
- Map official file models to
IAliGetFileModel; at minimum filldrive_id,file_id,parent_file_id,name,isDir,category,icon,size,time,thumbnail, andcontent_hash. - Store provider-specific path, parent id, download URL, or similar metadata in
description. - Wire root loading, directory listing, and search-listing branches in
src/pan/pandal.ts. - Wire provider file detail in
src/aliapi/file.tsApiFileInfo.
4. Download And Playback
- Wire provider direct download URLs in
src/aliapi/file.tsApiFileDownloadUrl. - Third-party drives usually do not have Aliyun transcode APIs;
ApiVideoPreviewUrl/ApiAudioPreviewUrlshould return暂无转码信息orundefined, letting playback use the raw download URL. - Wire same-directory playlist loading in
src/layout/PageVideo.vue,src/utils/openfile.ts, andsrc/utils/playerhelper.ts. - Confirm clicking provider videos does not call Aliyun APIs.
5. Search
- Add
src/<provider>/search.ts. - Support keyword search.
- If the existing UI uses special
file_idvalues for search conditions, implement parse/build/filter helpers. - Wire search-directory branches in
src/pan/pandal.ts.
6. Thumbnails And Preview
- Prefer thumbnails returned by list/detail APIs.
- If the provider needs a separate thumbnail API, add
src/<provider>/thumbnail.ts. - Fill
thumbnailduring file mapping so media library and file lists can use it directly.
7. File Operations
- Add
src/<provider>/filecmd.ts. - Implement create folder, move to recycle bin, permanent delete, rename, move, and copy. Clearly report unsupported official capabilities.
- Wire
ApiCreatNewForder,ApiTrashBatch,ApiDeleteBatch,ApiRenameBatch,ApiMoveBatch, andApiCopyBatchinsrc/aliapi/filecmd.ts. - Unsupported capabilities must not fall through to the Aliyun default branch.
8. Sharing
- Add
src/<provider>/share.tswith share-link creation. - If the provider does not support password, expiration, or multi-file sharing, block it clearly in
src/aliapi/share.ts. - Update context-menu and top-menu share visibility.
9. Upload And New Files
- Add or extend
src/<provider>/upload.ts. - Implement in-memory upload for new text files.
- Implement local-file upload worker support for ordinary file/folder uploads.
- Implement large files using the provider-recommended session/chunk API.
- Wire
src/aliapi/uploadmem.tsandsrc/workerpage/uploader.ts. - Third-party drives do not support app custom encrypted/private uploads by default; hide those menu items.
10. Media Library Scanning
- Wire provider folder traversal in
src/utils/mediaScanner.ts. - Wire provider subfolder entry in
src/components/MediaLibrary.vue. - Show the provider name in media-library source labels.
- Confirm scanning does not call Aliyun list APIs.
11. Folder Picker Modal
- Treat the provider as a single-root drive in
src/pan/topbtns/SelectPanDirModal.vue. - Wire lazy child directory loading.
- Move, copy, and save-location picker modals must browse provider directories.
12. Menu Capability Boundaries
- Update
src/pan/menus/FileRightMenu.vue,src/pan/menus/FileTopbtn.vue,src/pan/menus/DirLeftMenu.vue, andsrc/pan/menus/PanTopbtn.vue. - Show common capabilities: download, share, rename, move to, copy to, delete, properties, scan media library, normal upload, and normal new file/folder.
- Hide Aliyun-only capabilities: lucky bottle, fast transfer, favorite, transcode-related actions, mark encrypted, clear history, color labels, copy directory tree, encrypted/private new file, encrypted/private upload, and import share.
13. Properties, Versions, And Recycle Bin
- Wire provider file detail in
src/pan/topbtns/ShuXingModal.vue. - If folder size has no official API, return 0 or hide it; do not call Aliyun folder-size APIs.
- If the provider supports file versions, add
src/<provider>/revisions.tsand expose version listing/restoration in properties. - If the provider supports recycle bin list/restore, add
src/<provider>/recyclebin.tsand wire recycle-bin directory, restore, and permanent delete. - If the official API supports only some account types or needs higher permissions, record the limitation and keep it disabled by default.
14. Tests
- Add provider helper tests under
src/<provider>/__tests__/. - Cover at least OAuth URL/token body, file-list path/body, model mapping, search path/body, share body/result mapping, file-operation body, upload session/chunk path, and version/recycle-bin path where applicable.
- Update
vitest.config.tsto include provider tests.
15. Verification
- Run provider unit tests.
- Run
pnpm run build. - If playback/media library is involved, verify provider videos use provider download URLs, same-directory playlists use provider list APIs, media-library scans use provider list APIs, and context menus do not expose unsupported capabilities.
Recommended File Structure
src/<provider>/
auth.ts
dirfilelist.ts
filecmd.ts
search.ts
share.ts
upload.ts
revisions.ts # optional
recyclebin.ts # optional
thumbnail.ts # optional
__tests__/
Completion Criteria
- Login and token refresh work.
- The left tree shows root and folders.
- File list opens root, subdirectories, and search results.
- File detail, download, and video playback do not call the wrong provider API.
- Context menus and top menus expose only supported capabilities.
- Create folder, rename, move, copy, and delete call provider APIs.
- Share, upload, and media-library scanning are wired or explicitly reported unsupported.
- Provider unit tests pass.
pnpm run buildpasses.
Current Experience Notes
- Dropbox and OneDrive should both be treated as single-root third-party drives.
- Third-party drive video playback should prefer raw download URLs and must not reuse Aliyun transcode APIs.
- Media-library scanning and same-directory playlists are the easiest provider branches to miss.
- Auth pages should usually open in the system browser unless the provider explicitly requires an embedded flow.
- App key / secret placeholder constants must be included in the generated-secrets flow, not committed as real values.
Cloud Download URL And Subtitle Contract
Treat a cloud download URL and its request headers as one value. ApiFileDownloadUrl must return every provider-required header in IDownloadUrl.headers; callers must preserve those headers through playback, downloads, previews, and subtitles.
| Provider | Download request requirements |
|---|---|
| Aliyun / Alipan | Signed CDN URL; Electron may add the Aliyun Referer/Origin for direct renderer requests. |
| 115 | Authorization: Bearer <account token> and DRIVE115_DOWN_AGENT. The User-Agent used to consume the URL must match the one used to obtain it. Direct renderer playback must register URL-to-account auth context; never rely only on the currently selected account. |
| Baidu | URL includes access_token; send User-Agent: pan.baidu.com and Referer: https://pan.baidu.com/. |
| 123 | The returned signed download URL is self-authorizing; API Authorization is only for obtaining the URL. |
| Quark | Account Cookie plus Quark download User-Agent, Referer: https://pan.quark.cn/, Origin: https://pan.quark.cn, and x-urlp when required by the OSS URL. |
| 139 | cloud139DownloadHeaders(): provider User-Agent, Referer, and Origin. |
| 189 | cloud189DownloadHeaders(): provider User-Agent and Referer. |
| PikPak | Temporary download/stream URL returned by the provider. |
| Dropbox / OneDrive | Temporary or pre-authenticated download URL returned by the provider. |
| Box | Access token is encoded into the constructed download URL. |
Subtitle rules:
- Same-directory cloud subtitles must use
ApiFileDownloadUrl; never fetch a guessed provider URL directly. - The web player and external players must use the subtitle-specific local proxy URL and pass
IDownloadUrl.headersasproxy_headers. - The subtitle proxy must identify the account with
user_idand the provider withdrive_id; do not use a global/current-account token when an account-specific token is available. - Video streams remain direct URLs unless encryption requires the proxy. DLNA behavior is separate and must not be changed as part of subtitle or direct-play fixes.
- Any provider download-header change requires regression coverage for both the video/download URL contract and same-directory subtitle loading.
clouddrive-cli
Standalone CLI + MCP server for agent-driven cloud-drive operations. Docs: clouddrive-cli/README.md. The CLI supports 8 providers and has its own npm package (clouddrive-cli). Separate test command: pnpm run test:clouddrive-cli.
CI
Manual trigger only (workflow_dispatch) via .github/workflows/release.yml. Builds on windows-latest + ubuntu-latest, publishes draft GitHub Release. No automatic CI on push/PR.
Release ownership: drafts only
- The human user owns the final publication of every GitHub Release. The agent and CI may prepare commits/tags, build and upload assets, and create/update draft releases only when requested.
- Requests such as “发布 release”, “更新 release”, “重新提交代码更新 release”, or “继续发布” mean prepare/update a draft; they do not authorize making it public. The user publishes it manually in GitHub.
- Never run
gh release edit --draft=false, create a non-draft release, or enable automatic publication in a workflow or packaging configuration. Keep electron-builderreleaseTypeset todraftand the workflow's final release update in draft state. - Do not delete, recreate, demote, overwrite assets on, or otherwise modify an already-public release without a separate explicit request for that exact operation. A rerun targeting a public tag must stop rather than modify that release.
- Determine the next version from the latest public release plus one patch version, unless the user explicitly specifies a different version. Do not move an existing public tag to new code.
- Report draft creation and build/test status accurately. Never describe a draft or queued workflow as a published release.
Subprojects (gitignored, referenced locally)
Motrix/, koodo-reader/, CloudServiceKit/, XbyVideoHub/, OpenCLI/, QuarkPan/, PikPakAPI/ — these are local-only and not part of the main repo build.
