@superblocksteam/sdk-api 0.0.12-body-types → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/README.md +23 -0
  2. package/dist/errors.d.ts +36 -3
  3. package/dist/errors.d.ts.map +1 -1
  4. package/dist/errors.js +231 -38
  5. package/dist/errors.js.map +1 -1
  6. package/dist/errors.test.d.ts +2 -0
  7. package/dist/errors.test.d.ts.map +1 -0
  8. package/dist/errors.test.js +189 -0
  9. package/dist/errors.test.js.map +1 -0
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/integrations/anthropic/types.d.ts +1 -1
  14. package/dist/integrations/base/decode-worker-binary-response.d.ts.map +1 -1
  15. package/dist/integrations/base/decode-worker-binary-response.js +2 -5
  16. package/dist/integrations/base/decode-worker-binary-response.js.map +1 -1
  17. package/dist/integrations/base/index.d.ts +1 -1
  18. package/dist/integrations/base/index.d.ts.map +1 -1
  19. package/dist/integrations/base/request-body.d.ts +18 -10
  20. package/dist/integrations/base/request-body.d.ts.map +1 -1
  21. package/dist/integrations/base/request-body.js +131 -215
  22. package/dist/integrations/base/request-body.js.map +1 -1
  23. package/dist/integrations/base/request-body.test.js +306 -164
  24. package/dist/integrations/base/request-body.test.js.map +1 -1
  25. package/dist/integrations/base/rest-api-client-base.d.ts.map +1 -1
  26. package/dist/integrations/base/rest-api-client-base.js +13 -26
  27. package/dist/integrations/base/rest-api-client-base.js.map +1 -1
  28. package/dist/integrations/base/rest-api-integration-client.d.ts.map +1 -1
  29. package/dist/integrations/base/rest-api-integration-client.js +2 -5
  30. package/dist/integrations/base/rest-api-integration-client.js.map +1 -1
  31. package/dist/integrations/base/types.d.ts +0 -8
  32. package/dist/integrations/base/types.d.ts.map +1 -1
  33. package/dist/integrations/cosmosdb/client.d.ts.map +1 -1
  34. package/dist/integrations/cosmosdb/client.js +2 -5
  35. package/dist/integrations/cosmosdb/client.js.map +1 -1
  36. package/dist/integrations/documentation-resolver.test.js +88 -2
  37. package/dist/integrations/documentation-resolver.test.js.map +1 -1
  38. package/dist/integrations/documentation.test.js +10 -0
  39. package/dist/integrations/documentation.test.js.map +1 -1
  40. package/dist/integrations/dynamodb/client.d.ts +6 -2
  41. package/dist/integrations/dynamodb/client.d.ts.map +1 -1
  42. package/dist/integrations/dynamodb/client.js +85 -15
  43. package/dist/integrations/dynamodb/client.js.map +1 -1
  44. package/dist/integrations/dynamodb/client.test.d.ts +8 -0
  45. package/dist/integrations/dynamodb/client.test.d.ts.map +1 -0
  46. package/dist/integrations/dynamodb/client.test.js +198 -0
  47. package/dist/integrations/dynamodb/client.test.js.map +1 -0
  48. package/dist/integrations/dynamodb/index.d.ts +1 -1
  49. package/dist/integrations/dynamodb/index.d.ts.map +1 -1
  50. package/dist/integrations/dynamodb/index.js.map +1 -1
  51. package/dist/integrations/dynamodb/types.d.ts +27 -1
  52. package/dist/integrations/dynamodb/types.d.ts.map +1 -1
  53. package/dist/integrations/gcs/client.d.ts.map +1 -1
  54. package/dist/integrations/gcs/client.js +2 -5
  55. package/dist/integrations/gcs/client.js.map +1 -1
  56. package/dist/integrations/gsheets/client.d.ts.map +1 -1
  57. package/dist/integrations/gsheets/client.js +2 -5
  58. package/dist/integrations/gsheets/client.js.map +1 -1
  59. package/dist/integrations/index.d.ts +1 -1
  60. package/dist/integrations/index.d.ts.map +1 -1
  61. package/dist/integrations/index.js.map +1 -1
  62. package/dist/integrations/mongodb/client.d.ts.map +1 -1
  63. package/dist/integrations/mongodb/client.js +2 -5
  64. package/dist/integrations/mongodb/client.js.map +1 -1
  65. package/dist/integrations/restapiintegration/client.body-types.test.js +107 -25
  66. package/dist/integrations/restapiintegration/client.body-types.test.js.map +1 -1
  67. package/dist/integrations/restapiintegration/client.test.js +16 -0
  68. package/dist/integrations/restapiintegration/client.test.js.map +1 -1
  69. package/dist/integrations/s3/client.d.ts.map +1 -1
  70. package/dist/integrations/s3/client.js +2 -5
  71. package/dist/integrations/s3/client.js.map +1 -1
  72. package/dist/integrations/salesforce/client.d.ts.map +1 -1
  73. package/dist/integrations/salesforce/client.js +2 -5
  74. package/dist/integrations/salesforce/client.js.map +1 -1
  75. package/dist/integrations/slack/client.d.ts.map +1 -1
  76. package/dist/integrations/slack/client.js +4 -13
  77. package/dist/integrations/slack/client.js.map +1 -1
  78. package/dist/integrations/slack/client.test.js +2 -5
  79. package/dist/integrations/slack/client.test.js.map +1 -1
  80. package/dist/integrations/utils.d.ts +8 -3
  81. package/dist/integrations/utils.d.ts.map +1 -1
  82. package/dist/integrations/utils.js +42 -1
  83. package/dist/integrations/utils.js.map +1 -1
  84. package/dist/runtime/context.d.ts +6 -0
  85. package/dist/runtime/context.d.ts.map +1 -1
  86. package/dist/runtime/context.js +2 -1
  87. package/dist/runtime/context.js.map +1 -1
  88. package/dist/runtime/executor.d.ts +6 -0
  89. package/dist/runtime/executor.d.ts.map +1 -1
  90. package/dist/runtime/executor.js +1 -0
  91. package/dist/runtime/executor.js.map +1 -1
  92. package/dist/types.d.ts +10 -0
  93. package/dist/types.d.ts.map +1 -1
  94. package/package.json +3 -3
  95. package/src/errors.test.ts +257 -0
  96. package/src/errors.ts +289 -46
  97. package/src/index.ts +1 -0
  98. package/src/integrations/anthropic/README.md +12 -59
  99. package/src/integrations/anthropic/types.ts +1 -1
  100. package/src/integrations/base/decode-worker-binary-response.ts +5 -7
  101. package/src/integrations/base/index.ts +0 -5
  102. package/src/integrations/base/request-body.test.ts +358 -195
  103. package/src/integrations/base/request-body.ts +170 -326
  104. package/src/integrations/base/rest-api-client-base.ts +22 -31
  105. package/src/integrations/base/rest-api-integration-client.ts +8 -7
  106. package/src/integrations/base/types.ts +0 -18
  107. package/src/integrations/box/README.md +4 -60
  108. package/src/integrations/cosmosdb/client.ts +8 -7
  109. package/src/integrations/documentation-resolver.test.ts +108 -2
  110. package/src/integrations/documentation.test.ts +12 -0
  111. package/src/integrations/dropbox/README.md +1 -71
  112. package/src/integrations/dropbox/docs.manifest.json +10 -1
  113. package/src/integrations/dropbox/overlays/upload-unsupported.md +3 -0
  114. package/src/integrations/dropbox/overlays/upload.md +89 -0
  115. package/src/integrations/dynamodb/README.md +33 -12
  116. package/src/integrations/dynamodb/client.test.ts +254 -0
  117. package/src/integrations/dynamodb/client.ts +129 -23
  118. package/src/integrations/dynamodb/index.ts +5 -1
  119. package/src/integrations/dynamodb/types.ts +33 -1
  120. package/src/integrations/elasticsearch/README.md +0 -80
  121. package/src/integrations/elasticsearch/docs.manifest.json +10 -1
  122. package/src/integrations/elasticsearch/overlays/bulk-unsupported.md +3 -0
  123. package/src/integrations/elasticsearch/overlays/bulk.md +80 -0
  124. package/src/integrations/gcs/client.ts +8 -7
  125. package/src/integrations/googledrive/README.md +10 -36
  126. package/src/integrations/gsheets/client.ts +8 -7
  127. package/src/integrations/index.ts +1 -0
  128. package/src/integrations/jira/README.md +0 -26
  129. package/src/integrations/jira/docs.manifest.json +10 -1
  130. package/src/integrations/jira/overlays/attachments-unsupported.md +3 -0
  131. package/src/integrations/jira/overlays/attachments.md +30 -0
  132. package/src/integrations/mongodb/client.ts +8 -7
  133. package/src/integrations/restapiintegration/client.body-types.test.ts +135 -26
  134. package/src/integrations/restapiintegration/client.test.ts +23 -0
  135. package/src/integrations/restapiintegration/docs.manifest.json +2 -2
  136. package/src/integrations/restapiintegration/overlays/request-body-types.md +26 -17
  137. package/src/integrations/s3/client.ts +8 -7
  138. package/src/integrations/salesforce/client.ts +9 -7
  139. package/src/integrations/slack/client.test.ts +2 -5
  140. package/src/integrations/slack/client.ts +13 -19
  141. package/src/integrations/stabilityai/README.md +0 -87
  142. package/src/integrations/stabilityai/docs.manifest.json +10 -1
  143. package/src/integrations/stabilityai/overlays/image-uploads-unsupported.md +3 -0
  144. package/src/integrations/stabilityai/overlays/image-uploads.md +120 -0
  145. package/src/integrations/stripe/README.md +0 -201
  146. package/src/integrations/stripe/docs.manifest.json +10 -1
  147. package/src/integrations/stripe/overlays/request-bodies-unsupported.md +3 -0
  148. package/src/integrations/stripe/overlays/request-bodies.md +204 -0
  149. package/src/integrations/utils.ts +51 -1
  150. package/src/runtime/context.ts +9 -0
  151. package/src/runtime/executor.ts +8 -0
  152. package/src/types.ts +11 -0
