Use scripts/s3-cli.js to operate S3-compatible storage (Qiniu Kodo, MinIO, AWS S3, etc.). Configuration comes from environment variables, consistent with src/app-agent/lib/server/s3-storage.ts and .env-template .
Agent Workflow
- Check configuration first — Do not guess keys when unconfigured; prompt the user to fill in
.env:node --env-file-if-exists=.env .cursor/skills/s3-upload/scripts/s3-cli.js config - Choose a command — For upload use
upload; for browsing uselist; to search by name usefind; to get a public URL useurl。 - Prefer
--json— Use JSON output when parsed results are needed for subsequent steps. - Run from the repository root — Depends on
node_modulesin the repository root@aws-sdk/client-s3。 - Deletion requires
--yes— Avoid accidental deletion; confirm with the user before deleting. - On failure, correct feedback must be given — See "Error Handling" below; do not fabricate URLs or pretend the upload succeeded.
Error Handling (Agent Must Follow)
On CLI failure exit code = 1. When adding --json , stdout is structured JSON (including code、error、hint),Agent must be parsed before replying to the user。
Reply Format to User
On failure, inform the user in this structure (in Chinese, do not just paste the raw stack):
- What happened — Cite the
errorfield - Possible cause — Combine
codewith context - Next step — Cite
hint; if it can be auto-fixed (e.g.--no-acl), try first then report
Prohibited: Ignoring exit code; treating stderr as success; replying with a public URL without verification; showing the full Secret Key to the user.
Error Code Reference
code |
Meaning | Agent Action |
|---|---|---|
CONFIG_MISSING |
Missing environment variable | List missing, point to .env-template, ask the user to complete it then rerun config |
INVALID_CREDENTIALS |
Wrong key | Ask the user to verify AK/SK,do notrepeatedly blind-try with the same wrong key |
ACCESS_DENIED |
No permission | Explain insufficient permissions; on upload, if the hint mentions --no-acl, automatically retry once |
ACL_NOT_SUPPORTED |
ACL not supported | Use --no-acl to retry upload |
BUCKET_NOT_FOUND |
Bucket does not exist | Check whether S3_BUCKET_NAME and S3_ENDPOINT match |
OBJECT_NOT_FOUND |
Object does not exist | Suggest list --prefix / find to look up the correct key |
LOCAL_FILE_NOT_FOUND |
Local file does not exist | Check whether the path and cwd are in the repository root |
DELETE_REQUIRES_CONFIRM |
Not added --yes |
Confirm with the user then add --yes |
USAGE_ERROR |
Missing parameter | Fix the command per the hint, do not guess parameters |
NETWORK_ERROR |
Network/endpoint unreachable | Check endpoint, region, proxy |
DEPENDENCY_MISSING |
Missing SDK | In the repository root bun install , then retry |
S3_ERROR |
Other S3 error | Report error + details/httpStatus, suggest running config |
Diagnostic Flow
命令失败 (exit 1)
├─ 加 --json 重跑,读取 code
├─ CONFIG_* / INVALID_CREDENTIALS → config
├─ OBJECT_NOT_FOUND → list/find 定位 key
├─ ACCESS_DENIED + upload → 试 --no-acl
└─ 仍失败 → 原样汇报 error/hint/details,不要擅自改密钥
Example: Feedback to user
Missing configuration:
S3 is not fully configured, missing
S3_SECRET_ACCESS_KEY. Please complete in the project root directory.env(refer to.env-template), and I will verify after saving.
Failed to upload ACL (Agent should retry first):
Storage does not support ACL, switched to
--no-aclto re-upload, and it succeeded. Public URL: ...
Object does not exist:
No
assets/demo/missing.pngin the bucket. Currently underassets/demo/there are 3 files in total: ...
Quick Start
node --env-file-if-exists=.env .cursor/skills/s3-upload/scripts/s3-cli.js --help
node --env-file-if-exists=.env .cursor/skills/s3-upload/scripts/s3-cli.js config
Upload and get the public URL:
node --env-file-if-exists=.env .cursor/skills/s3-upload/scripts/s3-cli.js upload ./local.png --prefix assets/demo/
List / Find:
node --env-file-if-exists=.env .cursor/skills/s3-upload/scripts/s3-cli.js list --prefix uploads/ --max 50
node --env-file-if-exists=.env .cursor/skills/s3-upload/scripts/s3-cli.js find "photo" --prefix assets/
node --env-file-if-exists=.env .cursor/skills/s3-upload/scripts/s3-cli.js find "*.png" --prefix assets/
Download / Delete / URL:
node --env-file-if-exists=.env .cursor/skills/s3-upload/scripts/s3-cli.js download assets/demo/photo.png --output ./photo.png
node --env-file-if-exists=.env .cursor/skills/s3-upload/scripts/s3-cli.js url assets/demo/photo.png
node --env-file-if-exists=.env .cursor/skills/s3-upload/scripts/s3-cli.js delete assets/demo/old.png --yes
Environment Variables
| Variable | Required | Description |
|---|---|---|
S3_BUCKET_NAME |
Yes | Bucket |
S3_ACCESS_KEY_ID |
Yes | Access Key |
S3_SECRET_ACCESS_KEY |
Yes | Secret Key |
S3_ENDPOINT |
Yes | Service endpoint (e.g. https://s3.cn-south-1.qiniucs.com) |
S3_REGION |
No | Default cn-south-1(Qiniu); for Alibaba Cloud fill in cn-hangzhou , etc. |
S3_CDN |
No | CDN domain;url / Upload results preferentially use the CDN to compose the public URL |
S3_PROVIDER |
No | Provider identifier (kodo / aliyun / minio , etc.);aliyun when upload does not include ACL by default |
The CLI automatically reads the repository root .env(does not overwrite existing process.env). You can also explicitly pass node --env-file-if-exists=.env。
Forbidden Write keys into the skill, scripts, or commit them to git. Read-only .env or the user-provided env.
Alibaba Cloud OSS (S3_PROVIDER=aliyun)
S3_BUCKET_NAME=<bucket>
S3_ACCESS_KEY_ID=<RAM AccessKey ID>
S3_SECRET_ACCESS_KEY=<RAM AccessKey Secret>
S3_ENDPOINT=https://<bucket>.s3.oss-<region>.aliyuncs.com
S3_REGION=cn-hangzhou
S3_CDN=https://<your-cdn-domain>
S3_PROVIDER=aliyun
Key points:
- Endpoint — Recommended S3-compatible format:
https://<bucket>.s3.oss-cn-hangzhou.aliyuncs.com(consistent withS3_BUCKET_NAME); you can also usehttps://s3.oss-cn-hangzhou.aliyuncs.com(path-style). The CLI automatically detects whether the endpoint already contains the bucket subdomain. - Region — Consistent with the region in the endpoint (e.g.
cn-hangzhou), do not use the placeholder<your-region>。 - ACL — New OSS buckets usually disable Object ACL; the CLI under
aliyununder does not upload ACL by default. If an ACL error is still reported, explicitly add--no-acl。 - Public URL — When
S3_CDNis configured:https://cdn.example.com/<objectKey>; ensure the CDN is bound to the bucket and origin fetching is enabled. - RAM Permissions — The key needs the target bucket's
oss:PutObject、oss:GetObject、oss:ListObjects、oss:DeleteObject, etc.
Command Reference
| Command | Purpose |
|---|---|
config |
Verify configuration, output keys with sensitive data masked |
upload <file> |
Upload;--key / --prefix / --content-type / --acl / --no-acl |
list |
List by --prefix ;--max limit the number of entries |
find <pattern> |
key substring or * ? wildcard; combined with --prefix |
head <key> |
whether it exists and metadata |
download <key> |
Download;--output specify path |
delete <key> --yes |
Delete object |
url <key> |
Output public URL |
Global:--json、-h / --help。
Relationship with Application Code
- See server-side upload logic in
src/app-agent/lib/server/s3-storage.ts(Presigned POST, CDN URL parsing, etc.). - Agent operations/one-off upload Use this skill's CLI, do not rewrite SDK logic in the conversation.
- In-app functionality continue to use
s3-storage.tsor the existing OSS pipeline, do not mix in the CLI.
Common Scenarios
The user wants to upload a screenshot/build artifact and share a link
node --env-file-if-exists=.env .cursor/skills/s3-upload/scripts/s3-cli.js upload ./dist/bundle.zip --key releases/v1.2.0/bundle.zip --json
Get from JSON publicUrl reply to the user.
The user asks which files exist under a certain prefix
node --env-file-if-exists=.env .cursor/skills/s3-upload/scripts/s3-cli.js list --prefix static/_next/ --max 100 --json
The user gave a CDN URL and wants to confirm whether the key exists
Use objectKeyFromPublicUrl logic: remove the CDN prefix to get the key, then head。
Configuration error
Missing required env → CLI returns CONFIG_MISSING and missing array → compare against .env-template ask the user to complete it → rerun config until ok: true。
CLI returns JSON error example
{
"ok": false,
"code": "ACL_NOT_SUPPORTED",
"error": "当前存储不支持 ACL 字段",
"hint": "重试: upload <file> ... --no-acl",
"details": "…"
}
The Agent should execute the retry in the hint, and after success reply to the user.
.env do not repeatedly define S3_*
When multiple groups of S3_BUCKET_NAME and other variables appear in the same file, the later ones will overwrite the earlier ones (Node --env-file behavior). Keep only one set of configuration, and comment out the old configuration.