diff --git a/package.json b/package.json index 571242c..8339f39 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ }, "dependencies": { "@modelcontextprotocol/sdk": "^1.29.0", - "@rendobar/sdk": "^1.0.0", + "@rendobar/sdk": "^3.0.0", "zod": "^3.25.0" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c806fcf..9cfbdf0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: ^1.29.0 version: 1.29.0(zod@3.25.76) '@rendobar/sdk': - specifier: ^1.0.0 - version: 1.0.0 + specifier: ^3.0.0 + version: 3.0.0 zod: specifier: ^3.25.0 version: 3.25.76 @@ -483,8 +483,8 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} - '@rendobar/sdk@1.0.0': - resolution: {integrity: sha512-nxBuZ11+0XzecmyxxV3ckiErf4mBc5hYMjFGW58NSmD2nDVv7bpEtZYfLKqBFCRDFcy1sbGJsjCMpvR7mddMUA==} + '@rendobar/sdk@3.0.0': + resolution: {integrity: sha512-i9RrML7qq7E0j9Tl4oT8QIt5lizwYE+9sqdq7PESE/zcrLYA+/YuMcBF1fMmhKtOHAsEdgzzpGGLIZQjSdBrSw==} engines: {node: '>=18'} '@rollup/rollup-android-arm-eabi@4.60.2': @@ -2102,7 +2102,7 @@ snapshots: '@open-draft/until@2.1.0': {} - '@rendobar/sdk@1.0.0': + '@rendobar/sdk@3.0.0': dependencies: partysocket: 1.1.18 transitivePeerDependencies: diff --git a/src/tools/uploads.ts b/src/tools/uploads.ts index 9dec805..5e4206f 100644 --- a/src/tools/uploads.ts +++ b/src/tools/uploads.ts @@ -70,20 +70,22 @@ const uploadFileTool = defineTool({ // bodies and the SDK request layer doesn't set it. Buffering avoids that // entirely — Blob is a fully-buffered BodyInit and works everywhere. // - // Memory bound: maxInputFileSize (free=100MB, pro=2GB) — same ceiling the + // Memory bound: maxInputFileSize (free=500MB, pro=10GB) — same ceiling the // pre-stream gate enforces. v2 candidate: patch SDK to set duplex, then // restore streaming + ProgressTransform for files >5MB. const buffer = await fh.readFile(); const blob = new Blob([buffer]); - const result = await getSdk(ctx).uploads.upload(blob, { + // SDK 3.0.0: uploads.create() presigns + uploads + finalizes in one call + // and returns the ready asset; reference it by its stable content URL. + const asset = await getSdk(ctx).uploads.create(blob, { filename: args.filename ?? path.basename(resolved), signal: extra.signal, }); ctx.logger.info({ msg: "upload_complete", basename: path.basename(resolved), sizeBytes }); - return { downloadUrl: result.downloadUrl, sizeBytes }; + return { downloadUrl: asset.url, sizeBytes }; } finally { await fh.close(); } diff --git a/test/unit/tools/uploads.test.ts b/test/unit/tools/uploads.test.ts index 33b0884..4f71147 100644 --- a/test/unit/tools/uploads.test.ts +++ b/test/unit/tools/uploads.test.ts @@ -57,9 +57,9 @@ describe("upload_file", () => { }); it("uploads a small file and returns downloadUrl + sizeBytes", async () => { - const upload = vi.fn(async () => ({ downloadUrl: "https://api.rendobar.com/uploads/dl/abc" })); + const create = vi.fn(async () => ({ url: "https://api.rendobar.com/uploads/dl/abc" })); const sdk = { - uploads: { upload }, + uploads: { create }, billing: { state: vi.fn(async () => billingState(999_999_999)) }, }; const c = ctx({ sdk: sdk as never }); @@ -75,13 +75,13 @@ describe("upload_file", () => { downloadUrl: "https://api.rendobar.com/uploads/dl/abc", sizeBytes: 11, // "hello world" }); - expect(upload).toHaveBeenCalledOnce(); + expect(create).toHaveBeenCalledOnce(); }); it("respects custom filename", async () => { - const upload = vi.fn(async () => ({ downloadUrl: "https://x" })); + const create = vi.fn(async () => ({ url: "https://x" })); const sdk = { - uploads: { upload }, + uploads: { create }, billing: { state: vi.fn(async () => billingState(999_999_999)) }, }; const c = ctx({ sdk: sdk as never }); @@ -91,16 +91,16 @@ describe("upload_file", () => { c, { signal: new AbortController().signal } as never, ); - expect(upload).toHaveBeenCalledWith( + expect(create).toHaveBeenCalledWith( expect.anything(), expect.objectContaining({ filename: "renamed.txt" }), ); }); it("rejects oversize files using cached limit", async () => { - const upload = vi.fn(); + const create = vi.fn(); const sdk = { - uploads: { upload }, + uploads: { create }, billing: { state: vi.fn() }, // should NOT be called — cache is set }; const c = ctx({ sdk: sdk as never, cachedMaxFileSize: 5 }); @@ -108,15 +108,15 @@ describe("upload_file", () => { await expect( tool!.execute({ path: small }, c, { signal: new AbortController().signal } as never), ).rejects.toThrow(/exceeds.*limit/i); - expect(upload).not.toHaveBeenCalled(); + expect(create).not.toHaveBeenCalled(); expect(sdk.billing.state).not.toHaveBeenCalled(); }); it("populates limit cache via billing.state when cold", async () => { - const upload = vi.fn(async () => ({ downloadUrl: "https://x" })); + const create = vi.fn(async () => ({ url: "https://x" })); const billingStateFn = vi.fn(async () => billingState(100)); const sdk = { - uploads: { upload }, + uploads: { create }, billing: { state: billingStateFn }, }; const c = ctx({ sdk: sdk as never });