@nordsym/apiclaw 1.3.13 → 1.4.0
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/PRD-API-CHAINING.md +483 -0
- package/PRD-HARDEN-SHELL.md +18 -12
- package/convex/_generated/api.d.ts +2 -0
- package/convex/chains.ts +1095 -0
- package/convex/schema.ts +101 -0
- package/dist/chain-types.d.ts +187 -0
- package/dist/chain-types.d.ts.map +1 -0
- package/dist/chain-types.js +33 -0
- package/dist/chain-types.js.map +1 -0
- package/dist/chainExecutor.d.ts +122 -0
- package/dist/chainExecutor.d.ts.map +1 -0
- package/dist/chainExecutor.js +454 -0
- package/dist/chainExecutor.js.map +1 -0
- package/dist/chainResolver.d.ts +100 -0
- package/dist/chainResolver.d.ts.map +1 -0
- package/dist/chainResolver.js +519 -0
- package/dist/chainResolver.js.map +1 -0
- package/dist/chainResolver.test.d.ts +5 -0
- package/dist/chainResolver.test.d.ts.map +1 -0
- package/dist/chainResolver.test.js +201 -0
- package/dist/chainResolver.test.js.map +1 -0
- package/dist/execute.d.ts +4 -1
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js +3 -0
- package/dist/execute.js.map +1 -1
- package/dist/index.js +382 -2
- package/dist/index.js.map +1 -1
- package/landing/public/logos/chattgpt.svg +1 -0
- package/landing/public/logos/claude.svg +1 -0
- package/landing/public/logos/gemini.svg +1 -0
- package/landing/public/logos/grok.svg +1 -0
- package/landing/src/app/page.tsx +12 -21
- package/landing/src/app/workspace/chains/page.tsx +520 -0
- package/landing/src/components/AITestimonials.tsx +15 -9
- package/landing/src/components/ChainStepDetail.tsx +310 -0
- package/landing/src/components/ChainTrace.tsx +261 -0
- package/landing/src/lib/stats.json +1 -1
- package/package.json +1 -1
- package/src/chain-types.ts +270 -0
- package/src/chainExecutor.ts +730 -0
- package/src/chainResolver.test.ts +246 -0
- package/src/chainResolver.ts +658 -0
- package/src/execute.ts +23 -0
- package/src/index.ts +423 -2
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
# APIClaw: Orchestration Layer
|
|
2
|
+
|
|
3
|
+
**Status:** SPEC
|
|
4
|
+
**Philosophy:** Agents don't call APIs. They express intent. APIClaw executes.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Core Truth
|
|
9
|
+
|
|
10
|
+
An agent that needs to:
|
|
11
|
+
1. Generate an image
|
|
12
|
+
2. Upload it somewhere
|
|
13
|
+
3. Email the link to someone
|
|
14
|
+
|
|
15
|
+
Should not think about this as three separate operations. It's one intent: *"Create and deliver an image."*
|
|
16
|
+
|
|
17
|
+
APIClaw is the layer that translates intent into execution.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## The Interface
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
call_api({
|
|
25
|
+
intent: "generate_and_deliver_image",
|
|
26
|
+
|
|
27
|
+
// Or explicit chain:
|
|
28
|
+
chain: [
|
|
29
|
+
{
|
|
30
|
+
id: "generate",
|
|
31
|
+
provider: "replicate",
|
|
32
|
+
action: "run",
|
|
33
|
+
params: {
|
|
34
|
+
model: "stability-ai/sdxl",
|
|
35
|
+
prompt: "A sunset over mountains"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: "upload",
|
|
40
|
+
provider: "cloudinary",
|
|
41
|
+
action: "upload",
|
|
42
|
+
params: {
|
|
43
|
+
url: "$generate.url"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: "notify",
|
|
48
|
+
provider: "resend",
|
|
49
|
+
action: "send",
|
|
50
|
+
params: {
|
|
51
|
+
to: "client@example.com",
|
|
52
|
+
subject: "Your image",
|
|
53
|
+
html: "<img src='$upload.secure_url' />"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
|
|
58
|
+
// Execution modifiers
|
|
59
|
+
parallel: ["generate_alt_1", "generate_alt_2"], // Run these in parallel
|
|
60
|
+
continueOnError: false, // Stop on first failure
|
|
61
|
+
timeout: 30000, // Max execution time
|
|
62
|
+
retry: { maxAttempts: 3, backoff: "exponential" } // Retry policy
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Reference System
|
|
69
|
+
|
|
70
|
+
Steps reference each other by ID. Full JSONPath support.
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// Direct reference
|
|
74
|
+
"$stepId.property"
|
|
75
|
+
|
|
76
|
+
// Nested access
|
|
77
|
+
"$generate.output.images[0].url"
|
|
78
|
+
|
|
79
|
+
// Previous step shorthand
|
|
80
|
+
"$prev.url"
|
|
81
|
+
|
|
82
|
+
// Array from parallel execution
|
|
83
|
+
"$parallel[0].url"
|
|
84
|
+
|
|
85
|
+
// Conditional reference
|
|
86
|
+
"$generate.success ? $generate.url : $fallback.url"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Built-in Variables
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
"$chain.startedAt" // ISO timestamp
|
|
93
|
+
"$chain.workspace" // Workspace ID
|
|
94
|
+
"$chain.index" // Current step index
|
|
95
|
+
"$env.CUSTOM_VAR" // Workspace environment variables
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Execution Modes
|
|
101
|
+
|
|
102
|
+
### Sequential (Default)
|
|
103
|
+
```typescript
|
|
104
|
+
chain: [A, B, C] // A → B → C
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Parallel
|
|
108
|
+
```typescript
|
|
109
|
+
chain: [
|
|
110
|
+
{
|
|
111
|
+
parallel: [
|
|
112
|
+
{ id: "img1", provider: "replicate", ... },
|
|
113
|
+
{ id: "img2", provider: "replicate", ... },
|
|
114
|
+
{ id: "img3", provider: "replicate", ... }
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: "combine",
|
|
119
|
+
provider: "cloudinary",
|
|
120
|
+
action: "create_collage",
|
|
121
|
+
params: {
|
|
122
|
+
images: ["$img1.url", "$img2.url", "$img3.url"]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Conditional
|
|
129
|
+
```typescript
|
|
130
|
+
chain: [
|
|
131
|
+
{ id: "analyze", provider: "openrouter", action: "chat", ... },
|
|
132
|
+
{
|
|
133
|
+
if: "$analyze.intent === 'image'",
|
|
134
|
+
then: { id: "gen", provider: "replicate", ... },
|
|
135
|
+
else: { id: "search", provider: "brave", ... }
|
|
136
|
+
},
|
|
137
|
+
{ id: "deliver", provider: "resend", params: { content: "$prev.result" } }
|
|
138
|
+
]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Loop
|
|
142
|
+
```typescript
|
|
143
|
+
chain: [
|
|
144
|
+
{ id: "get_urls", provider: "brave", action: "search", ... },
|
|
145
|
+
{
|
|
146
|
+
forEach: "$get_urls.results",
|
|
147
|
+
as: "result",
|
|
148
|
+
do: {
|
|
149
|
+
id: "scrape_$index",
|
|
150
|
+
provider: "firecrawl",
|
|
151
|
+
action: "scrape",
|
|
152
|
+
params: { url: "$result.url" }
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
id: "summarize",
|
|
157
|
+
provider: "openrouter",
|
|
158
|
+
action: "chat",
|
|
159
|
+
params: { content: "$forEach.results" }
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Map-Reduce
|
|
165
|
+
```typescript
|
|
166
|
+
chain: [
|
|
167
|
+
{
|
|
168
|
+
map: {
|
|
169
|
+
input: "$urls",
|
|
170
|
+
fn: { provider: "firecrawl", action: "scrape", params: { url: "$item" } }
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
reduce: {
|
|
175
|
+
input: "$map.results",
|
|
176
|
+
fn: { provider: "openrouter", action: "chat", params: { summarize: "$items" } }
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Error Handling
|
|
185
|
+
|
|
186
|
+
### Per-Step Configuration
|
|
187
|
+
```typescript
|
|
188
|
+
{
|
|
189
|
+
id: "critical_step",
|
|
190
|
+
provider: "stripe",
|
|
191
|
+
action: "charge",
|
|
192
|
+
params: { ... },
|
|
193
|
+
|
|
194
|
+
onError: {
|
|
195
|
+
retry: { attempts: 3, backoff: [1000, 2000, 4000] },
|
|
196
|
+
fallback: { id: "fallback_step", ... },
|
|
197
|
+
abort: false // Continue chain even if this fails
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Chain-Level Policies
|
|
203
|
+
```typescript
|
|
204
|
+
chain: [...],
|
|
205
|
+
errorPolicy: {
|
|
206
|
+
mode: "fail-fast" | "best-effort" | "transactional",
|
|
207
|
+
|
|
208
|
+
// Transactional mode: rollback on failure
|
|
209
|
+
rollback: [
|
|
210
|
+
{ if: "stripe.charge.success", do: { provider: "stripe", action: "refund", ... } }
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Error Response
|
|
216
|
+
```typescript
|
|
217
|
+
{
|
|
218
|
+
success: false,
|
|
219
|
+
completedSteps: ["generate", "upload"],
|
|
220
|
+
failedStep: {
|
|
221
|
+
id: "notify",
|
|
222
|
+
error: "Rate limited",
|
|
223
|
+
code: "RATE_LIMITED",
|
|
224
|
+
retryAfter: 30
|
|
225
|
+
},
|
|
226
|
+
partialResults: {
|
|
227
|
+
generate: { url: "https://..." },
|
|
228
|
+
upload: { secure_url: "https://..." }
|
|
229
|
+
},
|
|
230
|
+
canResume: true,
|
|
231
|
+
resumeToken: "chain_abc123_step_3"
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Resumable Chains
|
|
238
|
+
|
|
239
|
+
Long-running or failed chains can be resumed:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
call_api({
|
|
243
|
+
resume: "chain_abc123_step_3",
|
|
244
|
+
// Optional: override params for the failed step
|
|
245
|
+
overrides: {
|
|
246
|
+
notify: { to: "different@email.com" }
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Stored Chains (Templates)
|
|
254
|
+
|
|
255
|
+
Save chains as reusable templates:
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
// Define once
|
|
259
|
+
call_api({
|
|
260
|
+
defineChain: {
|
|
261
|
+
name: "content_pipeline",
|
|
262
|
+
description: "Generate, optimize, publish, notify",
|
|
263
|
+
inputs: {
|
|
264
|
+
prompt: { type: "string", required: true },
|
|
265
|
+
email: { type: "string", required: true }
|
|
266
|
+
},
|
|
267
|
+
chain: [
|
|
268
|
+
{ provider: "replicate", params: { prompt: "$inputs.prompt" } },
|
|
269
|
+
{ provider: "cloudinary", params: { url: "$prev.url" } },
|
|
270
|
+
{ provider: "ghost", params: { image: "$prev.secure_url" } },
|
|
271
|
+
{ provider: "resend", params: { to: "$inputs.email", content: "$prev.url" } }
|
|
272
|
+
]
|
|
273
|
+
}
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
// Use anywhere
|
|
277
|
+
call_api({
|
|
278
|
+
useChain: "content_pipeline",
|
|
279
|
+
inputs: {
|
|
280
|
+
prompt: "A cyberpunk cityscape",
|
|
281
|
+
email: "client@example.com"
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Webhooks & Async
|
|
289
|
+
|
|
290
|
+
For long-running chains:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
call_api({
|
|
294
|
+
chain: [...],
|
|
295
|
+
async: true,
|
|
296
|
+
webhook: "https://your-server.com/chain-complete",
|
|
297
|
+
// Or poll:
|
|
298
|
+
pollInterval: 5000
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
// Response:
|
|
302
|
+
{
|
|
303
|
+
chainId: "chain_xyz",
|
|
304
|
+
status: "running",
|
|
305
|
+
estimatedCompletion: "2026-03-02T01:15:00Z",
|
|
306
|
+
statusUrl: "https://apiclaw.com/api/chain/chain_xyz/status"
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Observability
|
|
313
|
+
|
|
314
|
+
Every chain execution is fully traced:
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
{
|
|
318
|
+
chainId: "chain_xyz",
|
|
319
|
+
trace: [
|
|
320
|
+
{
|
|
321
|
+
stepId: "generate",
|
|
322
|
+
startedAt: "...",
|
|
323
|
+
completedAt: "...",
|
|
324
|
+
latencyMs: 2340,
|
|
325
|
+
provider: "replicate",
|
|
326
|
+
cost: { cents: 2 },
|
|
327
|
+
input: { ... },
|
|
328
|
+
output: { ... }
|
|
329
|
+
},
|
|
330
|
+
// ...
|
|
331
|
+
],
|
|
332
|
+
totalLatencyMs: 4521,
|
|
333
|
+
totalCost: { cents: 5 },
|
|
334
|
+
tokensSaved: 1600 // vs discrete calls
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Dashboard shows:
|
|
339
|
+
- Chain execution timeline (Gantt-style)
|
|
340
|
+
- Cost breakdown per step
|
|
341
|
+
- Error rates per provider
|
|
342
|
+
- Most-used chain templates
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## Security
|
|
347
|
+
|
|
348
|
+
### Credential Isolation
|
|
349
|
+
Each step runs with only the credentials it needs. Step B cannot access Step A's provider credentials.
|
|
350
|
+
|
|
351
|
+
### Input Validation
|
|
352
|
+
References are validated before execution:
|
|
353
|
+
- `$nonexistent.url` → Error before any step runs
|
|
354
|
+
- Type mismatches caught early
|
|
355
|
+
|
|
356
|
+
### Rate Limiting
|
|
357
|
+
Chain-level rate limits prevent runaway execution:
|
|
358
|
+
```typescript
|
|
359
|
+
chain: [...],
|
|
360
|
+
limits: {
|
|
361
|
+
maxSteps: 20,
|
|
362
|
+
maxParallel: 5,
|
|
363
|
+
maxCost: { cents: 100 }
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Pricing
|
|
370
|
+
|
|
371
|
+
**Chain execution = sum of step costs.**
|
|
372
|
+
|
|
373
|
+
No orchestration fee. Chaining is a feature, not a product.
|
|
374
|
+
|
|
375
|
+
Why: Chains reduce our compute overhead (1 request vs N). We pass savings to users.
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## The Complete Picture
|
|
380
|
+
|
|
381
|
+
```
|
|
382
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
383
|
+
│ AGENT │
|
|
384
|
+
│ │
|
|
385
|
+
│ "Generate 3 variations, pick the best, publish, notify team" │
|
|
386
|
+
│ │
|
|
387
|
+
└─────────────────────────┬───────────────────────────────────────┘
|
|
388
|
+
│
|
|
389
|
+
▼
|
|
390
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
391
|
+
│ call_api({ chain: [...] }) │
|
|
392
|
+
└─────────────────────────┬───────────────────────────────────────┘
|
|
393
|
+
│
|
|
394
|
+
▼
|
|
395
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
396
|
+
│ APICLAW ORCHESTRATOR │
|
|
397
|
+
│ │
|
|
398
|
+
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
|
399
|
+
│ │ Parse │→ │Validate │→ │ Plan │ │
|
|
400
|
+
│ │ Chain │ │ Refs │ │ Exec │ │
|
|
401
|
+
│ └─────────┘ └─────────┘ └─────────┘ │
|
|
402
|
+
│ │ │
|
|
403
|
+
│ ▼ │
|
|
404
|
+
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
405
|
+
│ │ EXECUTION ENGINE │ │
|
|
406
|
+
│ │ │ │
|
|
407
|
+
│ │ ┌─────┐ ┌─────┐ ┌─────┐ │ │
|
|
408
|
+
│ │ │ A │───→│ B │───→│ C │ Sequential │ │
|
|
409
|
+
│ │ └─────┘ └─────┘ └─────┘ │ │
|
|
410
|
+
│ │ │ │
|
|
411
|
+
│ │ ┌─────┐ │ │
|
|
412
|
+
│ │ │ A │─┐ │ │
|
|
413
|
+
│ │ └─────┘ │ ┌─────┐ │ │
|
|
414
|
+
│ │ ┌─────┐ ├─→│ D │ Parallel + Join │ │
|
|
415
|
+
│ │ │ B │─┤ └─────┘ │ │
|
|
416
|
+
│ │ └─────┘ │ │ │
|
|
417
|
+
│ │ ┌─────┐ │ │ │
|
|
418
|
+
│ │ │ C │─┘ │ │
|
|
419
|
+
│ │ └─────┘ │ │
|
|
420
|
+
│ │ │ │
|
|
421
|
+
│ │ ┌─────┐ ┌─────┐ │ │
|
|
422
|
+
│ │ │ A │───→│ B? │───→ B₁ or B₂ Conditional │ │
|
|
423
|
+
│ │ └─────┘ └─────┘ │ │
|
|
424
|
+
│ │ │ │
|
|
425
|
+
│ └──────────────────────────────────────────────────────────┘ │
|
|
426
|
+
│ │ │
|
|
427
|
+
│ ▼ │
|
|
428
|
+
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
429
|
+
│ │ Replicate │ Cloudinary │ Resend │ Stripe │ ... 18 more │ │
|
|
430
|
+
│ └──────────────────────────────────────────────────────────┘ │
|
|
431
|
+
│ │
|
|
432
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
433
|
+
│
|
|
434
|
+
▼
|
|
435
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
436
|
+
│ AGENT │
|
|
437
|
+
│ │
|
|
438
|
+
│ { success: true, finalResult: { publishedUrl: "..." } } │
|
|
439
|
+
│ │
|
|
440
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## What This Enables
|
|
446
|
+
|
|
447
|
+
### Self-Orchestrating Agents
|
|
448
|
+
Agent receives a complex task → breaks it into chain → executes atomically → reports result.
|
|
449
|
+
|
|
450
|
+
No human writing glue code. No n8n. No Zapier. The agent IS the orchestrator, APIClaw is the executor.
|
|
451
|
+
|
|
452
|
+
### Compound Actions
|
|
453
|
+
"Research this company and draft an outreach email" becomes one chain:
|
|
454
|
+
```
|
|
455
|
+
Brave → Firecrawl (×3) → OpenRouter (analyze) → OpenRouter (draft) → Resend (preview)
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Agent-to-Agent Workflows
|
|
459
|
+
Agent A triggers a chain that includes calling Agent B's endpoint, which triggers its own chain.
|
|
460
|
+
|
|
461
|
+
Recursive. Distributed. Autonomous.
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## The Positioning
|
|
466
|
+
|
|
467
|
+
> "APIClaw is not an API aggregator. It's the execution layer for autonomous AI."
|
|
468
|
+
|
|
469
|
+
> "Agents don't integrate APIs. They declare intent. APIClaw handles the rest."
|
|
470
|
+
|
|
471
|
+
> "From tool-calling to orchestration. From 10 round-trips to 1."
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
*"A chain of three call_api calls with no context switching."*
|
|
476
|
+
|
|
477
|
+
*Three? Try thirty. Try three hundred.*
|
|
478
|
+
|
|
479
|
+
*No switching. No waiting. No glue code.*
|
|
480
|
+
|
|
481
|
+
*Just execution.*
|
|
482
|
+
|
|
483
|
+
🦞
|
package/PRD-HARDEN-SHELL.md
CHANGED
|
@@ -85,13 +85,19 @@ Pricing: Free (50 calls/week), Pay-as-you-go (usage-based), or Founding Backer (
|
|
|
85
85
|
**Critique:** "Centralizing keys/billing is a major trust shift" (GPT, Grok)
|
|
86
86
|
|
|
87
87
|
**Action:**
|
|
88
|
-
- [
|
|
89
|
-
- [
|
|
90
|
-
- AES-256-GCM encryption for stored keys
|
|
91
|
-
- No logging of request/response payloads
|
|
92
|
-
- Tenant isolation
|
|
93
|
-
- SOC2 roadmap mention (if planned)
|
|
94
|
-
- [
|
|
88
|
+
- [x] Add Security section to landing OR dedicated /security page ✓ (2026-03-02)
|
|
89
|
+
- [x] Cover:
|
|
90
|
+
- AES-256-GCM encryption for stored keys ✓
|
|
91
|
+
- No logging of request/response payloads ✓
|
|
92
|
+
- Tenant isolation ✓
|
|
93
|
+
- SOC2 roadmap mention (if planned) ✓
|
|
94
|
+
- [x] Add trust badge to footer: "🔒 Enterprise-grade security" ✓
|
|
95
|
+
|
|
96
|
+
**Implemented:**
|
|
97
|
+
- Created dedicated `/security` page with full coverage
|
|
98
|
+
- Added "AES-256 Encrypted" badge to footer (links to /security)
|
|
99
|
+
- Added Security link in footer Product menu
|
|
100
|
+
- Live at: https://apiclaw.nordsym.com/security
|
|
95
101
|
|
|
96
102
|
**FAQ addition:**
|
|
97
103
|
```
|
|
@@ -107,9 +113,9 @@ A: All credentials encrypted with AES-256-GCM. Keys never logged or exposed in r
|
|
|
107
113
|
**Critique:** "What happens when Replicate fails? Structured errors?" (Claude, GPT)
|
|
108
114
|
|
|
109
115
|
**Action:**
|
|
110
|
-
- [
|
|
111
|
-
- [
|
|
112
|
-
- [
|
|
116
|
+
- [x] Document error response format in docs ✓ (v1.3.13)
|
|
117
|
+
- [x] Ensure all providers return: `{ success: false, error: "message", code: "ERROR_CODE" }` ✓ (v1.3.13)
|
|
118
|
+
- [x] Add retry logic for transient failures (503, 429) ✓ (v1.3.13 - exponential backoff with jitter)
|
|
113
119
|
- [ ] Add to copy-context: "Structured error responses across all providers"
|
|
114
120
|
|
|
115
121
|
**Already done:**
|
|
@@ -228,8 +234,8 @@ Settings → Billing → Monthly budget cap: $____
|
|
|
228
234
|
- [ ] Clarify Direct Call vs Indexed distinction on landing
|
|
229
235
|
|
|
230
236
|
### Phase 3: Reliability Features
|
|
231
|
-
- [
|
|
232
|
-
- [
|
|
237
|
+
- [x] Implement retry logic with backoff ✓ (v1.3.13)
|
|
238
|
+
- [x] Document error format ✓ (v1.3.13)
|
|
233
239
|
- [ ] Add spend alerts to workspace
|
|
234
240
|
- [ ] Add budget cap option
|
|
235
241
|
|
|
@@ -12,6 +12,7 @@ import type * as agents from "../agents.js";
|
|
|
12
12
|
import type * as analytics from "../analytics.js";
|
|
13
13
|
import type * as billing from "../billing.js";
|
|
14
14
|
import type * as capabilities from "../capabilities.js";
|
|
15
|
+
import type * as chains from "../chains.js";
|
|
15
16
|
import type * as credits from "../credits.js";
|
|
16
17
|
import type * as crons from "../crons.js";
|
|
17
18
|
import type * as directCall from "../directCall.js";
|
|
@@ -43,6 +44,7 @@ declare const fullApi: ApiFromModules<{
|
|
|
43
44
|
analytics: typeof analytics;
|
|
44
45
|
billing: typeof billing;
|
|
45
46
|
capabilities: typeof capabilities;
|
|
47
|
+
chains: typeof chains;
|
|
46
48
|
credits: typeof credits;
|
|
47
49
|
crons: typeof crons;
|
|
48
50
|
directCall: typeof directCall;
|