@@ -6,11 +6,11 @@
6
6
  - `"raw"` for caller-encoded string bodies such as XML, CSV, or plain text.
7
7
  - `"formUrlencoded"` for `application/x-www-form-urlencoded` payloads.
8
8
  - `"multipartForm"` for multipart fields and file parts.
9
- - `"binary"` for byte payloads such as `Uint8Array`, `ArrayBuffer`, `DataView`, or `Buffer`.
9
+ - `"binary"` for byte payloads. Pass bytes such as a `Uint8Array`, `ArrayBuffer`, `DataView`, or `Buffer`.
10
10
 
11
11
  ### Form URL-Encoded Requests
12
12
 
13
- Use `bodyType: "formUrlencoded"` for Stripe, OAuth token exchanges, and APIs that expect HTML form encoding. Arrays repeat the key in order, and nested fields should be flattened with bracket notation:
13
+ Use `bodyType: "formUrlencoded"` for OAuth token exchanges and other APIs that expect HTML form encoding. Arrays repeat the key, so `tags: ["a", "b"]` encodes as `tags=a&tags=b`. APIs that expect bracket keys need the brackets in the key, and nested fields must be flattened with bracket notation:
14
14
 
15
15
  ```typescript
16
16
  const token = await ctx.integrations.authApi.apiRequest(
@@ -21,16 +21,16 @@ const token = await ctx.integrations.authApi.apiRequest(
21
21
  body: {
22
22
  grant_type: "client_credentials",
23
23
  client_id: input.clientId,
24
- scope: ["read:orders", "write:orders"],
24
+ scope: "read:orders write:orders",
25
25
  },
26
26
  },
27
27
  { response: TokenResponseSchema },
28
28
  );
29
29
 
30
- const charge = await ctx.integrations.stripe.apiRequest(
30
+ const order = await ctx.integrations.ordersApi.apiRequest(
31
31
  {
32
32
  method: "POST",
33
- path: "/v1/charges",
33
+ path: "/v1/orders",
34
34
  bodyType: "formUrlencoded",
35
35
  body: {
36
36
  amount: 2000,
@@ -38,7 +38,7 @@ const charge = await ctx.integrations.stripe.apiRequest(
38
38
  "metadata[order_id]": "6735",
39
39
  },
40
40
  },
41
- { response: ChargeResponseSchema },
41
+ { response: OrderResponseSchema },
42
42
  );
43
43
  ```
