@superglue/cli 1.1.11 → 1.1.12
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.
- package/.claude-plugin/plugin.json +9 -2
- package/dist/cli.js +131 -78
- package/dist/dist/cli.js +24047 -0
- package/dist/dist/skills/superglue/SKILL.md +667 -0
- package/dist/dist/skills/superglue/agents/openai.yaml +7 -0
- package/dist/dist/skills/superglue/assets/superglue.png +0 -0
- package/dist/dist/skills/superglue/references/databases.md +88 -0
- package/dist/dist/skills/superglue/references/file-servers.md +136 -0
- package/dist/dist/skills/superglue/references/integration.md +279 -0
- package/dist/dist/skills/superglue/references/redis.md +190 -0
- package/dist/dist/skills/superglue/references/transforms-and-output.md +92 -0
- package/dist/skills/superglue/SKILL.md +2 -2
- package/package.json +1 -2
- package/skills/superglue/SKILL.md +2 -2
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Database Steps (PostgreSQL)
|
|
2
|
+
|
|
3
|
+
## Step Configuration
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
{
|
|
7
|
+
type: "request",
|
|
8
|
+
systemId: "my_postgres_db",
|
|
9
|
+
url: "postgres://<<my_postgres_db_user>>:<<my_postgres_db_password>>@<<my_postgres_db_host>>:<<my_postgres_db_port>>/<<my_postgres_db_database>>",
|
|
10
|
+
method: "POST", // ignored for Postgres but required by schema
|
|
11
|
+
body: '{"query": "SELECT * FROM users WHERE id = $1", "params": [<<userId>>]}'
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Configure the connection url depending on which credentials are saved in the stored db system - reference them accordingly.
|
|
16
|
+
|
|
17
|
+
## URL Format
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
postgres://user:password@host:port/database
|
|
21
|
+
postgresql://user:password@host:port/database
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
All credential variables are resolved before connecting. Trailing slashes and extra slashes before query strings are cleaned.
|
|
25
|
+
|
|
26
|
+
## Body Format
|
|
27
|
+
|
|
28
|
+
JSON string with `query` and optional `params` (or `values`):
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{ "query": "SELECT * FROM users WHERE age > $1 AND status = $2", "params": [25, "active"] }
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Parameterized Queries
|
|
35
|
+
|
|
36
|
+
**Always use parameterized queries** with `$1`, `$2`, etc. — prevents SQL injection.
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{ "query": "SELECT * FROM orders WHERE customer_id = $1", "params": ["<<customerId>>"] }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
With expressions:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"query": "INSERT INTO logs (message, level) VALUES ($1, $2)",
|
|
47
|
+
"params": ["<<(sourceData) => sourceData.message>>", "error"]
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
With RETURNING:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"query": "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *",
|
|
56
|
+
"params": [
|
|
57
|
+
"<<(sourceData) => sourceData.currentItem.name>>",
|
|
58
|
+
"<<(sourceData) => sourceData.currentItem.email>>"
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Connection Pooling
|
|
64
|
+
|
|
65
|
+
- Pools cached by connection string, max 10 clients, 5s connection timeout
|
|
66
|
+
- Idle pools cleaned up automatically
|
|
67
|
+
- SSL auto-configured: enabled (with `rejectUnauthorized: false`) unless connecting to localhost without explicit sslmode
|
|
68
|
+
|
|
69
|
+
## Retry Logic
|
|
70
|
+
|
|
71
|
+
Default retries from server config. On final failure, error includes query text and params.
|
|
72
|
+
|
|
73
|
+
## Return Value
|
|
74
|
+
|
|
75
|
+
Returns `result.rows` — array of row objects with column names as keys.
|
|
76
|
+
|
|
77
|
+
- `SELECT`: array of matching rows
|
|
78
|
+
- `INSERT/UPDATE/DELETE` with `RETURNING`: array of returned rows
|
|
79
|
+
- Mutations without `RETURNING`: empty array
|
|
80
|
+
|
|
81
|
+
## Batch Operations via Loop
|
|
82
|
+
|
|
83
|
+
Use a data selector returning an array to execute a query per item:
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
// dataSelector: (sourceData) => sourceData.newUsers
|
|
87
|
+
// body: {"query": "INSERT INTO users (name) VALUES ($1)", "params": ["<<(sourceData) => sourceData.currentItem.name>>"]}
|
|
88
|
+
```
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# File Server Steps (FTP / SFTP / SMB)
|
|
2
|
+
|
|
3
|
+
FTP, SFTP, and SMB share an identical operation interface. Only the URL protocol and connection details differ.
|
|
4
|
+
|
|
5
|
+
## URL Formats
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
ftp://user:password@host:port/basePath (port 21)
|
|
9
|
+
ftps://user:password@host:port/basePath (port 21, TLS)
|
|
10
|
+
sftp://user:password@host:port/basePath (port 22)
|
|
11
|
+
smb://user:password@host/sharename/basePath (port 445)
|
|
12
|
+
smb://domain\user:password@host/sharename/ (domain auth)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
All support `<<variable>>` syntax in the URL.
|
|
16
|
+
|
|
17
|
+
SMB requires a share name as the first path segment. Additional segments become a base path prepended to all operation paths.
|
|
18
|
+
|
|
19
|
+
## Path Handling
|
|
20
|
+
|
|
21
|
+
**All operation paths are relative to the base path in the URL.** Do NOT use absolute filesystem paths.
|
|
22
|
+
|
|
23
|
+
If the system URL is `sftp://user:pass@host/home/sftptest/uploads`:
|
|
24
|
+
|
|
25
|
+
- `report.csv` → `/home/sftptest/uploads/report.csv`
|
|
26
|
+
- `subdir/data.csv` → `/home/sftptest/uploads/subdir/data.csv`
|
|
27
|
+
|
|
28
|
+
Do NOT use `/home/sftptest/uploads/report.csv` — the base path is already set in the URL.
|
|
29
|
+
|
|
30
|
+
If the URL has no base path (e.g., `sftp://user:pass@host`), operation paths are used as-is from the server root.
|
|
31
|
+
|
|
32
|
+
## Body Format
|
|
33
|
+
|
|
34
|
+
JSON string — single operation or array of operations (batch):
|
|
35
|
+
|
|
36
|
+
### Single
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{ "operation": "get", "path": "data/report.csv" }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Batch (sequential, same connection)
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
[
|
|
46
|
+
{ "operation": "mkdir", "path": "backup" },
|
|
47
|
+
{ "operation": "get", "path": "data/report.csv" },
|
|
48
|
+
{ "operation": "put", "path": "backup/report.csv", "content": "data here" }
|
|
49
|
+
]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Supported Operations
|
|
53
|
+
|
|
54
|
+
| Operation | Required fields | Returns |
|
|
55
|
+
| --------- | ----------------- | ----------------------------------------------------------------------- |
|
|
56
|
+
| `list` | `path` | Array of `{ name, path, size, type, modifyTime, ... }` |
|
|
57
|
+
| `get` | `path` | Auto-parsed file content (CSV→objects, JSON→parsed, etc.) or raw string |
|
|
58
|
+
| `put` | `path`, `content` | `{ success, message, size }` |
|
|
59
|
+
| `delete` | `path` | `{ success, message }` |
|
|
60
|
+
| `rename` | `path`, `newPath` | `{ success, message }` |
|
|
61
|
+
| `mkdir` | `path` | `{ success, message }` |
|
|
62
|
+
| `rmdir` | `path` | `{ success, message }` |
|
|
63
|
+
| `exists` | `path` | `{ exists: boolean, path }` |
|
|
64
|
+
| `stat` | `path` | File metadata or `{ exists: false }` |
|
|
65
|
+
|
|
66
|
+
### `get` — Auto-Parsing
|
|
67
|
+
|
|
68
|
+
Downloaded files are all parsed automatically:
|
|
69
|
+
|
|
70
|
+
- CSV, JSON, XML, Excel, PDF, etc. → parsed to JS objects
|
|
71
|
+
- Falls back to UTF-8 string
|
|
72
|
+
- SMB additionally detects binary files (returns `{ _binary: true, encoding: "base64", data: "..." }`)
|
|
73
|
+
|
|
74
|
+
### `put` — Content Handling
|
|
75
|
+
|
|
76
|
+
- String → written as-is
|
|
77
|
+
- Buffer → written directly
|
|
78
|
+
- Object/Array → `JSON.stringify` with 2-space indent
|
|
79
|
+
|
|
80
|
+
## Connection Details
|
|
81
|
+
|
|
82
|
+
### SFTP
|
|
83
|
+
|
|
84
|
+
Uses `ssh2-sftp-client`. Supports password + private key auth (`credentials.privateKey`, `credentials.passphrase`).
|
|
85
|
+
|
|
86
|
+
### FTP/FTPS
|
|
87
|
+
|
|
88
|
+
Uses `basic-ftp`. FTPS uses TLS with `rejectUnauthorized: false`.
|
|
89
|
+
|
|
90
|
+
### SMB
|
|
91
|
+
|
|
92
|
+
Uses `@awo00/smb2`. Connection lifecycle: create client → authenticate (domain optional) → connect tree (share) → execute → disconnect/logoff/close. Entire operation wrapped in timeout race.
|
|
93
|
+
|
|
94
|
+
All paths use forward slashes (`/`) — SMB library handles Windows conversion internally.
|
|
95
|
+
|
|
96
|
+
Credentials must be injected into the URL using placeholders:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
smb://<<systemId_username>>:<<systemId_password>>@fileserver.example.com/ShareName
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
For private systems via Secure Gateway, the host is `tunnelId.tunnel` (e.g., `my_tunnel.tunnel`).
|
|
103
|
+
|
|
104
|
+
## Return Value
|
|
105
|
+
|
|
106
|
+
Single operation → result directly. Multiple operations → array of results.
|
|
107
|
+
|
|
108
|
+
## Common Patterns
|
|
109
|
+
|
|
110
|
+
### Download and process
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{ "operation": "get", "path": "incoming/<<(sourceData) => sourceData.currentItem.filename>>" }
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Upload results
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"operation": "put",
|
|
121
|
+
"path": "output/results.json",
|
|
122
|
+
"content": "<<(sourceData) => JSON.stringify(sourceData.processData.data)>>"
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### List then loop
|
|
127
|
+
|
|
128
|
+
Step 1 lists files, step 2 uses data selector to loop over them:
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
// Step 1: list files
|
|
132
|
+
// body: {"operation": "list", "path": "incoming"}
|
|
133
|
+
|
|
134
|
+
// Step 2 dataSelector: (sourceData) => sourceData.listFiles.data.filter(f => f.type === "file")
|
|
135
|
+
// body: {"operation": "get", "path": "<<(sourceData) => sourceData.currentItem.path>>"}
|
|
136
|
+
```
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# Integrating superglue into Your Codebase
|
|
2
|
+
|
|
3
|
+
After building and saving tools with the CLI, integrate them into your application.
|
|
4
|
+
|
|
5
|
+
## Quick Reference: REST API (curl)
|
|
6
|
+
|
|
7
|
+
For any language, use the REST API directly:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Set your API key and endpoint
|
|
11
|
+
export SUPERGLUE_API_KEY="your-api-key"
|
|
12
|
+
export SUPERGLUE_API_ENDPOINT="https://api.superglue.cloud"
|
|
13
|
+
|
|
14
|
+
# Run a tool (sync - waits for completion)
|
|
15
|
+
curl -X POST "$SUPERGLUE_API_ENDPOINT/v1/tools/my-tool-id/run" \
|
|
16
|
+
-H "Authorization: Bearer $SUPERGLUE_API_KEY" \
|
|
17
|
+
-H "Content-Type: application/json" \
|
|
18
|
+
-d '{"inputs": {"userId": "123"}}'
|
|
19
|
+
|
|
20
|
+
# Run a tool (async - returns immediately)
|
|
21
|
+
curl -X POST "$SUPERGLUE_API_ENDPOINT/v1/tools/my-tool-id/run" \
|
|
22
|
+
-H "Authorization: Bearer $SUPERGLUE_API_KEY" \
|
|
23
|
+
-H "Content-Type: application/json" \
|
|
24
|
+
-d '{"inputs": {"userId": "123"}, "options": {"async": true}}'
|
|
25
|
+
|
|
26
|
+
# Get run status/result
|
|
27
|
+
curl "$SUPERGLUE_API_ENDPOINT/v1/runs/{runId}" \
|
|
28
|
+
-H "Authorization: Bearer $SUPERGLUE_API_KEY"
|
|
29
|
+
|
|
30
|
+
# List tools
|
|
31
|
+
curl "$SUPERGLUE_API_ENDPOINT/v1/tools?page=1&limit=50" \
|
|
32
|
+
-H "Authorization: Bearer $SUPERGLUE_API_KEY"
|
|
33
|
+
|
|
34
|
+
# Get tool details
|
|
35
|
+
curl "$SUPERGLUE_API_ENDPOINT/v1/tools/my-tool-id" \
|
|
36
|
+
-H "Authorization: Bearer $SUPERGLUE_API_KEY"
|
|
37
|
+
|
|
38
|
+
# Cancel a running tool
|
|
39
|
+
curl -X POST "$SUPERGLUE_API_ENDPOINT/v1/runs/{runId}/cancel" \
|
|
40
|
+
-H "Authorization: Bearer $SUPERGLUE_API_KEY"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Response Format
|
|
44
|
+
|
|
45
|
+
**Sync execution (200 OK):**
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"runId": "7f3e9c1a-2b4d-4e8f-9a3b-1c5d7e9f2a4b",
|
|
50
|
+
"toolId": "my-tool-id",
|
|
51
|
+
"status": "success",
|
|
52
|
+
"data": { "result": "..." },
|
|
53
|
+
"stepResults": [{ "stepId": "step-1", "success": true, "data": {} }],
|
|
54
|
+
"metadata": {
|
|
55
|
+
"startedAt": "2024-01-15T10:00:00Z",
|
|
56
|
+
"completedAt": "2024-01-15T10:00:05Z",
|
|
57
|
+
"durationMs": 5234
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Async execution (202 Accepted):**
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"runId": "7f3e9c1a-2b4d-4e8f-9a3b-1c5d7e9f2a4b",
|
|
67
|
+
"toolId": "my-tool-id",
|
|
68
|
+
"status": "running"
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## SDK Installation
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# TypeScript/JavaScript
|
|
78
|
+
npm install @superglue/client
|
|
79
|
+
|
|
80
|
+
# Python
|
|
81
|
+
pip install superglue-client
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Both SDKs are auto-generated from the OpenAPI spec (JS via orval, Python via openapi-python-client).
|
|
85
|
+
|
|
86
|
+
## TypeScript/JavaScript Usage
|
|
87
|
+
|
|
88
|
+
The JS SDK exports: `configure`, `listTools`, `getTool`, `runTool`, `getRun`, `cancelRun`, `triggerWebhook`, `listRuns`.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { configure, runTool, getRun, listTools, getTool } from "@superglue/client";
|
|
92
|
+
|
|
93
|
+
configure({
|
|
94
|
+
apiKey: process.env.SUPERGLUE_API_KEY!,
|
|
95
|
+
baseUrl: "https://api.superglue.cloud/v1",
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Run a Tool (Sync)
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
const response = await runTool("my-tool-id", {
|
|
103
|
+
inputs: { userId: "123", date: "2024-01-15" },
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (response.status === 200) {
|
|
107
|
+
const run = response.data;
|
|
108
|
+
console.log(run.status); // "success" | "failed"
|
|
109
|
+
console.log(run.data); // tool output
|
|
110
|
+
} else {
|
|
111
|
+
console.error("Error:", response.data);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The response is `{ data: Run, status: number, headers: Headers }`. The `Run` object has: `runId`, `toolId`, `status`, `data`, `error`, `stepResults`, `metadata`.
|
|
116
|
+
|
|
117
|
+
### Async Execution (Fire-and-Forget)
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const response = await runTool("my-tool-id", {
|
|
121
|
+
inputs: { userId: "123" },
|
|
122
|
+
options: { async: true },
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (response.status === 202) {
|
|
126
|
+
const run = response.data;
|
|
127
|
+
console.log(`Run started: ${run.runId}`);
|
|
128
|
+
|
|
129
|
+
let latest = run;
|
|
130
|
+
while (latest.status === "running") {
|
|
131
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
132
|
+
const poll = await getRun(latest.runId);
|
|
133
|
+
latest = poll.data;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### List and Discover Tools
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
const { data: tools } = await listTools({ page: 1, limit: 50 });
|
|
142
|
+
console.log(tools.data);
|
|
143
|
+
|
|
144
|
+
const { data: tool } = await getTool("my-tool-id");
|
|
145
|
+
console.log(tool.inputSchema);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Python Usage
|
|
149
|
+
|
|
150
|
+
The Python SDK uses `AuthenticatedClient` with httpx. Functions are in `superglue_client.api.tools` and `superglue_client.api.runs`.
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
import os
|
|
154
|
+
from superglue_client import AuthenticatedClient
|
|
155
|
+
from superglue_client.api.tools import run_tool, list_tools, get_tool
|
|
156
|
+
from superglue_client.api.runs import get_run
|
|
157
|
+
from superglue_client.models.run_request import RunRequest
|
|
158
|
+
from superglue_client.models.run_request_inputs import RunRequestInputs
|
|
159
|
+
|
|
160
|
+
client = AuthenticatedClient(
|
|
161
|
+
base_url="https://api.superglue.cloud/v1",
|
|
162
|
+
token=os.environ["SUPERGLUE_API_KEY"],
|
|
163
|
+
)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Run a Tool (Sync)
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
with client as c:
|
|
170
|
+
result = run_tool.sync(
|
|
171
|
+
"my-tool-id",
|
|
172
|
+
client=c,
|
|
173
|
+
body=RunRequest(
|
|
174
|
+
inputs=RunRequestInputs.from_dict({"userId": "123"}),
|
|
175
|
+
),
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
if hasattr(result, "status") and result.status == "success":
|
|
179
|
+
print(result.data)
|
|
180
|
+
elif hasattr(result, "error"):
|
|
181
|
+
print(result.error)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
`sync()` returns `Run | Error | None`. Use `sync_detailed()` for the full `Response` wrapper with status code and headers.
|
|
185
|
+
|
|
186
|
+
### Async Python
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
async with client as c:
|
|
190
|
+
result = await run_tool.asyncio(
|
|
191
|
+
"my-tool-id",
|
|
192
|
+
client=c,
|
|
193
|
+
body=RunRequest(
|
|
194
|
+
inputs=RunRequestInputs.from_dict({"userId": "123"}),
|
|
195
|
+
),
|
|
196
|
+
)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Webhook Triggers
|
|
200
|
+
|
|
201
|
+
Trigger tools via HTTP POST without SDK. Webhooks execute asynchronously.
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
curl -X POST "https://api.superglue.cloud/v1/hooks/{toolId}?token={apiKey}" \
|
|
205
|
+
-H "Content-Type: application/json" \
|
|
206
|
+
-d '{"userId": "123"}'
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Webhook URL Pattern
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
POST {endpoint}/v1/hooks/{toolId}?token={apiKey}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
- `endpoint` — Your superglue API endpoint
|
|
216
|
+
- `toolId` — The saved tool ID (from `sg tool save`)
|
|
217
|
+
- `apiKey` — Your superglue API key (passed as `token` query param)
|
|
218
|
+
|
|
219
|
+
### Webhook Response
|
|
220
|
+
|
|
221
|
+
Webhooks return `202 Accepted` immediately:
|
|
222
|
+
|
|
223
|
+
```json
|
|
224
|
+
{
|
|
225
|
+
"runId": "uuid-of-execution",
|
|
226
|
+
"status": "accepted",
|
|
227
|
+
"toolId": "my-tool-id"
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
To get the result, poll the run status:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
curl "https://api.superglue.cloud/v1/runs/{runId}" \
|
|
235
|
+
-H "Authorization: Bearer {apiKey}"
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Error Handling
|
|
239
|
+
|
|
240
|
+
### Retry Pattern (TypeScript)
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
async function runToolWithRetry(toolId: string, inputs: Record<string, any>, maxRetries = 3) {
|
|
244
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
245
|
+
const response = await runTool(toolId, { inputs });
|
|
246
|
+
|
|
247
|
+
if (response.status === 200 && response.data.status === "success") {
|
|
248
|
+
return response.data;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (response.data.error?.includes("validation")) {
|
|
252
|
+
throw new Error(response.data.error);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (attempt < maxRetries) {
|
|
256
|
+
await new Promise((r) => setTimeout(r, 1000 * Math.pow(2, attempt)));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
throw new Error(`Tool ${toolId} failed after ${maxRetries} attempts`);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Environment Configuration
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
# Required
|
|
267
|
+
SUPERGLUE_API_KEY=your-api-key
|
|
268
|
+
|
|
269
|
+
# Optional (defaults to hosted)
|
|
270
|
+
SUPERGLUE_API_ENDPOINT=https://api.superglue.cloud
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Best Practices
|
|
274
|
+
|
|
275
|
+
1. **Store API keys in secrets** — Never commit to version control
|
|
276
|
+
2. **Validate payloads client-side** — Check against `inputSchema` before calling
|
|
277
|
+
3. **Handle errors gracefully** — Tools can fail due to external API issues
|
|
278
|
+
4. **Monitor run history** — Use `sg run list` or the web UI to track executions
|
|
279
|
+
5. **Use async mode for long-running tools** — Avoid timeouts on complex workflows
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Redis Steps
|
|
2
|
+
|
|
3
|
+
## Step Configuration
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
{
|
|
7
|
+
type: "request",
|
|
8
|
+
systemId: "my_redis",
|
|
9
|
+
url: "redis://<<my_redis_username>>:<<my_redis_password>>@<<my_redis_host>>:<<my_redis_port>>/<<my_redis_database>>",
|
|
10
|
+
method: "POST", // ignored for Redis but required by schema
|
|
11
|
+
body: '{"command": "GET", "args": ["<<keyName>>"]}'
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Configure the connection URL depending on which credentials are saved in the stored Redis system - reference them accordingly.
|
|
16
|
+
|
|
17
|
+
## URL Format
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
redis://user:password@host:port/database
|
|
21
|
+
rediss://user:password@host:port/database (TLS)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- `redis://` for standard connections
|
|
25
|
+
- `rediss://` for TLS-encrypted connections (e.g. Redis Cloud, AWS ElastiCache with encryption)
|
|
26
|
+
- `/database` is the database number (0-15), defaults to 0 if omitted
|
|
27
|
+
|
|
28
|
+
All credential variables are resolved before connecting. Trailing slashes are cleaned.
|
|
29
|
+
|
|
30
|
+
## Body Format
|
|
31
|
+
|
|
32
|
+
JSON string with `command` (required) and optional `args` array. Body can be a single command object or an array of commands (pipelined in a single round-trip):
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{ "command": "GET", "args": ["mykey"] }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Multiple commands:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
[
|
|
42
|
+
{ "command": "GET", "args": ["user:1:name"] },
|
|
43
|
+
{ "command": "HGETALL", "args": ["user:1"] }
|
|
44
|
+
]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Common Commands
|
|
48
|
+
|
|
49
|
+
**String operations:**
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{ "command": "GET", "args": ["user:123:name"] }
|
|
53
|
+
{ "command": "SET", "args": ["user:123:name", "Alice"] }
|
|
54
|
+
{ "command": "MGET", "args": ["key1", "key2", "key3"] }
|
|
55
|
+
{ "command": "INCR", "args": ["counter"] }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Hash operations:**
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{ "command": "HGETALL", "args": ["user:123"] }
|
|
62
|
+
{ "command": "HGET", "args": ["user:123", "email"] }
|
|
63
|
+
{ "command": "HSET", "args": ["user:123", "email", "alice@example.com"] }
|
|
64
|
+
{ "command": "HMGET", "args": ["user:123", "name", "email"] }
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**List operations:**
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{ "command": "LRANGE", "args": ["queue:tasks", "0", "-1"] }
|
|
71
|
+
{ "command": "LPUSH", "args": ["queue:tasks", "task1"] }
|
|
72
|
+
{ "command": "RPOP", "args": ["queue:tasks"] }
|
|
73
|
+
{ "command": "LLEN", "args": ["queue:tasks"] }
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Set operations:**
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{ "command": "SMEMBERS", "args": ["tags:post:1"] }
|
|
80
|
+
{ "command": "SADD", "args": ["tags:post:1", "redis", "database"] }
|
|
81
|
+
{ "command": "SISMEMBER", "args": ["tags:post:1", "redis"] }
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Sorted set operations:**
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{ "command": "ZRANGE", "args": ["leaderboard", "0", "-1", "WITHSCORES"] }
|
|
88
|
+
{ "command": "ZADD", "args": ["leaderboard", "100", "player1"] }
|
|
89
|
+
{ "command": "ZRANK", "args": ["leaderboard", "player1"] }
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Key operations:**
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{ "command": "KEYS", "args": ["user:*"] }
|
|
96
|
+
{ "command": "EXISTS", "args": ["mykey"] }
|
|
97
|
+
{ "command": "TTL", "args": ["session:abc"] }
|
|
98
|
+
{ "command": "EXPIRE", "args": ["session:abc", "3600"] }
|
|
99
|
+
{ "command": "DEL", "args": ["mykey"] }
|
|
100
|
+
{ "command": "TYPE", "args": ["mykey"] }
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Scan (safe iteration):**
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{ "command": "SCAN", "args": ["0", "MATCH", "user:*", "COUNT", "100"] }
|
|
107
|
+
{ "command": "HSCAN", "args": ["myhash", "0", "MATCH", "field*"] }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Dynamic Arguments with Variables
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{ "command": "GET", "args": ["<<(sourceData) => `user:${sourceData.userId}:profile`>>"] }
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{ "command": "HSET", "args": ["<<userKey>>", "<<fieldName>>", "<<fieldValue>>"] }
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Connection Management
|
|
121
|
+
|
|
122
|
+
- A fresh connection is created per execution and closed after completion
|
|
123
|
+
- TLS auto-configured for `rediss://` URLs (with `rejectUnauthorized: false`)
|
|
124
|
+
- 5-second connection timeout, 30-second command timeout
|
|
125
|
+
- Array commands (pipeline) run on a single connection for efficiency
|
|
126
|
+
|
|
127
|
+
## Retry Logic
|
|
128
|
+
|
|
129
|
+
Default retries from server config. On final failure, error includes command and args.
|
|
130
|
+
|
|
131
|
+
## Return Value
|
|
132
|
+
|
|
133
|
+
Returns the raw Redis response:
|
|
134
|
+
|
|
135
|
+
- `GET` / `HGET`: string or null
|
|
136
|
+
- `HGETALL`: object with field-value pairs (flattened array from Redis, parsed by ioredis)
|
|
137
|
+
- `LRANGE` / `SMEMBERS` / `KEYS`: array of strings
|
|
138
|
+
- `SET` / `DEL` / `EXPIRE`: "OK" or integer
|
|
139
|
+
- `INCR` / `LLEN` / `SADD`: integer
|
|
140
|
+
- `MGET` / `HMGET`: array (nulls for missing keys)
|
|
141
|
+
- `SCAN` / `HSCAN`: array of [cursor, results]
|
|
142
|
+
|
|
143
|
+
## Multiple Commands (Pipeline)
|
|
144
|
+
|
|
145
|
+
Pass an array of commands to execute them in a single round-trip (like FTP/SFTP batch operations):
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
[
|
|
149
|
+
{ "command": "GET", "args": ["user:1:name"] },
|
|
150
|
+
{ "command": "HGETALL", "args": ["user:1"] },
|
|
151
|
+
{ "command": "LRANGE", "args": ["tasks", "0", "-1"] }
|
|
152
|
+
]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Returns an array of results, one per command:
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
[
|
|
159
|
+
{ "command": "GET", "result": "Alice" },
|
|
160
|
+
{ "command": "HGETALL", "result": ["name", "Alice", "email", "alice@example.com"] },
|
|
161
|
+
{ "command": "LRANGE", "result": ["task3", "task2", "task1"] }
|
|
162
|
+
]
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
If an individual command fails, that entry has `error` instead of `result`:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
[
|
|
169
|
+
{ "command": "GET", "result": "Alice" },
|
|
170
|
+
{ "command": "WRONGCMD", "error": "ERR unknown command 'WRONGCMD'" }
|
|
171
|
+
]
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
A single command returns a single result. Multiple commands return an array — same pattern as FTP/SFTP operations.
|
|
175
|
+
|
|
176
|
+
## Batch Operations via Loop
|
|
177
|
+
|
|
178
|
+
Use a data selector returning an array to execute a command per item:
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
// dataSelector: (sourceData) => sourceData.userIds
|
|
182
|
+
// body: {"command": "HGETALL", "args": ["<<(sourceData) => `user:${sourceData.currentItem}`>>"]}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Important Notes
|
|
186
|
+
|
|
187
|
+
- All args must be strings (Redis protocol is text-based) - numbers are auto-coerced
|
|
188
|
+
- Use `SCAN` instead of `KEYS` in production for large keyspaces
|
|
189
|
+
- `HGETALL` returns an empty object `{}` for non-existent keys
|
|
190
|
+
- Use array body for multi-command pipelines instead of separate steps
|