@q1k-oss/btree-workflows 0.0.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.
Files changed (203) hide show
  1. package/.claude/settings.local.json +31 -0
  2. package/CLAUDE.md +181 -0
  3. package/LICENSE +21 -0
  4. package/README.md +920 -0
  5. package/behaviour-tree-workflows-landing/index.html +16 -0
  6. package/behaviour-tree-workflows-landing/package-lock.json +2074 -0
  7. package/behaviour-tree-workflows-landing/package.json +31 -0
  8. package/behaviour-tree-workflows-landing/public/favicon.svg +17 -0
  9. package/behaviour-tree-workflows-landing/src/App.css +103 -0
  10. package/behaviour-tree-workflows-landing/src/App.tsx +176 -0
  11. package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.css +89 -0
  12. package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.tsx +64 -0
  13. package/behaviour-tree-workflows-landing/src/components/ExampleSelector.css +64 -0
  14. package/behaviour-tree-workflows-landing/src/components/ExampleSelector.tsx +34 -0
  15. package/behaviour-tree-workflows-landing/src/components/ExecutionLog.css +107 -0
  16. package/behaviour-tree-workflows-landing/src/components/ExecutionLog.tsx +85 -0
  17. package/behaviour-tree-workflows-landing/src/components/Header.css +50 -0
  18. package/behaviour-tree-workflows-landing/src/components/Header.tsx +26 -0
  19. package/behaviour-tree-workflows-landing/src/components/StatusBadge.css +45 -0
  20. package/behaviour-tree-workflows-landing/src/components/StatusBadge.tsx +15 -0
  21. package/behaviour-tree-workflows-landing/src/components/Toolbar.css +74 -0
  22. package/behaviour-tree-workflows-landing/src/components/Toolbar.tsx +53 -0
  23. package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.css +67 -0
  24. package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.tsx +192 -0
  25. package/behaviour-tree-workflows-landing/src/components/YamlEditor.css +18 -0
  26. package/behaviour-tree-workflows-landing/src/components/YamlEditor.tsx +96 -0
  27. package/behaviour-tree-workflows-landing/src/lib/count-nodes.ts +11 -0
  28. package/behaviour-tree-workflows-landing/src/lib/execution-engine.ts +96 -0
  29. package/behaviour-tree-workflows-landing/src/lib/tree-layout.ts +136 -0
  30. package/behaviour-tree-workflows-landing/src/lib/yaml-examples.ts +549 -0
  31. package/behaviour-tree-workflows-landing/src/main.tsx +9 -0
  32. package/behaviour-tree-workflows-landing/src/stubs/activepieces.ts +18 -0
  33. package/behaviour-tree-workflows-landing/src/stubs/fs.ts +24 -0
  34. package/behaviour-tree-workflows-landing/src/stubs/path.ts +16 -0
  35. package/behaviour-tree-workflows-landing/src/stubs/temporal-activity.ts +6 -0
  36. package/behaviour-tree-workflows-landing/src/stubs/temporal-workflow.ts +22 -0
  37. package/behaviour-tree-workflows-landing/tsconfig.json +25 -0
  38. package/behaviour-tree-workflows-landing/vite.config.ts +40 -0
  39. package/demo-google-sheets.ts +181 -0
  40. package/demo-runtime-variables.ts +174 -0
  41. package/demo-template.ts +208 -0
  42. package/docs/ARCHITECTURE_SUMMARY.md +613 -0
  43. package/docs/NODE_REFERENCE.md +504 -0
  44. package/docs/README.md +53 -0
  45. package/docs/custom-nodes-architecture.md +826 -0
  46. package/docs/observability.md +175 -0
  47. package/docs/yaml-specification.md +990 -0
  48. package/examples/temporal/README.md +117 -0
  49. package/examples/temporal/activities.ts +373 -0
  50. package/examples/temporal/client.ts +115 -0
  51. package/examples/temporal/python-worker/activities.py +339 -0
  52. package/examples/temporal/python-worker/requirements.txt +12 -0
  53. package/examples/temporal/python-worker/worker.py +106 -0
  54. package/examples/temporal/worker.ts +66 -0
  55. package/examples/temporal/workflows.ts +6 -0
  56. package/examples/temporal/yaml-workflow-loader.ts +105 -0
  57. package/examples/yaml-test.ts +97 -0
  58. package/examples/yaml-workflows/01-simple-sequence.yaml +25 -0
  59. package/examples/yaml-workflows/02-parallel-timeout.yaml +45 -0
  60. package/examples/yaml-workflows/03-ecommerce-checkout.yaml +94 -0
  61. package/examples/yaml-workflows/04-ai-agent-workflow.yaml +346 -0
  62. package/examples/yaml-workflows/05-order-processing.yaml +146 -0
  63. package/examples/yaml-workflows/06-activity-test.yaml +71 -0
  64. package/examples/yaml-workflows/07-activity-simple-test.yaml +43 -0
  65. package/examples/yaml-workflows/08-file-processing.yaml +141 -0
  66. package/examples/yaml-workflows/09-http-request.yaml +137 -0
  67. package/examples/yaml-workflows/README.md +211 -0
  68. package/package.json +38 -0
  69. package/src/actions/code-execution.schema.ts +27 -0
  70. package/src/actions/code-execution.ts +218 -0
  71. package/src/actions/generate-file.test.ts +516 -0
  72. package/src/actions/generate-file.ts +166 -0
  73. package/src/actions/http-request.test.ts +784 -0
  74. package/src/actions/http-request.ts +228 -0
  75. package/src/actions/index.ts +20 -0
  76. package/src/actions/parse-file.test.ts +448 -0
  77. package/src/actions/parse-file.ts +139 -0
  78. package/src/actions/python-script.test.ts +439 -0
  79. package/src/actions/python-script.ts +154 -0
  80. package/src/base-node.test.ts +511 -0
  81. package/src/base-node.ts +605 -0
  82. package/src/behavior-tree.test.ts +431 -0
  83. package/src/behavior-tree.ts +283 -0
  84. package/src/blackboard.test.ts +222 -0
  85. package/src/blackboard.ts +192 -0
  86. package/src/composites/conditional.schema.ts +19 -0
  87. package/src/composites/conditional.test.ts +309 -0
  88. package/src/composites/conditional.ts +129 -0
  89. package/src/composites/for-each.schema.ts +23 -0
  90. package/src/composites/for-each.test.ts +254 -0
  91. package/src/composites/for-each.ts +132 -0
  92. package/src/composites/index.ts +15 -0
  93. package/src/composites/memory-sequence.schema.ts +19 -0
  94. package/src/composites/memory-sequence.test.ts +223 -0
  95. package/src/composites/memory-sequence.ts +98 -0
  96. package/src/composites/parallel.schema.ts +28 -0
  97. package/src/composites/parallel.test.ts +502 -0
  98. package/src/composites/parallel.ts +157 -0
  99. package/src/composites/reactive-sequence.schema.ts +19 -0
  100. package/src/composites/reactive-sequence.test.ts +170 -0
  101. package/src/composites/reactive-sequence.ts +85 -0
  102. package/src/composites/recovery.schema.ts +19 -0
  103. package/src/composites/recovery.test.ts +366 -0
  104. package/src/composites/recovery.ts +90 -0
  105. package/src/composites/selector.schema.ts +19 -0
  106. package/src/composites/selector.test.ts +387 -0
  107. package/src/composites/selector.ts +85 -0
  108. package/src/composites/sequence.schema.ts +19 -0
  109. package/src/composites/sequence.test.ts +337 -0
  110. package/src/composites/sequence.ts +72 -0
  111. package/src/composites/sub-tree.schema.ts +21 -0
  112. package/src/composites/sub-tree.test.ts +893 -0
  113. package/src/composites/sub-tree.ts +177 -0
  114. package/src/composites/while.schema.ts +24 -0
  115. package/src/composites/while.test.ts +381 -0
  116. package/src/composites/while.ts +149 -0
  117. package/src/data-store/index.ts +10 -0
  118. package/src/data-store/memory-store.ts +161 -0
  119. package/src/data-store/types.ts +94 -0
  120. package/src/debug/breakpoint.test.ts +47 -0
  121. package/src/debug/breakpoint.ts +30 -0
  122. package/src/debug/index.ts +17 -0
  123. package/src/debug/resume-point.test.ts +49 -0
  124. package/src/debug/resume-point.ts +29 -0
  125. package/src/decorators/delay.schema.ts +21 -0
  126. package/src/decorators/delay.test.ts +261 -0
  127. package/src/decorators/delay.ts +140 -0
  128. package/src/decorators/force-result.schema.ts +32 -0
  129. package/src/decorators/force-result.test.ts +133 -0
  130. package/src/decorators/force-result.ts +63 -0
  131. package/src/decorators/index.ts +13 -0
  132. package/src/decorators/invert.schema.ts +19 -0
  133. package/src/decorators/invert.test.ts +135 -0
  134. package/src/decorators/invert.ts +42 -0
  135. package/src/decorators/keep-running.schema.ts +20 -0
  136. package/src/decorators/keep-running.test.ts +105 -0
  137. package/src/decorators/keep-running.ts +49 -0
  138. package/src/decorators/precondition.schema.ts +19 -0
  139. package/src/decorators/precondition.test.ts +351 -0
  140. package/src/decorators/precondition.ts +139 -0
  141. package/src/decorators/repeat.schema.ts +21 -0
  142. package/src/decorators/repeat.test.ts +187 -0
  143. package/src/decorators/repeat.ts +94 -0
  144. package/src/decorators/run-once.schema.ts +19 -0
  145. package/src/decorators/run-once.test.ts +140 -0
  146. package/src/decorators/run-once.ts +61 -0
  147. package/src/decorators/soft-assert.schema.ts +19 -0
  148. package/src/decorators/soft-assert.test.ts +107 -0
  149. package/src/decorators/soft-assert.ts +68 -0
  150. package/src/decorators/timeout.schema.ts +21 -0
  151. package/src/decorators/timeout.test.ts +274 -0
  152. package/src/decorators/timeout.ts +159 -0
  153. package/src/errors.test.ts +63 -0
  154. package/src/errors.ts +34 -0
  155. package/src/events.test.ts +347 -0
  156. package/src/events.ts +183 -0
  157. package/src/index.ts +80 -0
  158. package/src/integrations/index.ts +30 -0
  159. package/src/integrations/integration-action.test.ts +571 -0
  160. package/src/integrations/integration-action.ts +233 -0
  161. package/src/integrations/piece-executor.ts +320 -0
  162. package/src/observability/execution-tracker.ts +320 -0
  163. package/src/observability/index.ts +23 -0
  164. package/src/observability/sinks.ts +138 -0
  165. package/src/observability/types.ts +130 -0
  166. package/src/registry-utils.ts +147 -0
  167. package/src/registry.test.ts +466 -0
  168. package/src/registry.ts +334 -0
  169. package/src/schemas/base.schema.ts +104 -0
  170. package/src/schemas/index.ts +223 -0
  171. package/src/schemas/integration.test.ts +238 -0
  172. package/src/schemas/tree-definition.schema.ts +170 -0
  173. package/src/schemas/validation.test.ts +146 -0
  174. package/src/schemas/validation.ts +122 -0
  175. package/src/scripting/index.ts +22 -0
  176. package/src/templates/template-loader.test.ts +281 -0
  177. package/src/templates/template-loader.ts +152 -0
  178. package/src/temporal-integration.test.ts +213 -0
  179. package/src/test-nodes.ts +259 -0
  180. package/src/types.ts +503 -0
  181. package/src/utilities/index.ts +17 -0
  182. package/src/utilities/log-message.test.ts +275 -0
  183. package/src/utilities/log-message.ts +134 -0
  184. package/src/utilities/regex-extract.test.ts +138 -0
  185. package/src/utilities/regex-extract.ts +108 -0
  186. package/src/utilities/variable-resolver.test.ts +416 -0
  187. package/src/utilities/variable-resolver.ts +318 -0
  188. package/src/utils/error-handler.test.ts +117 -0
  189. package/src/utils/error-handler.ts +48 -0
  190. package/src/utils/signal-check.test.ts +234 -0
  191. package/src/utils/signal-check.ts +140 -0
  192. package/src/yaml/errors.ts +143 -0
  193. package/src/yaml/index.ts +30 -0
  194. package/src/yaml/loader.ts +39 -0
  195. package/src/yaml/parser.ts +286 -0
  196. package/src/yaml/validation/semantic-validator.ts +196 -0
  197. package/templates/google-sheets/insert-row.yaml +76 -0
  198. package/templates/notification-sender.yaml +33 -0
  199. package/templates/order-validation.yaml +44 -0
  200. package/tsconfig.json +24 -0
  201. package/vitest.config.ts +25 -0
  202. package/workflows/order-processor.yaml +59 -0
  203. package/workflows/process-order-workflow.yaml +142 -0