44
44
 
@@ -47,28 +47,36 @@ Fields whose value is `undefined` are omitted. Empty arrays are omitted. The bod
47
47
 
48
48
  ### Binary Uploads
49
49
 
50
- Use `bodyType: "binary"` for APIs that accept the request body as bytes, such as SharePoint-style upload endpoints:
50
+ Use `bodyType: "binary"` for APIs that accept the request body as bytes, such as SharePoint-style upload endpoints. For an uploaded file, pass the `Uint8Array` returned by `await file.readContentsAsync("raw")` directly as the body:
51
51
 
52
52
  ```typescript
53
+ const file = input.upload.files[0];
54
+
53
55
  await ctx.integrations.sharepoint.apiRequest(
54
56
  {
55
57
  method: "PUT",
56
- path: `/sites/${siteId}/drive/root:/reports/monthly.pdf:/content`,
58
+ path: `/sites/${siteId}/drive/root:/reports/${file.name}:/content`,
57
59
  bodyType: "binary",
58
- body: pdfBytes,
60
+ body: await file.readContentsAsync("raw"),
61
+ headers: { "Content-Type": file.type || "application/octet-stream" },
59
62
  },
60
63
  { response: UploadResponseSchema },
61
64
  );
62
65
  ```
63
66
 
64
- The SDK sends the exact byte window for views such as `Uint8Array.subarray()` and `DataView`. It adds `Content-Type: application/octet-stream` unless you already provided a `Content-Type` header such as `application/pdf`.
65
- Request bodies are limited to 20 MiB before serialization so the agent request stays under its 28 MiB serialized size cap.
67
+ For bytes that do not come from an uploaded file, pass a byte container supported by the SDK, such as `Uint8Array`, `ArrayBuffer`, `DataView`, or `Buffer`. Do not convert file bytes to a string. `readContentsAsync("binary")` returns base64 text, not bytes. Never send that text as a file body. Use `readContentsAsync("raw")` instead. If only base64 text is available, decode it with `Buffer.from(text, "base64")` and send the result with `bodyType: "binary"`.
68
+
69
+ The SDK sends the exact byte window of a byte view, so `bytes.subarray(start, end)` uploads a slice. It adds `Content-Type: application/octet-stream` unless you already provided a `Content-Type` header such as `application/pdf`.
70
+
71
+ Binary request bodies must be non-empty; a zero-length body throws a validation error.
66
72
 
