@mediar-ai/workflow
v0.24.28
Published
TypeScript workflow SDK for Terminator
Maintainers
Readme
@mediar-ai/workflow
TypeScript SDK for building Terminator workflows with type safety, error recovery, and easy parsing for mediar-app UI.
Installation
npm install @mediar-ai/workflow zodQuick Start
import { createStep, createWorkflow, z } from '@mediar-ai/workflow';
// Define input schema
const InputSchema = z.object({
userName: z.string().default('World'),
});
// Create steps
const openApp = createStep({
id: 'open-app',
name: 'Open Notepad',
execute: async ({ desktop }) => {
desktop.openApplication('notepad');
await desktop.delay(2000);
},
});
const typeGreeting = createStep({
id: 'type-greeting',
name: 'Type Greeting',
execute: async ({ desktop, input }) => {
const textbox = await desktop.locator('role:Edit').first(2000);
await textbox.typeText(`Hello, ${input.userName}!`);
},
});
// Create workflow
const workflow = createWorkflow({
name: 'Simple Demo',
input: InputSchema,
})
.step(openApp)
.step(typeGreeting)
.build();
// Run it
workflow.run({ userName: 'Alice' });Features
✅ Type Safety
Full TypeScript support with Zod schemas:
const InputSchema = z.object({
jsonFile: z.string().describe('Path to JSON file'),
maxRetries: z.number().default(3).min(0).max(10),
sendEmail: z.boolean().default(true),
});
type Input = z.infer<typeof InputSchema>; // Fully typed!✅ Error Recovery
Built-in error recovery and retry logic:
const step = createStep({
execute: async ({ desktop }) => {
// Your logic
},
onError: async ({ error, retry, attempt }) => {
if (error.message.includes('temporary')) {
await new Promise(r => setTimeout(r, 1000 * attempt));
return retry();
}
return { recoverable: false };
},
});✅ Context Sharing
Share state between steps using return { state: {...} }:
const step1 = createStep({
execute: async ({ context }) => {
return { state: { userId: 123 } };
},
});
const step2 = createStep({
execute: async ({ context }) => {
console.log(context.state.userId); // 123
},
});✅ Conditional Execution
Steps run conditionally:
const step = createStep({
condition: ({ input }) => input.sendEmail === true,
execute: async ({ desktop }) => {
// Only runs if sendEmail is true
},
});✅ Success/Error Handlers
Workflow-level handlers:
const workflow = createWorkflow({ ... })
.step(step1)
.onSuccess(async ({ logger }) => {
logger.success('All done!');
})
.onError(async ({ error, step }) => {
console.error(`Failed at: ${step.config.name}`);
})
.build();API Reference
createStep(config)
Creates a workflow step.
Parameters:
config.id- Unique step identifierconfig.name- Human-readable nameconfig.description- Optional descriptionconfig.execute- Main execution functionconfig.onError- Optional error recovery functionconfig.timeout- Optional timeout in msconfig.condition- Optional condition function
createWorkflow(config)
Creates a workflow builder.
Parameters:
config.name- Workflow nameconfig.description- Optional descriptionconfig.version- Optional versionconfig.input- Zod input schema
Methods:
.step(step)- Add a step.onSuccess(handler)- Set success handler.onError(handler)- Set error handler.build()- Build the workflow
Control Flow
Early Success
Complete the workflow early and skip remaining steps:
import { createStep, success } from '@mediar-ai/workflow';
const checkFiles = createStep({
id: 'check-files',
name: 'Check Files',
execute: async () => {
if (noFilesFound) {
return success({
message: 'No files to process',
data: { filesChecked: 0 }
});
}
return { state: { hasFiles: true } };
},
});Step Navigation
Jump to a specific step:
import { createStep, next } from '@mediar-ai/workflow';
const checkDuplicate = createStep({
id: 'check-duplicate',
name: 'Check Duplicate',
execute: async ({ context }) => {
if (context.state.isDuplicate) {
return next('handle_duplicate'); // Jump to handle_duplicate step
}
return { state: { checked: true } };
},
});Retry from Execute
Retry the current step:
import { createStep, retry } from '@mediar-ai/workflow';
const clickButton = createStep({
id: 'click-button',
name: 'Click Button',
execute: async ({ desktop }) => {
const button = await desktop.locator('role:Button').first(1000);
if (!button) {
return retry(); // Re-execute this step
}
await button.click();
},
});Automatic Retries
Use retries for simple retry logic with exponential backoff:
const flakyStep = createStep({
id: 'flaky-step',
name: 'Flaky Operation',
retries: 3, // Retry up to 3 times
retryDelayMs: 1000, // Start with 1s delay (doubles each retry)
execute: async ({ desktop }) => {
await desktop.locator('role:Button').first(2000);
},
});Post-Execution Validation
Validate step outcomes with expect:
const submitForm = createStep({
id: 'submit-form',
name: 'Submit Form',
execute: async ({ desktop }) => {
await desktop.locator('role:Button|name:Submit').first(2000);
},
expect: async ({ desktop }) => {
const successMsg = await desktop.locator('name:Success').first(3000);
return {
success: !!successMsg,
message: successMsg ? 'Form submitted' : 'Success message not found',
};
},
});Examples
See examples/simple_notepad_workflow/ for a complete example:
src/terminator.ts- Main workflow definitionsrc/steps/- Individual step modules
License
MIT
