@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.
Files changed (44) hide show
  1. package/PRD-API-CHAINING.md +483 -0
  2. package/PRD-HARDEN-SHELL.md +18 -12
  3. package/convex/_generated/api.d.ts +2 -0
  4. package/convex/chains.ts +1095 -0
  5. package/convex/schema.ts +101 -0
  6. package/dist/chain-types.d.ts +187 -0
  7. package/dist/chain-types.d.ts.map +1 -0
  8. package/dist/chain-types.js +33 -0
  9. package/dist/chain-types.js.map +1 -0
  10. package/dist/chainExecutor.d.ts +122 -0
  11. package/dist/chainExecutor.d.ts.map +1 -0
  12. package/dist/chainExecutor.js +454 -0
  13. package/dist/chainExecutor.js.map +1 -0
  14. package/dist/chainResolver.d.ts +100 -0
  15. package/dist/chainResolver.d.ts.map +1 -0
  16. package/dist/chainResolver.js +519 -0
  17. package/dist/chainResolver.js.map +1 -0
  18. package/dist/chainResolver.test.d.ts +5 -0
  19. package/dist/chainResolver.test.d.ts.map +1 -0
  20. package/dist/chainResolver.test.js +201 -0
  21. package/dist/chainResolver.test.js.map +1 -0
  22. package/dist/execute.d.ts +4 -1
  23. package/dist/execute.d.ts.map +1 -1
  24. package/dist/execute.js +3 -0
  25. package/dist/execute.js.map +1 -1
  26. package/dist/index.js +382 -2
  27. package/dist/index.js.map +1 -1
  28. package/landing/public/logos/chattgpt.svg +1 -0
  29. package/landing/public/logos/claude.svg +1 -0
  30. package/landing/public/logos/gemini.svg +1 -0
  31. package/landing/public/logos/grok.svg +1 -0
  32. package/landing/src/app/page.tsx +12 -21
  33. package/landing/src/app/workspace/chains/page.tsx +520 -0
  34. package/landing/src/components/AITestimonials.tsx +15 -9
  35. package/landing/src/components/ChainStepDetail.tsx +310 -0
  36. package/landing/src/components/ChainTrace.tsx +261 -0
  37. package/landing/src/lib/stats.json +1 -1
  38. package/package.json +1 -1
  39. package/src/chain-types.ts +270 -0
  40. package/src/chainExecutor.ts +730 -0
  41. package/src/chainResolver.test.ts +246 -0
  42. package/src/chainResolver.ts +658 -0
  43. package/src/execute.ts +23 -0
  44. 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
+ 🦞
@@ -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
- - [ ] Add Security section to landing OR dedicated /security page
89
- - [ ] 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
- - [ ] Add trust badge to footer: "🔒 Enterprise-grade security"
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
- - [ ] Document error response format in docs
111
- - [ ] Ensure all providers return: `{ success: false, error: "message", code: "ERROR_CODE" }`
112
- - [ ] Add retry logic for transient failures (503, 429)
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
- - [ ] Implement retry logic with backoff
232
- - [ ] Document error format
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;