vargai 0.3.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 (154) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.env.example +27 -0
  3. package/.github/workflows/ci.yml +23 -0
  4. package/.husky/README.md +102 -0
  5. package/.husky/commit-msg +6 -0
  6. package/.husky/pre-commit +9 -0
  7. package/.husky/pre-push +6 -0
  8. package/.size-limit.json +8 -0
  9. package/.test-hooks.ts +5 -0
  10. package/CLAUDE.md +125 -0
  11. package/CONTRIBUTING.md +150 -0
  12. package/LICENSE.md +53 -0
  13. package/README.md +78 -0
  14. package/SKILLS.md +173 -0
  15. package/STRUCTURE.md +92 -0
  16. package/biome.json +34 -0
  17. package/bun.lock +1254 -0
  18. package/commitlint.config.js +22 -0
  19. package/docs/plan.md +66 -0
  20. package/docs/todo.md +14 -0
  21. package/docs/varg-sdk.md +812 -0
  22. package/ffmpeg/CLAUDE.md +68 -0
  23. package/package.json +69 -0
  24. package/pipeline/cookbooks/SKILL.md +285 -0
  25. package/pipeline/cookbooks/remotion-video.md +585 -0
  26. package/pipeline/cookbooks/round-video-character.md +337 -0
  27. package/pipeline/cookbooks/scripts/animate-frames-parallel.ts +84 -0
  28. package/pipeline/cookbooks/scripts/combine-scenes.sh +53 -0
  29. package/pipeline/cookbooks/scripts/generate-frames-parallel.ts +99 -0
  30. package/pipeline/cookbooks/scripts/still-to-video.sh +37 -0
  31. package/pipeline/cookbooks/talking-character.md +59 -0
  32. package/pipeline/cookbooks/text-to-tiktok.md +669 -0
  33. package/pipeline/cookbooks/trendwatching.md +156 -0
  34. package/plan.md +281 -0
  35. package/scripts/.gitkeep +0 -0
  36. package/src/ai-sdk/cache.ts +142 -0
  37. package/src/ai-sdk/examples/cached-generation.ts +53 -0
  38. package/src/ai-sdk/examples/duet-scene-4.ts +53 -0
  39. package/src/ai-sdk/examples/duet-scene-5-audio.ts +32 -0
  40. package/src/ai-sdk/examples/duet-video.ts +56 -0
  41. package/src/ai-sdk/examples/editly-composition.ts +63 -0
  42. package/src/ai-sdk/examples/editly-test.ts +57 -0
  43. package/src/ai-sdk/examples/editly-video-test.ts +52 -0
  44. package/src/ai-sdk/examples/fal-lipsync.ts +43 -0
  45. package/src/ai-sdk/examples/higgsfield-image.ts +61 -0
  46. package/src/ai-sdk/examples/music-generation.ts +19 -0
  47. package/src/ai-sdk/examples/openai-sora.ts +34 -0
  48. package/src/ai-sdk/examples/replicate-bg-removal.ts +52 -0
  49. package/src/ai-sdk/examples/simpsons-scene.ts +61 -0
  50. package/src/ai-sdk/examples/talking-lion.ts +55 -0
  51. package/src/ai-sdk/examples/video-generation.ts +39 -0
  52. package/src/ai-sdk/examples/workflow-animated-girl.ts +104 -0
  53. package/src/ai-sdk/examples/workflow-before-after.ts +114 -0
  54. package/src/ai-sdk/examples/workflow-character-grid.ts +112 -0
  55. package/src/ai-sdk/examples/workflow-slideshow.ts +161 -0
  56. package/src/ai-sdk/file-cache.ts +112 -0
  57. package/src/ai-sdk/file.ts +238 -0
  58. package/src/ai-sdk/generate-element.ts +92 -0
  59. package/src/ai-sdk/generate-music.ts +46 -0
  60. package/src/ai-sdk/generate-video.ts +165 -0
  61. package/src/ai-sdk/index.ts +72 -0
  62. package/src/ai-sdk/music-model.ts +110 -0
  63. package/src/ai-sdk/providers/editly/editly.test.ts +1108 -0
  64. package/src/ai-sdk/providers/editly/ffmpeg.ts +60 -0
  65. package/src/ai-sdk/providers/editly/index.ts +817 -0
  66. package/src/ai-sdk/providers/editly/layers.ts +772 -0
  67. package/src/ai-sdk/providers/editly/plan.md +144 -0
  68. package/src/ai-sdk/providers/editly/types.ts +328 -0
  69. package/src/ai-sdk/providers/elevenlabs-provider.ts +255 -0
  70. package/src/ai-sdk/providers/fal-provider.ts +512 -0
  71. package/src/ai-sdk/providers/higgsfield.ts +379 -0
  72. package/src/ai-sdk/providers/openai.ts +251 -0
  73. package/src/ai-sdk/providers/replicate.ts +16 -0
  74. package/src/ai-sdk/video-model.ts +185 -0
  75. package/src/cli/commands/find.tsx +137 -0
  76. package/src/cli/commands/help.tsx +85 -0
  77. package/src/cli/commands/index.ts +9 -0
  78. package/src/cli/commands/list.tsx +238 -0
  79. package/src/cli/commands/run.tsx +511 -0
  80. package/src/cli/commands/which.tsx +253 -0
  81. package/src/cli/index.ts +112 -0
  82. package/src/cli/quiet.ts +44 -0
  83. package/src/cli/types.ts +32 -0
  84. package/src/cli/ui/components/Badge.tsx +29 -0
  85. package/src/cli/ui/components/DataTable.tsx +51 -0
  86. package/src/cli/ui/components/Header.tsx +23 -0
  87. package/src/cli/ui/components/HelpBlock.tsx +44 -0
  88. package/src/cli/ui/components/KeyValue.tsx +33 -0
  89. package/src/cli/ui/components/OptionRow.tsx +81 -0
  90. package/src/cli/ui/components/Separator.tsx +23 -0
  91. package/src/cli/ui/components/StatusBox.tsx +108 -0
  92. package/src/cli/ui/components/VargBox.tsx +51 -0
  93. package/src/cli/ui/components/VargProgress.tsx +36 -0
  94. package/src/cli/ui/components/VargSpinner.tsx +34 -0
  95. package/src/cli/ui/components/VargText.tsx +56 -0
  96. package/src/cli/ui/components/index.ts +19 -0
  97. package/src/cli/ui/index.ts +12 -0
  98. package/src/cli/ui/render.ts +35 -0
  99. package/src/cli/ui/theme.ts +63 -0
  100. package/src/cli/utils.ts +78 -0
  101. package/src/core/executor/executor.ts +201 -0
  102. package/src/core/executor/index.ts +13 -0
  103. package/src/core/executor/job.ts +214 -0
  104. package/src/core/executor/pipeline.ts +222 -0
  105. package/src/core/index.ts +11 -0
  106. package/src/core/registry/index.ts +9 -0
  107. package/src/core/registry/loader.ts +149 -0
  108. package/src/core/registry/registry.ts +221 -0
  109. package/src/core/registry/resolver.ts +206 -0
  110. package/src/core/schema/helpers.ts +134 -0
  111. package/src/core/schema/index.ts +8 -0
  112. package/src/core/schema/shared.ts +102 -0
  113. package/src/core/schema/types.ts +279 -0
  114. package/src/core/schema/validator.ts +92 -0
  115. package/src/definitions/actions/captions.ts +261 -0
  116. package/src/definitions/actions/edit.ts +298 -0
  117. package/src/definitions/actions/image.ts +125 -0
  118. package/src/definitions/actions/index.ts +114 -0
  119. package/src/definitions/actions/music.ts +205 -0
  120. package/src/definitions/actions/sync.ts +128 -0
  121. package/src/definitions/actions/transcribe.ts +200 -0
  122. package/src/definitions/actions/upload.ts +111 -0
  123. package/src/definitions/actions/video.ts +163 -0
  124. package/src/definitions/actions/voice.ts +119 -0
  125. package/src/definitions/index.ts +23 -0
  126. package/src/definitions/models/elevenlabs.ts +50 -0
  127. package/src/definitions/models/flux.ts +56 -0
  128. package/src/definitions/models/index.ts +36 -0
  129. package/src/definitions/models/kling.ts +56 -0
  130. package/src/definitions/models/llama.ts +54 -0
  131. package/src/definitions/models/nano-banana-pro.ts +102 -0
  132. package/src/definitions/models/sonauto.ts +68 -0
  133. package/src/definitions/models/soul.ts +65 -0
  134. package/src/definitions/models/wan.ts +54 -0
  135. package/src/definitions/models/whisper.ts +44 -0
  136. package/src/definitions/skills/index.ts +12 -0
  137. package/src/definitions/skills/talking-character.ts +87 -0
  138. package/src/definitions/skills/text-to-tiktok.ts +97 -0
  139. package/src/index.ts +118 -0
  140. package/src/providers/apify.ts +269 -0
  141. package/src/providers/base.ts +264 -0
  142. package/src/providers/elevenlabs.ts +217 -0
  143. package/src/providers/fal.ts +392 -0
  144. package/src/providers/ffmpeg.ts +544 -0
  145. package/src/providers/fireworks.ts +193 -0
  146. package/src/providers/groq.ts +149 -0
  147. package/src/providers/higgsfield.ts +145 -0
  148. package/src/providers/index.ts +143 -0
  149. package/src/providers/replicate.ts +147 -0
  150. package/src/providers/storage.ts +206 -0
  151. package/src/tests/all.test.ts +509 -0
  152. package/src/tests/index.ts +33 -0
  153. package/src/tests/unit.test.ts +403 -0
  154. package/tsconfig.json +45 -0
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Main Execution Orchestrator
3
+ * Routes execution to appropriate handlers based on definition type
4
+ */
5
+
6
+ import { registry } from "../registry/registry";
7
+ import { resolve } from "../registry/resolver";
8
+ import type {
9
+ ActionDefinition,
10
+ Definition,
11
+ ExecutionResult,
12
+ ModelDefinition,
13
+ RunOptions,
14
+ SkillDefinition,
15
+ } from "../schema/types";
16
+ import { validateAndPrepare } from "../schema/validator";
17
+ import { jobRunner } from "./job";
18
+ import { pipelineRunner } from "./pipeline";
19
+
20
+ export class Executor {
21
+ /**
22
+ * Run a definition by name
23
+ */
24
+ async run(
25
+ name: string,
26
+ inputs: Record<string, unknown>,
27
+ options?: RunOptions,
28
+ ): Promise<ExecutionResult> {
29
+ // Resolve name to definition
30
+ const result = resolve(name, { required: true });
31
+ const definition = result.definition;
32
+ if (!definition) {
33
+ throw new Error(`Definition not found: ${name}`);
34
+ }
35
+
36
+ // Validate and prepare inputs using Zod schema
37
+ const validation = validateAndPrepare(inputs, definition.schema.input);
38
+ if (!validation.valid) {
39
+ const errorMessages = validation.errors
40
+ .map((e) => (e.path ? `${e.path}: ${e.message}` : e.message))
41
+ .join(", ");
42
+ throw new Error(`Validation failed: ${errorMessages}`);
43
+ }
44
+
45
+ // Use validated and transformed data (Zod applies defaults)
46
+ const validatedInputs = validation.data as Record<string, unknown>;
47
+
48
+ // Route to appropriate handler
49
+ switch (definition.type) {
50
+ case "model":
51
+ return this.runModel(definition, validatedInputs, options);
52
+ case "action":
53
+ return this.runAction(definition, validatedInputs, options);
54
+ case "skill":
55
+ return this.runSkill(definition, validatedInputs, options);
56
+ default:
57
+ throw new Error(
58
+ `Unknown definition type: ${(definition as Definition).type}`,
59
+ );
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Run a model directly
65
+ */
66
+ async runModel(
67
+ model: ModelDefinition,
68
+ inputs: Record<string, unknown>,
69
+ options?: RunOptions,
70
+ ): Promise<ExecutionResult> {
71
+ // Determine provider
72
+ const providerName = options?.provider ?? model.defaultProvider;
73
+ const provider = registry.getProvider(providerName);
74
+
75
+ if (!provider) {
76
+ throw new Error(
77
+ `Provider not found: ${providerName}. Available: ${registry
78
+ .listProviders()
79
+ .map((p) => p.name)
80
+ .join(", ")}`,
81
+ );
82
+ }
83
+
84
+ // Get provider-specific model ID
85
+ const providerModelId = model.providerModels?.[providerName] ?? model.name;
86
+
87
+ // Run via job runner
88
+ return jobRunner.run({
89
+ provider,
90
+ model: providerModelId,
91
+ inputs,
92
+ options,
93
+ });
94
+ }
95
+
96
+ /**
97
+ * Run an action
98
+ * Actions can route to models or have direct execution
99
+ */
100
+ async runAction(
101
+ action: ActionDefinition,
102
+ inputs: Record<string, unknown>,
103
+ options?: RunOptions,
104
+ ): Promise<ExecutionResult> {
105
+ // If action has direct execute function, use it
106
+ if (action.execute) {
107
+ const startTime = Date.now();
108
+ const output = await action.execute(inputs);
109
+
110
+ return {
111
+ output: output as string | Record<string, unknown>,
112
+ duration: Date.now() - startTime,
113
+ provider: "local",
114
+ model: action.name,
115
+ };
116
+ }
117
+
118
+ // Otherwise, route to a model
119
+ const route = this.selectRoute(action, inputs, options);
120
+
121
+ if (!route) {
122
+ throw new Error(`No valid route found for action: ${action.name}`);
123
+ }
124
+
125
+ // Transform inputs if needed
126
+ const transformedInputs = route.transform
127
+ ? route.transform(inputs)
128
+ : inputs;
129
+
130
+ // Recursive call to run the target
131
+ return this.run(route.target, transformedInputs, options);
132
+ }
133
+
134
+ /**
135
+ * Run a skill (multi-step pipeline)
136
+ */
137
+ async runSkill(
138
+ skill: SkillDefinition,
139
+ inputs: Record<string, unknown>,
140
+ options?: RunOptions,
141
+ ): Promise<ExecutionResult> {
142
+ return pipelineRunner.run(
143
+ skill,
144
+ inputs,
145
+ (name, stepInputs, stepOptions) =>
146
+ this.run(name, stepInputs, stepOptions),
147
+ options,
148
+ );
149
+ }
150
+
151
+ /**
152
+ * Select the best route for an action based on inputs and conditions
153
+ */
154
+ private selectRoute(
155
+ action: ActionDefinition,
156
+ inputs: Record<string, unknown>,
157
+ _options?: RunOptions,
158
+ ) {
159
+ // Filter routes by conditions
160
+ const validRoutes = action.routes.filter((route) => {
161
+ if (!route.when) return true;
162
+
163
+ // Check each condition
164
+ for (const [key, expected] of Object.entries(route.when)) {
165
+ const actual = inputs[key];
166
+
167
+ // Handle special condition operators
168
+ if (typeof expected === "object" && expected !== null) {
169
+ const cond = expected as Record<string, unknown>;
170
+
171
+ if ("$lt" in cond && !(Number(actual) < Number(cond.$lt)))
172
+ return false;
173
+ if ("$lte" in cond && !(Number(actual) <= Number(cond.$lte)))
174
+ return false;
175
+ if ("$gt" in cond && !(Number(actual) > Number(cond.$gt)))
176
+ return false;
177
+ if ("$gte" in cond && !(Number(actual) >= Number(cond.$gte)))
178
+ return false;
179
+ if ("$eq" in cond && actual !== cond.$eq) return false;
180
+ if ("$ne" in cond && actual === cond.$ne) return false;
181
+ if ("$in" in cond && !Array.isArray(cond.$in)) return false;
182
+ if ("$in" in cond && !(cond.$in as unknown[]).includes(actual))
183
+ return false;
184
+ } else if (actual !== expected) {
185
+ return false;
186
+ }
187
+ }
188
+
189
+ return true;
190
+ });
191
+
192
+ // Sort by priority (higher = preferred)
193
+ validRoutes.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
194
+
195
+ // Return the highest priority route
196
+ return validRoutes[0];
197
+ }
198
+ }
199
+
200
+ // Global executor instance
201
+ export const executor = new Executor();
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Executor module exports
3
+ */
4
+
5
+ export { Executor, executor } from "./executor";
6
+ export type { JobConfig } from "./job";
7
+ export { JobRunner, jobRunner } from "./job";
8
+ export type {
9
+ PipelineContext,
10
+ PipelineOptions,
11
+ StepExecutor,
12
+ } from "./pipeline";
13
+ export { PipelineRunner, pipelineRunner } from "./pipeline";
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Job State Machine
3
+ * Manages job lifecycle: queued -> processing -> completed/failed
4
+ */
5
+
6
+ import type {
7
+ ExecutionResult,
8
+ Job,
9
+ JobStatus,
10
+ JobStatusUpdate,
11
+ Provider,
12
+ RunOptions,
13
+ } from "../schema/types";
14
+
15
+ export interface JobConfig {
16
+ provider: Provider;
17
+ model: string;
18
+ inputs: Record<string, unknown>;
19
+ options?: RunOptions;
20
+ }
21
+
22
+ export class JobRunner {
23
+ private jobs = new Map<string, Job>();
24
+ private pollInterval = 2000; // Start with 2s
25
+ private maxPollInterval = 10000; // Cap at 10s
26
+
27
+ /**
28
+ * Create and run a job
29
+ */
30
+ async run(config: JobConfig): Promise<ExecutionResult> {
31
+ const { provider, model, inputs, options } = config;
32
+ const startTime = Date.now();
33
+
34
+ // Create job record
35
+ const job: Job = {
36
+ id: "",
37
+ status: "pending",
38
+ provider: provider.name,
39
+ model,
40
+ inputs,
41
+ createdAt: new Date(),
42
+ updatedAt: new Date(),
43
+ logs: [],
44
+ };
45
+
46
+ try {
47
+ // Submit to provider
48
+ job.status = "queued";
49
+ job.id = await provider.submit(model, inputs);
50
+ this.jobs.set(job.id, job);
51
+
52
+ if (options?.onStatusChange) {
53
+ options.onStatusChange("queued");
54
+ }
55
+
56
+ // If not waiting, return immediately
57
+ if (options?.wait === false) {
58
+ return {
59
+ output: { jobId: job.id },
60
+ duration: Date.now() - startTime,
61
+ provider: provider.name,
62
+ model,
63
+ jobId: job.id,
64
+ };
65
+ }
66
+
67
+ // Poll for completion
68
+ const result = await this.waitForCompletion(job, provider, options);
69
+
70
+ // Update job record
71
+ job.status = "completed";
72
+ job.output = result;
73
+ job.completedAt = new Date();
74
+ job.updatedAt = new Date();
75
+
76
+ const duration = Date.now() - startTime;
77
+
78
+ return {
79
+ output: result as string | Record<string, unknown>,
80
+ duration,
81
+ provider: provider.name,
82
+ model,
83
+ jobId: job.id,
84
+ };
85
+ } catch (error) {
86
+ job.status = "failed";
87
+ job.error = error instanceof Error ? error.message : String(error);
88
+ job.updatedAt = new Date();
89
+
90
+ if (options?.onStatusChange) {
91
+ options.onStatusChange("failed");
92
+ }
93
+
94
+ throw error;
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Wait for a job to complete with polling
100
+ */
101
+ private async waitForCompletion(
102
+ job: Job,
103
+ provider: Provider,
104
+ options?: RunOptions,
105
+ ): Promise<unknown> {
106
+ const timeout = options?.timeout ?? 300000; // 5 minutes default
107
+ const startTime = Date.now();
108
+ let currentInterval = this.pollInterval;
109
+
110
+ while (Date.now() - startTime < timeout) {
111
+ const status = await provider.getStatus(job.id);
112
+
113
+ // Update job record
114
+ job.status = this.mapStatus(status.status);
115
+ job.updatedAt = new Date();
116
+
117
+ if (status.progress !== undefined) {
118
+ job.progress = status.progress;
119
+ }
120
+
121
+ if (status.logs) {
122
+ job.logs = [...(job.logs || []), ...status.logs];
123
+ }
124
+
125
+ // Notify progress
126
+ if (options?.onProgress && status.progress !== undefined) {
127
+ options.onProgress(status.progress, status.logs);
128
+ }
129
+
130
+ if (options?.onStatusChange) {
131
+ options.onStatusChange(job.status);
132
+ }
133
+
134
+ // Check terminal states
135
+ if (status.status === "completed") {
136
+ return status.output ?? (await provider.getResult(job.id));
137
+ }
138
+
139
+ if (status.status === "failed") {
140
+ throw new Error(status.error ?? "Job failed");
141
+ }
142
+
143
+ if (status.status === "cancelled") {
144
+ throw new Error("Job was cancelled");
145
+ }
146
+
147
+ // Wait with exponential backoff
148
+ await this.sleep(currentInterval);
149
+ currentInterval = Math.min(currentInterval * 1.5, this.maxPollInterval);
150
+ }
151
+
152
+ throw new Error(`Job ${job.id} timed out after ${timeout}ms`);
153
+ }
154
+
155
+ /**
156
+ * Map provider status to our status
157
+ */
158
+ private mapStatus(status: JobStatusUpdate["status"]): JobStatus {
159
+ return status;
160
+ }
161
+
162
+ /**
163
+ * Get a job by ID
164
+ */
165
+ get(jobId: string): Job | undefined {
166
+ return this.jobs.get(jobId);
167
+ }
168
+
169
+ /**
170
+ * Cancel a job
171
+ */
172
+ async cancel(jobId: string, provider: Provider): Promise<void> {
173
+ const job = this.jobs.get(jobId);
174
+ if (!job) {
175
+ throw new Error(`Job not found: ${jobId}`);
176
+ }
177
+
178
+ if (provider.cancel) {
179
+ await provider.cancel(jobId);
180
+ }
181
+
182
+ job.status = "cancelled";
183
+ job.updatedAt = new Date();
184
+ }
185
+
186
+ /**
187
+ * List all jobs
188
+ */
189
+ list(): Job[] {
190
+ return Array.from(this.jobs.values());
191
+ }
192
+
193
+ /**
194
+ * Clear completed jobs
195
+ */
196
+ clearCompleted(): void {
197
+ for (const [id, job] of this.jobs) {
198
+ if (
199
+ job.status === "completed" ||
200
+ job.status === "failed" ||
201
+ job.status === "cancelled"
202
+ ) {
203
+ this.jobs.delete(id);
204
+ }
205
+ }
206
+ }
207
+
208
+ private sleep(ms: number): Promise<void> {
209
+ return new Promise((resolve) => setTimeout(resolve, ms));
210
+ }
211
+ }
212
+
213
+ // Global job runner instance
214
+ export const jobRunner = new JobRunner();
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Pipeline Runner
3
+ * Executes multi-step skills/workflows
4
+ */
5
+
6
+ import type {
7
+ ExecutionResult,
8
+ RunOptions,
9
+ SkillDefinition,
10
+ SkillStep,
11
+ } from "../schema/types";
12
+
13
+ export interface PipelineContext {
14
+ /** Initial inputs provided to the pipeline */
15
+ inputs: Record<string, unknown>;
16
+ /** Results from each completed step, keyed by step name */
17
+ results: Record<string, unknown>;
18
+ /** Current step index */
19
+ stepIndex: number;
20
+ /** Total number of steps */
21
+ totalSteps: number;
22
+ }
23
+
24
+ export interface PipelineOptions extends RunOptions {
25
+ /** If true, stop on first error */
26
+ stopOnError?: boolean;
27
+ /** Step-level callback */
28
+ onStepStart?: (step: SkillStep, context: PipelineContext) => void;
29
+ /** Step completion callback */
30
+ onStepComplete?: (
31
+ step: SkillStep,
32
+ result: unknown,
33
+ context: PipelineContext,
34
+ ) => void;
35
+ }
36
+
37
+ export type StepExecutor = (
38
+ name: string,
39
+ inputs: Record<string, unknown>,
40
+ options?: RunOptions,
41
+ ) => Promise<ExecutionResult>;
42
+
43
+ export class PipelineRunner {
44
+ /**
45
+ * Execute a skill definition
46
+ */
47
+ async run(
48
+ skill: SkillDefinition,
49
+ inputs: Record<string, unknown>,
50
+ executor: StepExecutor,
51
+ options?: PipelineOptions,
52
+ ): Promise<ExecutionResult> {
53
+ const startTime = Date.now();
54
+ const context: PipelineContext = {
55
+ inputs,
56
+ results: {},
57
+ stepIndex: 0,
58
+ totalSteps: skill.steps.length,
59
+ };
60
+
61
+ console.log(`[pipeline] starting skill: ${skill.name}`);
62
+ console.log(`[pipeline] steps: ${skill.steps.length}`);
63
+
64
+ let lastResult: ExecutionResult | null = null;
65
+
66
+ for (let i = 0; i < skill.steps.length; i++) {
67
+ const step = skill.steps[i];
68
+ if (!step) continue;
69
+
70
+ context.stepIndex = i;
71
+
72
+ // Check step condition
73
+ if (step.when && !this.evaluateCondition(step.when, context)) {
74
+ console.log(
75
+ `[pipeline] skipping step ${i + 1}: ${step.name} (condition not met)`,
76
+ );
77
+ continue;
78
+ }
79
+
80
+ console.log(
81
+ `[pipeline] step ${i + 1}/${skill.steps.length}: ${step.name}`,
82
+ );
83
+
84
+ if (options?.onStepStart) {
85
+ options.onStepStart(step, context);
86
+ }
87
+
88
+ try {
89
+ // Resolve inputs for this step
90
+ const stepInputs = this.resolveInputs(step.inputs, context);
91
+
92
+ // Execute the step
93
+ const result = await executor(step.run, stepInputs, options);
94
+
95
+ // Store result
96
+ context.results[step.name] = result.output;
97
+ lastResult = result;
98
+
99
+ if (options?.onStepComplete) {
100
+ options.onStepComplete(step, result, context);
101
+ }
102
+
103
+ console.log(`[pipeline] step ${step.name} completed`);
104
+ } catch (error) {
105
+ console.error(`[pipeline] step ${step.name} failed:`, error);
106
+
107
+ if (options?.stopOnError !== false) {
108
+ throw error;
109
+ }
110
+
111
+ // Store error as result
112
+ context.results[step.name] = {
113
+ error: error instanceof Error ? error.message : String(error),
114
+ };
115
+ }
116
+ }
117
+
118
+ const duration = Date.now() - startTime;
119
+ console.log(`[pipeline] skill ${skill.name} completed in ${duration}ms`);
120
+
121
+ return {
122
+ output: lastResult?.output ?? context.results,
123
+ duration,
124
+ provider: lastResult?.provider ?? "pipeline",
125
+ model: skill.name,
126
+ metadata: {
127
+ stepResults: context.results,
128
+ },
129
+ };
130
+ }
131
+
132
+ /**
133
+ * Evaluate a condition against the current context
134
+ */
135
+ private evaluateCondition(
136
+ condition: Record<string, unknown>,
137
+ context: PipelineContext,
138
+ ): boolean {
139
+ for (const [key, expected] of Object.entries(condition)) {
140
+ const actual = this.resolveValue(key, context);
141
+
142
+ // Simple equality check
143
+ if (actual !== expected) {
144
+ return false;
145
+ }
146
+ }
147
+ return true;
148
+ }
149
+
150
+ /**
151
+ * Resolve inputs for a step, substituting references
152
+ */
153
+ private resolveInputs(
154
+ inputs: Record<string, unknown>,
155
+ context: PipelineContext,
156
+ ): Record<string, unknown> {
157
+ const resolved: Record<string, unknown> = {};
158
+
159
+ for (const [key, value] of Object.entries(inputs)) {
160
+ resolved[key] = this.resolveValue(value, context);
161
+ }
162
+
163
+ return resolved;
164
+ }
165
+
166
+ /**
167
+ * Resolve a value, handling references like $inputs.foo or $results.step1.output
168
+ */
169
+ private resolveValue(value: unknown, context: PipelineContext): unknown {
170
+ if (typeof value !== "string") {
171
+ return value;
172
+ }
173
+
174
+ // Check for reference syntax
175
+ if (!value.startsWith("$")) {
176
+ return value;
177
+ }
178
+
179
+ const path = value.slice(1).split(".");
180
+ const source = path[0];
181
+ const rest = path.slice(1);
182
+
183
+ if (!source) {
184
+ return value;
185
+ }
186
+
187
+ let current: unknown;
188
+
189
+ switch (source) {
190
+ case "inputs":
191
+ current = context.inputs;
192
+ break;
193
+ case "results":
194
+ current = context.results;
195
+ break;
196
+ default:
197
+ // Check if it's a direct step result reference
198
+ if (source in context.results) {
199
+ current = context.results[source];
200
+ } else {
201
+ return value; // Return as-is if not a valid reference
202
+ }
203
+ }
204
+
205
+ // Navigate the path
206
+ for (const part of rest) {
207
+ if (current === null || current === undefined) {
208
+ return undefined;
209
+ }
210
+ if (typeof current === "object") {
211
+ current = (current as Record<string, unknown>)[part];
212
+ } else {
213
+ return undefined;
214
+ }
215
+ }
216
+
217
+ return current;
218
+ }
219
+ }
220
+
221
+ // Global pipeline runner instance
222
+ export const pipelineRunner = new PipelineRunner();
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Core module exports
3
+ */
4
+
5
+ // Executor
6
+ export * from "./executor";
7
+
8
+ // Registry
9
+ export * from "./registry";
10
+ // Schema
11
+ export * from "./schema";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Registry module exports
3
+ */
4
+
5
+ export type { LoaderOptions } from "./loader";
6
+ export { loadDefinitions, loadUserSkills, reloadDefinitions } from "./loader";
7
+ export { Registry, registry } from "./registry";
8
+ export type { ResolveOptions, ResolveResult } from "./resolver";
9
+ export { findSimilar, resolve, search, suggest } from "./resolver";