67
73
  ### Multipart Uploads
68
74
 
69
- Use `bodyType: "multipartForm"` for endpoints that expect fields plus file parts. File parts must be wrapped as `{ value, filename }`:
75
+ Use `bodyType: "multipartForm"` for endpoints that expect fields plus file parts. File parts must be wrapped as `{ value, filename }`; a bare binary value without that wrapper is rejected with a message naming the required `{ value, filename }` shape. A file part `value` is a string or bytes such as the `Uint8Array` returned by `await file.readContentsAsync("raw")`. `readContentsAsync("binary")` returns base64 text, not bytes; never pass that text as a file part value, it will be UTF-8 encoded a second time and silently corrupt the upload. Use `readContentsAsync("raw")` for file part bytes.
70
76
 
71
77
  ```typescript
78
+ const invoice = input.upload.files[0];
79
+
72
80
  const upload = await ctx.integrations.filesApi.apiRequest(
73
81
  {
74
82
  method: "POST",
@@ -76,7 +84,10 @@ const upload = await ctx.integrations.filesApi.apiRequest(
76
84
  bodyType: "multipartForm",
77
85
  body: {
78
86
  folder: "invoices",
79
- invoice: { value: invoicePdfBytes, filename: "invoice.pdf" },
87
+ invoice: {
88
+ value: await invoice.readContentsAsync("raw"),
89
+ filename: invoice.name,
90
+ },
80
91
  },
81
92
  },
82
93
  { response: UploadResponseSchema },
@@ -84,11 +95,11 @@ const upload = await ctx.integrations.filesApi.apiRequest(
84
95
  ```
