@dura-run/cli 0.1.5 → 0.2.1
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/LICENSE +201 -21
- package/README.md +36 -6
- package/dist/dura.js +428 -253
- package/package.json +7 -10
- package/skills/dura-debug.md +360 -0
- package/skills/dura-deploy.md +272 -0
- package/skills/dura-develop.md +379 -0
- package/skills/dura-overview.md +196 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
# dura.run — Development Guide
|
|
2
|
+
|
|
3
|
+
> This skill teaches an AI agent how to scaffold projects, write automation handlers, use SDK globals, and run locally with `dura dev`.
|
|
4
|
+
|
|
5
|
+
## Scaffolding a New Project
|
|
6
|
+
|
|
7
|
+
### `dura new <name>` — Create a New Project
|
|
8
|
+
|
|
9
|
+
Creates a new directory with the full project skeleton:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Create a project called "my-api"
|
|
13
|
+
dura new my-api
|
|
14
|
+
|
|
15
|
+
# Create in a specific parent directory
|
|
16
|
+
dura new my-api --dir ~/projects
|
|
17
|
+
|
|
18
|
+
# Create from a template
|
|
19
|
+
dura new my-api --template rest-api --org org_abc123
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**What gets created:**
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
my-api/
|
|
26
|
+
├── dura.json # Manifest with a "hello" automation
|
|
27
|
+
├── routes/
|
|
28
|
+
│ └── hello.ts # Starter HTTP handler
|
|
29
|
+
├── jobs/ # Empty directory for cron/event handlers
|
|
30
|
+
└── .gitignore # Ignores .env.local, node_modules, .dura, dist
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Project name rules:** Must start with a letter, can contain letters, numbers, hyphens, and underscores.
|
|
34
|
+
|
|
35
|
+
### `dura init` — Initialize an Existing Directory
|
|
36
|
+
|
|
37
|
+
Use `dura init` when you already have a directory and want to turn it into a dura project:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
cd my-existing-project
|
|
41
|
+
dura init
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This creates `dura.json`, starter files, and installs AI agent skills.
|
|
45
|
+
|
|
46
|
+
## Writing Automation Handlers
|
|
47
|
+
|
|
48
|
+
### Handler Signature
|
|
49
|
+
|
|
50
|
+
Every automation handler is a default-exported async function:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import type { DuraRequest, DuraResponse } from "@dura/sdk";
|
|
54
|
+
|
|
55
|
+
export default async function handler(req: DuraRequest): Promise<DuraResponse> {
|
|
56
|
+
// Your logic here
|
|
57
|
+
return {
|
|
58
|
+
status: 200,
|
|
59
|
+
headers: { "content-type": "application/json" },
|
|
60
|
+
body: { result: "success" },
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### DuraRequest Shape
|
|
66
|
+
|
|
67
|
+
The `req` object contains:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
interface DuraRequest {
|
|
71
|
+
method: string; // HTTP method (GET, POST, etc.)
|
|
72
|
+
path: string; // Request path
|
|
73
|
+
headers: Record<string, string>;
|
|
74
|
+
query: Record<string, string>;
|
|
75
|
+
body: unknown; // Parsed JSON body (or raw string)
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### DuraResponse Shape
|
|
80
|
+
|
|
81
|
+
Return an object with:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
interface DuraResponse {
|
|
85
|
+
status: number; // HTTP status code
|
|
86
|
+
headers?: Record<string, string>; // Response headers
|
|
87
|
+
body?: unknown; // JSON-serializable response body
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Example: REST API Handler
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import type { DuraRequest, DuraResponse } from "@dura/sdk";
|
|
95
|
+
|
|
96
|
+
export default async function handler(req: DuraRequest): Promise<DuraResponse> {
|
|
97
|
+
if (req.method === "GET") {
|
|
98
|
+
const users = await dura.kv.get("users") as string[] ?? [];
|
|
99
|
+
return {
|
|
100
|
+
status: 200,
|
|
101
|
+
headers: { "content-type": "application/json" },
|
|
102
|
+
body: { users },
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (req.method === "POST") {
|
|
107
|
+
const { name, email } = req.body as { name: string; email: string };
|
|
108
|
+
if (!name || !email) {
|
|
109
|
+
return { status: 400, body: { error: "name and email are required" } };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const users = await dura.kv.get("users") as string[] ?? [];
|
|
113
|
+
users.push(name);
|
|
114
|
+
await dura.kv.set("users", users);
|
|
115
|
+
|
|
116
|
+
dura.log.info("User created", { name, email });
|
|
117
|
+
return { status: 201, body: { created: true, name } };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return { status: 405, body: { error: "Method not allowed" } };
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Example: Cron Job Handler
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import type { DuraRequest, DuraResponse } from "@dura/sdk";
|
|
128
|
+
|
|
129
|
+
export default async function handler(req: DuraRequest): Promise<DuraResponse> {
|
|
130
|
+
dura.log.info("Starting daily cleanup");
|
|
131
|
+
|
|
132
|
+
const response = await dura.fetch("https://api.example.com/stale-records", {
|
|
133
|
+
method: "DELETE",
|
|
134
|
+
headers: { Authorization: `Bearer ${dura.env.get("API_TOKEN")}` },
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const result = await response.json();
|
|
138
|
+
dura.log.info("Cleanup complete", { deleted: result.count });
|
|
139
|
+
|
|
140
|
+
return { status: 200, body: { deleted: result.count } };
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Example: Chained Automation
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// routes/process-order.ts
|
|
148
|
+
import type { DuraRequest, DuraResponse } from "@dura/sdk";
|
|
149
|
+
|
|
150
|
+
export default async function handler(req: DuraRequest): Promise<DuraResponse> {
|
|
151
|
+
const order = req.body as { orderId: string; items: string[] };
|
|
152
|
+
|
|
153
|
+
// Process the order...
|
|
154
|
+
dura.log.info("Order processed", { orderId: order.orderId });
|
|
155
|
+
|
|
156
|
+
// Trigger the notification automation (fire-and-forget)
|
|
157
|
+
await dura.trigger("send-notification", {
|
|
158
|
+
type: "order_complete",
|
|
159
|
+
orderId: order.orderId,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Or wait for the result
|
|
163
|
+
const result = await dura.triggerAndWait("calculate-shipping", {
|
|
164
|
+
items: order.items,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return { status: 200, body: { orderId: order.orderId, shipping: result?.body } };
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## SDK Globals Reference
|
|
172
|
+
|
|
173
|
+
Inside automation handlers, the `dura` global object is always available. You do not need to import it — it is injected by the runtime.
|
|
174
|
+
|
|
175
|
+
### `dura.log` — Structured Logging
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
dura.log.debug("Debug message");
|
|
179
|
+
dura.log.info("Processing request", { userId: "123" });
|
|
180
|
+
dura.log.warn("Rate limit approaching", { current: 95, max: 100 });
|
|
181
|
+
dura.log.error("Payment failed", { error: err.message, orderId });
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
All log calls accept `(message: string, data?: unknown)`. The `data` argument is included as structured metadata in log entries, searchable via `dura logs --filter`.
|
|
185
|
+
|
|
186
|
+
### `dura.fetch` — HTTP Client
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// Works exactly like the standard fetch API
|
|
190
|
+
const res = await dura.fetch("https://api.example.com/data", {
|
|
191
|
+
method: "POST",
|
|
192
|
+
headers: { "Content-Type": "application/json" },
|
|
193
|
+
body: JSON.stringify({ key: "value" }),
|
|
194
|
+
});
|
|
195
|
+
const data = await res.json();
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
In production, `dura.fetch` routes through the executor's network sandbox. In local dev (`dura dev`), it uses the standard `fetch` directly.
|
|
199
|
+
|
|
200
|
+
### `dura.env` — Environment Variables
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
const dbUrl = dura.env.get("DATABASE_URL");
|
|
204
|
+
const apiKey = dura.env.get("STRIPE_SECRET_KEY");
|
|
205
|
+
|
|
206
|
+
if (!dbUrl) {
|
|
207
|
+
dura.log.error("DATABASE_URL is not set");
|
|
208
|
+
return { status: 500, body: { error: "Configuration error" } };
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
In local dev, env vars are loaded from `.env.local`. In production, they come from the environment's secret store (managed via `dura secrets`).
|
|
213
|
+
|
|
214
|
+
### `dura.kv` — Key-Value Store
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
// Set a value
|
|
218
|
+
await dura.kv.set("user:123", { name: "Alice", role: "admin" });
|
|
219
|
+
|
|
220
|
+
// Set with TTL (seconds)
|
|
221
|
+
await dura.kv.set("session:abc", { token: "xyz" }, { ttl: 3600 });
|
|
222
|
+
|
|
223
|
+
// Get a value
|
|
224
|
+
const user = await dura.kv.get("user:123");
|
|
225
|
+
|
|
226
|
+
// Check existence
|
|
227
|
+
const exists = await dura.kv.has("user:123");
|
|
228
|
+
|
|
229
|
+
// Delete
|
|
230
|
+
await dura.kv.delete("user:123");
|
|
231
|
+
|
|
232
|
+
// List keys by prefix
|
|
233
|
+
const userKeys = await dura.kv.list("user:");
|
|
234
|
+
|
|
235
|
+
// Atomic increment
|
|
236
|
+
await dura.kv.incr("page-views", 1);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
In local dev, KV is backed by an in-memory Map (resets when the dev server restarts). In production, it uses Redis.
|
|
240
|
+
|
|
241
|
+
### `dura.trigger` / `dura.triggerAndWait` — Chain Automations
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// Fire-and-forget — trigger another automation without waiting
|
|
245
|
+
await dura.trigger("send-email", { to: "user@example.com", subject: "Hello" });
|
|
246
|
+
|
|
247
|
+
// Trigger and wait for the result
|
|
248
|
+
const result = await dura.triggerAndWait("validate-address", {
|
|
249
|
+
street: "123 Main St",
|
|
250
|
+
city: "Springfield",
|
|
251
|
+
});
|
|
252
|
+
// result = { status: 200, body: { valid: true } }
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Local Development with `dura dev`
|
|
256
|
+
|
|
257
|
+
### Starting the Dev Server
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# Start in the current directory on port 3000 (default)
|
|
261
|
+
dura dev
|
|
262
|
+
|
|
263
|
+
# Specify a different directory or port
|
|
264
|
+
dura dev --dir ./my-project --port 8080
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### What `dura dev` Does
|
|
268
|
+
|
|
269
|
+
1. **Reads `dura.json`** — discovers all automations and their triggers
|
|
270
|
+
2. **Loads `.env.local`** — injects secrets into the dev environment
|
|
271
|
+
3. **Starts HTTP server** — maps HTTP triggers to handler files
|
|
272
|
+
4. **Starts cron scheduler** — schedules cron triggers locally
|
|
273
|
+
5. **Starts file watcher** — watches `routes/`, `jobs/`, `lib/` for hot-reload
|
|
274
|
+
6. **Installs SDK globals** — sets up `dura.log`, `dura.fetch`, `dura.env`, `dura.kv`, `dura.trigger`
|
|
275
|
+
|
|
276
|
+
### Testing Locally
|
|
277
|
+
|
|
278
|
+
Once the dev server is running:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
# Test an HTTP handler
|
|
282
|
+
curl http://localhost:3000/hello
|
|
283
|
+
|
|
284
|
+
# Test a POST handler
|
|
285
|
+
curl -X POST http://localhost:3000/webhook \
|
|
286
|
+
-H "Content-Type: application/json" \
|
|
287
|
+
-d '{"event": "user.created"}'
|
|
288
|
+
|
|
289
|
+
# Manually trigger any automation
|
|
290
|
+
dura run hello --project local
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Local Environment File
|
|
294
|
+
|
|
295
|
+
Create `.env.local` in the project root for local secrets:
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
DATABASE_URL=postgres://localhost:5432/mydb
|
|
299
|
+
API_TOKEN=sk_test_abc123
|
|
300
|
+
SLACK_WEBHOOK_URL=https://hooks.slack.com/...
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
This file is gitignored by default. These values are accessible via `dura.env.get("DATABASE_URL")` in your handlers.
|
|
304
|
+
|
|
305
|
+
## Registering Automations in `dura.json`
|
|
306
|
+
|
|
307
|
+
### Adding an HTTP Automation
|
|
308
|
+
|
|
309
|
+
```json
|
|
310
|
+
{
|
|
311
|
+
"name": "get-users",
|
|
312
|
+
"entrypoint": "routes/users.ts",
|
|
313
|
+
"triggers": [
|
|
314
|
+
{ "type": "http", "method": "GET", "path": "/users" }
|
|
315
|
+
]
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Adding a Cron Job
|
|
320
|
+
|
|
321
|
+
```json
|
|
322
|
+
{
|
|
323
|
+
"name": "daily-report",
|
|
324
|
+
"entrypoint": "jobs/daily-report.ts",
|
|
325
|
+
"triggers": [
|
|
326
|
+
{ "type": "cron", "schedule": "0 9 * * *" }
|
|
327
|
+
],
|
|
328
|
+
"config": {
|
|
329
|
+
"timeout": 60000,
|
|
330
|
+
"retries": 2
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Adding a Chained Automation
|
|
336
|
+
|
|
337
|
+
```json
|
|
338
|
+
{
|
|
339
|
+
"name": "send-notification",
|
|
340
|
+
"entrypoint": "routes/notify.ts",
|
|
341
|
+
"triggers": [
|
|
342
|
+
{ "type": "chain", "from": "process-order" }
|
|
343
|
+
]
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Adding a Manual-Only Automation
|
|
348
|
+
|
|
349
|
+
```json
|
|
350
|
+
{
|
|
351
|
+
"name": "migrate-data",
|
|
352
|
+
"entrypoint": "jobs/migrate.ts",
|
|
353
|
+
"triggers": [
|
|
354
|
+
{ "type": "manual" }
|
|
355
|
+
],
|
|
356
|
+
"config": {
|
|
357
|
+
"timeout": 120000,
|
|
358
|
+
"memory": 512
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Project-Level Config
|
|
364
|
+
|
|
365
|
+
Set defaults that apply to all automations:
|
|
366
|
+
|
|
367
|
+
```json
|
|
368
|
+
{
|
|
369
|
+
"name": "my-project",
|
|
370
|
+
"automations": [...],
|
|
371
|
+
"config": {
|
|
372
|
+
"timeout": 15000,
|
|
373
|
+
"memory": 128,
|
|
374
|
+
"concurrency": 10
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Per-automation `config` overrides project-level `config`.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# dura.run — Platform Overview
|
|
2
|
+
|
|
3
|
+
> This skill teaches an AI agent the core concepts, project structure, and deployment lifecycle of the dura.run automation platform.
|
|
4
|
+
|
|
5
|
+
## What Is dura.run?
|
|
6
|
+
|
|
7
|
+
dura.run is a managed automation platform where developers build, deploy, and debug JavaScript/TypeScript automations. Automations run in secure V8 isolates on the dura cloud — you write handler functions, define triggers, and dura handles execution, scaling, logging, and monitoring.
|
|
8
|
+
|
|
9
|
+
Think of it as "serverless functions with built-in observability and an automation-first developer experience."
|
|
10
|
+
|
|
11
|
+
## Core Concepts
|
|
12
|
+
|
|
13
|
+
### Automations
|
|
14
|
+
|
|
15
|
+
An automation is a single unit of work — a TypeScript function that receives a request and returns a response. Each automation has:
|
|
16
|
+
|
|
17
|
+
- **Name** — unique identifier within a project (e.g., `sync-users`, `process-payment`)
|
|
18
|
+
- **Entrypoint** — path to the handler file (e.g., `routes/sync-users.ts`)
|
|
19
|
+
- **Triggers** — how the automation gets invoked (HTTP, cron, event, manual, chain)
|
|
20
|
+
- **Config** — optional overrides for timeout, memory, concurrency, and retries
|
|
21
|
+
|
|
22
|
+
### Triggers
|
|
23
|
+
|
|
24
|
+
Automations can be triggered in five ways:
|
|
25
|
+
|
|
26
|
+
| Type | Description | Example |
|
|
27
|
+
|------|-------------|---------|
|
|
28
|
+
| `http` | Incoming HTTP request at a specific path and method | `GET /api/users` |
|
|
29
|
+
| `cron` | Scheduled execution on a cron expression | `0 */6 * * *` (every 6 hours) |
|
|
30
|
+
| `event` | Fired when an external event source emits a matching event | Webhook relay events |
|
|
31
|
+
| `manual` | Triggered explicitly via CLI (`dura run`) or API | One-off tasks, debugging |
|
|
32
|
+
| `chain` | Triggered by another automation completing | Multi-step pipelines |
|
|
33
|
+
|
|
34
|
+
### Projects
|
|
35
|
+
|
|
36
|
+
A project is a collection of related automations deployed together. Every project has:
|
|
37
|
+
|
|
38
|
+
- An organization owner
|
|
39
|
+
- A `dura.json` manifest defining all automations and their triggers
|
|
40
|
+
- One or more environments (production, staging, preview, custom)
|
|
41
|
+
- A deployment history with rollback capability
|
|
42
|
+
|
|
43
|
+
### Organizations
|
|
44
|
+
|
|
45
|
+
Organizations own projects and manage team access. Each user gets a personal workspace (organization) on signup. Organizations have:
|
|
46
|
+
|
|
47
|
+
- Members with roles: `owner`, `admin`, `member`
|
|
48
|
+
- API keys scoped to the org
|
|
49
|
+
- Usage quotas and billing (free, pro, enterprise plans)
|
|
50
|
+
|
|
51
|
+
### Environments
|
|
52
|
+
|
|
53
|
+
Environments isolate deployments. A project can have multiple environments:
|
|
54
|
+
|
|
55
|
+
- **production** — live traffic, default deployment target
|
|
56
|
+
- **staging** — pre-production testing
|
|
57
|
+
- **preview** — ephemeral environments for branch previews
|
|
58
|
+
- **custom** — user-defined environments
|
|
59
|
+
|
|
60
|
+
Each environment has its own secrets, deployment state, and URL.
|
|
61
|
+
|
|
62
|
+
## Project Structure
|
|
63
|
+
|
|
64
|
+
A dura project is a directory with this layout:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
my-project/
|
|
68
|
+
├── dura.json # Project manifest — defines automations and triggers
|
|
69
|
+
├── routes/ # HTTP-triggered automation handlers
|
|
70
|
+
│ ├── hello.ts
|
|
71
|
+
│ └── users.ts
|
|
72
|
+
├── jobs/ # Cron and event-triggered handlers
|
|
73
|
+
│ └── cleanup.ts
|
|
74
|
+
├── lib/ # Shared utilities (imported by handlers)
|
|
75
|
+
│ └── db.ts
|
|
76
|
+
├── .env.local # Local secrets (never committed)
|
|
77
|
+
├── .gitignore
|
|
78
|
+
└── node_modules/
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### `dura.json` — The Project Manifest
|
|
82
|
+
|
|
83
|
+
The manifest is the single source of truth for what a project contains. Example:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"name": "my-project",
|
|
88
|
+
"automations": [
|
|
89
|
+
{
|
|
90
|
+
"name": "hello",
|
|
91
|
+
"entrypoint": "routes/hello.ts",
|
|
92
|
+
"triggers": [
|
|
93
|
+
{ "type": "http", "method": "GET", "path": "/hello" }
|
|
94
|
+
]
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"name": "sync-users",
|
|
98
|
+
"entrypoint": "jobs/sync-users.ts",
|
|
99
|
+
"triggers": [
|
|
100
|
+
{ "type": "cron", "schedule": "0 */6 * * *" }
|
|
101
|
+
],
|
|
102
|
+
"config": {
|
|
103
|
+
"timeout": 30000,
|
|
104
|
+
"retries": 3
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "process-webhook",
|
|
109
|
+
"entrypoint": "routes/webhook.ts",
|
|
110
|
+
"triggers": [
|
|
111
|
+
{ "type": "http", "method": "POST", "path": "/webhook" }
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
"env": [
|
|
116
|
+
{ "name": "DATABASE_URL", "required": true, "description": "Postgres connection string" },
|
|
117
|
+
{ "name": "SLACK_TOKEN", "required": false }
|
|
118
|
+
],
|
|
119
|
+
"config": {
|
|
120
|
+
"timeout": 10000,
|
|
121
|
+
"memory": 128
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Manifest fields:**
|
|
127
|
+
|
|
128
|
+
| Field | Required | Description |
|
|
129
|
+
|-------|----------|-------------|
|
|
130
|
+
| `name` | Yes | Project name — letters, numbers, hyphens, underscores |
|
|
131
|
+
| `automations` | Yes | Array of automation definitions (at least one) |
|
|
132
|
+
| `automations[].name` | Yes | Automation name — must start with a letter |
|
|
133
|
+
| `automations[].entrypoint` | Yes | Path to the handler file (relative to project root) |
|
|
134
|
+
| `automations[].triggers` | No | Array of trigger configurations |
|
|
135
|
+
| `automations[].config` | No | Per-automation overrides (timeout, memory, concurrency, retries) |
|
|
136
|
+
| `env` | No | Declared environment variables the project uses |
|
|
137
|
+
| `config` | No | Project-level defaults for timeout, memory, concurrency |
|
|
138
|
+
|
|
139
|
+
### Handler File Format
|
|
140
|
+
|
|
141
|
+
Every handler file must export a default async function:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import type { DuraRequest, DuraResponse } from "@dura/sdk";
|
|
145
|
+
|
|
146
|
+
export default async function handler(req: DuraRequest): Promise<DuraResponse> {
|
|
147
|
+
return {
|
|
148
|
+
status: 200,
|
|
149
|
+
headers: { "content-type": "application/json" },
|
|
150
|
+
body: { message: "Hello from Dura!" },
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Deployment Lifecycle
|
|
156
|
+
|
|
157
|
+
1. **Develop** — Write handlers locally, test with `dura dev`
|
|
158
|
+
2. **Bundle** — `dura deploy` bundles all automations into a deployable archive
|
|
159
|
+
3. **Upload** — Bundle is uploaded to Dura's object storage via a presigned URL
|
|
160
|
+
4. **Validate** — The control plane validates the manifest and bundle integrity
|
|
161
|
+
5. **Activate** — If validation passes, the deployment becomes active
|
|
162
|
+
6. **Execute** — Incoming triggers invoke automation handlers in V8 isolates
|
|
163
|
+
7. **Monitor** — Execution logs, metrics, and diagnostics are available in real time
|
|
164
|
+
|
|
165
|
+
### Deployment States
|
|
166
|
+
|
|
167
|
+
| Status | Meaning |
|
|
168
|
+
|--------|---------|
|
|
169
|
+
| `pending` | Deployment created, awaiting bundle upload |
|
|
170
|
+
| `building` | Bundle uploaded, being processed |
|
|
171
|
+
| `deploying` | Validation passed, activating |
|
|
172
|
+
| `active` | Live — serving traffic |
|
|
173
|
+
| `failed` | Validation or activation failed |
|
|
174
|
+
| `rolled_back` | Was active, replaced by a rollback |
|
|
175
|
+
| `superseded` | Was active, replaced by a newer deployment |
|
|
176
|
+
|
|
177
|
+
## CLI Command Reference
|
|
178
|
+
|
|
179
|
+
| Command | Purpose |
|
|
180
|
+
|---------|---------|
|
|
181
|
+
| `dura login` | Authenticate with the dura control plane |
|
|
182
|
+
| `dura init` | Initialize a dura project in the current directory |
|
|
183
|
+
| `dura new <name>` | Scaffold a new dura project in a new directory |
|
|
184
|
+
| `dura dev` | Start local development server with hot-reload |
|
|
185
|
+
| `dura deploy` | Bundle and deploy to an environment |
|
|
186
|
+
| `dura run <name>` | Manually trigger an automation |
|
|
187
|
+
| `dura logs` | View execution logs |
|
|
188
|
+
| `dura diagnose` | AI-powered root cause analysis of failed executions |
|
|
189
|
+
| `dura replay <id>` | Replay a past execution |
|
|
190
|
+
| `dura test` | Run the project test suite |
|
|
191
|
+
| `dura rollback` | Revert to a previous deployment |
|
|
192
|
+
| `dura env` | Manage project environments |
|
|
193
|
+
| `dura secrets` | Manage project secrets |
|
|
194
|
+
| `dura deployments` | View deployment history |
|
|
195
|
+
| `dura status` | Show project and automation status |
|
|
196
|
+
| `dura config` | Manage CLI configuration |
|