@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.
- package/.claude/settings.local.json +31 -0
- package/CLAUDE.md +181 -0
- package/LICENSE +21 -0
- package/README.md +920 -0
- package/behaviour-tree-workflows-landing/index.html +16 -0
- package/behaviour-tree-workflows-landing/package-lock.json +2074 -0
- package/behaviour-tree-workflows-landing/package.json +31 -0
- package/behaviour-tree-workflows-landing/public/favicon.svg +17 -0
- package/behaviour-tree-workflows-landing/src/App.css +103 -0
- package/behaviour-tree-workflows-landing/src/App.tsx +176 -0
- package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.css +89 -0
- package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.tsx +64 -0
- package/behaviour-tree-workflows-landing/src/components/ExampleSelector.css +64 -0
- package/behaviour-tree-workflows-landing/src/components/ExampleSelector.tsx +34 -0
- package/behaviour-tree-workflows-landing/src/components/ExecutionLog.css +107 -0
- package/behaviour-tree-workflows-landing/src/components/ExecutionLog.tsx +85 -0
- package/behaviour-tree-workflows-landing/src/components/Header.css +50 -0
- package/behaviour-tree-workflows-landing/src/components/Header.tsx +26 -0
- package/behaviour-tree-workflows-landing/src/components/StatusBadge.css +45 -0
- package/behaviour-tree-workflows-landing/src/components/StatusBadge.tsx +15 -0
- package/behaviour-tree-workflows-landing/src/components/Toolbar.css +74 -0
- package/behaviour-tree-workflows-landing/src/components/Toolbar.tsx +53 -0
- package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.css +67 -0
- package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.tsx +192 -0
- package/behaviour-tree-workflows-landing/src/components/YamlEditor.css +18 -0
- package/behaviour-tree-workflows-landing/src/components/YamlEditor.tsx +96 -0
- package/behaviour-tree-workflows-landing/src/lib/count-nodes.ts +11 -0
- package/behaviour-tree-workflows-landing/src/lib/execution-engine.ts +96 -0
- package/behaviour-tree-workflows-landing/src/lib/tree-layout.ts +136 -0
- package/behaviour-tree-workflows-landing/src/lib/yaml-examples.ts +549 -0
- package/behaviour-tree-workflows-landing/src/main.tsx +9 -0
- package/behaviour-tree-workflows-landing/src/stubs/activepieces.ts +18 -0
- package/behaviour-tree-workflows-landing/src/stubs/fs.ts +24 -0
- package/behaviour-tree-workflows-landing/src/stubs/path.ts +16 -0
- package/behaviour-tree-workflows-landing/src/stubs/temporal-activity.ts +6 -0
- package/behaviour-tree-workflows-landing/src/stubs/temporal-workflow.ts +22 -0
- package/behaviour-tree-workflows-landing/tsconfig.json +25 -0
- package/behaviour-tree-workflows-landing/vite.config.ts +40 -0
- package/demo-google-sheets.ts +181 -0
- package/demo-runtime-variables.ts +174 -0
- package/demo-template.ts +208 -0
- package/docs/ARCHITECTURE_SUMMARY.md +613 -0
- package/docs/NODE_REFERENCE.md +504 -0
- package/docs/README.md +53 -0
- package/docs/custom-nodes-architecture.md +826 -0
- package/docs/observability.md +175 -0
- package/docs/yaml-specification.md +990 -0
- package/examples/temporal/README.md +117 -0
- package/examples/temporal/activities.ts +373 -0
- package/examples/temporal/client.ts +115 -0
- package/examples/temporal/python-worker/activities.py +339 -0
- package/examples/temporal/python-worker/requirements.txt +12 -0
- package/examples/temporal/python-worker/worker.py +106 -0
- package/examples/temporal/worker.ts +66 -0
- package/examples/temporal/workflows.ts +6 -0
- package/examples/temporal/yaml-workflow-loader.ts +105 -0
- package/examples/yaml-test.ts +97 -0
- package/examples/yaml-workflows/01-simple-sequence.yaml +25 -0
- package/examples/yaml-workflows/02-parallel-timeout.yaml +45 -0
- package/examples/yaml-workflows/03-ecommerce-checkout.yaml +94 -0
- package/examples/yaml-workflows/04-ai-agent-workflow.yaml +346 -0
- package/examples/yaml-workflows/05-order-processing.yaml +146 -0
- package/examples/yaml-workflows/06-activity-test.yaml +71 -0
- package/examples/yaml-workflows/07-activity-simple-test.yaml +43 -0
- package/examples/yaml-workflows/08-file-processing.yaml +141 -0
- package/examples/yaml-workflows/09-http-request.yaml +137 -0
- package/examples/yaml-workflows/README.md +211 -0
- package/package.json +38 -0
- package/src/actions/code-execution.schema.ts +27 -0
- package/src/actions/code-execution.ts +218 -0
- package/src/actions/generate-file.test.ts +516 -0
- package/src/actions/generate-file.ts +166 -0
- package/src/actions/http-request.test.ts +784 -0
- package/src/actions/http-request.ts +228 -0
- package/src/actions/index.ts +20 -0
- package/src/actions/parse-file.test.ts +448 -0
- package/src/actions/parse-file.ts +139 -0
- package/src/actions/python-script.test.ts +439 -0
- package/src/actions/python-script.ts +154 -0
- package/src/base-node.test.ts +511 -0
- package/src/base-node.ts +605 -0
- package/src/behavior-tree.test.ts +431 -0
- package/src/behavior-tree.ts +283 -0
- package/src/blackboard.test.ts +222 -0
- package/src/blackboard.ts +192 -0
- package/src/composites/conditional.schema.ts +19 -0
- package/src/composites/conditional.test.ts +309 -0
- package/src/composites/conditional.ts +129 -0
- package/src/composites/for-each.schema.ts +23 -0
- package/src/composites/for-each.test.ts +254 -0
- package/src/composites/for-each.ts +132 -0
- package/src/composites/index.ts +15 -0
- package/src/composites/memory-sequence.schema.ts +19 -0
- package/src/composites/memory-sequence.test.ts +223 -0
- package/src/composites/memory-sequence.ts +98 -0
- package/src/composites/parallel.schema.ts +28 -0
- package/src/composites/parallel.test.ts +502 -0
- package/src/composites/parallel.ts +157 -0
- package/src/composites/reactive-sequence.schema.ts +19 -0
- package/src/composites/reactive-sequence.test.ts +170 -0
- package/src/composites/reactive-sequence.ts +85 -0
- package/src/composites/recovery.schema.ts +19 -0
- package/src/composites/recovery.test.ts +366 -0
- package/src/composites/recovery.ts +90 -0
- package/src/composites/selector.schema.ts +19 -0
- package/src/composites/selector.test.ts +387 -0
- package/src/composites/selector.ts +85 -0
- package/src/composites/sequence.schema.ts +19 -0
- package/src/composites/sequence.test.ts +337 -0
- package/src/composites/sequence.ts +72 -0
- package/src/composites/sub-tree.schema.ts +21 -0
- package/src/composites/sub-tree.test.ts +893 -0
- package/src/composites/sub-tree.ts +177 -0
- package/src/composites/while.schema.ts +24 -0
- package/src/composites/while.test.ts +381 -0
- package/src/composites/while.ts +149 -0
- package/src/data-store/index.ts +10 -0
- package/src/data-store/memory-store.ts +161 -0
- package/src/data-store/types.ts +94 -0
- package/src/debug/breakpoint.test.ts +47 -0
- package/src/debug/breakpoint.ts +30 -0
- package/src/debug/index.ts +17 -0
- package/src/debug/resume-point.test.ts +49 -0
- package/src/debug/resume-point.ts +29 -0
- package/src/decorators/delay.schema.ts +21 -0
- package/src/decorators/delay.test.ts +261 -0
- package/src/decorators/delay.ts +140 -0
- package/src/decorators/force-result.schema.ts +32 -0
- package/src/decorators/force-result.test.ts +133 -0
- package/src/decorators/force-result.ts +63 -0
- package/src/decorators/index.ts +13 -0
- package/src/decorators/invert.schema.ts +19 -0
- package/src/decorators/invert.test.ts +135 -0
- package/src/decorators/invert.ts +42 -0
- package/src/decorators/keep-running.schema.ts +20 -0
- package/src/decorators/keep-running.test.ts +105 -0
- package/src/decorators/keep-running.ts +49 -0
- package/src/decorators/precondition.schema.ts +19 -0
- package/src/decorators/precondition.test.ts +351 -0
- package/src/decorators/precondition.ts +139 -0
- package/src/decorators/repeat.schema.ts +21 -0
- package/src/decorators/repeat.test.ts +187 -0
- package/src/decorators/repeat.ts +94 -0
- package/src/decorators/run-once.schema.ts +19 -0
- package/src/decorators/run-once.test.ts +140 -0
- package/src/decorators/run-once.ts +61 -0
- package/src/decorators/soft-assert.schema.ts +19 -0
- package/src/decorators/soft-assert.test.ts +107 -0
- package/src/decorators/soft-assert.ts +68 -0
- package/src/decorators/timeout.schema.ts +21 -0
- package/src/decorators/timeout.test.ts +274 -0
- package/src/decorators/timeout.ts +159 -0
- package/src/errors.test.ts +63 -0
- package/src/errors.ts +34 -0
- package/src/events.test.ts +347 -0
- package/src/events.ts +183 -0
- package/src/index.ts +80 -0
- package/src/integrations/index.ts +30 -0
- package/src/integrations/integration-action.test.ts +571 -0
- package/src/integrations/integration-action.ts +233 -0
- package/src/integrations/piece-executor.ts +320 -0
- package/src/observability/execution-tracker.ts +320 -0
- package/src/observability/index.ts +23 -0
- package/src/observability/sinks.ts +138 -0
- package/src/observability/types.ts +130 -0
- package/src/registry-utils.ts +147 -0
- package/src/registry.test.ts +466 -0
- package/src/registry.ts +334 -0
- package/src/schemas/base.schema.ts +104 -0
- package/src/schemas/index.ts +223 -0
- package/src/schemas/integration.test.ts +238 -0
- package/src/schemas/tree-definition.schema.ts +170 -0
- package/src/schemas/validation.test.ts +146 -0
- package/src/schemas/validation.ts +122 -0
- package/src/scripting/index.ts +22 -0
- package/src/templates/template-loader.test.ts +281 -0
- package/src/templates/template-loader.ts +152 -0
- package/src/temporal-integration.test.ts +213 -0
- package/src/test-nodes.ts +259 -0
- package/src/types.ts +503 -0
- package/src/utilities/index.ts +17 -0
- package/src/utilities/log-message.test.ts +275 -0
- package/src/utilities/log-message.ts +134 -0
- package/src/utilities/regex-extract.test.ts +138 -0
- package/src/utilities/regex-extract.ts +108 -0
- package/src/utilities/variable-resolver.test.ts +416 -0
- package/src/utilities/variable-resolver.ts +318 -0
- package/src/utils/error-handler.test.ts +117 -0
- package/src/utils/error-handler.ts +48 -0
- package/src/utils/signal-check.test.ts +234 -0
- package/src/utils/signal-check.ts +140 -0
- package/src/yaml/errors.ts +143 -0
- package/src/yaml/index.ts +30 -0
- package/src/yaml/loader.ts +39 -0
- package/src/yaml/parser.ts +286 -0
- package/src/yaml/validation/semantic-validator.ts +196 -0
- package/templates/google-sheets/insert-row.yaml +76 -0
- package/templates/notification-sender.yaml +33 -0
- package/templates/order-validation.yaml +44 -0
- package/tsconfig.json +24 -0
- package/vitest.config.ts +25 -0
- package/workflows/order-processor.yaml +59 -0
- package/workflows/process-order-workflow.yaml +142 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [react()],
|
|
7
|
+
resolve: {
|
|
8
|
+
alias: {
|
|
9
|
+
// Map library source imports
|
|
10
|
+
'@btree': path.resolve(__dirname, '../src'),
|
|
11
|
+
// Stub out Node.js / Temporal modules that can't run in the browser
|
|
12
|
+
'@temporalio/workflow': path.resolve(__dirname, 'src/stubs/temporal-workflow.ts'),
|
|
13
|
+
'@temporalio/activity': path.resolve(__dirname, 'src/stubs/temporal-activity.ts'),
|
|
14
|
+
'fs/promises': path.resolve(__dirname, 'src/stubs/fs.ts'),
|
|
15
|
+
'fs': path.resolve(__dirname, 'src/stubs/fs.ts'),
|
|
16
|
+
'path': path.resolve(__dirname, 'src/stubs/path.ts'),
|
|
17
|
+
'@activepieces/piece-google-sheets': path.resolve(__dirname, 'src/stubs/activepieces.ts'),
|
|
18
|
+
'@activepieces/pieces-framework': path.resolve(__dirname, 'src/stubs/activepieces.ts'),
|
|
19
|
+
},
|
|
20
|
+
// Ensure dependencies imported by the library source are resolved
|
|
21
|
+
// from the playground's node_modules (parent has no node_modules)
|
|
22
|
+
dedupe: ['js-yaml', 'zod'],
|
|
23
|
+
},
|
|
24
|
+
// Allow Vite to serve files from the parent src/ directory
|
|
25
|
+
server: {
|
|
26
|
+
fs: {
|
|
27
|
+
allow: ['.', '../src'],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
// Force Rollup to resolve bare imports from the playground's node_modules
|
|
31
|
+
// even when processing files from the parent directory
|
|
32
|
+
optimizeDeps: {
|
|
33
|
+
include: ['js-yaml', 'zod'],
|
|
34
|
+
},
|
|
35
|
+
define: {
|
|
36
|
+
'process.env': '{}',
|
|
37
|
+
'process.versions': '{}',
|
|
38
|
+
'process.platform': '"browser"',
|
|
39
|
+
},
|
|
40
|
+
});
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Demo: Google Sheets Integration with btree
|
|
3
|
+
*
|
|
4
|
+
* This demo shows how to use the IntegrationAction node to interact
|
|
5
|
+
* with Google Sheets via Active Pieces.
|
|
6
|
+
*
|
|
7
|
+
* Prerequisites:
|
|
8
|
+
* 1. OAuth connection established via controlplane
|
|
9
|
+
* 2. Controlplane running at localhost:5001
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
Registry,
|
|
14
|
+
registerStandardNodes,
|
|
15
|
+
loadTreeFromYaml,
|
|
16
|
+
ScopedBlackboard,
|
|
17
|
+
NodeStatus,
|
|
18
|
+
} from "./src/index.js";
|
|
19
|
+
import type { IntegrationContext, TokenProvider } from "./src/integrations/integration-action.js";
|
|
20
|
+
|
|
21
|
+
// Token provider that fetches from controlplane
|
|
22
|
+
const controlplaneTokenProvider: TokenProvider = async (context, provider, connectionId) => {
|
|
23
|
+
const integrationContext = context as IntegrationContext;
|
|
24
|
+
|
|
25
|
+
const response = await fetch("http://localhost:5001/internal/integrations/token", {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: {
|
|
28
|
+
"Content-Type": "application/json",
|
|
29
|
+
"x-internal-api-key": process.env.INTERNAL_API_KEY || "dev-internal-key-12345",
|
|
30
|
+
},
|
|
31
|
+
body: JSON.stringify({
|
|
32
|
+
tenantId: integrationContext.tenantId,
|
|
33
|
+
userId: integrationContext.userId,
|
|
34
|
+
providerName: provider,
|
|
35
|
+
}),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
const error = await response.text();
|
|
40
|
+
throw new Error(`Failed to get token: ${error}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const data = await response.json() as { accessToken: string };
|
|
44
|
+
return { access_token: data.accessToken };
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
async function main() {
|
|
48
|
+
console.log("🚀 Google Sheets Integration Demo\n");
|
|
49
|
+
|
|
50
|
+
// Create registry with standard nodes
|
|
51
|
+
const registry = new Registry();
|
|
52
|
+
registerStandardNodes(registry);
|
|
53
|
+
|
|
54
|
+
// First, let's create a test spreadsheet
|
|
55
|
+
console.log("Step 1: Creating a test spreadsheet...\n");
|
|
56
|
+
|
|
57
|
+
const createSheetYaml = `
|
|
58
|
+
type: IntegrationAction
|
|
59
|
+
id: create-sheet
|
|
60
|
+
props:
|
|
61
|
+
provider: google
|
|
62
|
+
action: create-spreadsheet
|
|
63
|
+
inputs:
|
|
64
|
+
title: "BTree Demo Sheet - \${bb.timestamp}"
|
|
65
|
+
sheetName: "Orders"
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
const createNode = loadTreeFromYaml(createSheetYaml, registry);
|
|
69
|
+
const blackboard = new ScopedBlackboard();
|
|
70
|
+
blackboard.set("timestamp", new Date().toISOString().slice(0, 19));
|
|
71
|
+
|
|
72
|
+
const context: IntegrationContext = {
|
|
73
|
+
blackboard,
|
|
74
|
+
treeRegistry: registry,
|
|
75
|
+
timestamp: Date.now(),
|
|
76
|
+
deltaTime: 0,
|
|
77
|
+
tokenProvider: controlplaneTokenProvider,
|
|
78
|
+
tenantId: process.env.DEV_TENANT_ID || "00000000-0000-0000-0000-000000000000",
|
|
79
|
+
userId: process.env.DEV_USER_ID || "00000000-0000-0000-0000-000000000001",
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const createStatus = await createNode.tick(context);
|
|
83
|
+
|
|
84
|
+
if (createStatus !== NodeStatus.SUCCESS) {
|
|
85
|
+
console.log("❌ Failed to create spreadsheet");
|
|
86
|
+
console.log("Error:", createNode.lastError);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const createResult = blackboard.get("create-sheet.result") as { id?: string; spreadsheetId?: string } | undefined;
|
|
91
|
+
console.log("✅ Spreadsheet created!");
|
|
92
|
+
console.log(" Result:", JSON.stringify(createResult, null, 2));
|
|
93
|
+
|
|
94
|
+
const spreadsheetId = createResult?.id || createResult?.spreadsheetId;
|
|
95
|
+
if (!spreadsheetId) {
|
|
96
|
+
console.log("❌ No spreadsheet ID returned");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Store spreadsheet ID for next steps
|
|
101
|
+
blackboard.set("spreadsheetId", spreadsheetId);
|
|
102
|
+
console.log(`\n📊 Spreadsheet URL: https://docs.google.com/spreadsheets/d/${spreadsheetId}`);
|
|
103
|
+
|
|
104
|
+
// Test insert_row with correct Active Pieces format
|
|
105
|
+
console.log("\nStep 2: Inserting rows (array mode)...\n");
|
|
106
|
+
|
|
107
|
+
// Active Pieces expects { values: { values: [...] } } for first_row_headers=false
|
|
108
|
+
const insertRowYaml = `
|
|
109
|
+
type: Sequence
|
|
110
|
+
id: insert-data
|
|
111
|
+
children:
|
|
112
|
+
- type: IntegrationAction
|
|
113
|
+
id: insert-header
|
|
114
|
+
props:
|
|
115
|
+
provider: google
|
|
116
|
+
action: insert_row
|
|
117
|
+
inputs:
|
|
118
|
+
spreadsheetId: "\${bb.spreadsheetId}"
|
|
119
|
+
sheetId: 0
|
|
120
|
+
first_row_headers: false
|
|
121
|
+
as_string: true
|
|
122
|
+
values:
|
|
123
|
+
values:
|
|
124
|
+
- "Order ID"
|
|
125
|
+
- "Customer"
|
|
126
|
+
- "Amount"
|
|
127
|
+
- "Status"
|
|
128
|
+
- type: IntegrationAction
|
|
129
|
+
id: insert-row-1
|
|
130
|
+
props:
|
|
131
|
+
provider: google
|
|
132
|
+
action: insert_row
|
|
133
|
+
inputs:
|
|
134
|
+
spreadsheetId: "\${bb.spreadsheetId}"
|
|
135
|
+
sheetId: 0
|
|
136
|
+
first_row_headers: false
|
|
137
|
+
as_string: true
|
|
138
|
+
values:
|
|
139
|
+
values:
|
|
140
|
+
- "ORD-001"
|
|
141
|
+
- "John Doe"
|
|
142
|
+
- "99.99"
|
|
143
|
+
- "Completed"
|
|
144
|
+
- type: IntegrationAction
|
|
145
|
+
id: insert-row-2
|
|
146
|
+
props:
|
|
147
|
+
provider: google
|
|
148
|
+
action: insert_row
|
|
149
|
+
inputs:
|
|
150
|
+
spreadsheetId: "\${bb.spreadsheetId}"
|
|
151
|
+
sheetId: 0
|
|
152
|
+
first_row_headers: false
|
|
153
|
+
as_string: true
|
|
154
|
+
values:
|
|
155
|
+
values:
|
|
156
|
+
- "ORD-002"
|
|
157
|
+
- "Jane Smith"
|
|
158
|
+
- "149.50"
|
|
159
|
+
- "Pending"
|
|
160
|
+
`;
|
|
161
|
+
|
|
162
|
+
const insertNode = loadTreeFromYaml(insertRowYaml, registry);
|
|
163
|
+
const insertStatus = await insertNode.tick(context);
|
|
164
|
+
|
|
165
|
+
if (insertStatus === NodeStatus.SUCCESS) {
|
|
166
|
+
console.log("✅ Rows inserted successfully!");
|
|
167
|
+
} else {
|
|
168
|
+
console.log("⚠️ Row insertion failed (see gap analysis doc)");
|
|
169
|
+
console.log(" Error:", insertNode.lastError);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
console.log("\n✅ Demo complete!");
|
|
173
|
+
console.log("\n📋 Summary:");
|
|
174
|
+
console.log(" - OAuth connection: Working");
|
|
175
|
+
console.log(" - Token retrieval: Working");
|
|
176
|
+
console.log(" - Google Sheets API: Working");
|
|
177
|
+
console.log(` - Created spreadsheet: ${spreadsheetId}`);
|
|
178
|
+
console.log(`\n📊 View: https://docs.google.com/spreadsheets/d/${spreadsheetId}`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Demo: Runtime Variables & Input Parameters
|
|
3
|
+
*
|
|
4
|
+
* This demo showcases the unified variable resolution system:
|
|
5
|
+
*
|
|
6
|
+
* Variable Syntax:
|
|
7
|
+
* - ${input.key} - Workflow input parameters (immutable)
|
|
8
|
+
* - ${bb.key} - Blackboard values (mutable runtime state)
|
|
9
|
+
* - ${env.KEY} - Environment variables
|
|
10
|
+
* - ${param.key} - Test data parameters
|
|
11
|
+
* - ${key} - Shorthand for ${bb.key} (backward compatible)
|
|
12
|
+
*
|
|
13
|
+
* Script Node Proxies:
|
|
14
|
+
* - $input.key - Read-only workflow input
|
|
15
|
+
* - $env.KEY - Read-only allowed environment variables
|
|
16
|
+
* - $bb.key - Read/write blackboard
|
|
17
|
+
*
|
|
18
|
+
* SubTree Parameters:
|
|
19
|
+
* - params: {} - Values passed to subtree (supports variable resolution)
|
|
20
|
+
* - outputs: [] - Keys exported back to parent after execution
|
|
21
|
+
*
|
|
22
|
+
* Run with:
|
|
23
|
+
* TAX_RATE=0.10 BASE_DISCOUNT=0.05 npx tsx demo-runtime-variables.ts
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import * as fs from "fs";
|
|
27
|
+
import * as path from "path";
|
|
28
|
+
import {
|
|
29
|
+
Registry,
|
|
30
|
+
registerStandardNodes,
|
|
31
|
+
loadTreeFromYaml,
|
|
32
|
+
BehaviorTree,
|
|
33
|
+
NodeStatus,
|
|
34
|
+
} from "./src/index.js";
|
|
35
|
+
|
|
36
|
+
async function main() {
|
|
37
|
+
console.log("=".repeat(60));
|
|
38
|
+
console.log(" Runtime Variables & Input Parameters Demo");
|
|
39
|
+
console.log("=".repeat(60));
|
|
40
|
+
console.log();
|
|
41
|
+
|
|
42
|
+
// Show environment configuration
|
|
43
|
+
console.log("Environment Configuration:");
|
|
44
|
+
console.log(` TAX_RATE: ${process.env.TAX_RATE || "0.08 (default)"}`);
|
|
45
|
+
console.log(` BASE_DISCOUNT: ${process.env.BASE_DISCOUNT || "0 (default)"}`);
|
|
46
|
+
console.log();
|
|
47
|
+
|
|
48
|
+
// 1. Setup registry
|
|
49
|
+
const registry = new Registry();
|
|
50
|
+
registerStandardNodes(registry);
|
|
51
|
+
|
|
52
|
+
// 2. Load YAML workflow files from workflows/ directory
|
|
53
|
+
const workflowsDir = path.join(import.meta.dirname, "workflows");
|
|
54
|
+
console.log(`Loading workflows from: ${workflowsDir}`);
|
|
55
|
+
console.log();
|
|
56
|
+
|
|
57
|
+
// Load order-processor subtree
|
|
58
|
+
const orderProcessorYaml = fs.readFileSync(
|
|
59
|
+
path.join(workflowsDir, "order-processor.yaml"),
|
|
60
|
+
"utf-8"
|
|
61
|
+
);
|
|
62
|
+
const orderProcessorTree = loadTreeFromYaml(orderProcessorYaml, registry);
|
|
63
|
+
const orderProcessorBT = new BehaviorTree(orderProcessorTree);
|
|
64
|
+
registry.registerTree("order-processor", orderProcessorBT, "order-processor.yaml");
|
|
65
|
+
console.log(" Loaded: order-processor.yaml (SubTree)");
|
|
66
|
+
|
|
67
|
+
// Load main workflow
|
|
68
|
+
const mainWorkflowYaml = fs.readFileSync(
|
|
69
|
+
path.join(workflowsDir, "process-order-workflow.yaml"),
|
|
70
|
+
"utf-8"
|
|
71
|
+
);
|
|
72
|
+
const mainWorkflowTree = loadTreeFromYaml(mainWorkflowYaml, registry);
|
|
73
|
+
const mainWorkflowBT = new BehaviorTree(mainWorkflowTree);
|
|
74
|
+
console.log(" Loaded: process-order-workflow.yaml (Main)");
|
|
75
|
+
console.log();
|
|
76
|
+
|
|
77
|
+
// 3. Convert to workflow function (Temporal-compatible)
|
|
78
|
+
const workflow = mainWorkflowBT.toWorkflow();
|
|
79
|
+
|
|
80
|
+
// 4. Define workflow input (passed at runtime)
|
|
81
|
+
const workflowInput = {
|
|
82
|
+
orderId: "ORD-2024-0042",
|
|
83
|
+
customerId: "CUST-001", // John Doe - gold tier
|
|
84
|
+
items: [
|
|
85
|
+
{ name: "Widget Pro", price: 49.99, quantity: 2 },
|
|
86
|
+
{ name: "Gadget Plus", price: 29.99, quantity: 1 },
|
|
87
|
+
{ name: "Super Cable", price: 9.99, quantity: 3 },
|
|
88
|
+
],
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
console.log("Workflow Input:");
|
|
92
|
+
console.log(` orderId: ${workflowInput.orderId}`);
|
|
93
|
+
console.log(` customerId: ${workflowInput.customerId}`);
|
|
94
|
+
console.log(` items: ${workflowInput.items.length} items`);
|
|
95
|
+
for (const item of workflowInput.items) {
|
|
96
|
+
console.log(` - ${item.name}: $${item.price} x ${item.quantity}`);
|
|
97
|
+
}
|
|
98
|
+
console.log();
|
|
99
|
+
|
|
100
|
+
// 5. Execute workflow
|
|
101
|
+
console.log("-".repeat(60));
|
|
102
|
+
console.log("Workflow Execution:");
|
|
103
|
+
console.log("-".repeat(60));
|
|
104
|
+
console.log();
|
|
105
|
+
|
|
106
|
+
const result = await workflow({
|
|
107
|
+
input: workflowInput,
|
|
108
|
+
treeRegistry: registry,
|
|
109
|
+
sessionId: "demo-session-001",
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
console.log();
|
|
113
|
+
console.log("-".repeat(60));
|
|
114
|
+
console.log("Workflow Result:");
|
|
115
|
+
console.log("-".repeat(60));
|
|
116
|
+
console.log();
|
|
117
|
+
|
|
118
|
+
console.log(`Status: ${result.status}`);
|
|
119
|
+
console.log();
|
|
120
|
+
|
|
121
|
+
if (result.status === NodeStatus.SUCCESS) {
|
|
122
|
+
const output = result.output;
|
|
123
|
+
|
|
124
|
+
console.log("Customer:");
|
|
125
|
+
const customer = output.customer as Record<string, unknown>;
|
|
126
|
+
console.log(` Name: ${customer?.name}`);
|
|
127
|
+
console.log(` Email: ${customer?.email}`);
|
|
128
|
+
console.log(` Tier: ${customer?.tier}`);
|
|
129
|
+
console.log();
|
|
130
|
+
|
|
131
|
+
console.log("Order Result (from SubTree):");
|
|
132
|
+
const orderResult = output.orderResult as Record<string, unknown>;
|
|
133
|
+
console.log(` Order ID: ${orderResult?.orderId}`);
|
|
134
|
+
console.log(` Items: ${orderResult?.itemCount}`);
|
|
135
|
+
console.log(` Subtotal: $${orderResult?.subtotal}`);
|
|
136
|
+
console.log(` Tax Rate: ${((orderResult?.taxRate as number) * 100).toFixed(0)}%`);
|
|
137
|
+
console.log(` Tax: $${orderResult?.tax}`);
|
|
138
|
+
console.log(` Total: $${orderResult?.total}`);
|
|
139
|
+
console.log();
|
|
140
|
+
|
|
141
|
+
console.log("Final Order (with discount):");
|
|
142
|
+
const finalOrder = output.finalOrder as Record<string, unknown>;
|
|
143
|
+
console.log(` Discount: ${finalOrder?.discountPercent}% (${customer?.tier} tier + base)`);
|
|
144
|
+
console.log(` Savings: $${finalOrder?.discountAmount}`);
|
|
145
|
+
console.log(` Final: $${finalOrder?.finalTotal}`);
|
|
146
|
+
console.log();
|
|
147
|
+
|
|
148
|
+
console.log("Notification:");
|
|
149
|
+
const notification = output.notification as Record<string, unknown>;
|
|
150
|
+
console.log(` To: ${notification?.to}`);
|
|
151
|
+
console.log(` Subject: ${notification?.subject}`);
|
|
152
|
+
console.log(` Sent: ${notification?.sentAt}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
console.log();
|
|
156
|
+
console.log("=".repeat(60));
|
|
157
|
+
console.log(" Demo Complete!");
|
|
158
|
+
console.log("=".repeat(60));
|
|
159
|
+
console.log();
|
|
160
|
+
console.log("Key Features Demonstrated:");
|
|
161
|
+
console.log(" 1. YAML workflows loaded from files (production-like)");
|
|
162
|
+
console.log(" 2. ${input.key} - Immutable workflow input parameters");
|
|
163
|
+
console.log(" 3. ${bb.key} - Mutable blackboard state");
|
|
164
|
+
console.log(" 4. ${env.KEY} - Environment variable access");
|
|
165
|
+
console.log(" 5. SubTree params - Pass values to subtrees with resolution");
|
|
166
|
+
console.log(" 6. SubTree outputs - Export values back to parent");
|
|
167
|
+
console.log(" 7. Script $input/$env/$bb - Full access in JavaScript");
|
|
168
|
+
console.log(" 8. allowedEnvVars - Security-conscious env access");
|
|
169
|
+
console.log();
|
|
170
|
+
console.log("Try with different env vars:");
|
|
171
|
+
console.log(" TAX_RATE=0.15 BASE_DISCOUNT=0.10 npx tsx demo-runtime-variables.ts");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
main().catch(console.error);
|
package/demo-template.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Demo: Using GoogleSheets.InsertRow Template
|
|
3
|
+
*
|
|
4
|
+
* This demonstrates using templates for friendly Google Sheets integration.
|
|
5
|
+
* The template handles data transformation automatically.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
Registry,
|
|
10
|
+
registerStandardNodes,
|
|
11
|
+
loadTreeFromYaml,
|
|
12
|
+
loadTemplatesFromDirectory,
|
|
13
|
+
ScopedBlackboard,
|
|
14
|
+
NodeStatus,
|
|
15
|
+
} from "./src/index.js";
|
|
16
|
+
import type { IntegrationContext, TokenProvider } from "./src/integrations/integration-action.js";
|
|
17
|
+
import * as path from "path";
|
|
18
|
+
|
|
19
|
+
// Token provider that fetches from controlplane
|
|
20
|
+
const controlplaneTokenProvider: TokenProvider = async (context, provider) => {
|
|
21
|
+
const integrationContext = context as IntegrationContext;
|
|
22
|
+
|
|
23
|
+
const response = await fetch("http://localhost:5001/internal/integrations/token", {
|
|
24
|
+
method: "POST",
|
|
25
|
+
headers: {
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
"x-internal-api-key": process.env.INTERNAL_API_KEY || "dev-internal-key-12345",
|
|
28
|
+
},
|
|
29
|
+
body: JSON.stringify({
|
|
30
|
+
tenantId: integrationContext.tenantId,
|
|
31
|
+
userId: integrationContext.userId,
|
|
32
|
+
providerName: provider,
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
const error = await response.text();
|
|
38
|
+
throw new Error(`Failed to get token: ${error}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const data = await response.json() as { accessToken: string };
|
|
42
|
+
return { access_token: data.accessToken };
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
async function main() {
|
|
46
|
+
console.log("===========================================");
|
|
47
|
+
console.log(" Google Sheets Template Demo");
|
|
48
|
+
console.log("===========================================\n");
|
|
49
|
+
|
|
50
|
+
// 1. Setup registry with standard nodes
|
|
51
|
+
const registry = new Registry();
|
|
52
|
+
registerStandardNodes(registry);
|
|
53
|
+
|
|
54
|
+
// 2. Load templates
|
|
55
|
+
console.log("Loading templates...\n");
|
|
56
|
+
const templatesDir = path.join(import.meta.dirname, "templates/google-sheets");
|
|
57
|
+
await loadTemplatesFromDirectory(registry, {
|
|
58
|
+
templatesDir,
|
|
59
|
+
idPrefix: "GoogleSheets."
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// 3. Create context
|
|
63
|
+
const blackboard = new ScopedBlackboard();
|
|
64
|
+
const context: IntegrationContext = {
|
|
65
|
+
blackboard,
|
|
66
|
+
treeRegistry: registry,
|
|
67
|
+
timestamp: Date.now(),
|
|
68
|
+
deltaTime: 0,
|
|
69
|
+
tokenProvider: controlplaneTokenProvider,
|
|
70
|
+
tenantId: process.env.DEV_TENANT_ID || "00000000-0000-0000-0000-000000000000",
|
|
71
|
+
userId: process.env.DEV_USER_ID || "00000000-0000-0000-0000-000000000001",
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// 4. First, create a spreadsheet (direct action)
|
|
75
|
+
console.log("Step 1: Creating spreadsheet...\n");
|
|
76
|
+
|
|
77
|
+
const timestamp = new Date().toISOString().slice(0, 19);
|
|
78
|
+
const createYaml = `
|
|
79
|
+
type: IntegrationAction
|
|
80
|
+
id: create-sheet
|
|
81
|
+
props:
|
|
82
|
+
provider: google
|
|
83
|
+
action: create-spreadsheet
|
|
84
|
+
inputs:
|
|
85
|
+
title: "Template Demo - ${timestamp}"
|
|
86
|
+
sheetName: "Orders"
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
const createNode = loadTreeFromYaml(createYaml, registry);
|
|
90
|
+
const createStatus = await createNode.tick(context);
|
|
91
|
+
|
|
92
|
+
if (createStatus !== NodeStatus.SUCCESS) {
|
|
93
|
+
console.log("Failed to create spreadsheet:", createNode.lastError);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const createResult = blackboard.get("create-sheet.result") as { id?: string };
|
|
98
|
+
const spreadsheetId = createResult?.id;
|
|
99
|
+
console.log(`Created spreadsheet: ${spreadsheetId}`);
|
|
100
|
+
console.log(`URL: https://docs.google.com/spreadsheets/d/${spreadsheetId}\n`);
|
|
101
|
+
|
|
102
|
+
// 5. Use TEMPLATE to insert rows with friendly syntax
|
|
103
|
+
// The template reads from _tpl.* keys, transforms, and calls IntegrationAction
|
|
104
|
+
console.log("Step 2: Inserting rows using template...\n");
|
|
105
|
+
|
|
106
|
+
// Build workflow that uses SubTree to call template
|
|
107
|
+
// Each insert: set _tpl.* inputs → call SubTree → template executes
|
|
108
|
+
const insertYaml = `
|
|
109
|
+
type: Sequence
|
|
110
|
+
id: insert-orders
|
|
111
|
+
children:
|
|
112
|
+
# --- Insert Header Row ---
|
|
113
|
+
- type: CodeExecution
|
|
114
|
+
id: set-header-inputs
|
|
115
|
+
props:
|
|
116
|
+
language: javascript
|
|
117
|
+
code: |
|
|
118
|
+
setBB('tpl_spreadsheet', '${spreadsheetId}');
|
|
119
|
+
setBB('tpl_sheet', 0);
|
|
120
|
+
setBB('tpl_values', ['Order ID', 'Customer', 'Amount', 'Status']);
|
|
121
|
+
|
|
122
|
+
- type: SubTree
|
|
123
|
+
id: insert-header
|
|
124
|
+
props:
|
|
125
|
+
treeId: "GoogleSheets.insert-row"
|
|
126
|
+
|
|
127
|
+
- type: LogMessage
|
|
128
|
+
id: log-header-done
|
|
129
|
+
props:
|
|
130
|
+
message: "Header row inserted"
|
|
131
|
+
level: info
|
|
132
|
+
|
|
133
|
+
# --- Insert Data Row 1 ---
|
|
134
|
+
- type: CodeExecution
|
|
135
|
+
id: set-row1-inputs
|
|
136
|
+
props:
|
|
137
|
+
language: javascript
|
|
138
|
+
code: |
|
|
139
|
+
setBB('tpl_spreadsheet', '${spreadsheetId}');
|
|
140
|
+
setBB('tpl_sheet', 0);
|
|
141
|
+
setBB('tpl_values', {
|
|
142
|
+
'Order ID': 'ORD-001',
|
|
143
|
+
'Customer': 'John Doe',
|
|
144
|
+
'Amount': '99.99',
|
|
145
|
+
'Status': 'Completed'
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
- type: SubTree
|
|
149
|
+
id: insert-row-1
|
|
150
|
+
props:
|
|
151
|
+
treeId: "GoogleSheets.insert-row"
|
|
152
|
+
|
|
153
|
+
- type: LogMessage
|
|
154
|
+
id: log-row1-done
|
|
155
|
+
props:
|
|
156
|
+
message: "Row 1 inserted"
|
|
157
|
+
level: info
|
|
158
|
+
|
|
159
|
+
# --- Insert Data Row 2 ---
|
|
160
|
+
- type: CodeExecution
|
|
161
|
+
id: set-row2-inputs
|
|
162
|
+
props:
|
|
163
|
+
language: javascript
|
|
164
|
+
code: |
|
|
165
|
+
setBB('tpl_spreadsheet', '${spreadsheetId}');
|
|
166
|
+
setBB('tpl_sheet', 0);
|
|
167
|
+
setBB('tpl_values', {
|
|
168
|
+
'Order ID': 'ORD-002',
|
|
169
|
+
'Customer': 'Jane Smith',
|
|
170
|
+
'Amount': '149.50',
|
|
171
|
+
'Status': 'Pending'
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
- type: SubTree
|
|
175
|
+
id: insert-row-2
|
|
176
|
+
props:
|
|
177
|
+
treeId: "GoogleSheets.insert-row"
|
|
178
|
+
|
|
179
|
+
- type: LogMessage
|
|
180
|
+
id: log-row2-done
|
|
181
|
+
props:
|
|
182
|
+
message: "Row 2 inserted"
|
|
183
|
+
level: info
|
|
184
|
+
`;
|
|
185
|
+
|
|
186
|
+
const insertNode = loadTreeFromYaml(insertYaml, registry);
|
|
187
|
+
const insertStatus = await insertNode.tick(context);
|
|
188
|
+
|
|
189
|
+
if (insertStatus === NodeStatus.SUCCESS) {
|
|
190
|
+
console.log("\nAll rows inserted successfully!\n");
|
|
191
|
+
} else {
|
|
192
|
+
console.log("\nRow insertion failed:", insertNode.lastError);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// 6. Summary
|
|
196
|
+
console.log("===========================================");
|
|
197
|
+
console.log(" Demo Complete!");
|
|
198
|
+
console.log("===========================================");
|
|
199
|
+
console.log(`\nSpreadsheet: https://docs.google.com/spreadsheets/d/${spreadsheetId}`);
|
|
200
|
+
console.log("\nWhat we demonstrated:");
|
|
201
|
+
console.log(" - Template loaded from YAML file");
|
|
202
|
+
console.log(" - Friendly input format (object with column names)");
|
|
203
|
+
console.log(" - Template transformed to Active Pieces format");
|
|
204
|
+
console.log(" - SubTree for reusable workflow execution");
|
|
205
|
+
console.log(" - LogMessage for debugging");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
main().catch(console.error);
|