85
96
 
86
97
  Do not set `Content-Type` for multipart requests. The worker generates the multipart boundary during execution.
87
- Do not set per-part `contentType`. Fields whose value is `undefined` are omitted. Empty file parts are allowed. The total multipart field and file-part content is limited to 20 MiB before serialization.
98
+ Do not set per-part `contentType`. Arrays repeat the key, so `files: [partA, partB]` sends two `files` parts. Fields whose value is `undefined` are omitted. Empty file parts are allowed.
88
99
 
89
100
  ### Raw String Bodies
90
101
 
91
- Use `bodyType: "raw"` for pre-encoded text bodies. The SDK does not add a `Content-Type` header in raw mode, so provide the header expected by the API:
102
+ Use `bodyType: "raw"` for pre-encoded text bodies. The SDK does not add a `Content-Type` header in raw mode, so provide the header expected by the API. Prefer `"json"`, `"formUrlencoded"`, `"multipartForm"`, or `"binary"` when the target API fits one of those shapes; reach for `"raw"` only when the API needs a pre-encoded format none of the typed body types cover, such as SOAP/XML or a signed payload:
92
103
 
93
104
  ```typescript
94
105
  await ctx.integrations.legacyApi.apiRequest(
@@ -102,5 +113,3 @@ await ctx.integrations.legacyApi.apiRequest(
102
113
  { response: SoapResponseSchema },
103
114
  );
104
115
  ```
105
-
106
- Raw string bodies are limited to 20 MiB.
@@ -9,7 +9,10 @@ import type { z } from "zod";
9
9
 
10
10
  import type { Plugin as S3Plugin } from "@superblocksteam/types/dist/src/plugins/s3/v1/plugin_pb";
11
11
 
12
- import { RestApiValidationError } from "../../errors.js";
12
+ import {
13
+ RestApiValidationError,
14
+ restApiValidationErrorFromZodError,
15
+ } from "../../errors.js";
13
16
  import { IntegrationError } from "../../runtime/errors.js";
14
17
  import type { QueryExecutor, TraceMetadata } from "../registry.js";
15
18
  import type { IntegrationConfig, IntegrationClientImpl } from "../types.js";