@@ -0,0 +1,117 @@
1
+ # Temporal Activity Test
2
+
3
+ This directory contains examples for running btree workflows with Temporal, including activity execution for I/O operations.
4
+
5
+ ## Prerequisites
6
+
7
+ 1. **Temporal Server** - Running locally on `localhost:7233`
8
+ ```bash
9
+ # Option 1: Use Temporal CLI (recommended for testing)
10
+ brew install temporal
11
+ temporal server start-dev
12
+
13
+ # Option 2: Use Docker
14
+ docker-compose up -d
15
+ ```
16
+
17
+ 2. **Build btree** - The dist folder must be built
18
+ ```bash
19
+ cd /path/to/btree
20
+ npm run build
21
+ ```
22
+
23
+ ## Running the Test
24
+
25
+ ### 1. Start the Worker (Terminal 1)
26
+
27
+ ```bash
28
+ cd /path/to/btree/examples/temporal
29
+
30
+ # For mock activities (no real API calls)
31
+ BTREE_MOCK_ACTIVITIES=true npx tsx worker.ts
32
+
33
+ # For real activities (requires actual credentials)
34
+ # GOOGLE_SHEETS_ACCESS_TOKEN=xxx npx tsx worker.ts
35
+ ```
36
+
37
+ You should see:
38
+ ```
39
+ 🚀 Starting Temporal worker for behavior tree workflows...
40
+ 📦 Bundling workflows...
41
+ ✅ Workflows bundled successfully
42
+ ✅ Worker started successfully!
43
+ 📋 Task Queue: btree-workflows
44
+ 🔄 Listening for workflow tasks...
45
+ ```
46
+
47
+ ### 2. Run the Client (Terminal 2)
48
+
49
+ ```bash
50
+ cd /path/to/btree/examples/temporal
51
+ npx tsx client.ts
52
+ ```
53
+
54
+ You should see:
55
+ ```
56
+ 🔌 Connecting to Temporal server at localhost:7233...
57
+ ✅ Connected to Temporal server
58
+
59
+ ============================================================
60
+ Workflow: Activity Test (IntegrationAction via Activity)
61
+ ============================================================
62
+ ✅ Result: { status: 'SUCCESS', output: { ... } }
63
+ ```
64
+
65
+ ## What's Being Tested
66
+
67
+ The activity test workflow (`06-activity-test.yaml`) demonstrates:
68
+
69
+ 1. **IntegrationAction with Activities** - The `IntegrationAction` node uses `context.activities.executePieceAction` instead of inline execution, making it deterministic for Temporal replay.
70
+
71
+ 2. **Mock Token Provider** - The `yaml-workflow-loader.ts` includes a mock token provider for testing without real OAuth credentials.
72
+
73
+ 3. **Mock Activity Responses** - When `BTREE_MOCK_ACTIVITIES=true`, activities return simulated responses instead of making real API calls.
74
+
75
+ ## Files
76
+
77
+ | File | Description |
78
+ |------|-------------|
79
+ | `worker.ts` | Temporal worker that registers workflows and activities |
80
+ | `client.ts` | Client that executes workflows |
81
+ | `activities.ts` | Activity implementations (run outside workflow sandbox) |
82
+ | `yaml-workflow-loader.ts` | Workflow that loads YAML and passes activities to btree |
83
+ | `workflows.ts` | Re-exports workflows for Temporal bundler |
84
+
85
+ ## Activity Flow
86
+
87
+ ```
88
+ Client Worker (Workflow) Worker (Activity)
89
+ | | |
90
+ | execute(workflow) | |
91
+ |------------------------>| |
92
+ | | |
93
+ | | IntegrationAction.tick() |
94
+ | | -> context.activities |
95
+ | | .executePieceAction() |
96
+ | | |
97
+ | |----------- activity -------->|
98
+ | | |
99
+ | | | executePieceActionActivity()
100
+ | | | -> executePieceAction()
101
+ | | | -> API call (or mock)
102
+ | | |
103
+ | |<---------- result -----------|
104
+ | | |
105
+ | | Store result in blackboard |
106
+ | | |
107
+ |<-- WorkflowResult ------| |
108
+ ```
109
+
110
+ ## Node Behavior Summary
111
+
112
+ | Node | Activity Required | Standalone Fallback |
113
+ |------|-------------------|---------------------|
114
+ | `IntegrationAction` | Optional | Yes (inline execution) |
115
+ | `PythonScript` | **Required** | No (needs Python worker) |
116
+ | `ParseFile` | **Required** | No (needs file I/O) |
117
+ | `GenerateFile` | **Required** | No (needs file I/O) |
@@ -0,0 +1,373 @@
1
+ /**
2
+ * Activity implementations for btree workflows
3
+ *
4
+ * These run in the worker process (outside the workflow sandbox)
5
+ * and handle all I/O operations deterministically for Temporal.
6
+ */
7
+
8
+ import {
9
+ executePieceAction,
10
+ type PieceActivityRequest,
11
+ type PythonScriptRequest,
12
+ type PythonScriptResult,
13
+ type ParseFileRequest,
14
+ type ParseFileResult,
15
+ type GenerateFileRequest,
16
+ type GenerateFileResult,
17
+ type HttpRequestActivity,
18
+ type HttpResponseActivity,
19
+ } from "../../dist/index.js";
20
+
21
+ /**
22
+ * Execute an Active Pieces action
23
+ * This wraps the btree executePieceAction for use as a Temporal activity
24
+ */
25
+ export async function executePieceActionActivity(
26
+ request: PieceActivityRequest
27
+ ): Promise<unknown> {
28
+ console.log(
29
+ `[Activity] executePieceAction: ${request.provider}/${request.action}`
30
+ );
31
+ console.log(`[Activity] Inputs:`, JSON.stringify(request.inputs, null, 2));
32
+
33
+ // For testing without actual credentials, we can mock responses
34
+ if (process.env.BTREE_MOCK_ACTIVITIES === "true") {
35
+ console.log(`[Activity] MOCK MODE - returning simulated response`);
36
+ return mockPieceAction(request);
37
+ }
38
+
39
+ // Real execution via Active Pieces
40
+ const result = await executePieceAction(request);
41
+ console.log(`[Activity] Result:`, JSON.stringify(result, null, 2));
42
+ return result;
43
+ }
44
+
45
+ /**
46
+ * Execute Python code (placeholder - would need Python worker)
47
+ */
48
+ export async function executePythonScriptActivity(
49
+ request: PythonScriptRequest
50
+ ): Promise<PythonScriptResult> {
51
+ console.log(`[Activity] executePythonScript`);
52
+ console.log(`[Activity] Code length: ${request.code.length} chars`);
53
+
54
+ // Mock implementation for testing
55
+ if (process.env.BTREE_MOCK_ACTIVITIES === "true") {
56
+ console.log(`[Activity] MOCK MODE - returning simulated Python result`);
57
+ return {
58
+ blackboard: request.blackboard,
59
+ stdout: "Mock Python execution",
60
+ stderr: "",
61
+ };
62
+ }
63
+
64
+ // Real implementation would call a Python worker via gRPC or subprocess
65
+ throw new Error(
66
+ "Real Python execution requires a Python worker. Set BTREE_MOCK_ACTIVITIES=true for testing."
67
+ );
68
+ }
69
+
70
+ /**
71
+ * Parse a file (CSV/Excel)
72
+ */
73
+ export async function parseFileActivity(
74
+ request: ParseFileRequest
75
+ ): Promise<ParseFileResult> {
76
+ console.log(`[Activity] parseFile: ${request.file}`);
77
+
78
+ // Mock implementation for testing
79
+ if (process.env.BTREE_MOCK_ACTIVITIES === "true") {
80
+ console.log(`[Activity] MOCK MODE - returning simulated parsed data`);
81
+ return {
82
+ data: [
83
+ { orderId: "ORD-001", product: "Widget", quantity: 5, price: 10.99 },
84
+ { orderId: "ORD-002", product: "Gadget", quantity: 3, price: 24.99 },
85
+ { orderId: "ORD-003", product: "Gizmo", quantity: 10, price: 5.49 },
86
+ ],
87
+ rowCount: 3,
88
+ columns: ["orderId", "product", "quantity", "price"],
89
+ };
90
+ }
91
+
92
+ // Real implementation would use papaparse for CSV, xlsx for Excel
93
+ throw new Error(
94
+ "Real file parsing requires file system access. Set BTREE_MOCK_ACTIVITIES=true for testing."
95
+ );
96
+ }
97
+
98
+ /**
99
+ * Generate a file (CSV/Excel/JSON)
100
+ */
101
+ export async function generateFileActivity(
102
+ request: GenerateFileRequest
103
+ ): Promise<GenerateFileResult> {
104
+ console.log(`[Activity] generateFile: ${request.filename} (${request.format})`);
105
+ console.log(`[Activity] Data rows: ${request.data.length}`);
106
+
107
+ // Mock implementation for testing
108
+ if (process.env.BTREE_MOCK_ACTIVITIES === "true") {
109
+ console.log(`[Activity] MOCK MODE - returning simulated file metadata`);
110
+ return {
111
+ filename: request.filename,
112
+ contentType: getContentType(request.format),
113
+ size: JSON.stringify(request.data).length,
114
+ path: `/tmp/${request.filename}`,
115
+ url:
116
+ request.storage === "persistent"
117
+ ? `https://storage.example.com/files/${request.filename}`
118
+ : undefined,
119
+ };
120
+ }
121
+
122
+ // Real implementation would use json2csv, xlsx, etc.
123
+ throw new Error(
124
+ "Real file generation requires file system access. Set BTREE_MOCK_ACTIVITIES=true for testing."
125
+ );
126
+ }
127
+
128
+ /**
129
+ * Fetch a URL (HTTP request)
130
+ * Uses native fetch() API with timeout support via AbortController
131
+ */
132
+ export async function fetchUrlActivity(
133
+ request: HttpRequestActivity
134
+ ): Promise<HttpResponseActivity> {
135
+ console.log(`[Activity] fetchUrl: ${request.method} ${request.url}`);
136
+
137
+ // Mock implementation for testing
138
+ if (process.env.BTREE_MOCK_ACTIVITIES === "true") {
139
+ console.log(`[Activity] MOCK MODE - returning simulated HTTP response`);
140
+ return mockFetchUrl(request);
141
+ }
142
+
143
+ // Real implementation using native fetch
144
+ const controller = new AbortController();
145
+ const timeoutId = request.timeout
146
+ ? setTimeout(() => controller.abort(), request.timeout)
147
+ : null;
148
+
149
+ try {
150
+ const fetchOptions: RequestInit = {
151
+ method: request.method,
152
+ headers: request.headers,
153
+ signal: controller.signal,
154
+ };
155
+
156
+ // Add body for non-GET requests
157
+ if (request.body !== undefined && request.method !== "GET") {
158
+ fetchOptions.body =
159
+ typeof request.body === "string"
160
+ ? request.body
161
+ : JSON.stringify(request.body);
162
+ }
163
+
164
+ const response = await fetch(request.url, fetchOptions);
165
+
166
+ // Extract headers into a plain object
167
+ const headers: Record<string, string> = {};
168
+ response.headers.forEach((value, key) => {
169
+ headers[key] = value;
170
+ });
171
+
172
+ // Parse response based on content type
173
+ const contentType = response.headers.get("content-type") || "";
174
+ let data: unknown;
175
+
176
+ if (contentType.includes("application/json")) {
177
+ data = await response.json();
178
+ } else if (contentType.includes("text/")) {
179
+ data = await response.text();
180
+ } else {
181
+ // For binary data, convert to base64
182
+ const buffer = await response.arrayBuffer();
183
+ data = Buffer.from(buffer).toString("base64");
184
+ }
185
+
186
+ console.log(`[Activity] fetchUrl response: ${response.status}`);
187
+ return {
188
+ status: response.status,
189
+ headers,
190
+ data,
191
+ };
192
+ } catch (error) {
193
+ if (error instanceof Error && error.name === "AbortError") {
194
+ throw new Error(`Request timed out after ${request.timeout}ms`);
195
+ }
196
+ throw error;
197
+ } finally {
198
+ if (timeoutId) {
199
+ clearTimeout(timeoutId);
200
+ }
201
+ }
202
+ }
203
+
204
+ // ─────────────────────────────────────────────────────────────────────────────
205
+ // Mock Helpers
206
+ // ─────────────────────────────────────────────────────────────────────────────
207
+
208
+ function mockPieceAction(request: PieceActivityRequest): unknown {
209
+ const { provider, action, inputs } = request;
210
+
211
+ // Google Sheets mocks
212
+ if (provider === "google-sheets" || provider === "google") {
213
+ if (action === "append_row" || action === "insert_row") {
214
+ return {
215
+ spreadsheetId: inputs.spreadsheetId,
216
+ updatedRange: "Sheet1!A1:C1",
217
+ updatedRows: 1,
218
+ updatedColumns: (inputs.values as unknown[])?.length || 3,
219
+ updatedCells: (inputs.values as unknown[])?.length || 3,
220
+ };
221
+ }
222
+ if (action === "get_values" || action === "read_rows") {
223
+ return {
224
+ values: [
225
+ ["Header1", "Header2", "Header3"],
226
+ ["Value1", "Value2", "Value3"],
227
+ ["Value4", "Value5", "Value6"],
228
+ ],
229
+ };
230
+ }
231
+ }
232
+
233
+ // Slack mocks
234
+ if (provider === "slack") {
235
+ if (action === "send_message" || action === "post_message") {
236
+ return {
237
+ ok: true,
238
+ channel: inputs.channel || "#general",
239
+ ts: `${Date.now()}.000000`,
240
+ message: {
241
+ text: inputs.text || inputs.message,
242
+ ts: `${Date.now()}.000000`,
243
+ },
244
+ };
245
+ }
246
+ }
247
+
248
+ // OpenAI mocks
249
+ if (provider === "openai") {
250
+ if (action === "chat" || action === "ask_chatgpt") {
251
+ return {
252
+ id: `chatcmpl-${Date.now()}`,
253
+ choices: [
254
+ {
255
+ message: {
256
+ role: "assistant",
257
+ content: "This is a mock response from the AI assistant.",
258
+ },
259
+ finish_reason: "stop",
260
+ },
261
+ ],
262
+ usage: { prompt_tokens: 10, completion_tokens: 20, total_tokens: 30 },
263
+ };
264
+ }
265
+ }
266
+
267
+ // Default mock response
268
+ return {
269
+ success: true,
270
+ provider,
271
+ action,
272
+ timestamp: new Date().toISOString(),
273
+ mock: true,
274
+ };
275
+ }
276
+
277
+ function mockFetchUrl(request: HttpRequestActivity): HttpResponseActivity {
278
+ const { url, method } = request;
279
+
280
+ // Simulate different API responses based on URL patterns
281
+ if (url.includes("/users")) {
282
+ if (method === "GET") {
283
+ return {
284
+ status: 200,
285
+ headers: { "content-type": "application/json" },
286
+ data: {
287
+ id: 1,
288
+ name: "Mock User",
289
+ email: "mock@example.com",
290
+ },
291
+ };
292
+ }
293
+ if (method === "POST") {
294
+ return {
295
+ status: 201,
296
+ headers: { "content-type": "application/json" },
297
+ data: {
298
+ id: 123,
299
+ success: true,
300
+ message: "User created",
301
+ },
302
+ };
303
+ }
304
+ }
305
+
306
+ if (url.includes("/orders")) {
307
+ if (method === "POST") {
308
+ return {
309
+ status: 201,
310
+ headers: { "content-type": "application/json" },
311
+ data: {
312
+ orderId: `ORD-${Date.now()}`,
313
+ status: "created",
314
+ timestamp: new Date().toISOString(),
315
+ },
316
+ };
317
+ }
318
+ if (method === "GET") {
319
+ return {
320
+ status: 200,
321
+ headers: { "content-type": "application/json" },
322
+ data: {
323
+ orders: [
324
+ { id: "ORD-001", total: 99.99, status: "shipped" },
325
+ { id: "ORD-002", total: 149.99, status: "pending" },
326
+ ],
327
+ },
328
+ };
329
+ }
330
+ }
331
+
332
+ if (url.includes("/error")) {
333
+ return {
334
+ status: 500,
335
+ headers: { "content-type": "application/json" },
336
+ data: { error: "Internal Server Error", mock: true },
337
+ };
338
+ }
339
+
340
+ if (url.includes("/not-found")) {
341
+ return {
342
+ status: 404,
343
+ headers: { "content-type": "application/json" },
344
+ data: { error: "Not Found", mock: true },
345
+ };
346
+ }
347
+
348
+ // Default success response
349
+ return {
350
+ status: 200,
351
+ headers: { "content-type": "application/json" },
352
+ data: {
353
+ success: true,
354
+ url,
355
+ method,
356
+ timestamp: new Date().toISOString(),
357
+ mock: true,
358
+ },
359
+ };
360
+ }
361
+
362
+ function getContentType(format: string): string {
363
+ switch (format) {
364
+ case "csv":
365
+ return "text/csv";
366
+ case "xlsx":
367
+ return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
368
+ case "json":
369
+ return "application/json";
370
+ default:
371
+ return "application/octet-stream";
372
+ }
373
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Temporal Client
3
+ * Executes YAML-defined behavior tree workflows on Temporal server
4
+ */
5
+
6
+ import { readFileSync } from "fs";
7
+ import { fileURLToPath } from "url";
8
+ import { dirname, join } from "path";
9
+ import { Connection, Client } from "@temporalio/client";
10
+ import { Registry } from "../../dist/index.js";
11
+ import type { YamlWorkflowArgs } from "./yaml-workflow-loader.js";
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+
16
+ async function run() {
17
+ console.log("🔌 Connecting to Temporal server at localhost:7233...");
18
+
19
+ const connection = await Connection.connect({
20
+ address: "localhost:7233",
21
+ });
22
+
23
+ const client = new Client({ connection });
24
+ console.log("✅ Connected to Temporal server\n");
25
+
26
+ // Create tree registry (required for SubTree nodes)
27
+ const treeRegistry = new Registry();
28
+
29
+ // Load YAML workflows
30
+ const workflows = [
31
+ {
32
+ name: "Simple Sequence",
33
+ file: "../yaml-workflows/01-simple-sequence.yaml",
34
+ id: "simple-sequence",
35
+ input: {},
36
+ },
37
+ {
38
+ name: "Parallel with Timeout",
39
+ file: "../yaml-workflows/02-parallel-timeout.yaml",
40
+ id: "parallel-timeout",
41
+ input: {},
42
+ },
43
+ {
44
+ name: "Activity Test (IntegrationAction via Activity)",
45
+ file: "../yaml-workflows/07-activity-simple-test.yaml",
46
+ id: "activity-test",
47
+ input: {
48
+ spreadsheetId: "test-spreadsheet-123",
49
+ orderId: "ORD-" + Date.now(),
50
+ customerName: "John Doe",
51
+ amount: 99.99,
52
+ },
53
+ },
54
+ // {
55
+ // name: "Order Processing",
56
+ // file: "../yaml-workflows/05-order-processing.yaml",
57
+ // id: "order-processing",
58
+ // input: {},
59
+ // },
60
+ // Complex workflows (comment out for initial test)
61
+ // {
62
+ // name: "E-commerce Checkout",
63
+ // file: "../yaml-workflows/03-ecommerce-checkout.yaml",
64
+ // id: "ecommerce-checkout",
65
+ // input: {},
66
+ // },
67
+ // {
68
+ // name: "AI Agent Workflow",
69
+ // file: "../yaml-workflows/04-ai-agent-workflow.yaml",
70
+ // id: "ai-agent",
71
+ // input: {},
72
+ // },
73
+ ];
74
+
75
+ // Execute each YAML workflow
76
+ for (const workflow of workflows) {
77
+ console.log("=".repeat(60));
78
+ console.log(`Workflow: ${workflow.name}`);
79
+ console.log("=".repeat(60));
80
+
81
+ try {
82
+ // Load YAML content from file
83
+ const yamlContent = readFileSync(join(__dirname, workflow.file), "utf-8");
84
+
85
+ // Create workflow args with YAML content
86
+ const args: YamlWorkflowArgs = {
87
+ input: workflow.input || {},
88
+ treeRegistry,
89
+ yamlContent,
90
+ };
91
+
92
+ // Execute workflow
93
+ const result = await client.workflow.execute("yamlWorkflow", {
94
+ taskQueue: "btree-workflows",
95
+ workflowId: `${workflow.id}-${Date.now()}`,
96
+ args: [args],
97
+ });
98
+
99
+ console.log("✅ Result:", result);
100
+ console.log("\n");
101
+ } catch (error) {
102
+ console.error(`❌ Workflow failed:`, error);
103
+ console.log("\n");
104
+ }
105
+ }
106
+
107
+ console.log("=".repeat(60));
108
+ console.log("🎉 All YAML workflows completed!");
109
+ console.log("=".repeat(60));
110
+ }
111
+
112
+ run().catch((err) => {
113
+ console.error("❌ Client error:", err);
114
+ process.exit(1);
115
+ });