一键导入
file-management
Search, get, create, upload (single & multipart), update, and delete files in Spuree projects with checksum-verified upload flow
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Search, get, create, upload (single & multipart), update, and delete files in Spuree projects with checksum-verified upload flow
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Guides people through using Spuree for the first time. Always use this skill when the user asks how to use Spuree skills or tools, what Spuree can do, which Spuree capabilities are available, for Spuree help or a tutorial, or to get started with Spuree. Explains the public capability set, confirms the connection with a safe read-only first step, and offers a guided walkthrough without searching the user's workspace files for documentation.
List recent folders across projects; create, update, delete, and browse folders (sessions), including assets, files, and batch downloads
Create, list, update, delete, and share projects in Spuree, including browsing project contents
Obtain and refresh JWT access tokens, and manage API keys for the Spuree V1 API
Manage project sharing invitations in Spuree — list, accept, decline, cancel, and resend invitations for non-workspace members
| name | file-management |
| description | Search, get, create, upload (single & multipart), update, and delete files in Spuree projects with checksum-verified upload flow |
Spuree is an agent-friendly cloud storage. Projects contain folders (nestable) and files at any level. This skill manages files — they can live directly under a project or inside any folder. In the API, projects and folders are both called sessions (sessionType: creative_project = project, session = folder).
Use this skill when an agent needs to:
Authorization: Bearer $SPUREE_ACCESS_TOKEN
Or: X-API-Key: $SPUREE_API_KEY. See the authentication skill.
| Base URL | Endpoints |
|---|---|
https://data.spuree.com/api/v1/files | File CRUD |
https://data.spuree.com/api/v1/search | Cross-project search |
Full-text search across files, folders, projects, and assets. Backed by the content_index collection (Atlas $search), kept in sync via MongoDB Change Streams.
⚠️ Breaking change (ENG-5362). This endpoint replaced the legacy
sessions/filesregex search. Both the request params and the response shape changed:
- Matching is now Atlas analyzer-based token matching, not case-insensitive regex substring.
uploadno longer substring-matchesspuree_upload_test.txt.typevalues changed: oldfile|session→ newfile|folder|project|asset(the oldsessionsplit intofolder/project, plus a newassetsource type).- Response is now grouped — one item per source object with a
matches[]array — and paginated by an opaquecursorinstead of a flat list withlimit/offset.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
q | string | — | Search query (required, 1–255 chars). Full-text token match. |
type | string | — | Filter by source type: file | folder | project | asset. Omit for all. |
searchIn | string | all | Which rows to search: name | content (body + annotation) | all. |
format | string | — | Comma-separated file formats, e.g. txt,md. Applies to file type only. |
entityType | string | — | Comma-separated entity types, e.g. character,prop. Applies to asset type only. |
workspaceId | string | — | Restrict to a single workspace (ObjectId). Must be one the caller is a member of, else empty page; malformed id → 422. |
projectId | string | — | Restrict to a single project (ObjectId). Must be one the caller can access, else empty page; malformed id → 422. |
createdAfter | string | — | ISO 8601 lower bound on source createdAt. Timezone required (Z or ±HH:MM) — naive datetimes → 422. |
createdBefore | string | — | ISO 8601 upper bound. Timezone required. |
limit | integer | 50 | Page size (1–200). |
cursor | string | — | Opaque pagination token from a previous response. Pass to fetch the next page. |
includePreview | boolean | false | When true, asset results add a short-lived signed previewUrl (~1h) and previewFileFormat for the cover image/video. Other source types are unaffected; the raw bucket/key pointer is never returned. |
Response: { "data": [...], "count": N, "cursor": "<opaque token or null>" }
Each data item is one matched source object. Common fields:
| Field | Type | Description |
|---|---|---|
sourceType | string | file | folder | project | asset |
sourceId | string | ObjectId of the source object (the file/folder/project/asset id) |
score | number | Top relevance score across this object's matches |
matchCount | integer | Number of matching rows |
matches | array | Per-row matches (see below) |
workspaceId | string? | Workspace ObjectId |
projectId | string? | Project ObjectId |
sourceCreatedAt | datetime | Source object's creation timestamp |
File / folder / project items additionally carry: fileName, fileFormat (file only), sessionId (parent project/folder), entitySessionId (owning entity, if any). Folder / project items also carry sessionName — the clean display name. Use it for display rather than deriving a name from snippets/row text, which mix the name with tags (folders) or description + tags (projects).
Asset items instead carry asset-specific fields (no fileName/fileFormat):
| Field | Type | Description |
|---|---|---|
assetName | string | Asset (entity session) name |
entityType | string | e.g. character, prop |
visibility | string | e.g. workspace | public |
previewUrl | string? | Signed cover URL — only when includePreview=true and the asset has a cover |
previewFileFormat | string? | Cover format — jpg/png (image) or mp4/mov (video). Render by this: the signed URL carries no extension. Present only with includePreview=true |
Each entry in matches[]:
| Field | Type | Description |
|---|---|---|
rowKind | string | name | body | annotation |
score | number | Relevance score for this row |
snippets | string[] | Pre-rendered HTML strings — hits wrapped in <mark>, surrounding context HTML-escaped. Safe to inject directly into an HTML renderer (e.g. React dangerouslySetInnerHTML); no client-side offset math. |
chunkIndex | integer? | Body chunk index (content rows) |
charOffset | integer? | Char offset of the chunk within the file (content rows) |
lineStart | integer? | Starting line number of the chunk (content rows) |
# Find files named/containing "hero"
curl "https://data.spuree.com/api/v1/search?q=hero&type=file" \
-H "Authorization: Bearer $SPUREE_ACCESS_TOKEN"
# Search only file names, restricted to one project, txt/md only
curl "https://data.spuree.com/api/v1/search?q=quarterly%20report&type=file&searchIn=name&projectId=...&format=txt,md&limit=50" \
-H "Authorization: Bearer $SPUREE_ACCESS_TOKEN"
# Next page
curl "https://data.spuree.com/api/v1/search?q=hero&cursor=<token>" \
-H "Authorization: Bearer $SPUREE_ACCESS_TOKEN"
Errors specific to search:
| Code | messageCode | Cause | Resolution |
|---|---|---|---|
| 400 | search_query_too_broad | Query would match more than ~100,000 rows. Body includes lowerBound and threshold. | Prompt the user to refine with more specific terms. |
| 422 | — | Missing/invalid q, naive (timezone-less) createdAfter/createdBefore, or malformed workspaceId/projectId. | Fix the parameter. |
Get file metadata and presigned download URL.
Response: { "data": { id, fileName, fileFormat, mimeType, size, workspaceId, sessionId, entitySessionId, downloadUrl, createdAt, updatedAt } }
curl "https://data.spuree.com/api/v1/files/{fileId}" \
-H "Authorization: Bearer $SPUREE_ACCESS_TOKEN"
Get inline UTF-8 text content for a small text-like file. Use this when an agent needs to read markdown, text, JSON, CSV, YAML, XML, subtitles, logs, or source files directly instead of following a short-lived CloudFront download URL.
This endpoint is not a replacement for file downloads. Binary files and large files should still use GET /v1/files/{fileId} and its downloadUrl.
Response: { "data": { id, fileName, fileFormat, mimeType, size, encoding, content, truncated } }
truncated is reserved: oversize files return 413 rather than a partial body, so it is currently always false.
| Code | Description |
|---|---|
| 200 | Text content returned |
| 413 | File is too large to return inline |
| 415 | Unsupported/binary format or invalid UTF-8 |
curl "https://data.spuree.com/api/v1/files/{fileId}/content" \
-H "Authorization: Bearer $SPUREE_ACCESS_TOKEN"
Create a file record and get presigned upload URL(s). Automatically selects upload mode based on file size:
mode: "single")mode: "multipart")The caller must then upload to S3 and call POST /v1/files/{fileId}/upload/complete. Without the complete call, the file remains pending and unusable.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
fileName | string | Yes | File name without extension |
fileFormat | string | Yes | Extension in lowercase (e.g., fbx, png) |
fileSize | integer | Yes | File size in bytes (accepts string for backward compat) |
sessionId | string | Yes | Target project or folder ObjectId |
checksum | string | No | CRC32 base64 (8 chars) for end-to-end verification. When provided, the presigned URL is signed with the checksum and S3 verifies the upload matches. |
Response (single mode): { messageCode, fileId, mode: "single", uploadUrl, checksumBase64, contentType }
Response (multipart mode): { messageCode, fileId, mode: "multipart", parts: [{partNumber, startByte, endByte, url}, ...], totalParts, expiresAt }
| Code | Description |
|---|---|
| 200 | Record created, presigned URL(s) returned |
| 404 | Session not found |
| 409 | File already exists |
curl -X POST "https://data.spuree.com/api/v1/files" \
-H "Authorization: Bearer $SPUREE_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"fileName":"hero_walk","fileFormat":"fbx","fileSize":5242880,"sessionId":"...","checksum":"AAAAAA=="}'
Mark a file upload as completed. Works for both single and multipart uploads — the server auto-detects by checking whether the file has an active multipart uploadId. For multipart, the server calls S3 ListParts to collect ETags and then CompleteMultipartUpload.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
fileId | string | Yes | Must match path parameter |
clientChecksumCRC32 | string | No | Client-computed CRC32 for end-to-end verification |
Response: { messageCode, fileId, checksum, checksumCRC32 }
checksum: S3-verified CRC32 (base64) for both single and multipart uploadschecksumCRC32: S3 FULL_OBJECT CRC32 (multipart only)curl -X POST "https://data.spuree.com/api/v1/files/{fileId}/upload/complete" \
-H "Authorization: Bearer $SPUREE_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"fileId": "..."}'
Update file metadata. At least one field required.
| Field | Type | Description |
|---|---|---|
fileName | string | New name (without extension) |
fileFormat | string | New extension (lowercase) |
checksum | string | CRC32 base64 (write-once backfill, no edit permission needed) |
sessionId | string | Move to target project or folder |
Move: Only creative_project (project) and session (folder) targets supported.
Response: { messageCode, fileId }
| Code | Description |
|---|---|
| 200 | Updated |
| 409 | Filename conflict in target |
curl -X PATCH "https://data.spuree.com/api/v1/files/{fileId}" \
-H "Authorization: Bearer $SPUREE_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"fileName": "hero_run_cycle"}'
Prepare a content update with optimistic concurrency. Acquires an upload lock. Like POST /v1/files, automatically selects single or multipart mode based on fileSize.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
expectedChecksum | string | Yes | CRC32 base64 of current content (for conflict detection) |
newChecksum | string | Yes | CRC32 base64 of new content to upload |
fileSize | integer | No | Size in bytes. If ≥ 100 MB, returns multipart mode. |
Response (single): { messageCode, fileId, mode: "single", uploadUrl, checksumBase64, contentType }
Response (multipart): { messageCode, fileId, mode: "multipart", parts: [{partNumber, startByte, endByte, url}, ...], totalParts, expiresAt }
After receiving the response, upload to S3 then call POST /v1/files/{fileId}/upload/complete.
Upload lock: Single uploads lock for 1 hour. Multipart uploads lock for 6 hours. Same user can re-acquire. Expired locks auto-release.
| Code | Description |
|---|---|
| 200 | Lock acquired, presigned URL(s) returned |
| 409 | Checksum mismatch or locked by another user |
curl -X PUT "https://data.spuree.com/api/v1/files/{fileId}" \
-H "Authorization: Bearer $SPUREE_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"expectedChecksum": "a1b2...","newChecksum": "f6e5..."}'
Resume a multipart upload. Returns which parts are already in S3 and fresh presigned URLs for remaining parts.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
uploadId | string | No | Client's expected uploadId for mismatch detection |
checksum | string | No | Client's expected pendingChecksum for conflict detection |
Response: { messageCode, fileId, completedParts: [{partNumber, etag, size}, ...], remainingParts: [{partNumber, startByte, endByte, url}, ...], totalParts }
| Code | Description |
|---|---|
| 200 | Success |
| 400 | No active multipart upload |
| 409 | Not in pending status or locked by another user |
curl "https://data.spuree.com/api/v1/files/{fileId}/upload" \
-H "Authorization: Bearer $SPUREE_ACCESS_TOKEN"
Refresh presigned URLs for specific parts of a multipart upload. Use when URLs have expired before upload completes.
Request Body: { "partNumbers": [1, 3, 5] } (1-indexed, min 1 part)
Response: { messageCode, urls: [{partNumber, startByte, endByte, url}, ...] }
| Code | Description |
|---|---|
| 200 | Success |
| 400 | No active multipart upload or invalid part numbers |
curl -X POST "https://data.spuree.com/api/v1/files/{fileId}/upload/urls" \
-H "Authorization: Bearer $SPUREE_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"partNumbers": [1, 3, 5]}'
Abort a multipart upload. Cleans up S3 parts and marks the file as failed. Idempotent — safe to call on already-aborted uploads.
Response: { messageCode, fileId, message }
| Code | Description |
|---|---|
| 200 | Aborted |
| 400 | No active multipart upload |
curl -X DELETE "https://data.spuree.com/api/v1/files/{fileId}/upload" \
-H "Authorization: Bearer $SPUREE_ACCESS_TOKEN"
Soft-delete a file.
Response: { messageCode, fileId }
curl -X DELETE "https://data.spuree.com/api/v1/files/{fileId}" \
-H "Authorization: Bearer $SPUREE_ACCESS_TOKEN"
Compute CRC32 checksum (base64):
CHECKSUM=$(python3 -c "import base64, zlib, sys; print(base64.b64encode(zlib.crc32(open('$FILE_PATH','rb').read()).to_bytes(4,'big')).decode())")
Create file record — sessionId is the target project or folder:
POST /v1/files { fileName, fileFormat, fileSize, sessionId, checksum }
→ { fileId, mode: "single", uploadUrl, checksumBase64, contentType }
Upload binary to S3:
PUT {uploadUrl}
Content-Type: {contentType}
x-amz-checksum-crc32: {checksumBase64}
Body: <file binary>
Complete the upload — REQUIRED (file is not visible until this is called):
POST /v1/files/{fileId}/upload/complete
Important: All three steps (create → S3 upload → complete) must be performed. Skipping complete leaves the file in a pending state.
Create file record with fileSize ≥ 100 MB:
POST /v1/files { fileName, fileFormat, fileSize, sessionId, checksum }
→ { fileId, mode: "multipart", parts: [{partNumber, startByte, endByte, url}, ...], totalParts, expiresAt }
Upload each part to its presigned URL:
PUT {part.url}
Content-Length: {part.endByte - part.startByte + 1}
Body: <file slice from startByte to endByte>
If interrupted, resume with GET /v1/files/{fileId}/upload to get completed parts and fresh URLs for remaining parts.
If URLs expire, refresh with POST /v1/files/{fileId}/upload/urls for specific parts.
Complete — server auto-detects multipart and calls S3 CompleteMultipartUpload:
POST /v1/files/{fileId}/upload/complete
To abort, call DELETE /v1/files/{fileId}/upload to clean up S3 parts.
PUT /v1/files/{fileId} with checksums (and fileSize for multipart) → get upload URL(s)POST /v1/files/{fileId}/upload/complete — REQUIREDWhen a user wants to view, preview, or see a file:
If the agent has browser tools (e.g., Chrome DevTools MCP), open the Studio preview URL directly:
navigate_page → https://studio.spuree.com/file/{fileId}
Otherwise, return the Studio preview URL for the user to click:
https://studio.spuree.com/file/{fileId}
Do not download the file or attempt to open it locally. The Studio URL is permanent, permission-aware, and renders images, video, 3D, and other supported formats inline in the browser.
| Use case | URL | Properties |
|---|---|---|
| Share with user / preview | https://studio.spuree.com/file/{fileId} | Permanent, permission-aware, renders preview UI |
| Programmatic download | downloadUrl from GET /v1/files/{fileId} | Short-lived presigned S3 URL — do not share, bypasses permissions and expires |
Rule of thumb: after POST /v1/files (upload) build the Studio URL from the returned fileId; after GET /v1/search use the result's sourceId (the file id, for sourceType: "file" items). Return that URL to the user. Only fetch downloadUrl when the agent itself needs the bytes.
GET /v1/search?q=hero_walk&type=file → take sourceId from a sourceType: "file" resultGET /v1/files/{sourceId} → get downloadUrlProjects and folders are both sessions. sessionId always refers to a session. Session types: creative_project (project), session (folder). Folders nest inside projects or other folders.
S3 key: works_{workspaceId}/sess_{sessionId}/file_{fileId}
| Resource | URL Pattern |
|---|---|
| Project | https://studio.spuree.com/projects/{projectId} |
| Folder (top-level) | https://studio.spuree.com/projects/{projectId}/folders/{folderId} |
| Folder (nested) | .../folders/{parentId}/{childId} (up to 5 levels) |
| File | https://studio.spuree.com/file/{fileId} |
| Code | Cause | Resolution |
|---|---|---|
| 400 | Invalid checksum format, missing fields, bad ID | Fix input format |
| 403 | No workspace access or edit permission | Check permissions |
| 404 | File or session not found | Verify IDs |
| 409 (checksum mismatch) | File modified by another user | Re-fetch checksum, retry |
| 409 (upload lock) | Another user is uploading | Wait (lock expires in 1 hour for single, 6 hours for multipart) |
| 409 (filename conflict) | Same name exists in target | Use different name or target |
| 401 | Invalid or expired token | Refresh via authentication skill |