@@ -71,12 +74,10 @@ export class S3ClientImpl implements S3Client, IntegrationClientImpl {
71
74
  const parseResult = schema.safeParse(result);
72
75
 
73
76
  if (!parseResult.success) {
74
- throw new RestApiValidationError(
75
- `Result validation failed: ${parseResult.error.message}`,
76
- {
77
- zodError: parseResult.error,
78
- data: result,
79
- },
77
+ throw restApiValidationErrorFromZodError(
78
+ "Result validation failed",
79
+ parseResult.error,
80
+ result,
80
81
  );
81
82
  }
82
83
 
@@ -7,7 +7,11 @@
7
7
 
8
8
  import type { z } from "zod";
9
9
 
10
- import { QueryValidationError, RestApiValidationError } from "../../errors.js";
10
+ import {
11
+ QueryValidationError,
12
+ RestApiValidationError,
13
+ restApiValidationErrorFromZodError,
14
+ } from "../../errors.js";
11
15
  import { IntegrationError } from "../../runtime/errors.js";
12
16
  import type { QueryExecutor, TraceMetadata } from "../registry.js";
13
17
  import type { IntegrationConfig, IntegrationClientImpl } from "../types.js";
@@ -205,12 +209,10 @@ export class SalesforceClientImpl
205
209
 
206
210
  const parseResult = schema.safeParse(result);
207
211
  if (!parseResult.success) {
208
- throw new RestApiValidationError(
209
- `Result validation failed: ${parseResult.error.message}`,
210
- {
211
- zodError: parseResult.error,
212
- data: result,
213
- },
212
+ throw restApiValidationErrorFromZodError(
213
+ "Result validation failed",
214
+ parseResult.error,
215
+ result,
214
216
  );
215
217
  }
216
218
 
@@ -326,13 +326,10 @@ describe("SlackClientImpl", () => {
326
326
 
327
327
  expect(executeQuery).toHaveBeenCalledWith(
328
328
  expect.objectContaining({
329
- body: "channel=%23alerts&text=hello",
329
+ body: expect.any(String),
330
330
  bodyType: "rawBody",
331
331
  headers: [
332
- {
333
- key: "Content-Type",
334
- value: "application/x-www-form-urlencoded",
335
- },
332
+ { key: "Content-Type", value: "application/x-www-form-urlencoded" },
336
333
  ],
337
334
  }),
338
335
  undefined,
@@ -8,7 +8,7 @@
8
8
 
9
9
  import type { z } from "zod";
10
10
 
11
- import { RestApiValidationError } from "../../errors.js";
11
+ import { restApiValidationErrorFromZodError } from "../../errors.js";
12
12
  import { RestApiClientBase } from "../base/rest-api-client-base.js";
13
13
  import type { ApiRequestOptions } from "../base/types.js";
14
14
  import type { TraceMetadata } from "../registry.js";
@@ -51,24 +51,20 @@ export class SlackClientImpl extends RestApiClientBase implements SlackClient {
51
51
  "ok" in result &&
52
52
  (result as Record<string, unknown>).ok === false
53
53
  ) {
54
- throw new RestApiValidationError(
55
- `Slack returned ok: false but the response did not match the expected error shape: ${errorResult.error.message}`,
56
- {
57
- zodError: errorResult.error,
58
- data: result,
59
- },
54
+ throw restApiValidationErrorFromZodError(
55
+ "Slack returned ok: false but the response did not match the expected error shape",
56
+ errorResult.error,
57
+ result,
60
58
  );
61
59
  }
62
60
 
63
61
  // Success responses must carry the Slack envelope discriminant.
64
62
  const successEnvelopeResult = SlackSuccessEnvelopeSchema.safeParse(result);
65
63
  if (!successEnvelopeResult.success) {
66
- throw new RestApiValidationError(
67
- `Slack success response is missing the expected ok: true envelope: ${successEnvelopeResult.error.message}`,
68
- {
69
- zodError: successEnvelopeResult.error,
70
- data: result,
71
- },
64
+ throw restApiValidationErrorFromZodError(
65
+ "Slack success response is missing the expected ok: true envelope",
66
+ successEnvelopeResult.error,
67
+ result,
72
68
  );
73
69
  }
74
70
 
@@ -81,12 +77,10 @@ export class SlackClientImpl extends RestApiClientBase implements SlackClient {
81
77
 
82
78
  const payloadResult = schema.response.safeParse(payloadCandidate);
83
79
  if (!payloadResult.success) {
84
- throw new RestApiValidationError(
85
- `Response validation failed: ${payloadResult.error.message}`,
86
- {
87
- zodError: payloadResult.error,
88
- data: result,
89
- },
80
+ throw restApiValidationErrorFromZodError(
81
+ "Response validation failed",
82
+ payloadResult.error,
83
+ result,
90
84
  );
91
85
  }
92
86
 
@@ -79,84 +79,6 @@ export default api({
79
79
  });
80
80
  ```
81
81
 
82
- ### Image-to-Image (Transform Existing Image)
83
-
84
- ```typescript
85
- const result = await ctx.integrations.stability.apiRequest(
86
- {
87
- method: "POST",
88
- path: "/v1/generation/stable-diffusion-xl-1024-v1-0/image-to-image",
89
- headers: {
90
- "Content-Type": "multipart/form-data",
91
- },
92
- body: {
93
- init_image: sourceImageBase64,
94
- text_prompts: [{ text: "A vibrant oil painting style", weight: 1 }],
95
- image_strength: 0.35, // How much to modify (0-1)
96
- cfg_scale: 7,
97
- samples: 1,
98
- steps: 30,
99
- },
100
- },
101
- { response: GenerationResponseSchema },
102
- );
103
- ```
104
-
105
- ### Upscale an Image
106
-
107
- ```typescript
108
- const UpscaleResponseSchema = z.object({
109
- artifacts: z.array(
110
- z.object({
111
- base64: z.string(),
112
- finishReason: z.string(),
113
- seed: z.number(),
114
- }),
115
- ),
116
- });
117
-
118
- const result = await ctx.integrations.stability.apiRequest(
119
- {
120
- method: "POST",
121
- path: "/v1/generation/esrgan-v1-x2plus/image-to-image/upscale",
122
- headers: {
123
- "Content-Type": "multipart/form-data",
124
- },
125
- body: {
126
- image: sourceImageBase64,
127
- width: 2048, // Target width (optional)
128
- },
129
- },
130
- { response: UpscaleResponseSchema },
131
- );
132
-
133
- const upscaledImage = result.artifacts[0].base64;
134
- ```
135
-
136
- ### Inpainting (Edit Parts of an Image)
137
-
138
- ```typescript
139
- const result = await ctx.integrations.stability.apiRequest(
140
- {
141
- method: "POST",
142
- path: "/v1/generation/stable-diffusion-xl-1024-v1-0/image-to-image/masking",
143
- headers: {
144
- "Content-Type": "multipart/form-data",
145
- },
146
- body: {
147
- init_image: sourceImageBase64,
148
- mask_image: maskImageBase64, // White areas will be regenerated
149
- mask_source: "MASK_IMAGE_WHITE",
150
- text_prompts: [{ text: "A golden retriever sitting", weight: 1 }],
151
- cfg_scale: 7,
152
- samples: 1,
153
- steps: 30,
154
- },
155
- },
156
- { response: GenerationResponseSchema },
157
- );
158
- ```
159
-
160
82
  ### Generate with Specific Style
161
83
 
162
84
  ```typescript
@@ -263,12 +185,6 @@ The engine/model ID is part of the URL path:
263
185
  // Text-to-image with SDXL 1.0
264
186
  const path = "/v1/generation/stable-diffusion-xl-1024-v1-0/text-to-image";
265
187
 
266
- // Image-to-image
267
- const path = "/v1/generation/stable-diffusion-xl-1024-v1-0/image-to-image";
268
-
269
- // Upscaling
270
- const path = "/v1/generation/esrgan-v1-x2plus/image-to-image/upscale";
271
-
272
188
  // Available engines:
273
189
  // - stable-diffusion-xl-1024-v1-0 (SDXL 1.0)
274
190
  // - stable-diffusion-v1-6
@@ -402,9 +318,6 @@ const imageBase64 = result.artifacts[0].base64;
402
318
 
403
319
  // To save or display, you may need to add data URL prefix
404
320
  const dataUrl = `data:image/png;base64,${imageBase64}`;
405
-
406
- // For input images (image-to-image), provide raw base64 without prefix
407
- const inputImage = rawBase64WithoutPrefix;
408
321
  ```
409
322
 
410
323
  ### Seed for Reproducibility
@@ -1,5 +1,14 @@
1
1
  {
2
2
  "pluginId": "stabilityai",
3
3
  "base": "README.md",
4
- "overlays": []
4
+ "overlays": [
5
+ {
6
+ "file": "overlays/image-uploads.md",
7
+ "sdkVersionRange": ">=0.0.7"
8
+ },
9
+ {
10
+ "file": "overlays/image-uploads-unsupported.md",
11
+ "sdkVersionRange": "<0.0.7"
12
+ }
13
+ ]
5
14
  }
@@ -0,0 +1,3 @@
1
+ ## Image-to-Image, Upscale, and Inpainting
2
+
3
+ The `/image-to-image`, `/image-to-image/upscale`, and `/image-to-image/masking` endpoints require `multipart/form-data`. This agent's sdk-api cannot upload images to them through `apiRequest()` because it serializes every body as JSON. Do not generate image-to-image, upscale, or inpainting code until the agent is upgraded. Text-to-image, account balance, and engine listing work as documented above.
@@ -0,0 +1,120 @@
1
+ ## Image-to-Image, Upscale, and Inpainting
2
+
3
+ The `/image-to-image`, `/image-to-image/upscale`, and `/image-to-image/masking` endpoints expect `multipart/form-data`, so pass `bodyType: "multipartForm"`. Image parts are `{ value, filename }` with raw bytes. Multipart fields are flat, so `text_prompts` uses bracketed keys such as `"text_prompts[0][text]"`. Do not set `Content-Type`. The worker generates the multipart boundary.
4
+
5
+ For an uploaded file, pass `await file.readContentsAsync("raw")` as the part value. For an image returned by a previous generation, decode its `base64` artifact with `Buffer.from(artifact.base64, "base64")`.
6
+
7
+ The engine/model ID is part of the URL path:
8
+
9
+ ```typescript
10
+ // Image-to-image
11
+ const path = "/v1/generation/stable-diffusion-xl-1024-v1-0/image-to-image";
12
+
13
+ // Upscaling
14
+ const path = "/v1/generation/esrgan-v1-x2plus/image-to-image/upscale";
15
+ ```
16
+
17
+ ### Image-to-Image (Transform Existing Image)
18
+
19
+ ```typescript
20
+ const source = input.upload.files[0];
21
+
22
+ const result = await ctx.integrations.stability.apiRequest(
23
+ {
24
+ method: "POST",
25
+ path: "/v1/generation/stable-diffusion-xl-1024-v1-0/image-to-image",
26
+ bodyType: "multipartForm",
27
+ body: {
28
+ init_image: {
29
+ value: await source.readContentsAsync("raw"),
30
+ filename: source.name,
31
+ },
32
+ "text_prompts[0][text]": "A vibrant oil painting style",
33
+ "text_prompts[0][weight]": 1,
34
+ image_strength: 0.35, // How much to modify (0-1)
35
+ cfg_scale: 7,
36
+ samples: 1,
37
+ steps: 30,
38
+ },
39
+ },
40
+ { response: GenerationResponseSchema },
41
+ );
42
+ ```
43
+
44
+ ### Upscale an Image
45
+
46
+ ```typescript
47
+ const UpscaleResponseSchema = z.object({
48
+ artifacts: z.array(
49
+ z.object({
50
+ base64: z.string(),
51
+ finishReason: z.string(),
52
+ seed: z.number(),
53
+ }),
54
+ ),
55
+ });
56
+
57
+ // Response of an earlier text-to-image apiRequest call.
58
+ const generated = await ctx.integrations.stability.apiRequest(
59
+ {
60
+ method: "POST",
61
+ path: "/v1/generation/stable-diffusion-xl-1024-v1-0/text-to-image",
62
+ body: {
63
+ text_prompts: [{ text: "A vibrant oil painting style", weight: 1 }],
64
+ cfg_scale: 7,
65
+ samples: 1,
66
+ steps: 30,
67
+ },
68
+ },
69
+ { response: GenerationResponseSchema },
70
+ );
71
+
72
+ const result = await ctx.integrations.stability.apiRequest(
73
+ {
74
+ method: "POST",
75
+ path: "/v1/generation/esrgan-v1-x2plus/image-to-image/upscale",
76
+ bodyType: "multipartForm",
77
+ body: {
78
+ image: {
79
+ value: Buffer.from(generated.artifacts[0].base64, "base64"),
80
+ filename: "generated.png",
81
+ },
82
+ width: 2048, // Target width (optional)
83
+ },
84
+ },
85
+ { response: UpscaleResponseSchema },
86
+ );
87
+
88
+ const upscaledImage = result.artifacts[0].base64;
89
+ ```
90
+
91
+ ### Inpainting (Edit Parts of an Image)
92
+
93
+ ```typescript
94
+ const [source, mask] = input.upload.files;
95
+
96
+ const result = await ctx.integrations.stability.apiRequest(
97
+ {
98
+ method: "POST",
99
+ path: "/v1/generation/stable-diffusion-xl-1024-v1-0/image-to-image/masking",
100
+ bodyType: "multipartForm",
101
+ body: {
102
+ init_image: {
103
+ value: await source.readContentsAsync("raw"),
104
+ filename: source.name,
105
+ },
106
+ mask_image: {
107
+ value: await mask.readContentsAsync("raw"),
108
+ filename: mask.name,
109
+ },
110
+ mask_source: "MASK_IMAGE_WHITE", // White areas will be regenerated
111
+ "text_prompts[0][text]": "A golden retriever sitting",
112
+ "text_prompts[0][weight]": 1,
113
+ cfg_scale: 7,
114
+ samples: 1,
115
+ steps: 30,
116
+ },
117
+ },
118
+ { response: GenerationResponseSchema },
119
+ );
120
+ ```