@superglue/cli 1.1.20 → 1.1.22

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 (32) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +1 -7
  3. package/dist/cli.js +303 -74
  4. package/dist/skills/superglue/SKILL.md +313 -621
  5. package/dist/skills/superglue/references/file-handling.md +116 -90
  6. package/dist/skills/superglue/references/graphql.md +131 -0
  7. package/dist/skills/superglue/references/http.md +246 -0
  8. package/dist/skills/superglue/references/integration.md +4 -2
  9. package/dist/skills/superglue/references/mssql.md +207 -0
  10. package/dist/skills/superglue/references/postgres.md +154 -0
  11. package/dist/skills/superglue/references/redis.md +68 -118
  12. package/dist/skills/superglue/references/sftp-smb.md +156 -0
  13. package/dist/skills/superglue/references/superglue-info.md +91 -0
  14. package/package.json +1 -1
  15. package/skills/superglue/SKILL.md +313 -621
  16. package/skills/superglue/references/file-handling.md +116 -90
  17. package/skills/superglue/references/graphql.md +131 -0
  18. package/skills/superglue/references/http.md +246 -0
  19. package/skills/superglue/references/integration.md +4 -2
  20. package/skills/superglue/references/mssql.md +207 -0
  21. package/skills/superglue/references/postgres.md +154 -0
  22. package/skills/superglue/references/redis.md +68 -118
  23. package/skills/superglue/references/sftp-smb.md +156 -0
  24. package/skills/superglue/references/superglue-info.md +91 -0
  25. package/dist/skills/superglue/references/data-handling.md +0 -303
  26. package/dist/skills/superglue/references/databases.md +0 -253
  27. package/dist/skills/superglue/references/file-servers.md +0 -112
  28. package/dist/skills/superglue/references/http-apis.md +0 -248
  29. package/skills/superglue/references/data-handling.md +0 -303
  30. package/skills/superglue/references/databases.md +0 -253
  31. package/skills/superglue/references/file-servers.md +0 -112
  32. package/skills/superglue/references/http-apis.md +0 -248
