Skip to content

Commit d3c6778

Browse files
MajorTalclaude
andauthored
chore: sunset legacy storage surfaces (clean slate) (#121)
The private gateway repo big-bang-sunset the legacy storage shim on 2026-04-28 (#120). The four MCP tools (upload_file, download_file, list_files, delete_file) and CLI 'run402 storage' subcommand in this repo were pointed at routes that now 404. Pre-revenue position is clean slate: delete every reference, no migration tables, no "supersedes" mentions, no redirector stubs. New agents read new docs and discover blob_* in its own terms. - Delete 4 MCP tool handlers + tests; strip imports + registrations from src/index.ts; remove "supersedes upload_file" mentions from the blob_* tool descriptions - Delete cli/lib/storage.mjs + openclaw/scripts/storage.mjs; remove the dispatcher case + HELP entry from cli/cli.mjs - Scrub SKILL.md, README.md, README.zh-CN.md, openclaw/SKILL.md, cli/llms-cli.txt — drop tool-table rows, drop deprecated sections, swap upload_file examples for blob_put. Final grep returns zero - Update sync.test.ts SURFACE + IGNORED_ENDPOINTS; SKILL.test.ts; cli-help.test.mjs; remove storage-targeting cases from cli-integration.test.ts and cli-e2e.test.mjs - Apply REMOVE delta to incremental-deploy spec for the dead "Upload file shows public URL" requirement; mark obsolete carve-outs in active add-run402-sdk change - Bump run402-mcp, run402, @run402/sdk to 1.47.0 Tests: 422 unit + 271 e2e all green. Build clean. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7aeb028 commit d3c6778

30 files changed

Lines changed: 264 additions & 675 deletions

README.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,6 @@ npx run402-mcp
6262
| `setup_rls` | Apply row-level security templates to tables. |
6363
| `get_schema` | Introspect database schema — tables, columns, types, constraints, RLS policies. |
6464
| `get_usage` | Get project usage report — API calls, storage, limits, lease expiry. |
65-
| `upload_file` | Upload text content to project storage. |
66-
| `download_file` | Download a file from project storage. |
67-
| `delete_file` | Delete a file from project storage. |
68-
| `list_files` | List files in a storage bucket. |
6965
| `deploy_function` | Deploy a serverless function (Node 22) to a project. |
7066
| `invoke_function` | Invoke a deployed function via HTTP. |
7167
| `get_function_logs` | Get recent logs from a deployed function. |
@@ -214,7 +210,7 @@ claude mcp add run402 -- npx -y run402-mcp
214210
## How It Works
215211

216212
1. **Provision** — Call `provision_postgres_project` to create a database. The server handles x402 payment negotiation and stores credentials locally.
217-
2. **Build** — Use `run_sql` to create tables, `rest_query` to insert/query data, and `upload_file` for storage.
213+
2. **Build** — Use `run_sql` to create tables, `rest_query` to insert/query data, and `blob_put` for storage.
218214
3. **Deploy** — Use `deploy_site` for static sites, `deploy_function` for serverless functions, or `bundle_deploy` for a full-stack app in one call.
219215
4. **Renew** — Call `set_tier` before your lease expires.
220216

README.zh-CN.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ npx run402-mcp
2020
| `provision_postgres_project` | 创建新的 Postgres 数据库(prototype/hobby/team 等级) |
2121
| `run_sql` | 对项目执行 SQL(DDL 或查询) |
2222
| `rest_query` | 通过 PostgREST 查询/修改数据 |
23-
| `upload_file` | 上传文件到项目存储 |
2423
| `renew_project` | 续期数据库租约 |
2524
| `deploy_site` | 部署静态 HTML/CSS/JS 站点 |
2625
| `deploy_function` | 部署 Node 22 Serverless 函数 |
@@ -103,7 +102,7 @@ Run402 已发布为 OpenClaw Skill。支持所有 OpenClaw 兼容的模型和平
103102
## 工作流程
104103

105104
1. **创建项目** — 调用 `provision_postgres_project` 创建数据库。服务器自动处理 x402 支付协商,并在本地保存凭证。
106-
2. **构建应用** — 用 `run_sql` 创建表结构,`rest_query` 插入/查询数据,`upload_file` 管理文件存储。
105+
2. **构建应用** — 用 `run_sql` 创建表结构,`rest_query` 插入/查询数据,`blob_put` 管理文件存储。
107106
3. **部署上线** — 用 `deploy_site` 部署前端,`deploy_function` 部署后端函数。
108107
4. **续期维护** — 在租约到期前调用 `renew_project`
109108

SKILL.md

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,6 @@ html += styles.linkTag();
136136

137137
The legacy `size_bytes`, `sha256`, `immutable_url` fields stay populated for back-compat with pre-v1.45 callers.
138138

139-
Supersedes `upload_file` (deprecated).
140-
141139
### blob_get
142140

143141
Download a blob to a local file. Writes bytes directly to disk — no context-window bloat.
@@ -147,7 +145,7 @@ Download a blob to a local file. Writes bytes directly to disk — no context-wi
147145
- `key` (required) — Object key
148146
- `local_path` (required) — Local path to write
149147

150-
**Returns:** `{ key, size_bytes, sha256? }`. Supersedes `download_file` (deprecated).
148+
**Returns:** `{ key, size_bytes, sha256? }`.
151149

152150
### blob_ls
153151

@@ -159,11 +157,11 @@ List blobs in a project with optional prefix filter and keyset pagination.
159157
- `limit` (optional, default: 100, max: 1000)
160158
- `cursor` (optional) — Pagination cursor from a prior call
161159

162-
**Returns:** `{ blobs: [{ key, size_bytes, content_type, visibility, sha256?, created_at }], next_cursor? }`. Supersedes `list_files` (deprecated).
160+
**Returns:** `{ blobs: [{ key, size_bytes, content_type, visibility, sha256?, created_at }], next_cursor? }`.
163161

164162
### blob_rm
165163

166-
Delete a blob and decrement the project's storage usage. Supersedes `delete_file` (deprecated).
164+
Delete a blob and decrement the project's storage usage.
167165

168166
**Parameters:**
169167
- `project_id` (required) — Project ID
@@ -210,26 +208,6 @@ Polls the CDN until a **mutable** blob URL serves the expected SHA-256, or the t
210208

211209
**Returns:** `{ fresh, observedSha256, attempts, elapsedMs, vantage }`. The tool returns `isError=true` on timeout so an agent can branch into a fallback (typically: switch to the immutableUrl, which is always immediately correct).
212210

213-
### upload_file (deprecated)
214-
215-
Legacy file upload. **Deprecated — sunset 2026-06-01.** Use `blob_put` instead.
216-
217-
**Parameters:**
218-
- `project_id` (required) — Project ID
219-
- `bucket` (required) — Storage bucket name (e.g., `"assets"`)
220-
- `path` (required) — File path within bucket (e.g., `"logs/2024-01-01.txt"`)
221-
- `content` (required) — Text content to upload
222-
- `content_type` (optional, default: `"text/plain"`) — MIME type
223-
224-
**Returns:** `{ key: "assets/logs/2024-01-01.txt", size: 1234 }` with the stored file path and size in bytes.
225-
226-
**Example:**
227-
```
228-
upload_file(project_id: "prj_...", bucket: "assets", path: "data.csv", content: "name,age\nAlice,30\nBob,25")
229-
```
230-
231-
Uses the stored `anon_key` automatically.
232-
233211
### renew_project
234212

235213
Renew a project's lease before it expires.
@@ -816,7 +794,7 @@ If your app has users, use the HTTP auth endpoints directly:
816794
### Step 7: Upload files (optional)
817795

818796
```
819-
upload_file(project_id: "prj_...", bucket: "assets", path: "report.csv", content: "col1,col2\nval1,val2")
797+
blob_put(project_id: "prj_...", key: "assets/report.csv", content: "col1,col2\nval1,val2")
820798
```
821799

822800
### Step 8: Monitor usage
@@ -830,7 +808,7 @@ run_sql(project_id: "prj_...", sql: "SELECT count(*) FROM todos")
830808

831809
Run402 supports two payment protocols: **x402** (USDC on Base) and **MPP** (pathUSD on Tempo). Both use the same wallet key. Here's what you need to know:
832810

833-
**When payment is needed:** Only `provision_postgres_project` and `renew_project` require x402 payment. All other tools (run_sql, rest_query, upload_file) use stored project keys — no payment needed.
811+
**When payment is needed:** Only `provision_postgres_project` and `renew_project` require x402 payment. All other tools (run_sql, rest_query, blob_put) use stored project keys — no payment needed.
834812

835813
**What a 402 response looks like:** When payment is required, the tool returns payment details as informational text (not an error). The response includes the price, network (Base L2), and payment address.
836814

@@ -859,7 +837,7 @@ Prototype uses testnet tokens — no real money needed. With x402: Base Sepolia
859837

860838
**Key usage patterns:**
861839
- Use `service_key` (via `run_sql` or `key_type: "service"`) for: table creation, RLS setup, seeding data, admin queries
862-
- Use `anon_key` (via `rest_query` default or `upload_file`) for: user-facing reads, file uploads
840+
- Use `anon_key` (via `rest_query` default or `blob_put`) for: user-facing reads, file uploads
863841
- Use `access_token` (from auth login, via HTTP) for: user-scoped CRUD subject to RLS
864842

865843
**Tier selection:**

SKILL.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ const TOOLS = [
8080
"provision_postgres_project",
8181
"run_sql",
8282
"rest_query",
83-
"upload_file",
83+
"blob_put",
8484
"renew_project",
8585
];
8686

cli-e2e.test.mjs

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1481,36 +1481,6 @@ describe("CLI e2e happy path", () => {
14811481
assert.ok(captured().includes("TEST_KEY"), "should list secrets");
14821482
});
14831483

1484-
// ── Storage ─────────────────────────────────────────────────────────────
1485-
1486-
it("storage upload", async () => {
1487-
const { run } = await import("./cli/lib/storage.mjs");
1488-
const filePath = join(tempDir, "readme.txt");
1489-
const { writeFileSync: wf } = await import("node:fs");
1490-
wf(filePath, "Hello, world!");
1491-
captureStart();
1492-
await run("upload", ["prj_test123", "assets", "readme.txt", "--file", filePath]);
1493-
captureStop();
1494-
assert.ok(captured().includes("readme.txt") || captured().includes("key"), "should upload file");
1495-
});
1496-
1497-
it("storage list", async () => {
1498-
const { run } = await import("./cli/lib/storage.mjs");
1499-
captureStart();
1500-
await run("list", ["prj_test123", "assets"]);
1501-
captureStop();
1502-
assert.ok(captured().includes("readme.txt"), "should list files");
1503-
});
1504-
1505-
it("storage download", async () => {
1506-
const { run } = await import("./cli/lib/storage.mjs");
1507-
captureStart();
1508-
await run("download", ["prj_test123", "assets", "readme.txt"]);
1509-
captureStop();
1510-
// download uses process.stdout.write, not console.log — just verify no error
1511-
assert.ok(true, "should download without error");
1512-
});
1513-
15141484
// ── Blob (GH-40: fall back to active project from 'projects use') ───────
15151485

15161486
it("blob ls falls back to active project (GH-40)", async () => {
@@ -1821,14 +1791,6 @@ describe("CLI e2e happy path", () => {
18211791

18221792
// ── Cleanup commands (deletions) ────────────────────────────────────────
18231793

1824-
it("storage delete", async () => {
1825-
const { run } = await import("./cli/lib/storage.mjs");
1826-
captureStart();
1827-
await run("delete", ["prj_test123", "assets", "readme.txt"]);
1828-
captureStop();
1829-
assert.ok(captured().includes("ok") || captured().includes("delete"), "should delete file");
1830-
});
1831-
18321794
it("secrets delete", async () => {
18331795
const { run } = await import("./cli/lib/secrets.mjs");
18341796
captureStart();

cli-help.test.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ const MATRIX = {
7171
shared: [],
7272
specific: ["put", "get", "ls", "rm", "sign"],
7373
},
74-
storage: { shared: ["download", "delete", "list"], specific: ["upload"] },
7574
sites: { shared: ["status"], specific: ["deploy", "deploy-dir"] },
7675
subdomains: { shared: ["delete", "list"], specific: ["claim"] },
7776
domains: { shared: ["add", "list", "status", "delete"], specific: [] },

cli-integration.test.ts

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -350,35 +350,6 @@ describe("CLI integration (live API, no mocks)", { timeout: 180_000 }, () => {
350350
assert.ok(captured().includes("TEST_KEY"), "should list the secret");
351351
});
352352

353-
// ── Storage ───────────────────────────────────────────────────────────
354-
355-
it("storage upload", async () => {
356-
const { run } = await import("./cli/lib/storage.mjs");
357-
const filePath = join(tempDir, "test-file.txt");
358-
writeFileSync(filePath, "Hello from integration test!");
359-
captureStart();
360-
await run("upload", [projectId, "assets", "test-file.txt", "--file", filePath]);
361-
captureStop();
362-
assert.ok(
363-
captured().includes("test-file") || captured().includes("key") || captured().includes("size"),
364-
"should upload file",
365-
);
366-
});
367-
368-
it("storage list", async () => {
369-
const { run } = await import("./cli/lib/storage.mjs");
370-
captureStart();
371-
try {
372-
await run("list", [projectId, "assets"]);
373-
captureStop();
374-
assert.ok(captured().includes("test-file"), "should list uploaded file");
375-
} catch {
376-
// Storage list may 404 if the bucket prefix doesn't exist yet — verify upload worked instead
377-
captureStop();
378-
assert.ok(true, "storage list returned 404 (bucket prefix may not be listable)");
379-
}
380-
});
381-
382353
// ── Sites ─────────────────────────────────────────────────────────────
383354

384355
it("sites deploy", async () => {
@@ -626,14 +597,6 @@ describe("CLI integration (live API, no mocks)", { timeout: 180_000 }, () => {
626597
assert.ok(captured().includes("ok") || captured().includes("delete"), "should delete subdomain");
627598
});
628599

629-
it("storage delete", async () => {
630-
const { run } = await import("./cli/lib/storage.mjs");
631-
captureStart();
632-
await run("delete", [projectId, "assets", "test-file.txt"]);
633-
captureStop();
634-
assert.ok(captured().includes("ok") || captured().includes("delete"), "should delete file");
635-
});
636-
637600
it("secrets delete", async () => {
638601
const { run } = await import("./cli/lib/secrets.mjs");
639602
captureStart();

cli/cli.mjs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ Commands:
2929
functions Manage serverless functions (deploy, invoke, logs, list, delete)
3030
secrets Manage project secrets (set, list, delete)
3131
blob Direct-to-S3 blob storage (put, get, ls, rm, sign, diagnose) — up to 5 TiB
32-
storage Legacy file storage (deprecated — sunset 2026-06-01, use 'blob')
3332
sites Deploy static sites
3433
cdn CloudFront CDN diagnostics (wait-fresh) for public blob URLs
3534
subdomains Manage custom subdomains (claim, list, delete)
@@ -116,15 +115,6 @@ switch (cmd) {
116115
await run(sub, rest);
117116
break;
118117
}
119-
case "storage": {
120-
process.stderr.write(
121-
"run402 storage is deprecated — sunset 2026-06-01. Use `run402 blob` instead.\n" +
122-
"See https://run402.com/docs/blob#migration\n\n",
123-
);
124-
const { run } = await import("./lib/storage.mjs");
125-
await run(sub, rest);
126-
break;
127-
}
128118
case "blob": {
129119
const { run } = await import("./lib/blob.mjs");
130120
await run(sub, rest);

cli/lib/storage.mjs

Lines changed: 0 additions & 125 deletions
This file was deleted.

0 commit comments

Comments
 (0)