@elevasis/sdk 0.4.5 → 0.4.7
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/dist/cli.cjs +829 -413
- package/dist/index.d.ts +79 -14
- package/dist/index.js +17 -12
- package/dist/templates.js +747 -0
- package/dist/types/templates.d.ts +1 -0
- package/dist/types/worker/index.d.ts +6 -0
- package/dist/types/worker/platform.d.ts +32 -0
- package/dist/worker/index.js +4701 -9
- package/package.json +10 -3
- package/reference/_index.md +95 -0
- package/reference/_navigation.md +104 -0
- package/reference/cli/index.mdx +497 -0
- package/reference/concepts/index.mdx +203 -0
- package/reference/deployment/api.mdx +297 -0
- package/reference/deployment/index.mdx +153 -0
- package/reference/developer/interaction-guidance.mdx +213 -0
- package/reference/framework/agent.mdx +175 -0
- package/reference/framework/documentation.mdx +92 -0
- package/reference/framework/index.mdx +95 -0
- package/reference/framework/memory.mdx +337 -0
- package/reference/framework/project-structure.mdx +294 -0
- package/reference/getting-started/index.mdx +148 -0
- package/reference/index.mdx +113 -0
- package/reference/platform-tools/examples.mdx +187 -0
- package/reference/platform-tools/index.mdx +182 -0
- package/reference/resources/index.mdx +289 -0
- package/reference/resources/patterns.mdx +341 -0
- package/reference/resources/types.mdx +207 -0
- package/reference/roadmap/index.mdx +147 -0
- package/reference/runtime/index.mdx +141 -0
- package/reference/runtime/limits.mdx +77 -0
- package/reference/security/credentials.mdx +141 -0
- package/reference/templates/data-enrichment.mdx +162 -0
- package/reference/templates/email-sender.mdx +135 -0
- package/reference/templates/lead-scorer.mdx +175 -0
- package/reference/templates/pdf-generator.mdx +151 -0
- package/reference/templates/recurring-job.mdx +189 -0
- package/reference/templates/text-classifier.mdx +147 -0
- package/reference/templates/web-scraper.mdx +135 -0
- package/reference/troubleshooting/common-errors.mdx +210 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Writing Resources
|
|
3
|
+
description: Guide to creating workflows and agents using WorkflowDefinition, AgentDefinition, and OrganizationResources with the Elevasis SDK
|
|
4
|
+
loadWhen: "Building or modifying a workflow"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Resources are the building blocks of your Elevasis project. A resource is a workflow or agent definition that the platform can execute on your behalf. You define them in TypeScript, export them from a single entry point, and the platform handles scheduling, retries, logging, and execution.
|
|
8
|
+
|
|
9
|
+
This page covers how to structure your resources, write step handlers, and export everything through `OrganizationResources`.
|
|
10
|
+
|
|
11
|
+
## WorkflowDefinition
|
|
12
|
+
|
|
13
|
+
A workflow is a series of named steps executed in sequence or branching paths. You define it with a `WorkflowDefinition` object.
|
|
14
|
+
|
|
15
|
+
A complete workflow definition has four required properties:
|
|
16
|
+
|
|
17
|
+
- `config` - metadata about the workflow (name, description, status)
|
|
18
|
+
- `contract` - Zod schemas for input and output validation
|
|
19
|
+
- `steps` - a record of named step handlers
|
|
20
|
+
- `entryPoint` - the name of the first step to execute
|
|
21
|
+
|
|
22
|
+
### Minimal Example
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { z } from 'zod';
|
|
26
|
+
import type { WorkflowDefinition, OrganizationResources } from '@elevasis/sdk';
|
|
27
|
+
|
|
28
|
+
const echoInput = z.object({
|
|
29
|
+
message: z.string(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const echoOutput = z.object({
|
|
33
|
+
result: z.string(),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
type EchoInput = z.infer<typeof echoInput>;
|
|
37
|
+
type EchoOutput = z.infer<typeof echoOutput>;
|
|
38
|
+
|
|
39
|
+
const echoWorkflow: WorkflowDefinition = {
|
|
40
|
+
config: {
|
|
41
|
+
name: 'echo',
|
|
42
|
+
description: 'Returns the input message unchanged',
|
|
43
|
+
status: 'dev',
|
|
44
|
+
},
|
|
45
|
+
contract: {
|
|
46
|
+
input: echoInput,
|
|
47
|
+
output: echoOutput,
|
|
48
|
+
},
|
|
49
|
+
steps: {
|
|
50
|
+
run: async (input: EchoInput): Promise<EchoOutput> => {
|
|
51
|
+
return { result: input.message };
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
entryPoint: 'run',
|
|
55
|
+
};
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### config
|
|
59
|
+
|
|
60
|
+
The `config` block identifies the workflow on the platform:
|
|
61
|
+
|
|
62
|
+
| Field | Type | Description |
|
|
63
|
+
| ----- | ---- | ----------- |
|
|
64
|
+
| `name` | `string` | Unique name within your organization. Used by the CLI and platform UI. |
|
|
65
|
+
| `description` | `string` | Human-readable summary shown in the dashboard. |
|
|
66
|
+
| `status` | `'dev' | 'production'` | Controls availability. Use `'dev'` while building. See [Resource Status](#resource-status). |
|
|
67
|
+
|
|
68
|
+
### contract
|
|
69
|
+
|
|
70
|
+
The `contract` block defines the Zod schemas for input and output. The platform validates input against `contract.input` before passing it to your step handler, and validates the step's return value against `contract.output`.
|
|
71
|
+
|
|
72
|
+
Always use `z.object()` for both schemas. Use `z.infer` to derive TypeScript types from the schemas -- this keeps types and runtime validation in sync automatically.
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
const contract = {
|
|
76
|
+
input: z.object({
|
|
77
|
+
userId: z.string().uuid(),
|
|
78
|
+
action: z.string(),
|
|
79
|
+
}),
|
|
80
|
+
output: z.object({
|
|
81
|
+
success: z.boolean(),
|
|
82
|
+
message: z.string(),
|
|
83
|
+
}),
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### steps
|
|
88
|
+
|
|
89
|
+
The `steps` record maps step names to handler functions. Each handler receives the validated input and an optional `StepContext` (execution metadata). Single-step workflows have one entry; multi-step workflows have one entry per step.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
const steps = {
|
|
93
|
+
validate: async (input: MyInput) => {
|
|
94
|
+
// first step -- returns output to pass to next step
|
|
95
|
+
return { validated: true, data: input };
|
|
96
|
+
},
|
|
97
|
+
process: async (input: ValidatedData) => {
|
|
98
|
+
// second step
|
|
99
|
+
return { result: 'done' };
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### entryPoint
|
|
105
|
+
|
|
106
|
+
`entryPoint` is the name of the first step. For single-step workflows it is always the only step name. For multi-step workflows it is the name of the first step in the chain.
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
const workflow: WorkflowDefinition = {
|
|
110
|
+
// ...
|
|
111
|
+
entryPoint: 'validate', // execution starts here
|
|
112
|
+
};
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Multi-Step Workflows
|
|
118
|
+
|
|
119
|
+
For workflows with more than one step, import `StepType` from `@elevasis/sdk` to configure step routing.
|
|
120
|
+
|
|
121
|
+
### Linear Steps
|
|
122
|
+
|
|
123
|
+
Use `StepType.LINEAR` to connect steps in a fixed sequence. Each step declares a `next` property pointing to the next step name.
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { z } from 'zod';
|
|
127
|
+
import { StepType } from '@elevasis/sdk';
|
|
128
|
+
import type { WorkflowDefinition, WorkflowStep } from '@elevasis/sdk';
|
|
129
|
+
|
|
130
|
+
const fetchStep: WorkflowStep = {
|
|
131
|
+
type: StepType.LINEAR,
|
|
132
|
+
handler: async (input) => {
|
|
133
|
+
const data = await fetchSomething(input.id);
|
|
134
|
+
return { data };
|
|
135
|
+
},
|
|
136
|
+
next: { target: 'process' },
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const processStep: WorkflowStep = {
|
|
140
|
+
type: StepType.LINEAR,
|
|
141
|
+
handler: async (input) => {
|
|
142
|
+
return { result: transform(input.data) };
|
|
143
|
+
},
|
|
144
|
+
next: null, // terminal step
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const pipeline: WorkflowDefinition = {
|
|
148
|
+
config: { name: 'pipeline', description: 'Fetch then process', status: 'dev' },
|
|
149
|
+
contract: { input: pipelineInput, output: pipelineOutput },
|
|
150
|
+
steps: { fetch: fetchStep, process: processStep },
|
|
151
|
+
entryPoint: 'fetch',
|
|
152
|
+
};
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Conditional Branching
|
|
156
|
+
|
|
157
|
+
Use `StepType.CONDITIONAL` to route to different steps based on output values. Each route has a `condition` function and a `target` step name. The first condition that returns `true` wins. A `defaultTarget` is required as a fallback.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import { StepType } from '@elevasis/sdk';
|
|
161
|
+
import type { WorkflowStep } from '@elevasis/sdk';
|
|
162
|
+
|
|
163
|
+
const routerStep: WorkflowStep = {
|
|
164
|
+
type: StepType.CONDITIONAL,
|
|
165
|
+
handler: async (input) => {
|
|
166
|
+
const score = await evaluate(input);
|
|
167
|
+
return { score };
|
|
168
|
+
},
|
|
169
|
+
next: {
|
|
170
|
+
routes: [
|
|
171
|
+
{
|
|
172
|
+
condition: (output) => output.score \>= 80,
|
|
173
|
+
target: 'highScorePath',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
condition: (output) => output.score \>= 50,
|
|
177
|
+
target: 'mediumScorePath',
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
defaultTarget: 'lowScorePath',
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## StepContext
|
|
188
|
+
|
|
189
|
+
Every step handler can optionally accept a second `context` parameter. TypeScript allows you to omit it if you do not need it -- you can write `handler: async (input) => { ... }` without declaring `context`.
|
|
190
|
+
|
|
191
|
+
When you do need it, the context provides runtime metadata:
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import type { StepHandler } from '@elevasis/sdk';
|
|
195
|
+
|
|
196
|
+
const myStep: StepHandler = async (input, context) => {
|
|
197
|
+
const { executionId, organizationId, resourceId, logger, store } = context;
|
|
198
|
+
|
|
199
|
+
logger.info('Starting step', { executionId });
|
|
200
|
+
|
|
201
|
+
// store is a simple key-value store scoped to this execution
|
|
202
|
+
await store.set('progress', '50%');
|
|
203
|
+
|
|
204
|
+
return { done: true };
|
|
205
|
+
};
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
| Field | Type | Description |
|
|
209
|
+
| ----- | ---- | ----------- |
|
|
210
|
+
| `executionId` | `string` | Unique ID for this execution run |
|
|
211
|
+
| `organizationId` | `string` | Your organization ID |
|
|
212
|
+
| `resourceId` | `string` | ID of the workflow or agent being executed |
|
|
213
|
+
| `logger` | `Logger` | Structured logger -- messages appear in the Elevasis dashboard |
|
|
214
|
+
| `store` | `Store` | Key-value store scoped to the current execution |
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## AgentDefinition
|
|
219
|
+
|
|
220
|
+
Agents are autonomous resources that use an LLM and tools to complete a goal. You define them with `AgentDefinition`.
|
|
221
|
+
|
|
222
|
+
**Note:** Agent execution in the worker runtime is not yet fully supported. Defining agents is supported for forward compatibility, but executions will return a failure response until agent execution ships.
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
import type { AgentDefinition } from '@elevasis/sdk';
|
|
226
|
+
|
|
227
|
+
const myAgent: AgentDefinition = {
|
|
228
|
+
config: {
|
|
229
|
+
name: 'my-agent',
|
|
230
|
+
description: 'Answers questions using platform tools',
|
|
231
|
+
status: 'dev',
|
|
232
|
+
},
|
|
233
|
+
agentConfig: {
|
|
234
|
+
model: { provider: 'openai', model: 'gpt-4o' },
|
|
235
|
+
systemPrompt: 'You are a helpful assistant.',
|
|
236
|
+
maxIterations: 10,
|
|
237
|
+
},
|
|
238
|
+
tools: [],
|
|
239
|
+
};
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## OrganizationResources
|
|
245
|
+
|
|
246
|
+
All resources must be exported through an `OrganizationResources` default export from `src/index.ts`. This is the entry point the platform reads when you deploy.
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
import type { OrganizationResources } from '@elevasis/sdk';
|
|
250
|
+
|
|
251
|
+
const org: OrganizationResources = {
|
|
252
|
+
workflows: {
|
|
253
|
+
echo: echoWorkflow,
|
|
254
|
+
pipeline: pipeline,
|
|
255
|
+
},
|
|
256
|
+
agents: {
|
|
257
|
+
// myAgent: myAgent,
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export default org;
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
The keys under `workflows` and `agents` are used as resource identifiers on the platform. They appear in CLI output (`elevasis resources`), execution commands (`elevasis exec`), and the dashboard.
|
|
265
|
+
|
|
266
|
+
### Resource Status
|
|
267
|
+
|
|
268
|
+
Set `config.status` to control whether the platform accepts execution requests:
|
|
269
|
+
|
|
270
|
+
- `'dev'` - Resource is visible in the dashboard but the platform will not route scheduled or webhook-triggered executions to it. You can still trigger it manually via `elevasis exec`.
|
|
271
|
+
- `'production'` - Resource is live and accepts all execution sources.
|
|
272
|
+
|
|
273
|
+
Use `'dev'` while you are building and testing. Switch to `'production'` when you are ready to go live.
|
|
274
|
+
|
|
275
|
+
You can also set a global default status in `elevasis.config.ts`:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
import type { ElevasConfig } from '@elevasis/sdk';
|
|
279
|
+
|
|
280
|
+
const config: ElevasConfig = {
|
|
281
|
+
defaultStatus: 'dev',
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
export default config;
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
**Last Updated:** 2026-02-25
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Common Patterns
|
|
3
|
+
description: Common resource patterns for Elevasis SDK developers -- sequential steps, conditional branching, platform tools, error handling, and resource status management
|
|
4
|
+
loadWhen: "Building or modifying a workflow"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
This page collects the patterns you will reach for most often when writing resources. Each pattern is self-contained and can be adapted directly into your project.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Sequential Workflow Steps
|
|
12
|
+
|
|
13
|
+
The simplest pattern: a chain of steps where each step feeds its output into the next.
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import { StepType } from '@elevasis/sdk';
|
|
18
|
+
import type { WorkflowDefinition, WorkflowStep } from '@elevasis/sdk';
|
|
19
|
+
|
|
20
|
+
const inputSchema = z.object({ orderId: z.string() });
|
|
21
|
+
const outputSchema = z.object({ shipped: z.boolean(), trackingNumber: z.string() });
|
|
22
|
+
|
|
23
|
+
type Input = z.infer<typeof inputSchema>;
|
|
24
|
+
type Output = z.infer<typeof outputSchema>;
|
|
25
|
+
|
|
26
|
+
const validateStep: WorkflowStep = {
|
|
27
|
+
type: StepType.LINEAR,
|
|
28
|
+
handler: async (input: Input) => {
|
|
29
|
+
const order = await getOrder(input.orderId);
|
|
30
|
+
if (!order) throw new Error(`Order ${input.orderId} not found`);
|
|
31
|
+
return { order };
|
|
32
|
+
},
|
|
33
|
+
next: { target: 'ship' },
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const shipStep: WorkflowStep = {
|
|
37
|
+
type: StepType.LINEAR,
|
|
38
|
+
handler: async (input) => {
|
|
39
|
+
const tracking = await createShipment(input.order);
|
|
40
|
+
return { shipped: true, trackingNumber: tracking.number };
|
|
41
|
+
},
|
|
42
|
+
next: null, // terminal -- no further steps
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const fulfillOrder: WorkflowDefinition = {
|
|
46
|
+
config: { name: 'fulfill-order', description: 'Validates and ships an order', status: 'dev' },
|
|
47
|
+
contract: { input: inputSchema, output: outputSchema },
|
|
48
|
+
steps: { validate: validateStep, ship: shipStep },
|
|
49
|
+
entryPoint: 'validate',
|
|
50
|
+
};
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Key points:**
|
|
54
|
+
|
|
55
|
+
- `next: { target: 'stepName' }` routes to the next step
|
|
56
|
+
- `next: null` marks the terminal step
|
|
57
|
+
- Each step receives the full return value of the previous step as its `input`
|
|
58
|
+
- The terminal step's return value must satisfy `contract.output`
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Conditional Branching
|
|
63
|
+
|
|
64
|
+
Use `StepType.CONDITIONAL` when the next step depends on the output of the current step.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { StepType } from '@elevasis/sdk';
|
|
68
|
+
import type { WorkflowStep } from '@elevasis/sdk';
|
|
69
|
+
|
|
70
|
+
const scoreStep: WorkflowStep = {
|
|
71
|
+
type: StepType.CONDITIONAL,
|
|
72
|
+
handler: async (input) => {
|
|
73
|
+
const score = await calculateRiskScore(input.applicationId);
|
|
74
|
+
return { score, applicationId: input.applicationId };
|
|
75
|
+
},
|
|
76
|
+
next: {
|
|
77
|
+
routes: [
|
|
78
|
+
{
|
|
79
|
+
condition: (output) => output.score \>= 80,
|
|
80
|
+
target: 'autoApprove',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
condition: (output) => output.score \>= 40,
|
|
84
|
+
target: 'manualReview',
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
defaultTarget: 'autoReject', // used when no condition matches
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Key points:**
|
|
93
|
+
|
|
94
|
+
- Routes are evaluated in order -- the first matching condition wins
|
|
95
|
+
- `defaultTarget` is required and acts as the `else` branch
|
|
96
|
+
- The condition function receives the full handler output
|
|
97
|
+
- All route targets and `defaultTarget` must be keys in your `steps` record
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Using Platform Tools in Steps
|
|
102
|
+
|
|
103
|
+
Platform tools let your steps call integrations managed by Elevasis (email, CRM, databases, etc.). Import `platform` from `@elevasis/sdk/worker` and call it with the tool name, method, parameters, and a credential reference.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { platform, PlatformToolError } from '@elevasis/sdk/worker';
|
|
107
|
+
import type { WorkflowStep } from '@elevasis/sdk';
|
|
108
|
+
import { StepType } from '@elevasis/sdk';
|
|
109
|
+
|
|
110
|
+
const sendEmailStep: WorkflowStep = {
|
|
111
|
+
type: StepType.LINEAR,
|
|
112
|
+
handler: async (input, context) => {
|
|
113
|
+
const result = await platform.call({
|
|
114
|
+
tool: 'email',
|
|
115
|
+
method: 'send',
|
|
116
|
+
params: {
|
|
117
|
+
to: input.recipientEmail,
|
|
118
|
+
subject: input.subject,
|
|
119
|
+
body: input.body,
|
|
120
|
+
},
|
|
121
|
+
credential: 'sendgrid', // name of the stored credential
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
context.logger.info('Email sent', { messageId: result.messageId });
|
|
125
|
+
return { sent: true, messageId: result.messageId };
|
|
126
|
+
},
|
|
127
|
+
next: null,
|
|
128
|
+
};
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Key points:**
|
|
132
|
+
|
|
133
|
+
- `platform.call()` is async and times out after 60 seconds
|
|
134
|
+
- `credential` is the name of a platform environment variable set via `elevasis env set`
|
|
135
|
+
- On failure, `platform.call()` throws `PlatformToolError` (not `ToolingError`)
|
|
136
|
+
- Always log success so executions are easy to debug in the dashboard
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Error Handling
|
|
141
|
+
|
|
142
|
+
### Catching Tool Errors
|
|
143
|
+
|
|
144
|
+
Use `PlatformToolError` (from `@elevasis/sdk/worker`) to handle tool-specific failures without catching everything:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { platform, PlatformToolError } from '@elevasis/sdk/worker';
|
|
148
|
+
|
|
149
|
+
const step = async (input) => {
|
|
150
|
+
try {
|
|
151
|
+
const result = await platform.call({
|
|
152
|
+
tool: 'crm',
|
|
153
|
+
method: 'createContact',
|
|
154
|
+
params: { email: input.email, name: input.name },
|
|
155
|
+
credential: 'CRM_API_KEY',
|
|
156
|
+
});
|
|
157
|
+
return { contactId: result.id };
|
|
158
|
+
} catch (err) {
|
|
159
|
+
if (err instanceof PlatformToolError) {
|
|
160
|
+
// Tool failed -- log it and return a degraded result
|
|
161
|
+
console.error('CRM tool failed:', err.message);
|
|
162
|
+
return { contactId: null, error: err.message };
|
|
163
|
+
}
|
|
164
|
+
throw err; // re-throw unexpected errors
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Failing an Execution Explicitly
|
|
170
|
+
|
|
171
|
+
Use `ExecutionError` when your step detects a condition that should mark the entire execution as failed:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import { ExecutionError } from '@elevasis/sdk';
|
|
175
|
+
|
|
176
|
+
const validateStep = async (input) => {
|
|
177
|
+
if (!input.userId) {
|
|
178
|
+
throw new ExecutionError('userId is required', { code: 'MISSING_INPUT' });
|
|
179
|
+
}
|
|
180
|
+
if (input.amount \<= 0) {
|
|
181
|
+
throw new ExecutionError('amount must be positive', { code: 'INVALID_AMOUNT' });
|
|
182
|
+
}
|
|
183
|
+
return { valid: true };
|
|
184
|
+
};
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`ExecutionError` messages and metadata appear in the Elevasis dashboard under the failed execution's detail view.
|
|
188
|
+
|
|
189
|
+
### Using ToolingError
|
|
190
|
+
|
|
191
|
+
`ToolingError` is thrown by lower-level platform operations (not `platform.call()` directly). You may encounter it in advanced scenarios:
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import { ToolingError } from '@elevasis/sdk';
|
|
195
|
+
|
|
196
|
+
const step = async (input) => {
|
|
197
|
+
try {
|
|
198
|
+
return await doSomething(input);
|
|
199
|
+
} catch (err) {
|
|
200
|
+
if (err instanceof ToolingError) {
|
|
201
|
+
// check err.type for the error category
|
|
202
|
+
console.error('Tooling error:', err.type, err.message);
|
|
203
|
+
}
|
|
204
|
+
throw err;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Logging in Steps
|
|
212
|
+
|
|
213
|
+
The `context.logger` writes structured logs attached to the execution. Use it instead of `console.log` so logs appear in the dashboard alongside the execution record.
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import type { StepHandler } from '@elevasis/sdk';
|
|
217
|
+
|
|
218
|
+
const processStep: StepHandler = async (input, context) => {
|
|
219
|
+
context.logger.info('Starting process', { userId: input.userId });
|
|
220
|
+
|
|
221
|
+
const result = await doWork(input);
|
|
222
|
+
|
|
223
|
+
context.logger.info('Process complete', { resultId: result.id });
|
|
224
|
+
return result;
|
|
225
|
+
};
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Avoid logging sensitive values (API keys, passwords, PII) since logs are stored and visible in the dashboard.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Using the Execution Store
|
|
233
|
+
|
|
234
|
+
`context.store` is a simple key-value store scoped to the current execution. Use it to pass data between steps without coupling step interfaces, or to checkpoint long-running work.
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const firstStep: StepHandler = async (input, context) => {
|
|
238
|
+
const data = await fetchExpensiveData(input.id);
|
|
239
|
+
|
|
240
|
+
// Save for use by later steps
|
|
241
|
+
await context.store.set('fetchedData', JSON.stringify(data));
|
|
242
|
+
|
|
243
|
+
return { fetched: true };
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const secondStep: StepHandler = async (input, context) => {
|
|
247
|
+
const raw = await context.store.get('fetchedData');
|
|
248
|
+
const data = JSON.parse(raw ?? '{}');
|
|
249
|
+
|
|
250
|
+
return { processed: transform(data) };
|
|
251
|
+
};
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Store values are strings. Serialize objects with `JSON.stringify` and parse with `JSON.parse`.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Resource Status
|
|
259
|
+
|
|
260
|
+
### Dev vs Production
|
|
261
|
+
|
|
262
|
+
While building a resource, set `config.status` to `'dev'`:
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
const myWorkflow: WorkflowDefinition = {
|
|
266
|
+
config: {
|
|
267
|
+
name: 'my-workflow',
|
|
268
|
+
description: 'Does something useful',
|
|
269
|
+
status: 'dev', // only manually triggerable
|
|
270
|
+
},
|
|
271
|
+
// ...
|
|
272
|
+
};
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Dev resources:
|
|
276
|
+
|
|
277
|
+
- Appear in `elevasis resources` output
|
|
278
|
+
- Can be triggered with `elevasis exec my-workflow --input '{...}'`
|
|
279
|
+
- Will NOT receive scheduled or webhook-triggered executions
|
|
280
|
+
- Will NOT appear as available to external callers
|
|
281
|
+
|
|
282
|
+
When you are ready to go live, change to `'production'` and redeploy:
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
config: {
|
|
286
|
+
name: 'my-workflow',
|
|
287
|
+
description: 'Does something useful',
|
|
288
|
+
status: 'production', // receives all execution sources
|
|
289
|
+
},
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Global Default Status
|
|
293
|
+
|
|
294
|
+
Set a project-wide default in `elevasis.config.ts` to keep all resources in `'dev'` mode during development without touching each resource file:
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
import type { ElevasConfig } from '@elevasis/sdk';
|
|
298
|
+
|
|
299
|
+
const config: ElevasConfig = {
|
|
300
|
+
defaultStatus: 'dev',
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
export default config;
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Individual resources that set their own `config.status` override this default.
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Organizing Multiple Resources
|
|
311
|
+
|
|
312
|
+
As your project grows, split resources into separate files and import them into `src/index.ts`:
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
// src/workflows/fulfill-order.ts
|
|
316
|
+
export const fulfillOrder: WorkflowDefinition = { ... };
|
|
317
|
+
|
|
318
|
+
// src/workflows/send-invoice.ts
|
|
319
|
+
export const sendInvoice: WorkflowDefinition = { ... };
|
|
320
|
+
|
|
321
|
+
// src/index.ts
|
|
322
|
+
import { fulfillOrder } from './workflows/fulfill-order';
|
|
323
|
+
import { sendInvoice } from './workflows/send-invoice';
|
|
324
|
+
import type { OrganizationResources } from '@elevasis/sdk';
|
|
325
|
+
|
|
326
|
+
const org: OrganizationResources = {
|
|
327
|
+
workflows: {
|
|
328
|
+
'fulfill-order': fulfillOrder,
|
|
329
|
+
'send-invoice': sendInvoice,
|
|
330
|
+
},
|
|
331
|
+
agents: {},
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
export default org;
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
The keys in `workflows` and `agents` are the resource identifiers used in CLI commands and the dashboard. Choose names that are descriptive and use kebab-case for consistency.
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
**Last Updated:** 2026-02-25
|