@@ -0,0 +1,156 @@
1
+ # SFTP / FTP / SMB
2
+
3
+ FTP, SFTP, and SMB share an identical operation interface. Only the URL protocol and connection details differ.
4
+
5
+ ## Step Configuration
6
+
7
+ ```typescript
8
+ {
9
+ type: "request",
10
+ systemId: "my_sftp_server",
11
+ url: "sftp://<<my_sftp_server_username>>:<<my_sftp_server_password>>@<<my_sftp_server_host>>:<<my_sftp_server_port>>/basePath",
12
+ body: '{"operation": "get", "path": "data/report.csv"}'
13
+ }
14
+ ```
15
+
16
+ Configure the connection URL based on which credentials are stored in the system — use `sg system find` to check `storedCredentials` and reference them accordingly.
17
+
18
+ `method`, `headers`, `queryParams`, and `pagination` are HTTP-only fields — omit them for file server steps.
19
+
20
+ ### Fields
21
+
22
+ | Field | Required | Notes |
23
+ | ---------- | -------- | -------------------------------------------------------------- |
24
+ | `url` | yes | Connection string with `<<credential>>` placeholders |
25
+ | `body` | yes | JSON: single operation object or array for batch |
26
+ | `systemId` | no | Links system credentials for `<<systemId_credKey>>` resolution |
27
+
28
+ ### URL Formats
29
+
30
+ ```
31
+ ftp://user:password@host:port/basePath (port 21)
32
+ ftps://user:password@host:port/basePath (port 21, TLS)
33
+ sftp://user:password@host:port/basePath (port 22)
34
+ smb://user:password@host/sharename/basePath (port 445)
35
+ smb://domain\user:password@host/sharename/ (domain auth)
36
+ ```
37
+
38
+ SMB requires a share name as the first path segment. Additional segments become a base path prepended to all operation paths.
39
+
40
+ ### Body Format
41
+
42
+ JSON string — single operation or array of operations (batch, executed sequentially on a single connection):
43
+
44
+ ```json
45
+ { "operation": "get", "path": "data/report.csv" }
46
+ ```
47
+
48
+ ```json
49
+ [
50
+ { "operation": "mkdir", "path": "backup" },
51
+ { "operation": "get", "path": "data/report.csv" },
52
+ { "operation": "put", "path": "backup/report.csv", "content": "file::report.raw" }
53
+ ]
54
+ ```
55
+
56
+ ## Authentication
57
+
58
+ Credentials are embedded in the connection URL using `<<systemId_credentialKey>>` placeholders. Nothing is injected automatically.
59
+
60
+ ```
61
+ sftp://<<my_server_username>>:<<my_server_password>>@host/path
62
+ ```
63
+
64
+ **SFTP private key auth:** use `credentials.privateKey` and `credentials.passphrase` on the system instead of URL password placeholders. Store the private key as a credential on the system.
65
+
66
+ **SMB domain auth:** use `domain\user` format in the URL, or store `domain` as a system credential.
67
+
68
+ Only store connection credentials on the system: `host`, `port`, `username`, `password`, `privateKey`, `passphrase`, `domain` (SMB), `home_dir`. Paths and share names belong in the URL, not in credentials.
69
+
70
+ ## File Server Runtime Details
71
+
72
+ ### Path Handling
73
+
74
+ All operation paths are relative to the base path in the URL. Do not use absolute filesystem paths.
75
+
76
+ If the system URL is `sftp://user:pass@host/home/sftptest/uploads`:
77
+
78
+ - `report.csv` resolves to `/home/sftptest/uploads/report.csv`
79
+ - `subdir/data.csv` resolves to `/home/sftptest/uploads/subdir/data.csv`
80
+
81
+ Do not use `/home/sftptest/uploads/report.csv` — the base path is already set in the URL.
82
+
83
+ If the URL has no base path (e.g. `sftp://user:pass@host`), paths are used as-is from the server root.
84
+
85
+ SMB paths use forward slashes — the library handles Windows path conversion internally.
86
+
87
+ ### Operations
88
+
89
+ | Operation | Required fields | Returns |
90
+ | --------- | ----------------- | --------------------------------------------------------------- |
91
+ | `list` | `path` | Array of `{ name, path, size, type, modifyTime, ... }` |
92
+ | `get` | `path` | Auto-parsed file content (CSV to objects, JSON to parsed, etc.) |
93
+ | `put` | `path`, `content` | `{ success, message, size }` |
94
+ | `delete` | `path` | `{ success, message }` |
95
+ | `rename` | `path`, `newPath` | `{ success, message }` |
96
+ | `mkdir` | `path` | `{ success, message }` |
97
+ | `rmdir` | `path` | `{ success, message }` |
98
+ | `exists` | `path` | `{ exists: boolean, path }` |
99
+ | `stat` | `path` | File metadata or `{ exists: false }` |
100
+
101
+ ### `get` — File Handling
102
+
103
+ Downloaded files are added to the runtime file store. See `file-handling.md` for detection, parsing, aliasing rules, and the full `file::` reference syntax.
104
+
105
+ - `data` contains auto-parsed content (CSV to objects, JSON to parsed, PDF to structured, etc.)
106
+ - The step result exposes `stepFileKeys`
107
+ - Later steps reference downloaded files via `file::<stepId>.raw`, `file::<stepId>.base64`, or `file::<stepId>.extracted`
108
+ - Multi-file batch downloads use bracket notation: `file::<stepId>["report.csv"].raw`
109
+
110
+ ### `put` — Content Handling
111
+
112
+ Use `file::<key>.raw` to preserve original bytes when uploading files from previous steps. See `file-handling.md` for all suffix options.
113
+
114
+ | Content type | Behavior |
115
+ | -------------------------------- | ------------------------------------ |
116
+ | `file::<key>.raw` (RawFileBytes) | Written as exact original bytes |
117
+ | String | Written as-is |
118
+ | Buffer | Written directly |
119
+ | Object / Array | JSON-stringified with 2-space indent |
120
+
121
+ ### Return Values
122
+
123
+ Single operation returns the result directly. Array of operations returns an array of results (one per operation, in order).
124
+
125
+ ### Connection Details
126
+
127
+ **SFTP:** uses `ssh2-sftp-client`. Supports password and private key auth. Connection timeout configurable, default retries from server config.
128
+
129
+ **FTP/FTPS:** uses `basic-ftp`. FTPS uses TLS with `rejectUnauthorized: false`. Connection timeout configurable.
130
+
131
+ **SMB:** uses `@tryjsky/v9u-smb2`. Connection lifecycle: create client (with share, domain, credentials), execute operations, disconnect. Entire operation wrapped in a timeout race. For private systems via Secure Gateway, the host is `tunnelId.tunnel` (e.g. `my_tunnel.tunnel`).
132
+
133
+ ### Retry Behavior
134
+
135
+ Default retries from server config. On final failure, error includes the full operations list. Each retry creates a fresh connection.
136
+
137
+ ## Common Pitfalls
138
+
139
+ - Using absolute filesystem paths instead of paths relative to the URL base path
140
+ - Forgetting that SMB requires a share name as the first URL path segment
141
+ - Using `file::<key>` without a suffix in `put` content. Use `file::<key>.raw` to preserve original bytes
142
+ - Missing the double-data access pattern for `get` results: `sourceData.stepId.data` gives you the parsed file content (through the step envelope)
143
+ - Trying to `put` to a path where intermediate directories don't exist — use `mkdir` first or batch the operations
144
+
145
+ ## Error Recovery
146
+
147
+ When a file server step fails and the cause is not obvious:
148
+
149
+ 1. **Read the error message** — superglue includes the operations list in the error. Check for typos in paths, missing fields, or unsupported operations.
150
+ 2. **Verify connection credentials** — use `sg system find` to confirm `storedCredentials` keys match the placeholders in your URL. Test with a simple `list` operation on the base path via `sg system call`.
151
+ 3. **Check path resolution** — if you get "file not found", verify the path is relative to the URL base path, not an absolute filesystem path. Use `list` on the parent directory to see what files exist.
152
+ 4. **Check permissions** — permission denied errors mean the user account lacks read/write access to the target path. Verify with the user that the account has the required grants.
153
+ 5. **Check protocol-specific issues:**
154
+ - **SFTP**: if the error mentions authentication, verify whether the server expects password or private key auth. If using private key, ensure `privateKey` and `passphrase` are stored as system credentials.
155
+ - **FTP/FTPS**: if the connection fails, try switching between `ftp://` and `ftps://` — some servers require TLS, others don't support it.
156
+ - **SMB**: verify the share name is correct (first path segment). If using domain auth, ensure the domain is included in the URL or stored as a credential. Check that the SMB port (445) is accessible.
@@ -0,0 +1,91 @@
1
+ # superglue Info
2
+
3
+ ## Company
4
+
5
+ superglue is an open-source, AI-native integration platform that builds and runs deterministic multi-step workflows ("tools") connecting APIs, databases, and file servers. AI generates tool configurations during building — execution is 100% deterministic JavaScript with no LLMs involved.
6
+
7
+ Developed by superglue (Y Combinator W25), founded by Adina Görres and Stefan Faistenauer in 2025, based in Munich and San Francisco.
8
+
9
+ - Website: https://superglue.ai
10
+ - Documentation: https://docs.superglue.cloud
11
+ - GitHub: https://github.com/superglue-ai/superglue
12
+
13
+ If the user is asking general questions (not building):
14
+
15
+ - Company/team/pricing → https://superglue.ai/
16
+ - Product/features → https://docs.superglue.cloud/getting-started/introduction
17
+ - Open-source/code → https://github.com/superglue-ai/superglue
18
+
19
+ ## Authentication
20
+
21
+ All superglue interfaces (API, SDK, CLI, MCP) require only an **API key**. Self-hosted deployments also need the API endpoint URL. There are no workspace IDs, project IDs, or other identifiers — the API key is the only credential needed.
22
+
23
+ Users can generate and manage API keys in **Control Panel → API Keys**. The API endpoint is displayed on the same page.
24
+
25
+ ## Interfaces
26
+
27
+ - **Web app** — primary UI for building, testing, and managing tools and systems
28
+ - **TypeScript/Python SDK** — https://docs.superglue.cloud/sdk/overview
29
+ - **REST API** — https://docs.superglue.cloud/api-reference/
30
+ - **MCP Server** — https://docs.superglue.cloud/mcp/using-the-mcp
31
+ - **CLI** — `npm install -g @superglue/cli` — https://docs.superglue.cloud/getting-started/cli-skills
32
+
33
+ ### Webhook Triggers
34
+
35
+ Tools can be triggered via webhook: `POST {apiEndpoint}/v1/hooks/{toolId}?token={apiKey}`
36
+
37
+ On superglue Cloud, the API endpoint is `https://api.superglue.cloud`. Self-hosted and enterprise deployments use their own API endpoint — users can find theirs under Control Panel → API Keys.
38
+
39
+ ### OAuth Callback URL
40
+
41
+ OAuth flows require a callback URL: `{appEndpoint}/api/auth/callback`
42
+
43
+ On superglue Cloud, the app endpoint is `https://app.superglue.cloud`. Self-hosted and enterprise deployments use their own app URL. If a user doesn't know their endpoints, they can check the browser URL bar for the app endpoint, and Control Panel → API Keys for the API endpoint.
44
+
45
+ ## Web App UI Layout
46
+
47
+ Left sidebar navigation:
48
+
49
+ - **Agent** — AI chat assistant for building and debugging tools/systems
50
+ - **Landscape** — Visual graph showing all tools and their connected systems
51
+ - **Tools** — List of saved tools. Click a tool to open its playground for editing, testing, and running.
52
+ - **Systems** — List of connected external systems with credentials and documentation
53
+ - **Control Panel** (expandable):
54
+ - Overview: Dashboard summary
55
+ - Runs: Execution history for all tool runs with status, errors, and step results
56
+ - Schedules: Manage scheduled/recurring tool runs
57
+ - API Keys: View and manage superglue API keys
58
+ - Organization: Team members and org settings
59
+ - Notifications: Configure alerts for tool run failures
60
+ - **Docs** — Link to docs.superglue.cloud
61
+
62
+ ## Internals
63
+
64
+ ### Execution Pipeline
65
+
66
+ `ToolExecutor.execute({ payload, credentials, options })`:
67
+
68
+ 1. **Validate** tool structure (id, steps array, URLs on request steps)
69
+ 2. **For each step in order:**
70
+ a. Build aggregated data: `{ ...originalPayload, ...previousStepResults }`
71
+ b. Resolve system credentials (refresh OAuth if needed), namespace as `systemId_key`
72
+ c. Run `dataSelector` → object means single execution, array means loop
73
+ d. For each item: merge `currentItem` into input and execute
74
+ e. Wrap result: `{ currentItem, data, success }`
75
+ f. On failure: abort if `failureBehavior !== "continue"`
76
+ 3. **Output transform** (if present): run JS function, validate against outputSchema
77
+ 4. **Response filters** (if present): remove/mask/fail on pattern matches
78
+
79
+ ### Strategy Routing
80
+
81
+ Steps are routed to execution strategies by protocol (first match wins):
82
+
83
+ 1. Transform steps → if `config.type === "transform"`
84
+ 2. HTTP → URL starts with `http://` or `https://`
85
+ 3. PostgreSQL → URL starts with `postgres://` or `postgresql://`
86
+ 4. MSSQL/Azure SQL → URL starts with `mssql://` or `sqlserver://`
87
+ 5. Redis → URL starts with `redis://` or `rediss://`
88
+ 6. FTP/SFTP → URL starts with `ftp://`, `ftps://`, or `sftp://`
89
+ 7. SMB → URL starts with `smb://`
90
+
91
+ All user-provided JS (data selectors, transforms, stop conditions) runs in an isolated Deno sandbox.
@@ -1,303 +0,0 @@
1
- # Data Handling: Variables, Selectors & Transforms
2
-
3
- Most tool building and editing failures trace back to incorrect variable syntax, wrong data selector returns, or misunderstanding of the tool result envelope.
4
-
5
- ## Variable Replacement (`<<>>` Syntax)
6
-
7
- The `<<expression>>` syntax injects dynamic values into step config fields (URL, headers, body, queryParams).
8
-
9
- ### Simple Variables (top-level keys only)
10
-
11
- ```
12
- <<userId>> payload keys
13
- <<currentItem>> whole current loop item
14
- <<page>> pagination variable
15
- <<systemId_api_key>> system credential
16
- <<systemId_url>> system base URL
17
- <<sg_auth_email>> email of the authenticated user
18
- ```
19
-
20
- **CRITICAL**: Simple `<<varName>>` only works for top-level keys. NO dots, NO nesting.
21
-
22
- - VALID: `<<userId>>`, `<<currentItem>>`, `<<page>>`, `<<stripe_api_key>>`, `<<stripe_url>>`, `<<sg_auth_email>>`
23
- - INVALID: `<<currentItem.id>>`, `<<sourceData.userId>>`, `<<user.name>>` — these FAIL at runtime
24
-
25
- ### Arrow Function Expressions (for everything else)
26
-
27
- ```
28
- <<(sourceData) => sourceData.currentItem.player_id>>
29
- <<(sourceData) => sourceData.getUsers.data.map(u => u.id)>>
30
- <<(sourceData) => JSON.stringify({ ids: sourceData.fetchUsers.data.map(u => u.id) })>>
31
- ```
32
-
33
- ### Resolution Order
34
-
35
- 1. Direct lookup: check if expression is a top-level key in the merged variable object
36
- 2. Arrow function: execute in Deno subprocess sandbox with full data as `sourceData`
37
- 3. Failure: throws error listing available keys
38
-
39
- ### Variable Sources
40
-
41
- All variables come from a single merged object:
42
-
43
- ```javascript
44
- {
45
- ...originalPayload, // user-provided input
46
- ...previousStepResults, // keyed by stepId
47
- ...systemCredentials, // namespaced: systemId_credKey
48
- ...systemUrls, // namespaced: systemId_url
49
- sg_auth_email, // email of authenticated user (context variable)
50
- currentItem, // current loop item (if in a loop)
51
- page, offset, cursor, // pagination variables
52
- limit, pageSize // pagination size
53
- }
54
- ```
55
-
56
- ## Context Variables
57
-
58
- Context variables provide information about the execution environment:
59
-
60
- - `<<sg_auth_email>>` — Email address of the authenticated user who triggered the tool execution
61
-
62
- **Use case**: Personalize tool outputs based on who is running the tool. For example:
63
-
64
- - Different permissions in Tableau based on user
65
- - User-specific account lookups
66
- - Audit logging with user identity
67
-
68
- **Important**: `sg_auth_email` is NOT available in scheduled executions (cron jobs). If a tool references `<<sg_auth_email>>` and runs via scheduler, it will fail with a clear error message.
69
-
70
- ### Return Type Handling
71
-
72
- - Objects/arrays returned from expressions → JSON.stringified automatically
73
- - Resolved values of `"undefined"` or `"null"` → stripped from headers and queryParams
74
-
75
- ### Special: Basic Auth
76
-
77
- `"Authorization": "Basic <<user>>:<<pass>>"` is auto-converted to Base64. Do NOT manually encode.
78
-
79
- ## Credential Resolution
80
-
81
- Credentials are available via `<<>>` syntax. Two sources, payload takes precedence:
82
-
83
- 1. **System credentials**: Stored on System, namespaced as `<<systemId_credKey>>`
84
-
85
- ```javascript
86
- // System id="stripe", credentials={ api_key: "sk_..." }
87
- // Available as: <<stripe_api_key>>
88
- ```
89
-
90
- 2. **Payload credentials**: Passed at runtime, accessed directly
91
- ```javascript
92
- // Payload: { user_access_token: "abc" }
93
- // Available as: <<user_access_token>>
94
- // Or: <<(sourceData) => sourceData.user_access_token>>
95
- ```
96
-
97
- For OAuth systems, use `<<systemId_access_token>>` — the token value is auto-refreshed during tool execution, but the header itself must be present in the step config.
98
-
99
- ## System URL Resolution
100
-
101
- System URLs are available via `<<systemId_url>>` syntax. This enables environment-agnostic tools:
102
-
103
- ```javascript
104
- // System id="salesforce", url="https://mycompany.salesforce.com"
105
- // Available as: <<salesforce_url>>
106
-
107
- // Step config:
108
- {
109
- "url": "<<salesforce_url>>/services/data/v58.0/sobjects/Account",
110
- "headers": { "Authorization": "Bearer <<salesforce_access_token>>" }
111
- }
112
- ```
113
-
114
- **Why use system URL variables?**
115
-
116
- - Same tool works across dev/prod environments without modification
117
- - When switching execution mode, the system URL resolves to the appropriate environment
118
- - Avoids hardcoding base URLs that differ between environments
119
-
120
- ## Data Selectors
121
-
122
- Every step has a `dataSelector` — a JS function that controls execution mode and what `currentItem` is.
123
-
124
- ### Returns OBJECT → Single Execution
125
-
126
- Step runs once. The object becomes `currentItem`.
127
-
128
- ```javascript
129
- (sourceData) => ({ userId: sourceData.userId })
130
- (sourceData) => ({}) // run once with no specific input
131
- ```
132
-
133
- Result stored as:
134
-
135
- ```javascript
136
- sourceData.stepId = { currentItem: <object>, data: <response>, success: true }
137
- ```
138
-
139
- ### Returns ARRAY → Loop Execution
140
-
141
- Step runs once per array element. Each element becomes `currentItem`.
142
-
143
- ```javascript
144
- (sourceData) => sourceData.getContacts.data.filter(c => c.active)
145
- (sourceData) => sourceData.userIds // if userIds is an array from payload
146
- ```
147
-
148
- Result stored as:
149
-
150
- ```javascript
151
- sourceData.stepId = [
152
- { currentItem: <item1>, data: <response1>, success: true },
153
- { currentItem: <item2>, data: <response2>, success: true },
154
- ...
155
- ]
156
- ```
157
-
158
- Empty arrays are valid — step just skips execution.
159
-
160
- ### Accessing currentItem in Step Config
161
-
162
- ```
163
- <<currentItem>> // whole value
164
- <<(sourceData) => sourceData.currentItem.id>> // property
165
- <<(sourceData) => sourceData.currentItem.name.toUpperCase()>> // with transform
166
- ```
167
-
168
- ## Result Envelope
169
-
170
- **Every** step result (request or transform) is wrapped identically:
171
-
172
- ### From Object Selector
173
-
174
- ```javascript
175
- sourceData.stepId = {
176
- currentItem: <selectorOutput>,
177
- data: <apiResponse or transformResult>,
178
- success: true
179
- }
180
-
181
- // Access:
182
- sourceData.stepId.data // the response
183
- sourceData.stepId.currentItem // what was passed in
184
- ```
185
-
186
- ### From Array Selector
187
-
188
- ```javascript
189
- sourceData.stepId = [
190
- { currentItem: <item1>, data: <response1>, success: true },
191
- { currentItem: <item2>, data: <response2>, success: false, error: "..." },
192
- ...
193
- ]
194
-
195
- // Access:
196
- sourceData.stepId.map(item => item.data) // all responses
197
- sourceData.stepId.flatMap(item => item.data.results) // nested arrays
198
- sourceData.stepId.filter(item => item.success).map(i => i.data) // only successes
199
- ```
200
-
201
- ### From Paginated Step
202
-
203
- Paginated steps merge all pages server-side into a **single** result. The result is always a single envelope, NOT an array — even though multiple pages were fetched.
204
-
205
- ```javascript
206
- sourceData.stepId = {
207
- data: <merged response from all pages>,
208
- success: true
209
- }
210
-
211
- // Access:
212
- sourceData.stepId.data // the merged response
213
- sourceData.stepId.data.items // if the API wraps results in .items
214
-
215
- // WRONG — do NOT treat paginated results as an array of envelopes:
216
- sourceData.stepId.map(...) // FAILS — stepId is an object, not an array
217
- ```
218
-
219
- ## sourceData Structure
220
-
221
- `sourceData` is the cumulative state available to data selectors, variable expressions, and transforms:
222
-
223
- ```javascript
224
- sourceData = {
225
- // Original payload fields at ROOT level (NOT under .payload)
226
- userId: "abc",
227
- date: "2024-01-15",
228
- companies: ["acme", "globex"],
229
-
230
- // Previous step results, keyed by step ID
231
- getUsers: { currentItem: {}, data: { users: [...] }, success: true },
232
- fetchDetails: [
233
- { currentItem: { id: 1 }, data: { name: "Alice" }, success: true },
234
- { currentItem: { id: 2 }, data: { name: "Bob" }, success: true },
235
- ],
236
-
237
- // Current item (only within a loop step's config)
238
- currentItem: { id: 1 },
239
-
240
- // Runtime file map — see file-handling skill for full reference
241
- __files__: { ... }
242
- }
243
- ```
244
-
245
- Treat `sourceData.__files__` as read-only. See the file-handling skill for file detection, the `RuntimeExecutionFile` shape, aliasing rules, and how to access files in transforms.
246
-
247
- ## Three Transformation Points
248
-
249
- | Point | When it runs | Purpose |
250
- | -------------------------------- | ------------------------ | ------------------------------------ |
251
- | `dataSelector` | Before step executes | Controls input + single vs loop mode |
252
- | Transform step (`transformCode`) | As the step itself | Reshape data between request steps |
253
- | `outputTransform` | After all steps complete | Shape final tool output |
254
-
255
- ## Transform Steps
256
-
257
- Transform steps use `type: "transform"` with a `transformCode` function. They do NOT have: systemId, url, method, headers, body, queryParams, or pagination.
258
-
259
- Results are wrapped in the **same envelope** as request steps:
260
-
261
- ```javascript
262
- sourceData.formatForInsert = { currentItem: <dataSelector output>, data: <transformCode result>, success: true }
263
- ```
264
-
265
- If the step has a dataSelector returning an array, the transform runs once per item. See tool-building guidelines for when to use transform steps vs dataSelectors vs outputTransform.
266
-
267
- ### Returning Files From a Transform
268
-
269
- Transform steps can produce files by returning a reserved `__files__` key in the result. See the file-handling skill for the full shape, aliasing rules, and access patterns.
270
-
271
- ## Output Transform
272
-
273
- Final transformation shaping the tool's output. Runs after all steps complete. Can access cumulative step result data and payloads.
274
-
275
- ### Requirements
276
-
277
- - Function signature: `(sourceData) => { ... }`
278
- - **Must have a return statement**
279
- - Async/await IS supported (same Deno sandbox as transform steps)
280
- - NEVER include newlines or tabs in the code string
281
-
282
- ## JS Code Constraints (all transform points)
283
-
284
- Runs in a Deno subprocess sandbox:
285
-
286
- - 8192 MB memory limit, 10 min timeout (transforms/selectors), 3s timeout (pagination stops)
287
- - Async/await IS supported in transforms
288
- - No filesystem access — `--deny-read`, `--deny-write`
289
- - No subprocess spawning — `--deny-run`
290
- - Network access IS available (`--allow-net`) — transforms can make HTTP requests
291
- - Environment variables are sandboxed — only safe vars (PATH, HOME, DENO_DIR) are visible, no server secrets
292
- - JSON-serializable I/O only
293
- - Must return a value (undefined returns null)
294
-
295
- ### Serialization Safety
296
-
297
- All JS code is stored as a JSON string, then MessagePack-serialized to the Deno subprocess, then `eval()`'d. Patterns that break across this pipeline:
298
-
299
- - **Regex literals with `/`** — `/https?:\/\/([^/]+)/` corrupts. Use `new URL(str).hostname` or `str.split("//")[1].split("/")[0]` instead.
300
- - **Backslash-heavy patterns** — `\n`, `\t`, `\\` in regex/strings get misinterpreted. Prefer string methods (`.split()`, `.indexOf()`, `.includes()`) over regex.
301
- - **Literal newlines** — code string must be single-line. Use `;` between statements.
302
-
303
- **Rule of thumb**: prefer string methods over regex. When regex is unavoidable, use `new RegExp("pattern")` constructor over `/pattern/` literals.