@igoruehara/canvas-flow 0.1.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.
- package/LICENSE +21 -0
- package/README.md +152 -0
- package/bin/canvas-flow.js +1132 -0
- package/package.json +68 -0
- package/public/assets/index-PCQkqMUe.css +1 -0
- package/public/assets/index-qV8twxcq.js +767 -0
- package/public/index.html +13 -0
- package/server/api-key/api-key-connect-provider.d.ts +104 -0
- package/server/api-key/api-key-connect-provider.js +14 -0
- package/server/api-key/api-key-connect-provider.js.map +1 -0
- package/server/api-key/api-key-constants-model.d.ts +2 -0
- package/server/api-key/api-key-constants-model.js +6 -0
- package/server/api-key/api-key-constants-model.js.map +1 -0
- package/server/api-key/api-key-controller.d.ts +12 -0
- package/server/api-key/api-key-controller.js +86 -0
- package/server/api-key/api-key-controller.js.map +1 -0
- package/server/api-key/api-key-module.d.ts +2 -0
- package/server/api-key/api-key-module.js +27 -0
- package/server/api-key/api-key-module.js.map +1 -0
- package/server/api-key/api-key-schema.d.ts +72 -0
- package/server/api-key/api-key-schema.js +98 -0
- package/server/api-key/api-key-schema.js.map +1 -0
- package/server/api-key/api-key-service.d.ts +45 -0
- package/server/api-key/api-key-service.js +151 -0
- package/server/api-key/api-key-service.js.map +1 -0
- package/server/api-key/dto/create-api-key.dto.d.ts +8 -0
- package/server/api-key/dto/create-api-key.dto.js +7 -0
- package/server/api-key/dto/create-api-key.dto.js.map +1 -0
- package/server/app.module.d.ts +2 -0
- package/server/app.module.js +53 -0
- package/server/app.module.js.map +1 -0
- package/server/auth/auth-connect-provider.d.ts +140 -0
- package/server/auth/auth-connect-provider.js +20 -0
- package/server/auth/auth-connect-provider.js.map +1 -0
- package/server/auth/auth-constants-model.d.ts +4 -0
- package/server/auth/auth-constants-model.js +8 -0
- package/server/auth/auth-constants-model.js.map +1 -0
- package/server/auth/auth-controller.d.ts +25 -0
- package/server/auth/auth-controller.js +96 -0
- package/server/auth/auth-controller.js.map +1 -0
- package/server/auth/auth-module.d.ts +2 -0
- package/server/auth/auth-module.js +26 -0
- package/server/auth/auth-module.js.map +1 -0
- package/server/auth/auth-organization-schema.d.ts +44 -0
- package/server/auth/auth-organization-schema.js +62 -0
- package/server/auth/auth-organization-schema.js.map +1 -0
- package/server/auth/auth-schema.d.ts +56 -0
- package/server/auth/auth-schema.js +77 -0
- package/server/auth/auth-schema.js.map +1 -0
- package/server/auth/auth-service.d.ts +64 -0
- package/server/auth/auth-service.js +343 -0
- package/server/auth/auth-service.js.map +1 -0
- package/server/canvas-flow/canvas-flow-connect-provider.d.ts +278 -0
- package/server/canvas-flow/canvas-flow-connect-provider.js +24 -0
- package/server/canvas-flow/canvas-flow-connect-provider.js.map +1 -0
- package/server/canvas-flow/canvas-flow-constants-model.d.ts +6 -0
- package/server/canvas-flow/canvas-flow-constants-model.js +10 -0
- package/server/canvas-flow/canvas-flow-constants-model.js.map +1 -0
- package/server/canvas-flow/canvas-flow-controller.d.ts +98 -0
- package/server/canvas-flow/canvas-flow-controller.js +423 -0
- package/server/canvas-flow/canvas-flow-controller.js.map +1 -0
- package/server/canvas-flow/canvas-flow-module.d.ts +2 -0
- package/server/canvas-flow/canvas-flow-module.js +27 -0
- package/server/canvas-flow/canvas-flow-module.js.map +1 -0
- package/server/canvas-flow/canvas-flow-schema.d.ts +192 -0
- package/server/canvas-flow/canvas-flow-schema.js +239 -0
- package/server/canvas-flow/canvas-flow-schema.js.map +1 -0
- package/server/canvas-flow/canvas-flow-service.d.ts +250 -0
- package/server/canvas-flow/canvas-flow-service.js +1681 -0
- package/server/canvas-flow/canvas-flow-service.js.map +1 -0
- package/server/canvas-flow/dto/create-canvas-flow.dto.d.ts +11 -0
- package/server/canvas-flow/dto/create-canvas-flow.dto.js +61 -0
- package/server/canvas-flow/dto/create-canvas-flow.dto.js.map +1 -0
- package/server/canvas-flow/dto/update-canvas-flow.dto.d.ts +10 -0
- package/server/canvas-flow/dto/update-canvas-flow.dto.js +56 -0
- package/server/canvas-flow/dto/update-canvas-flow.dto.js.map +1 -0
- package/server/constants-global.d.ts +1 -0
- package/server/constants-global.js +5 -0
- package/server/constants-global.js.map +1 -0
- package/server/database/database.module.d.ts +2 -0
- package/server/database/database.module.js +23 -0
- package/server/database/database.module.js.map +1 -0
- package/server/database/database.providers.d.ts +7 -0
- package/server/database/database.providers.js +26 -0
- package/server/database/database.providers.js.map +1 -0
- package/server/documents/documents-connect-provider.d.ts +140 -0
- package/server/documents/documents-connect-provider.js +14 -0
- package/server/documents/documents-connect-provider.js.map +1 -0
- package/server/documents/documents-constants-model.d.ts +2 -0
- package/server/documents/documents-constants-model.js +6 -0
- package/server/documents/documents-constants-model.js.map +1 -0
- package/server/documents/documents-controller.d.ts +16 -0
- package/server/documents/documents-controller.js +117 -0
- package/server/documents/documents-controller.js.map +1 -0
- package/server/documents/documents-module.d.ts +2 -0
- package/server/documents/documents-module.js +27 -0
- package/server/documents/documents-module.js.map +1 -0
- package/server/documents/documents-schema.d.ts +96 -0
- package/server/documents/documents-schema.js +38 -0
- package/server/documents/documents-schema.js.map +1 -0
- package/server/documents/documents-service.d.ts +164 -0
- package/server/documents/documents-service.js +1417 -0
- package/server/documents/documents-service.js.map +1 -0
- package/server/flow-tag/flow-tag-connect-provider.d.ts +146 -0
- package/server/flow-tag/flow-tag-connect-provider.js +14 -0
- package/server/flow-tag/flow-tag-connect-provider.js.map +1 -0
- package/server/flow-tag/flow-tag-constants-model.d.ts +2 -0
- package/server/flow-tag/flow-tag-constants-model.js +6 -0
- package/server/flow-tag/flow-tag-constants-model.js.map +1 -0
- package/server/flow-tag/flow-tag-module.d.ts +2 -0
- package/server/flow-tag/flow-tag-module.js +24 -0
- package/server/flow-tag/flow-tag-module.js.map +1 -0
- package/server/flow-tag/flow-tag-schema.d.ts +100 -0
- package/server/flow-tag/flow-tag-schema.js +131 -0
- package/server/flow-tag/flow-tag-schema.js.map +1 -0
- package/server/flow-tag/flow-tag-service.d.ts +77 -0
- package/server/flow-tag/flow-tag-service.js +156 -0
- package/server/flow-tag/flow-tag-service.js.map +1 -0
- package/server/health.controller.d.ts +7 -0
- package/server/health.controller.js +33 -0
- package/server/health.controller.js.map +1 -0
- package/server/http-batch/http-batch-controller.d.ts +345 -0
- package/server/http-batch/http-batch-controller.js +40 -0
- package/server/http-batch/http-batch-controller.js.map +1 -0
- package/server/http-batch/http-batch-module.d.ts +2 -0
- package/server/http-batch/http-batch-module.js +25 -0
- package/server/http-batch/http-batch-module.js.map +1 -0
- package/server/http-batch/http-batch-service.d.ts +381 -0
- package/server/http-batch/http-batch-service.js +268 -0
- package/server/http-batch/http-batch-service.js.map +1 -0
- package/server/lambda.d.ts +2 -0
- package/server/lambda.js +115 -0
- package/server/lambda.js.map +1 -0
- package/server/llm/openai-provider.d.ts +8 -0
- package/server/llm/openai-provider.js +256 -0
- package/server/llm/openai-provider.js.map +1 -0
- package/server/main.d.ts +1 -0
- package/server/main.js +80 -0
- package/server/main.js.map +1 -0
- package/server/mcp-oauth/mcp-oauth-connect-provider.d.ts +164 -0
- package/server/mcp-oauth/mcp-oauth-connect-provider.js +14 -0
- package/server/mcp-oauth/mcp-oauth-connect-provider.js.map +1 -0
- package/server/mcp-oauth/mcp-oauth-constants-model.d.ts +2 -0
- package/server/mcp-oauth/mcp-oauth-constants-model.js +6 -0
- package/server/mcp-oauth/mcp-oauth-constants-model.js.map +1 -0
- package/server/mcp-oauth/mcp-oauth-controller.d.ts +66 -0
- package/server/mcp-oauth/mcp-oauth-controller.js +166 -0
- package/server/mcp-oauth/mcp-oauth-controller.js.map +1 -0
- package/server/mcp-oauth/mcp-oauth-module.d.ts +2 -0
- package/server/mcp-oauth/mcp-oauth-module.js +27 -0
- package/server/mcp-oauth/mcp-oauth-module.js.map +1 -0
- package/server/mcp-oauth/mcp-oauth-schema.d.ts +112 -0
- package/server/mcp-oauth/mcp-oauth-schema.js +148 -0
- package/server/mcp-oauth/mcp-oauth-schema.js.map +1 -0
- package/server/mcp-oauth/mcp-oauth-service.d.ts +189 -0
- package/server/mcp-oauth/mcp-oauth-service.js +545 -0
- package/server/mcp-oauth/mcp-oauth-service.js.map +1 -0
- package/server/memory/memory-connect-provider.d.ts +200 -0
- package/server/memory/memory-connect-provider.js +26 -0
- package/server/memory/memory-connect-provider.js.map +1 -0
- package/server/memory/memory-constants-model.d.ts +6 -0
- package/server/memory/memory-constants-model.js +10 -0
- package/server/memory/memory-constants-model.js.map +1 -0
- package/server/memory/memory-controller.d.ts +15 -0
- package/server/memory/memory-controller.js +53 -0
- package/server/memory/memory-controller.js.map +1 -0
- package/server/memory/memory-history-schema.d.ts +48 -0
- package/server/memory/memory-history-schema.js +62 -0
- package/server/memory/memory-history-schema.js.map +1 -0
- package/server/memory/memory-module.d.ts +2 -0
- package/server/memory/memory-module.js +26 -0
- package/server/memory/memory-module.js.map +1 -0
- package/server/memory/memory-schema.d.ts +48 -0
- package/server/memory/memory-schema.js +62 -0
- package/server/memory/memory-schema.js.map +1 -0
- package/server/memory/memory-service.d.ts +134 -0
- package/server/memory/memory-service.js +317 -0
- package/server/memory/memory-service.js.map +1 -0
- package/server/memory/memory-trace-history-schema.d.ts +48 -0
- package/server/memory/memory-trace-history-schema.js +62 -0
- package/server/memory/memory-trace-history-schema.js.map +1 -0
- package/server/observability/observability.d.ts +3 -0
- package/server/observability/observability.js +62 -0
- package/server/observability/observability.js.map +1 -0
- package/server/production-guard.d.ts +9 -0
- package/server/production-guard.js +105 -0
- package/server/production-guard.js.map +1 -0
- package/server/provider-config/provider-config-connect-provider.d.ts +44 -0
- package/server/provider-config/provider-config-connect-provider.js +14 -0
- package/server/provider-config/provider-config-connect-provider.js.map +1 -0
- package/server/provider-config/provider-config-constants-model.d.ts +3 -0
- package/server/provider-config/provider-config-constants-model.js +7 -0
- package/server/provider-config/provider-config-constants-model.js.map +1 -0
- package/server/provider-config/provider-config-controller.d.ts +23 -0
- package/server/provider-config/provider-config-controller.js +80 -0
- package/server/provider-config/provider-config-controller.js.map +1 -0
- package/server/provider-config/provider-config-module.d.ts +2 -0
- package/server/provider-config/provider-config-module.js +27 -0
- package/server/provider-config/provider-config-module.js.map +1 -0
- package/server/provider-config/provider-config-schema.d.ts +32 -0
- package/server/provider-config/provider-config-schema.js +46 -0
- package/server/provider-config/provider-config-schema.js.map +1 -0
- package/server/provider-config/provider-config-service.d.ts +178 -0
- package/server/provider-config/provider-config-service.js +689 -0
- package/server/provider-config/provider-config-service.js.map +1 -0
- package/server/queue/queue-job-connect-provider.d.ts +128 -0
- package/server/queue/queue-job-connect-provider.js +14 -0
- package/server/queue/queue-job-connect-provider.js.map +1 -0
- package/server/queue/queue-job-constants-model.d.ts +2 -0
- package/server/queue/queue-job-constants-model.js +6 -0
- package/server/queue/queue-job-constants-model.js.map +1 -0
- package/server/queue/queue-job-schema.d.ts +88 -0
- package/server/queue/queue-job-schema.js +119 -0
- package/server/queue/queue-job-schema.js.map +1 -0
- package/server/queue/queue-lock-connect-provider.d.ts +44 -0
- package/server/queue/queue-lock-connect-provider.js +14 -0
- package/server/queue/queue-lock-connect-provider.js.map +1 -0
- package/server/queue/queue-lock-constants-model.d.ts +2 -0
- package/server/queue/queue-lock-constants-model.js +6 -0
- package/server/queue/queue-lock-constants-model.js.map +1 -0
- package/server/queue/queue-lock-schema.d.ts +32 -0
- package/server/queue/queue-lock-schema.js +47 -0
- package/server/queue/queue-lock-schema.js.map +1 -0
- package/server/queue/queue-message-dedupe-connect-provider.d.ts +116 -0
- package/server/queue/queue-message-dedupe-connect-provider.js +14 -0
- package/server/queue/queue-message-dedupe-connect-provider.js.map +1 -0
- package/server/queue/queue-message-dedupe-constants-model.d.ts +2 -0
- package/server/queue/queue-message-dedupe-constants-model.js +6 -0
- package/server/queue/queue-message-dedupe-constants-model.js.map +1 -0
- package/server/queue/queue-message-dedupe-schema.d.ts +80 -0
- package/server/queue/queue-message-dedupe-schema.js +108 -0
- package/server/queue/queue-message-dedupe-schema.js.map +1 -0
- package/server/queue/queue-module.d.ts +2 -0
- package/server/queue/queue-module.js +33 -0
- package/server/queue/queue-module.js.map +1 -0
- package/server/queue/queue-rate-limit-connect-provider.d.ts +56 -0
- package/server/queue/queue-rate-limit-connect-provider.js +14 -0
- package/server/queue/queue-rate-limit-connect-provider.js.map +1 -0
- package/server/queue/queue-rate-limit-constants-model.d.ts +2 -0
- package/server/queue/queue-rate-limit-constants-model.js +6 -0
- package/server/queue/queue-rate-limit-constants-model.js.map +1 -0
- package/server/queue/queue-rate-limit-schema.d.ts +40 -0
- package/server/queue/queue-rate-limit-schema.js +57 -0
- package/server/queue/queue-rate-limit-schema.js.map +1 -0
- package/server/queue/sqs-transition-service.d.ts +123 -0
- package/server/queue/sqs-transition-service.js +442 -0
- package/server/queue/sqs-transition-service.js.map +1 -0
- package/server/rag/rag-controller.d.ts +167 -0
- package/server/rag/rag-controller.js +232 -0
- package/server/rag/rag-controller.js.map +1 -0
- package/server/rag/rag-module.d.ts +2 -0
- package/server/rag/rag-module.js +30 -0
- package/server/rag/rag-module.js.map +1 -0
- package/server/rag/rag-service.d.ts +361 -0
- package/server/rag/rag-service.js +2864 -0
- package/server/rag/rag-service.js.map +1 -0
- package/server/runner/flow-templates.d.ts +55 -0
- package/server/runner/flow-templates.js +388 -0
- package/server/runner/flow-templates.js.map +1 -0
- package/server/runner/langgraph-runtime.service.d.ts +77 -0
- package/server/runner/langgraph-runtime.service.js +221 -0
- package/server/runner/langgraph-runtime.service.js.map +1 -0
- package/server/runner/runner-controller.d.ts +1044 -0
- package/server/runner/runner-controller.js +751 -0
- package/server/runner/runner-controller.js.map +1 -0
- package/server/runner/runner-module.d.ts +2 -0
- package/server/runner/runner-module.js +37 -0
- package/server/runner/runner-module.js.map +1 -0
- package/server/runner/runner-queue-processor.d.ts +29 -0
- package/server/runner/runner-queue-processor.js +259 -0
- package/server/runner/runner-queue-processor.js.map +1 -0
- package/server/runner/runner-service.d.ts +1761 -0
- package/server/runner/runner-service.js +14256 -0
- package/server/runner/runner-service.js.map +1 -0
- package/server/scripts/migrate-canvas-flow-versions.d.ts +1 -0
- package/server/scripts/migrate-canvas-flow-versions.js +72 -0
- package/server/scripts/migrate-canvas-flow-versions.js.map +1 -0
- package/server/scripts/migrate-mcp-oauth-user-scope.d.ts +1 -0
- package/server/scripts/migrate-mcp-oauth-user-scope.js +95 -0
- package/server/scripts/migrate-mcp-oauth-user-scope.js.map +1 -0
- package/templates/config.example.json +204 -0
- package/templates/config.production.example.json +206 -0
- package/templates/docker-compose.yml +60 -0
|
@@ -0,0 +1,1132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const childProcess = require('node:child_process');
|
|
5
|
+
const crypto = require('node:crypto');
|
|
6
|
+
const dns = require('node:dns').promises;
|
|
7
|
+
const fs = require('node:fs');
|
|
8
|
+
const Module = require('node:module');
|
|
9
|
+
const net = require('node:net');
|
|
10
|
+
const os = require('node:os');
|
|
11
|
+
const path = require('node:path');
|
|
12
|
+
|
|
13
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
14
|
+
const DEFAULT_HOME = path.join(os.homedir(), '.canvas-flow');
|
|
15
|
+
const DEFAULT_CONFIG_FILE = 'config.json';
|
|
16
|
+
const SAME_ORIGIN_FRONTEND_DIR = path.join(PACKAGE_ROOT, 'public');
|
|
17
|
+
const SERVER_ENTRY = path.join(PACKAGE_ROOT, 'server', 'main.js');
|
|
18
|
+
const INFRA_COMPOSE_FILE = path.join(PACKAGE_ROOT, 'templates', 'docker-compose.yml');
|
|
19
|
+
const INFRA_PROJECT_NAME = 'canvas-flow';
|
|
20
|
+
const INFRA_BASE_SERVICES = ['mongo'];
|
|
21
|
+
const INFRA_FULL_SERVICES = ['mongo', 'etcd', 'minio', 'milvus'];
|
|
22
|
+
|
|
23
|
+
function printHelp() {
|
|
24
|
+
console.log(`
|
|
25
|
+
Canvas Flow standalone
|
|
26
|
+
|
|
27
|
+
Usage:
|
|
28
|
+
canvas-flow Start Canvas Flow
|
|
29
|
+
canvas-flow start Start Canvas Flow
|
|
30
|
+
canvas-flow init Create ~/.canvas-flow/config.json
|
|
31
|
+
canvas-flow config Print the active config path
|
|
32
|
+
canvas-flow config --show Print config.json in the terminal
|
|
33
|
+
canvas-flow config --edit Open config.json in the default editor
|
|
34
|
+
canvas-flow doctor Validate local runtime readiness
|
|
35
|
+
canvas-flow infra up Start local Mongo with Docker
|
|
36
|
+
canvas-flow infra up --full Start Mongo, Milvus, MinIO and etcd
|
|
37
|
+
canvas-flow infra status Show Docker infrastructure status
|
|
38
|
+
canvas-flow infra down Stop Docker infrastructure
|
|
39
|
+
|
|
40
|
+
Options:
|
|
41
|
+
--config <path> Use a custom config.json
|
|
42
|
+
--home <path> Use a custom Canvas Flow home directory
|
|
43
|
+
--port <number> Override server.port
|
|
44
|
+
--public-url <url> Override server.publicUrl
|
|
45
|
+
--open Open the browser after starting
|
|
46
|
+
--no-open Do not open the browser
|
|
47
|
+
--with-docker Start local Docker infrastructure before Canvas Flow
|
|
48
|
+
--full Include Milvus, MinIO and etcd with Docker infrastructure
|
|
49
|
+
--show Show config content with "init" or "config"
|
|
50
|
+
--edit Open config file with "init" or "config"
|
|
51
|
+
--force Overwrite config on init
|
|
52
|
+
--offline Skip network checks with "doctor"
|
|
53
|
+
--strict Treat doctor warnings as failures
|
|
54
|
+
--help Show this help
|
|
55
|
+
|
|
56
|
+
Examples:
|
|
57
|
+
canvas-flow init
|
|
58
|
+
canvas-flow config --edit
|
|
59
|
+
canvas-flow config --show
|
|
60
|
+
canvas-flow doctor
|
|
61
|
+
canvas-flow doctor --offline
|
|
62
|
+
canvas-flow infra up
|
|
63
|
+
canvas-flow infra up --full
|
|
64
|
+
canvas-flow --with-docker --open
|
|
65
|
+
canvas-flow --port 3334
|
|
66
|
+
canvas-flow --config C:\\canvas-flow\\config.json
|
|
67
|
+
`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function parseArgs(argv) {
|
|
71
|
+
const args = {
|
|
72
|
+
command: 'start',
|
|
73
|
+
flags: {},
|
|
74
|
+
positionals: [],
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
let commandSet = false;
|
|
78
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
79
|
+
const arg = argv[index];
|
|
80
|
+
if (!arg.startsWith('-') && !commandSet) {
|
|
81
|
+
args.command = arg;
|
|
82
|
+
commandSet = true;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!arg.startsWith('-')) {
|
|
87
|
+
args.positionals.push(arg);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (arg.startsWith('--no-')) {
|
|
92
|
+
args.flags[arg.slice(5)] = false;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (arg.startsWith('--')) {
|
|
97
|
+
const [rawKey, rawValue] = arg.slice(2).split('=', 2);
|
|
98
|
+
if (rawValue !== undefined) {
|
|
99
|
+
args.flags[rawKey] = rawValue;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const next = argv[index + 1];
|
|
104
|
+
if (next && !next.startsWith('-')) {
|
|
105
|
+
args.flags[rawKey] = next;
|
|
106
|
+
index += 1;
|
|
107
|
+
} else {
|
|
108
|
+
args.flags[rawKey] = true;
|
|
109
|
+
}
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return args;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function randomSecret(prefix = '') {
|
|
118
|
+
return `${prefix}${crypto.randomBytes(32).toString('base64url')}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function baseConfig() {
|
|
122
|
+
return {
|
|
123
|
+
server: {
|
|
124
|
+
port: 3333,
|
|
125
|
+
publicUrl: 'http://localhost:3333',
|
|
126
|
+
enableSwagger: true,
|
|
127
|
+
requestBodyLimit: '2mb',
|
|
128
|
+
corsOrigins: [],
|
|
129
|
+
openBrowser: false,
|
|
130
|
+
},
|
|
131
|
+
runtime: {
|
|
132
|
+
nodeEnv: 'production',
|
|
133
|
+
timezone: 'America/Sao_Paulo',
|
|
134
|
+
logIsLambda: false,
|
|
135
|
+
ssmPrefix: '',
|
|
136
|
+
cronAutorun: true,
|
|
137
|
+
cronScanMs: 30000,
|
|
138
|
+
strictProduction: false,
|
|
139
|
+
langGraphCheckpointNamespace: 'canvas-flow-runtime-v1',
|
|
140
|
+
langGraphCheckpointCollection: 'canvas_langgraph_checkpoints',
|
|
141
|
+
langGraphWritesCollection: 'canvas_langgraph_checkpoint_writes',
|
|
142
|
+
langGraphCheckpointTtlHours: 720,
|
|
143
|
+
langGraphCheckpointIndexRetryAttempts: 3,
|
|
144
|
+
langGraphCheckpointIndexRetryDelayMs: 250,
|
|
145
|
+
maxParallelNodes: 50,
|
|
146
|
+
maxStepVisits: 10,
|
|
147
|
+
providerCacheMs: 10000,
|
|
148
|
+
},
|
|
149
|
+
aws: {
|
|
150
|
+
region: 'us-east-1',
|
|
151
|
+
mcpTargetRegion: '',
|
|
152
|
+
mcpSigningRegion: '',
|
|
153
|
+
mcpSigningService: '',
|
|
154
|
+
},
|
|
155
|
+
database: {
|
|
156
|
+
mongoUrl: 'mongodb://127.0.0.1:27017/canvas_flow',
|
|
157
|
+
mongoServerSelectionTimeoutMs: 8000,
|
|
158
|
+
mongoConnectTimeoutMs: 8000,
|
|
159
|
+
},
|
|
160
|
+
auth: {
|
|
161
|
+
login: false,
|
|
162
|
+
loginTtlHours: 24,
|
|
163
|
+
loginThrottleWindowMs: 600000,
|
|
164
|
+
loginMaxAttempts: 8,
|
|
165
|
+
apiToken: '',
|
|
166
|
+
jwtSecret: '',
|
|
167
|
+
mediaProxySecret: '',
|
|
168
|
+
mediaProxyTtlSeconds: 86400,
|
|
169
|
+
},
|
|
170
|
+
files: {
|
|
171
|
+
storage: 'local',
|
|
172
|
+
localDir: './tmp/canvas-flow-documents',
|
|
173
|
+
s3Bucket: '',
|
|
174
|
+
s3Region: 'us-east-1',
|
|
175
|
+
downloadTtlSeconds: 900,
|
|
176
|
+
},
|
|
177
|
+
providers: {
|
|
178
|
+
openai: {
|
|
179
|
+
provider: 'openai',
|
|
180
|
+
llmProvider: '',
|
|
181
|
+
apiKey: '',
|
|
182
|
+
chatModel: 'gpt-4o',
|
|
183
|
+
embeddingModel: 'text-embedding-3-large',
|
|
184
|
+
embeddingDimensions: 3072,
|
|
185
|
+
ocrModel: 'gpt-4o',
|
|
186
|
+
},
|
|
187
|
+
gemini: {
|
|
188
|
+
apiKey: '',
|
|
189
|
+
googleAiApiKey: '',
|
|
190
|
+
chatModel: 'gemini-3.5-flash',
|
|
191
|
+
},
|
|
192
|
+
claude: {
|
|
193
|
+
apiKey: '',
|
|
194
|
+
chatModel: 'claude-sonnet-4-6',
|
|
195
|
+
},
|
|
196
|
+
grok: {
|
|
197
|
+
apiKey: '',
|
|
198
|
+
baseUrl: 'https://api.x.ai/v1',
|
|
199
|
+
chatModel: 'grok-2-latest',
|
|
200
|
+
},
|
|
201
|
+
bedrock: {
|
|
202
|
+
apiKey: '',
|
|
203
|
+
baseUrl: '',
|
|
204
|
+
region: 'us-east-1',
|
|
205
|
+
chatModel: 'anthropic.claude-sonnet-4-6',
|
|
206
|
+
},
|
|
207
|
+
azureOpenAI: {
|
|
208
|
+
enabled: false,
|
|
209
|
+
apiKey: '',
|
|
210
|
+
endpoint: '',
|
|
211
|
+
apiBasePath: '',
|
|
212
|
+
apiVersion: '2024-02-15-preview',
|
|
213
|
+
chatDeploymentName: '',
|
|
214
|
+
chatModelName: '',
|
|
215
|
+
deployment: '',
|
|
216
|
+
modelName: '',
|
|
217
|
+
embeddingDeploymentName: '',
|
|
218
|
+
modelNameEmb: '',
|
|
219
|
+
ocrDeploymentName: '',
|
|
220
|
+
embeddingDimensions: 3072,
|
|
221
|
+
},
|
|
222
|
+
milvus: {
|
|
223
|
+
address: '',
|
|
224
|
+
token: '',
|
|
225
|
+
username: '',
|
|
226
|
+
password: '',
|
|
227
|
+
collectionName: 'canvas_flow_docs',
|
|
228
|
+
vectorProvider: 'milvus',
|
|
229
|
+
},
|
|
230
|
+
azureBlob: {
|
|
231
|
+
connectionString: '',
|
|
232
|
+
containerName: '',
|
|
233
|
+
},
|
|
234
|
+
azureSearch: {
|
|
235
|
+
endpoint: '',
|
|
236
|
+
apiKey: '',
|
|
237
|
+
indexName: '',
|
|
238
|
+
apiVersion: '2024-07-01',
|
|
239
|
+
},
|
|
240
|
+
mongoComponent: {
|
|
241
|
+
connectionString: '',
|
|
242
|
+
databaseName: '',
|
|
243
|
+
},
|
|
244
|
+
figmaOAuth: {
|
|
245
|
+
clientId: '',
|
|
246
|
+
clientSecret: '',
|
|
247
|
+
tokenAuthMethod: 'client_secret_post',
|
|
248
|
+
},
|
|
249
|
+
canvasMcpOAuth: {
|
|
250
|
+
clientId: '',
|
|
251
|
+
clientSecret: '',
|
|
252
|
+
tokenAuthMethod: 'client_secret_post',
|
|
253
|
+
},
|
|
254
|
+
webWidget: {
|
|
255
|
+
primaryColor: '#0f6bff',
|
|
256
|
+
accentColor: '#00b37e',
|
|
257
|
+
assistantName: 'Assistente IA',
|
|
258
|
+
subtitle: 'Online agora',
|
|
259
|
+
welcomeMessage: 'Ola! Como posso ajudar?',
|
|
260
|
+
placeholder: 'Digite sua mensagem',
|
|
261
|
+
bubbleLabel: 'Precisa de ajuda?',
|
|
262
|
+
avatarText: 'IA',
|
|
263
|
+
openByDefault: false,
|
|
264
|
+
position: 'right',
|
|
265
|
+
},
|
|
266
|
+
whatsapp: {
|
|
267
|
+
provider: 'meta',
|
|
268
|
+
deliveryMode: 'provider',
|
|
269
|
+
autoReply: true,
|
|
270
|
+
verifyToken: '',
|
|
271
|
+
accessToken: '',
|
|
272
|
+
businessAccountId: '',
|
|
273
|
+
wabaId: '',
|
|
274
|
+
phoneNumberId: '',
|
|
275
|
+
graphApiVersion: 'v20.0',
|
|
276
|
+
blipContractId: '',
|
|
277
|
+
blipAuthorizationKey: '',
|
|
278
|
+
sinchProjectId: '',
|
|
279
|
+
sinchAppId: '',
|
|
280
|
+
sinchRegion: 'us',
|
|
281
|
+
sinchAccessToken: '',
|
|
282
|
+
sinchChannel: 'WHATSAPP',
|
|
283
|
+
sinchApiMode: 'conversation',
|
|
284
|
+
sinchServiceNumber: '',
|
|
285
|
+
sinchServiceUsername: '',
|
|
286
|
+
sinchServiceToken: '',
|
|
287
|
+
},
|
|
288
|
+
sinch: {
|
|
289
|
+
apiUrl: 'https://api-messaging.wavy.global/v1/whatsapp/send',
|
|
290
|
+
canvasFlowApiUrl: '',
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
sqs: {
|
|
294
|
+
enabled: false,
|
|
295
|
+
queueUrl: '',
|
|
296
|
+
queueArn: '',
|
|
297
|
+
region: 'us-east-1',
|
|
298
|
+
triggerEnabled: true,
|
|
299
|
+
batchSize: 10,
|
|
300
|
+
batchWindowSeconds: 2,
|
|
301
|
+
jobTtlHours: 24,
|
|
302
|
+
consumerConcurrency: 10,
|
|
303
|
+
conversationLockTtlMs: 900000,
|
|
304
|
+
},
|
|
305
|
+
rateLimit: {
|
|
306
|
+
enabled: true,
|
|
307
|
+
windowMs: 60000,
|
|
308
|
+
perMinute: 600,
|
|
309
|
+
webwidgetPerMinute: 300,
|
|
310
|
+
whatsappPerMinute: 600,
|
|
311
|
+
apiPerMinute: 600,
|
|
312
|
+
messageDedupeTtlHours: 24,
|
|
313
|
+
},
|
|
314
|
+
httpBatch: {
|
|
315
|
+
timeoutMs: 120000,
|
|
316
|
+
maxRequests: 10,
|
|
317
|
+
pollingMaxAttempts: 20,
|
|
318
|
+
pollingMaxIntervalSeconds: 60,
|
|
319
|
+
pollingHistoryLimit: 8,
|
|
320
|
+
},
|
|
321
|
+
agentOps: {
|
|
322
|
+
defaultHistoryLimit: 80,
|
|
323
|
+
defaultTraceLimit: 600,
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function initialConfig() {
|
|
329
|
+
const config = baseConfig();
|
|
330
|
+
config.auth.apiToken = randomSecret('cf_master_');
|
|
331
|
+
config.auth.jwtSecret = randomSecret();
|
|
332
|
+
config.auth.mediaProxySecret = randomSecret();
|
|
333
|
+
return config;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function isPlainObject(value) {
|
|
337
|
+
return value && typeof value === 'object' && !Array.isArray(value);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function mergeConfig(defaults, overrides) {
|
|
341
|
+
const output = { ...defaults };
|
|
342
|
+
for (const [key, value] of Object.entries(overrides || {})) {
|
|
343
|
+
if (isPlainObject(value) && isPlainObject(output[key])) {
|
|
344
|
+
output[key] = mergeConfig(output[key], value);
|
|
345
|
+
} else {
|
|
346
|
+
output[key] = value;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return output;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function writeJson(filePath, value) {
|
|
353
|
+
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function ensureDir(dirPath) {
|
|
357
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function resolvePaths(flags) {
|
|
361
|
+
const homeDir = path.resolve(String(flags.home || process.env.CANVAS_FLOW_HOME || DEFAULT_HOME));
|
|
362
|
+
const configPath = path.resolve(String(flags.config || process.env.CANVAS_FLOW_CONFIG || path.join(homeDir, DEFAULT_CONFIG_FILE)));
|
|
363
|
+
return { homeDir, configPath };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function createConfig(configPath, force = false) {
|
|
367
|
+
ensureDir(path.dirname(configPath));
|
|
368
|
+
if (fs.existsSync(configPath) && !force) {
|
|
369
|
+
return { created: false, configPath };
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
writeJson(configPath, initialConfig());
|
|
373
|
+
return { created: true, configPath };
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function loadConfig(configPath) {
|
|
377
|
+
if (!fs.existsSync(configPath)) {
|
|
378
|
+
createConfig(configPath, false);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
let parsed;
|
|
382
|
+
try {
|
|
383
|
+
parsed = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
384
|
+
} catch (error) {
|
|
385
|
+
throw new Error(`Could not parse ${configPath}: ${error.message}`);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const config = mergeConfig(baseConfig(), parsed);
|
|
389
|
+
let changed = false;
|
|
390
|
+
if (!config.auth.apiToken) {
|
|
391
|
+
config.auth.apiToken = randomSecret('cf_master_');
|
|
392
|
+
changed = true;
|
|
393
|
+
}
|
|
394
|
+
if (!config.auth.jwtSecret) {
|
|
395
|
+
config.auth.jwtSecret = randomSecret();
|
|
396
|
+
changed = true;
|
|
397
|
+
}
|
|
398
|
+
if (!config.auth.mediaProxySecret) {
|
|
399
|
+
config.auth.mediaProxySecret = randomSecret();
|
|
400
|
+
changed = true;
|
|
401
|
+
}
|
|
402
|
+
if (changed) writeJson(configPath, config);
|
|
403
|
+
return config;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function showConfig(configPath) {
|
|
407
|
+
if (!fs.existsSync(configPath)) {
|
|
408
|
+
createConfig(configPath, false);
|
|
409
|
+
}
|
|
410
|
+
process.stdout.write(fs.readFileSync(configPath, 'utf8'));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function openConfigEditor(configPath) {
|
|
414
|
+
if (!fs.existsSync(configPath)) {
|
|
415
|
+
createConfig(configPath, false);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const configuredEditor = process.env.VISUAL || process.env.EDITOR;
|
|
419
|
+
if (configuredEditor) {
|
|
420
|
+
const result = childProcess.spawnSync(configuredEditor, [configPath], {
|
|
421
|
+
stdio: 'inherit',
|
|
422
|
+
shell: true,
|
|
423
|
+
});
|
|
424
|
+
if (result.error) throw result.error;
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (process.platform === 'win32') {
|
|
429
|
+
childProcess.spawnSync('notepad', [configPath], { stdio: 'inherit' });
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (process.platform === 'darwin') {
|
|
434
|
+
childProcess.spawnSync('open', ['-t', configPath], { stdio: 'inherit' });
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
childProcess.spawnSync('xdg-open', [configPath], { stdio: 'inherit' });
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function asBool(value) {
|
|
442
|
+
return ['true', '1', 'yes', 'sim'].includes(String(value).toLowerCase());
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function setEnv(name, value, options = {}) {
|
|
446
|
+
if (value === undefined || value === null) return;
|
|
447
|
+
const text = String(value);
|
|
448
|
+
if (!options.allowEmpty && text.trim() === '') return;
|
|
449
|
+
process.env[name] = text;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function setBoolEnv(name, value) {
|
|
453
|
+
process.env[name] = value ? 'true' : 'false';
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function joinCorsOrigins(config, publicUrl, port) {
|
|
457
|
+
const configured = config.server.corsOrigins;
|
|
458
|
+
if (Array.isArray(configured) && configured.length) return configured.join(',');
|
|
459
|
+
if (typeof configured === 'string' && configured.trim()) return configured;
|
|
460
|
+
return [
|
|
461
|
+
publicUrl,
|
|
462
|
+
`http://localhost:${port}`,
|
|
463
|
+
`http://127.0.0.1:${port}`,
|
|
464
|
+
].join(',');
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function applyEnvironment(config, paths, flags) {
|
|
468
|
+
const port = Number(flags.port || config.server.port || 3333);
|
|
469
|
+
const publicUrl = String(flags['public-url'] || config.server.publicUrl || `http://localhost:${port}`).replace(/\/$/, '');
|
|
470
|
+
const openai = config.providers.openai || {};
|
|
471
|
+
const gemini = config.providers.gemini || {};
|
|
472
|
+
const claude = config.providers.claude || {};
|
|
473
|
+
const grok = config.providers.grok || {};
|
|
474
|
+
const bedrock = config.providers.bedrock || {};
|
|
475
|
+
const azureOpenAI = config.providers.azureOpenAI || {};
|
|
476
|
+
const milvus = config.providers.milvus || {};
|
|
477
|
+
const azureBlob = config.providers.azureBlob || {};
|
|
478
|
+
const azureSearch = config.providers.azureSearch || {};
|
|
479
|
+
const mongoComponent = config.providers.mongoComponent || {};
|
|
480
|
+
const figmaOAuth = config.providers.figmaOAuth || {};
|
|
481
|
+
const canvasMcpOAuth = config.providers.canvasMcpOAuth || {};
|
|
482
|
+
const webWidget = config.providers.webWidget || {};
|
|
483
|
+
const whatsapp = config.providers.whatsapp || {};
|
|
484
|
+
const sinch = config.providers.sinch || {};
|
|
485
|
+
const files = config.files || {};
|
|
486
|
+
const sqs = config.sqs || {};
|
|
487
|
+
const rateLimit = config.rateLimit || {};
|
|
488
|
+
const httpBatch = config.httpBatch || {};
|
|
489
|
+
const agentOps = config.agentOps || {};
|
|
490
|
+
const aws = config.aws || {};
|
|
491
|
+
|
|
492
|
+
setEnv('CANVAS_FLOW_HOME', paths.homeDir);
|
|
493
|
+
setEnv('CANVAS_FLOW_CONFIG_FILE', paths.configPath);
|
|
494
|
+
setEnv('NODE_ENV', config.runtime.nodeEnv || 'production');
|
|
495
|
+
setEnv('TZ', config.runtime.timezone || 'America/Sao_Paulo');
|
|
496
|
+
setBoolEnv('LOG_IS_LAMBDA', asBool(config.runtime.logIsLambda));
|
|
497
|
+
setEnv('CANVAS_FLOW_SSM_PREFIX', config.runtime.ssmPrefix);
|
|
498
|
+
setBoolEnv('CANVAS_FLOW_STRICT_PRODUCTION', asBool(config.runtime.strictProduction));
|
|
499
|
+
setEnv('AWS_REGION', aws.region || 'us-east-1');
|
|
500
|
+
setEnv('CANVAS_FLOW_AWS_MCP_TARGET_REGION', aws.mcpTargetRegion || aws.region);
|
|
501
|
+
setEnv('CANVAS_FLOW_AWS_MCP_SIGNING_REGION', aws.mcpSigningRegion || aws.region);
|
|
502
|
+
setEnv('CANVAS_FLOW_AWS_MCP_SIGNING_SERVICE', aws.mcpSigningService);
|
|
503
|
+
setEnv('PORT', port);
|
|
504
|
+
setEnv('CANVAS_FLOW_PUBLIC_URL', publicUrl);
|
|
505
|
+
setEnv('CANVAS_FLOW_API_PUBLIC_URL', publicUrl);
|
|
506
|
+
setEnv('PUBLIC_API_URL', publicUrl);
|
|
507
|
+
setEnv('APP_URL', publicUrl);
|
|
508
|
+
setEnv('CANVAS_FLOW_STATIC_DIR', SAME_ORIGIN_FRONTEND_DIR);
|
|
509
|
+
setEnv('CORS_ORIGINS', joinCorsOrigins(config, publicUrl, port));
|
|
510
|
+
setEnv('REQUEST_BODY_LIMIT', config.server.requestBodyLimit || '2mb');
|
|
511
|
+
setBoolEnv('ENABLE_SWAGGER', config.server.enableSwagger !== false);
|
|
512
|
+
|
|
513
|
+
setEnv('MONGO_DB_CONNECTION_STRING', config.database.mongoUrl);
|
|
514
|
+
setEnv('MONGO_SERVER_SELECTION_TIMEOUT_MS', config.database.mongoServerSelectionTimeoutMs);
|
|
515
|
+
setEnv('MONGO_CONNECT_TIMEOUT_MS', config.database.mongoConnectTimeoutMs);
|
|
516
|
+
|
|
517
|
+
setBoolEnv('CANVAS_FLOW_LOGIN', asBool(config.auth.login));
|
|
518
|
+
setEnv('CANVAS_FLOW_LOGIN_TTL_HOURS', config.auth.loginTtlHours);
|
|
519
|
+
setEnv('CANVAS_FLOW_LOGIN_THROTTLE_WINDOW_MS', config.auth.loginThrottleWindowMs || 600000);
|
|
520
|
+
setEnv('CANVAS_FLOW_LOGIN_MAX_ATTEMPTS', config.auth.loginMaxAttempts || 8);
|
|
521
|
+
setEnv('CANVAS_FLOW_API_TOKEN', config.auth.apiToken);
|
|
522
|
+
setEnv('CANVAS_FLOW_JWT_SECRET', config.auth.jwtSecret);
|
|
523
|
+
setEnv('CANVAS_FLOW_MEDIA_PROXY_SECRET', config.auth.mediaProxySecret);
|
|
524
|
+
setEnv('CANVAS_FLOW_MEDIA_PROXY_TTL_SECONDS', config.auth.mediaProxyTtlSeconds);
|
|
525
|
+
|
|
526
|
+
setEnv('CANVAS_FLOW_FILES_STORAGE', files.storage || 'local');
|
|
527
|
+
setEnv('CANVAS_FLOW_FILES_LOCAL_DIR', files.localDir || './tmp/canvas-flow-documents');
|
|
528
|
+
setEnv('CANVAS_FLOW_FILES_S3_BUCKET', files.s3Bucket);
|
|
529
|
+
setEnv('CANVAS_FLOW_FILES_S3_REGION', files.s3Region || aws.region || 'us-east-1');
|
|
530
|
+
setEnv('CANVAS_FLOW_FILES_DOWNLOAD_TTL_SECONDS', files.downloadTtlSeconds || 900);
|
|
531
|
+
|
|
532
|
+
setBoolEnv('CANVAS_FLOW_CRON_AUTORUN', config.runtime.cronAutorun !== false);
|
|
533
|
+
setEnv('CANVAS_FLOW_CRON_SCAN_MS', config.runtime.cronScanMs || 30000);
|
|
534
|
+
setEnv('CANVAS_FLOW_LANGGRAPH_CHECKPOINT_NAMESPACE', config.runtime.langGraphCheckpointNamespace || 'canvas-flow-runtime-v1');
|
|
535
|
+
setEnv('CANVAS_FLOW_LANGGRAPH_CHECKPOINT_COLLECTION', config.runtime.langGraphCheckpointCollection || 'canvas_langgraph_checkpoints');
|
|
536
|
+
setEnv('CANVAS_FLOW_LANGGRAPH_WRITES_COLLECTION', config.runtime.langGraphWritesCollection || 'canvas_langgraph_checkpoint_writes');
|
|
537
|
+
setEnv('CANVAS_FLOW_LANGGRAPH_CHECKPOINT_TTL_HOURS', config.runtime.langGraphCheckpointTtlHours || 720);
|
|
538
|
+
setEnv('CANVAS_FLOW_LANGGRAPH_CHECKPOINT_INDEX_RETRY_ATTEMPTS', config.runtime.langGraphCheckpointIndexRetryAttempts || 3);
|
|
539
|
+
setEnv('CANVAS_FLOW_LANGGRAPH_CHECKPOINT_INDEX_RETRY_DELAY_MS', config.runtime.langGraphCheckpointIndexRetryDelayMs || 250);
|
|
540
|
+
setEnv('CANVAS_FLOW_MAX_PARALLEL_NODES', config.runtime.maxParallelNodes || 50);
|
|
541
|
+
setEnv('CANVAS_FLOW_MAX_STEP_VISITS', config.runtime.maxStepVisits || 10);
|
|
542
|
+
setEnv('CANVAS_FLOW_PROVIDER_CACHE_MS', config.runtime.providerCacheMs || 10000);
|
|
543
|
+
|
|
544
|
+
setEnv('OPENAI_PROVIDER', openai.provider || 'openai');
|
|
545
|
+
setEnv('LLM_PROVIDER', openai.llmProvider);
|
|
546
|
+
setEnv('OPENAI_API_KEY', openai.apiKey);
|
|
547
|
+
setEnv('OPENAI_CHAT_MODEL', openai.chatModel);
|
|
548
|
+
setEnv('OPENAI_EMBEDDING_MODEL', openai.embeddingModel);
|
|
549
|
+
setEnv('OPENAI_EMBEDDING_DIMENSIONS', openai.embeddingDimensions);
|
|
550
|
+
setEnv('OPENAI_OCR_MODEL', openai.ocrModel);
|
|
551
|
+
|
|
552
|
+
setEnv('GEMINI_API_KEY', gemini.apiKey || gemini.googleAiApiKey);
|
|
553
|
+
setEnv('GOOGLE_AI_API_KEY', gemini.googleAiApiKey || gemini.apiKey);
|
|
554
|
+
setEnv('GEMINI_CHAT_MODEL', gemini.chatModel || 'gemini-3.5-flash');
|
|
555
|
+
setEnv('GEMINI_MODEL', gemini.chatModel || 'gemini-3.5-flash');
|
|
556
|
+
|
|
557
|
+
setEnv('ANTHROPIC_API_KEY', claude.apiKey);
|
|
558
|
+
setEnv('CLAUDE_API_KEY', claude.apiKey);
|
|
559
|
+
setEnv('CLAUDE_CHAT_MODEL', claude.chatModel || 'claude-sonnet-4-6');
|
|
560
|
+
setEnv('ANTHROPIC_MODEL', claude.chatModel || 'claude-sonnet-4-6');
|
|
561
|
+
|
|
562
|
+
setEnv('XAI_API_KEY', grok.apiKey);
|
|
563
|
+
setEnv('GROK_API_KEY', grok.apiKey);
|
|
564
|
+
setEnv('XAI_BASE_URL', grok.baseUrl || 'https://api.x.ai/v1');
|
|
565
|
+
setEnv('GROK_BASE_URL', grok.baseUrl || 'https://api.x.ai/v1');
|
|
566
|
+
setEnv('GROK_CHAT_MODEL', grok.chatModel || 'grok-2-latest');
|
|
567
|
+
setEnv('XAI_MODEL', grok.chatModel || 'grok-2-latest');
|
|
568
|
+
|
|
569
|
+
setEnv('BEDROCK_API_KEY', bedrock.apiKey);
|
|
570
|
+
setEnv('BEDROCK_BASE_URL', bedrock.baseUrl);
|
|
571
|
+
setEnv('BEDROCK_REGION', bedrock.region || aws.region || 'us-east-1');
|
|
572
|
+
setEnv('BEDROCK_CHAT_MODEL', bedrock.chatModel || 'anthropic.claude-sonnet-4-6');
|
|
573
|
+
setEnv('BEDROCK_MODEL', bedrock.chatModel || 'anthropic.claude-sonnet-4-6');
|
|
574
|
+
|
|
575
|
+
setBoolEnv('AZURE_OPENAI_ENABLED', asBool(azureOpenAI.enabled));
|
|
576
|
+
setEnv('AZURE_OPENAI_API_KEY', azureOpenAI.apiKey);
|
|
577
|
+
setEnv('AZURE_OPENAI_ENDPOINT', azureOpenAI.endpoint || azureOpenAI.apiBasePath);
|
|
578
|
+
setEnv('AZURE_OPENAI_API_BASE_PATH', azureOpenAI.apiBasePath || azureOpenAI.endpoint);
|
|
579
|
+
setEnv('AZURE_OPENAI_API_VERSION', azureOpenAI.apiVersion);
|
|
580
|
+
setEnv('AZURE_OPENAI_API_CHAT_DEPLOYMENT_NAME', azureOpenAI.chatDeploymentName || azureOpenAI.deployment || azureOpenAI.chatModelName || azureOpenAI.modelName);
|
|
581
|
+
setEnv('AZURE_OPENAI_API_CHAT_MODEL_NAME', azureOpenAI.chatModelName || azureOpenAI.chatDeploymentName || azureOpenAI.modelName);
|
|
582
|
+
setEnv('AZURE_OPENAI_DEPLOYMENT', azureOpenAI.deployment || azureOpenAI.chatDeploymentName);
|
|
583
|
+
setEnv('AZURE_OPENAI_MODEL_NAME', azureOpenAI.modelName || azureOpenAI.chatModelName || azureOpenAI.chatDeploymentName);
|
|
584
|
+
setEnv('AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME', azureOpenAI.embeddingDeploymentName || azureOpenAI.modelNameEmb);
|
|
585
|
+
setEnv('AZURE_OPENAI_MODEL_NAME_EMB', azureOpenAI.modelNameEmb || azureOpenAI.embeddingDeploymentName);
|
|
586
|
+
setEnv('AZURE_OPENAI_OCR_DEPLOYMENT_NAME', azureOpenAI.ocrDeploymentName || azureOpenAI.chatDeploymentName || azureOpenAI.deployment);
|
|
587
|
+
setEnv('AZURE_OPENAI_EMBEDDING_DIMENSIONS', azureOpenAI.embeddingDimensions);
|
|
588
|
+
|
|
589
|
+
setEnv('MILVUS_ADDRESS', milvus.address);
|
|
590
|
+
setEnv('MILVUS_TOKEN', milvus.token);
|
|
591
|
+
setEnv('MILVUS_USERNAME', milvus.username);
|
|
592
|
+
setEnv('MILVUS_PASSWORD', milvus.password);
|
|
593
|
+
setEnv('COLLECTION_NAME', milvus.collectionName);
|
|
594
|
+
setEnv('RAG_VECTOR_PROVIDER', milvus.vectorProvider || 'milvus');
|
|
595
|
+
|
|
596
|
+
setEnv('BLOB_STRING_CONNECTION', azureBlob.connectionString);
|
|
597
|
+
setEnv('BLOB_CONTAINER_NAME', azureBlob.containerName);
|
|
598
|
+
setEnv('AZURE_STORAGE_CONNECTION_STRING', azureBlob.connectionString);
|
|
599
|
+
setEnv('AZURE_BLOB_CONTAINER_NAME', azureBlob.containerName);
|
|
600
|
+
|
|
601
|
+
setEnv('AZURE_SEARCH_ENDPOINT', azureSearch.endpoint);
|
|
602
|
+
setEnv('AZURE_SEARCH_API_BASE_PATH', azureSearch.endpoint);
|
|
603
|
+
setEnv('AZURE_SEARCH_API_KEY', azureSearch.apiKey);
|
|
604
|
+
setEnv('AZURE_SEARCH_KEY', azureSearch.apiKey);
|
|
605
|
+
setEnv('AZURE_SEARCH_INDEX_NAME', azureSearch.indexName);
|
|
606
|
+
setEnv('AZURE_SEARCH_API_VERSION', azureSearch.apiVersion);
|
|
607
|
+
|
|
608
|
+
setEnv('MONGO_COMPONENT_CONNECTION_STRING', mongoComponent.connectionString);
|
|
609
|
+
setEnv('MONGO_COMPONENT_DB_NAME', mongoComponent.databaseName);
|
|
610
|
+
|
|
611
|
+
setEnv('FIGMA_MCP_OAUTH_CLIENT_ID', figmaOAuth.clientId);
|
|
612
|
+
setEnv('FIGMA_MCP_OAUTH_CLIENT_SECRET', figmaOAuth.clientSecret);
|
|
613
|
+
setEnv('FIGMA_MCP_OAUTH_TOKEN_AUTH_METHOD', figmaOAuth.tokenAuthMethod || 'client_secret_post');
|
|
614
|
+
|
|
615
|
+
setEnv('CANVAS_MCP_OAUTH_CLIENT_ID', canvasMcpOAuth.clientId);
|
|
616
|
+
setEnv('CANVAS_MCP_OAUTH_CLIENT_SECRET', canvasMcpOAuth.clientSecret);
|
|
617
|
+
setEnv('CANVAS_MCP_OAUTH_TOKEN_AUTH_METHOD', canvasMcpOAuth.tokenAuthMethod || 'client_secret_post');
|
|
618
|
+
|
|
619
|
+
setEnv('CANVAS_FLOW_WIDGET_PRIMARY_COLOR', webWidget.primaryColor || '#0f6bff');
|
|
620
|
+
setEnv('CANVAS_FLOW_WIDGET_ACCENT_COLOR', webWidget.accentColor || '#00b37e');
|
|
621
|
+
setEnv('CANVAS_FLOW_WIDGET_ASSISTANT_NAME', webWidget.assistantName || 'Assistente IA');
|
|
622
|
+
setEnv('CANVAS_FLOW_WIDGET_SUBTITLE', webWidget.subtitle || 'Online agora');
|
|
623
|
+
setEnv('CANVAS_FLOW_WIDGET_WELCOME_MESSAGE', webWidget.welcomeMessage || 'Ola! Como posso ajudar?');
|
|
624
|
+
setEnv('CANVAS_FLOW_WIDGET_PLACEHOLDER', webWidget.placeholder || 'Digite sua mensagem');
|
|
625
|
+
setEnv('CANVAS_FLOW_WIDGET_BUBBLE_LABEL', webWidget.bubbleLabel || 'Precisa de ajuda?');
|
|
626
|
+
setEnv('CANVAS_FLOW_WIDGET_AVATAR_TEXT', webWidget.avatarText || 'IA');
|
|
627
|
+
setBoolEnv('CANVAS_FLOW_WIDGET_OPEN_BY_DEFAULT', asBool(webWidget.openByDefault));
|
|
628
|
+
setEnv('CANVAS_FLOW_WIDGET_POSITION', webWidget.position === 'left' ? 'left' : 'right');
|
|
629
|
+
|
|
630
|
+
setEnv('WHATSAPP_PROVIDER', whatsapp.provider || 'meta');
|
|
631
|
+
setEnv('WHATSAPP_DELIVERY_MODE', whatsapp.deliveryMode || 'provider');
|
|
632
|
+
setBoolEnv('WHATSAPP_AUTO_REPLY', whatsapp.autoReply !== false);
|
|
633
|
+
setEnv('WHATSAPP_VERIFY_TOKEN', whatsapp.verifyToken);
|
|
634
|
+
setEnv('WHATSAPP_ACCESS_TOKEN', whatsapp.accessToken);
|
|
635
|
+
setEnv('WHATSAPP_BUSINESS_ACCOUNT_ID', whatsapp.businessAccountId);
|
|
636
|
+
setEnv('WHATSAPP_WABA_ID', whatsapp.wabaId || whatsapp.businessAccountId);
|
|
637
|
+
setEnv('WHATSAPP_PHONE_NUMBER_ID', whatsapp.phoneNumberId);
|
|
638
|
+
setEnv('WHATSAPP_GRAPH_API_VERSION', whatsapp.graphApiVersion || 'v20.0');
|
|
639
|
+
setEnv('BLIP_CONTRACT_ID', whatsapp.blipContractId);
|
|
640
|
+
setEnv('BLIP_AUTHORIZATION_KEY', whatsapp.blipAuthorizationKey);
|
|
641
|
+
setEnv('SINCH_PROJECT_ID', whatsapp.sinchProjectId);
|
|
642
|
+
setEnv('SINCH_APP_ID', whatsapp.sinchAppId);
|
|
643
|
+
setEnv('SINCH_REGION', whatsapp.sinchRegion || 'us');
|
|
644
|
+
setEnv('SINCH_ACCESS_TOKEN', whatsapp.sinchAccessToken);
|
|
645
|
+
setEnv('SINCH_CHANNEL', whatsapp.sinchChannel || 'WHATSAPP');
|
|
646
|
+
setEnv('SINCH_API_MODE', whatsapp.sinchApiMode || 'conversation');
|
|
647
|
+
setEnv('SINCH_SERVICE_NUMBER', whatsapp.sinchServiceNumber);
|
|
648
|
+
setEnv('SINCH_SERVICE_USERNAME', whatsapp.sinchServiceUsername);
|
|
649
|
+
setEnv('SINCH_SERVICE_TOKEN', whatsapp.sinchServiceToken);
|
|
650
|
+
|
|
651
|
+
setEnv('SINCH_API_URL', sinch.apiUrl || 'https://api-messaging.wavy.global/v1/whatsapp/send');
|
|
652
|
+
setEnv('CANVAS_FLOW_SINCH_API_URL', sinch.canvasFlowApiUrl);
|
|
653
|
+
|
|
654
|
+
setBoolEnv('CANVAS_FLOW_SQS', asBool(sqs.enabled));
|
|
655
|
+
setEnv('CANVAS_FLOW_SQS_QUEUE_URL', sqs.queueUrl);
|
|
656
|
+
setEnv('SQS_QUEUE_URL', sqs.queueUrl);
|
|
657
|
+
setEnv('CANVAS_FLOW_SQS_QUEUE_ARN', sqs.queueArn);
|
|
658
|
+
setEnv('CANVAS_FLOW_SQS_REGION', sqs.region);
|
|
659
|
+
setBoolEnv('CANVAS_FLOW_SQS_TRIGGER_ENABLED', sqs.triggerEnabled !== false);
|
|
660
|
+
setEnv('CANVAS_FLOW_SQS_BATCH_SIZE', sqs.batchSize);
|
|
661
|
+
setEnv('CANVAS_FLOW_SQS_BATCH_WINDOW_SECONDS', sqs.batchWindowSeconds);
|
|
662
|
+
setEnv('CANVAS_FLOW_SQS_JOB_TTL_HOURS', sqs.jobTtlHours || 24);
|
|
663
|
+
setEnv('CANVAS_FLOW_SQS_CONSUMER_CONCURRENCY', sqs.consumerConcurrency || 10);
|
|
664
|
+
setEnv('CANVAS_FLOW_SQS_CONVERSATION_LOCK_TTL_MS', sqs.conversationLockTtlMs || 900000);
|
|
665
|
+
|
|
666
|
+
setBoolEnv('CANVAS_FLOW_RATE_LIMIT_ENABLED', rateLimit.enabled !== false);
|
|
667
|
+
setEnv('CANVAS_FLOW_RATE_LIMIT_WINDOW_MS', rateLimit.windowMs || 60000);
|
|
668
|
+
setEnv('CANVAS_FLOW_RATE_LIMIT_PER_MINUTE', rateLimit.perMinute || 600);
|
|
669
|
+
setEnv('CANVAS_FLOW_RATE_LIMIT_WEBWIDGET_PER_MINUTE', rateLimit.webwidgetPerMinute || 300);
|
|
670
|
+
setEnv('CANVAS_FLOW_RATE_LIMIT_WHATSAPP_PER_MINUTE', rateLimit.whatsappPerMinute || 600);
|
|
671
|
+
setEnv('CANVAS_FLOW_RATE_LIMIT_API_PER_MINUTE', rateLimit.apiPerMinute || 600);
|
|
672
|
+
setEnv('CANVAS_FLOW_MESSAGE_DEDUPE_TTL_HOURS', rateLimit.messageDedupeTtlHours || 24);
|
|
673
|
+
|
|
674
|
+
setEnv('HTTP_BATCH_TIMEOUT_MS', httpBatch.timeoutMs || 120000);
|
|
675
|
+
setEnv('HTTP_BATCH_MAX_REQUESTS', httpBatch.maxRequests || 10);
|
|
676
|
+
setEnv('HTTP_BATCH_POLLING_MAX_ATTEMPTS', httpBatch.pollingMaxAttempts || 20);
|
|
677
|
+
setEnv('HTTP_BATCH_POLLING_MAX_INTERVAL_SECONDS', httpBatch.pollingMaxIntervalSeconds || 60);
|
|
678
|
+
setEnv('HTTP_BATCH_POLLING_HISTORY_LIMIT', httpBatch.pollingHistoryLimit || 8);
|
|
679
|
+
|
|
680
|
+
setEnv('CANVAS_FLOW_AGENTOPS_HISTORY_LIMIT', agentOps.defaultHistoryLimit);
|
|
681
|
+
setEnv('CANVAS_FLOW_AGENTOPS_TRACE_LIMIT', agentOps.defaultTraceLimit);
|
|
682
|
+
|
|
683
|
+
return { port, publicUrl };
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
function openBrowser(url) {
|
|
687
|
+
const platform = process.platform;
|
|
688
|
+
let command;
|
|
689
|
+
let args;
|
|
690
|
+
|
|
691
|
+
if (platform === 'win32') {
|
|
692
|
+
command = 'cmd';
|
|
693
|
+
args = ['/c', 'start', '', url];
|
|
694
|
+
} else if (platform === 'darwin') {
|
|
695
|
+
command = 'open';
|
|
696
|
+
args = [url];
|
|
697
|
+
} else {
|
|
698
|
+
command = 'xdg-open';
|
|
699
|
+
args = [url];
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
const child = childProcess.spawn(command, args, {
|
|
703
|
+
detached: true,
|
|
704
|
+
stdio: 'ignore',
|
|
705
|
+
});
|
|
706
|
+
child.unref();
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
function assertBundleExists() {
|
|
710
|
+
if (!fs.existsSync(SERVER_ENTRY)) {
|
|
711
|
+
throw new Error(`Server bundle not found at ${SERVER_ENTRY}. Run "npm run bundle" in npm_canvas_flow before packing or installing this package locally.`);
|
|
712
|
+
}
|
|
713
|
+
if (!fs.existsSync(path.join(SAME_ORIGIN_FRONTEND_DIR, 'index.html'))) {
|
|
714
|
+
throw new Error(`Frontend bundle not found at ${SAME_ORIGIN_FRONTEND_DIR}. Run "npm run bundle" in npm_canvas_flow before packing or installing this package locally.`);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
function addSourceDependencyFallback() {
|
|
719
|
+
const sourceBackendModules = path.resolve(PACKAGE_ROOT, '..', 'backend', 'node_modules');
|
|
720
|
+
const packageModules = path.join(PACKAGE_ROOT, 'node_modules');
|
|
721
|
+
if (fs.existsSync(packageModules) || !fs.existsSync(sourceBackendModules)) return;
|
|
722
|
+
|
|
723
|
+
process.env.NODE_PATH = [
|
|
724
|
+
sourceBackendModules,
|
|
725
|
+
process.env.NODE_PATH,
|
|
726
|
+
].filter(Boolean).join(path.delimiter);
|
|
727
|
+
Module._initPaths();
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function nodeMajorVersion() {
|
|
731
|
+
return Number(process.versions.node.split('.')[0] || 0);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function isStrongSecret(value) {
|
|
735
|
+
const text = String(value || '');
|
|
736
|
+
return text.length >= 32 && !/^(changeme|change-me|secret|password|token|123456|canvas-flow)$/i.test(text);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
function parseBodyLimitBytes(value) {
|
|
740
|
+
const raw = String(value || '').trim().toLowerCase();
|
|
741
|
+
const match = raw.match(/^(\d+(?:\.\d+)?)(b|kb|mb|gb)?$/);
|
|
742
|
+
if (!match) return 0;
|
|
743
|
+
const amount = Number(match[1]);
|
|
744
|
+
const unit = match[2] || 'b';
|
|
745
|
+
const multiplier = unit === 'gb' ? 1024 * 1024 * 1024 : unit === 'mb' ? 1024 * 1024 : unit === 'kb' ? 1024 : 1;
|
|
746
|
+
return Math.floor(amount * multiplier);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
function mongoTargetsFromUri(uri) {
|
|
750
|
+
const raw = String(uri || '').trim();
|
|
751
|
+
if (!raw) return [];
|
|
752
|
+
|
|
753
|
+
if (raw.startsWith('mongodb+srv://')) {
|
|
754
|
+
const parsed = new URL(raw);
|
|
755
|
+
return [{ srv: true, host: parsed.hostname, port: 27017 }];
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const withoutScheme = raw.replace(/^mongodb:\/\//i, '');
|
|
759
|
+
const authority = withoutScheme.split('/')[0] || '';
|
|
760
|
+
const hosts = authority.split('@').pop() || '';
|
|
761
|
+
return hosts
|
|
762
|
+
.split(',')
|
|
763
|
+
.map((entry) => entry.trim())
|
|
764
|
+
.filter(Boolean)
|
|
765
|
+
.map((entry) => {
|
|
766
|
+
const bracketMatch = entry.match(/^\[([^\]]+)](?::(\d+))?$/);
|
|
767
|
+
if (bracketMatch) return { host: bracketMatch[1], port: Number(bracketMatch[2] || 27017) };
|
|
768
|
+
const [host, port] = entry.split(':');
|
|
769
|
+
return { host, port: Number(port || 27017) };
|
|
770
|
+
})
|
|
771
|
+
.filter((target) => target.host && Number.isFinite(target.port));
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function checkTcp(host, port, timeoutMs = 2000) {
|
|
775
|
+
return new Promise((resolve) => {
|
|
776
|
+
const socket = net.createConnection({ host, port });
|
|
777
|
+
const finish = (ok, message) => {
|
|
778
|
+
socket.removeAllListeners();
|
|
779
|
+
socket.destroy();
|
|
780
|
+
resolve({ ok, message });
|
|
781
|
+
};
|
|
782
|
+
socket.setTimeout(timeoutMs);
|
|
783
|
+
socket.once('connect', () => finish(true));
|
|
784
|
+
socket.once('timeout', () => finish(false, `timeout after ${timeoutMs}ms`));
|
|
785
|
+
socket.once('error', (error) => finish(false, error.message));
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
async function checkMongoReachability(uri) {
|
|
790
|
+
const targets = mongoTargetsFromUri(uri);
|
|
791
|
+
if (!targets.length) return { ok: false, message: 'MONGO URI is empty or invalid' };
|
|
792
|
+
|
|
793
|
+
let resolvedTargets = targets;
|
|
794
|
+
if (targets[0].srv) {
|
|
795
|
+
try {
|
|
796
|
+
const records = await dns.resolveSrv(`_mongodb._tcp.${targets[0].host}`);
|
|
797
|
+
resolvedTargets = records.map((record) => ({ host: record.name, port: record.port }));
|
|
798
|
+
} catch (error) {
|
|
799
|
+
return { ok: false, message: `SRV lookup failed: ${error.message}` };
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
const checks = await Promise.all(resolvedTargets.slice(0, 3).map((target) => checkTcp(target.host, target.port)));
|
|
804
|
+
const ok = checks.some((result) => result.ok);
|
|
805
|
+
return {
|
|
806
|
+
ok,
|
|
807
|
+
message: ok
|
|
808
|
+
? `reachable (${resolvedTargets.slice(0, 3).map((target) => `${target.host}:${target.port}`).join(', ')})`
|
|
809
|
+
: checks.map((result, index) => `${resolvedTargets[index].host}:${resolvedTargets[index].port} ${result.message}`).join('; '),
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
function dockerComposeBaseArgs() {
|
|
814
|
+
return [
|
|
815
|
+
'compose',
|
|
816
|
+
'-f',
|
|
817
|
+
INFRA_COMPOSE_FILE,
|
|
818
|
+
'-p',
|
|
819
|
+
INFRA_PROJECT_NAME,
|
|
820
|
+
];
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function dockerComposeServices(flags) {
|
|
824
|
+
return flags.full === true ? INFRA_FULL_SERVICES : INFRA_BASE_SERVICES;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function runDockerCompose(args) {
|
|
828
|
+
if (!fs.existsSync(INFRA_COMPOSE_FILE)) {
|
|
829
|
+
throw new Error(`Docker compose template not found at ${INFRA_COMPOSE_FILE}`);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
const result = childProcess.spawnSync('docker', [...dockerComposeBaseArgs(), ...args], {
|
|
833
|
+
stdio: 'inherit',
|
|
834
|
+
shell: process.platform === 'win32',
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
if (result.error) {
|
|
838
|
+
throw new Error(`Could not run Docker. Install Docker Desktop or Docker Engine, then try again. ${result.error.message}`);
|
|
839
|
+
}
|
|
840
|
+
if (result.status !== 0) {
|
|
841
|
+
throw new Error(`docker compose ${args.join(' ')} failed with exit code ${result.status}`);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
function infra(action, flags = {}) {
|
|
846
|
+
const requestedAction = action || 'status';
|
|
847
|
+
if (requestedAction === 'help') {
|
|
848
|
+
printInfraHelp();
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
if (requestedAction === 'up' || requestedAction === 'start') {
|
|
853
|
+
const services = dockerComposeServices(flags);
|
|
854
|
+
console.log(`Starting Canvas Flow Docker infrastructure: ${services.join(', ')}`);
|
|
855
|
+
runDockerCompose(['up', '-d', ...services]);
|
|
856
|
+
console.log('Docker infrastructure is ready to warm up.');
|
|
857
|
+
if (!flags.full) {
|
|
858
|
+
console.log('Use "canvas-flow infra up --full" when you also want local Milvus for RAG.');
|
|
859
|
+
}
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
if (requestedAction === 'pull') {
|
|
864
|
+
runDockerCompose(['pull', ...dockerComposeServices(flags)]);
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
if (requestedAction === 'status' || requestedAction === 'ps') {
|
|
869
|
+
runDockerCompose(['ps']);
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
if (requestedAction === 'logs') {
|
|
874
|
+
const logArgs = ['logs'];
|
|
875
|
+
if (flags.follow === true || flags.f === true) logArgs.push('-f');
|
|
876
|
+
runDockerCompose(logArgs);
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
if (requestedAction === 'down' || requestedAction === 'stop') {
|
|
881
|
+
runDockerCompose(['down']);
|
|
882
|
+
console.log('Stopped Canvas Flow Docker infrastructure. Volumes were kept.');
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
console.error(`Unknown infra command: ${requestedAction}`);
|
|
887
|
+
printInfraHelp();
|
|
888
|
+
process.exitCode = 1;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function printInfraHelp() {
|
|
892
|
+
console.log(`
|
|
893
|
+
Canvas Flow Docker infrastructure
|
|
894
|
+
|
|
895
|
+
Usage:
|
|
896
|
+
canvas-flow infra up Start local Mongo
|
|
897
|
+
canvas-flow infra up --full Start Mongo, Milvus, MinIO and etcd
|
|
898
|
+
canvas-flow infra status Show containers
|
|
899
|
+
canvas-flow infra logs Show container logs
|
|
900
|
+
canvas-flow infra down Stop containers and keep volumes
|
|
901
|
+
|
|
902
|
+
Options:
|
|
903
|
+
--full Include Milvus, MinIO and etcd
|
|
904
|
+
--follow Follow logs
|
|
905
|
+
`);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
function createDoctorReporter(strict) {
|
|
909
|
+
const state = { failures: 0, warnings: 0 };
|
|
910
|
+
const print = (kind, label, detail) => {
|
|
911
|
+
const prefix = kind === 'pass' ? 'PASS' : kind === 'warn' ? 'WARN' : 'FAIL';
|
|
912
|
+
console.log(`[${prefix}] ${label}${detail ? ` - ${detail}` : ''}`);
|
|
913
|
+
};
|
|
914
|
+
return {
|
|
915
|
+
pass(label, detail) {
|
|
916
|
+
print('pass', label, detail);
|
|
917
|
+
},
|
|
918
|
+
warn(label, detail) {
|
|
919
|
+
state.warnings += 1;
|
|
920
|
+
print('warn', label, detail);
|
|
921
|
+
},
|
|
922
|
+
fail(label, detail) {
|
|
923
|
+
state.failures += 1;
|
|
924
|
+
print('fail', label, detail);
|
|
925
|
+
},
|
|
926
|
+
finish() {
|
|
927
|
+
console.log('');
|
|
928
|
+
console.log(`Doctor finished with ${state.failures} failure(s) and ${state.warnings} warning(s).`);
|
|
929
|
+
process.exitCode = state.failures || (strict && state.warnings) ? 1 : 0;
|
|
930
|
+
if (strict && state.warnings && !state.failures) {
|
|
931
|
+
console.log('Strict mode treats warnings as failures.');
|
|
932
|
+
}
|
|
933
|
+
},
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
async function doctor(flags) {
|
|
938
|
+
const reporter = createDoctorReporter(flags.strict === true);
|
|
939
|
+
const paths = resolvePaths(flags);
|
|
940
|
+
ensureDir(paths.homeDir);
|
|
941
|
+
|
|
942
|
+
let config;
|
|
943
|
+
try {
|
|
944
|
+
config = loadConfig(paths.configPath);
|
|
945
|
+
reporter.pass('Config file', paths.configPath);
|
|
946
|
+
} catch (error) {
|
|
947
|
+
reporter.fail('Config file', error.message);
|
|
948
|
+
reporter.finish();
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
const runtime = applyEnvironment(config, paths, flags);
|
|
953
|
+
const isProduction = String(config.runtime.nodeEnv || 'production').toLowerCase() === 'production';
|
|
954
|
+
const loginRequired = asBool(config.auth.login);
|
|
955
|
+
const corsOrigins = joinCorsOrigins(config, runtime.publicUrl, runtime.port);
|
|
956
|
+
|
|
957
|
+
if (nodeMajorVersion() >= 20) {
|
|
958
|
+
reporter.pass('Node.js version', process.version);
|
|
959
|
+
} else {
|
|
960
|
+
reporter.fail('Node.js version', `${process.version} found; Node >=20 is required`);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
if (fs.existsSync(SERVER_ENTRY)) {
|
|
964
|
+
reporter.pass('Server bundle', SERVER_ENTRY);
|
|
965
|
+
} else {
|
|
966
|
+
reporter.fail('Server bundle', `missing at ${SERVER_ENTRY}; run npm run bundle`);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
if (fs.existsSync(path.join(SAME_ORIGIN_FRONTEND_DIR, 'index.html'))) {
|
|
970
|
+
reporter.pass('Frontend bundle', SAME_ORIGIN_FRONTEND_DIR);
|
|
971
|
+
} else {
|
|
972
|
+
reporter.fail('Frontend bundle', `missing at ${SAME_ORIGIN_FRONTEND_DIR}; run npm run bundle`);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
if (config.database.mongoUrl) {
|
|
976
|
+
reporter.pass('Mongo config', 'MONGO_DB_CONNECTION_STRING is set');
|
|
977
|
+
if (flags.offline === true) {
|
|
978
|
+
reporter.warn('Mongo reachability', 'skipped because --offline was used');
|
|
979
|
+
} else {
|
|
980
|
+
const mongoCheck = await checkMongoReachability(config.database.mongoUrl);
|
|
981
|
+
if (mongoCheck.ok) reporter.pass('Mongo reachability', mongoCheck.message);
|
|
982
|
+
else reporter.fail('Mongo reachability', `${mongoCheck.message}; run "canvas-flow infra up" to start local Mongo with Docker`);
|
|
983
|
+
}
|
|
984
|
+
} else {
|
|
985
|
+
reporter.fail('Mongo config', 'database.mongoUrl is required');
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
if (isProduction && !isStrongSecret(config.auth.apiToken)) {
|
|
989
|
+
reporter.fail('Master API token', 'auth.apiToken must be at least 32 characters in production');
|
|
990
|
+
} else {
|
|
991
|
+
reporter.pass('Master API token', isProduction ? 'strong enough for production gate' : 'configured');
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
if (loginRequired && !isStrongSecret(config.auth.jwtSecret)) {
|
|
995
|
+
reporter.fail('JWT secret', 'auth.jwtSecret must be at least 32 characters when login is enabled');
|
|
996
|
+
} else if (loginRequired) {
|
|
997
|
+
reporter.pass('JWT secret', 'configured for login');
|
|
998
|
+
} else {
|
|
999
|
+
reporter.warn('Login', 'disabled; expose only behind a trusted private boundary');
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
if (isProduction && config.server.enableSwagger !== false) {
|
|
1003
|
+
reporter.warn('Swagger', 'enabled while runtime.nodeEnv is production');
|
|
1004
|
+
} else {
|
|
1005
|
+
reporter.pass('Swagger', config.server.enableSwagger === false ? 'disabled' : 'enabled for non-production');
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
if (isProduction && /(^|,)\s*\*\s*(,|$)/.test(corsOrigins)) {
|
|
1009
|
+
reporter.fail('CORS', 'wildcard origin is not allowed in production');
|
|
1010
|
+
} else {
|
|
1011
|
+
reporter.pass('CORS', corsOrigins);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
const bodyLimitBytes = parseBodyLimitBytes(config.server.requestBodyLimit || '2mb');
|
|
1015
|
+
if (isProduction && bodyLimitBytes > 10 * 1024 * 1024) {
|
|
1016
|
+
reporter.warn('Request body limit', `${config.server.requestBodyLimit} is high for public production`);
|
|
1017
|
+
} else {
|
|
1018
|
+
reporter.pass('Request body limit', config.server.requestBodyLimit || '2mb');
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
if (asBool(config.sqs.enabled)) {
|
|
1022
|
+
if (config.sqs.queueUrl) reporter.pass('SQS', 'enabled and queueUrl is set');
|
|
1023
|
+
else reporter.fail('SQS', 'enabled but sqs.queueUrl is empty');
|
|
1024
|
+
} else {
|
|
1025
|
+
reporter.warn('SQS', 'disabled; async transitions and queue recovery are not active');
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
const hasOpenAi = Boolean(String(config.providers.openai?.apiKey || '').trim());
|
|
1029
|
+
const hasAzureOpenAi = asBool(config.providers.azureOpenAI?.enabled) && Boolean(String(config.providers.azureOpenAI?.apiKey || '').trim());
|
|
1030
|
+
if (hasOpenAi || hasAzureOpenAi) {
|
|
1031
|
+
reporter.pass('LLM provider', hasOpenAi ? 'OpenAI configured' : 'Azure OpenAI configured');
|
|
1032
|
+
} else {
|
|
1033
|
+
reporter.warn('LLM provider', 'not configured; LLM/RAG generation nodes will fail until configured');
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
const hasVectorStore = Boolean(String(config.providers.milvus?.address || '').trim())
|
|
1037
|
+
|| Boolean(String(config.providers.azureSearch?.endpoint || '').trim());
|
|
1038
|
+
if (hasVectorStore) {
|
|
1039
|
+
reporter.pass('RAG provider', config.providers.milvus?.address ? 'Milvus configured' : 'Azure AI Search configured');
|
|
1040
|
+
} else {
|
|
1041
|
+
reporter.warn('RAG provider', 'not configured; vector search is unavailable');
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
reporter.finish();
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
function start(flags) {
|
|
1048
|
+
assertBundleExists();
|
|
1049
|
+
addSourceDependencyFallback();
|
|
1050
|
+
if (flags['with-docker'] === true || flags.infra === true) {
|
|
1051
|
+
infra('up', flags);
|
|
1052
|
+
}
|
|
1053
|
+
const paths = resolvePaths(flags);
|
|
1054
|
+
ensureDir(paths.homeDir);
|
|
1055
|
+
const configExisted = fs.existsSync(paths.configPath);
|
|
1056
|
+
const config = loadConfig(paths.configPath);
|
|
1057
|
+
const runtime = applyEnvironment(config, paths, flags);
|
|
1058
|
+
|
|
1059
|
+
process.chdir(paths.homeDir);
|
|
1060
|
+
|
|
1061
|
+
console.log(`Canvas Flow config: ${paths.configPath}`);
|
|
1062
|
+
console.log(`Canvas Flow home: ${paths.homeDir}`);
|
|
1063
|
+
console.log(`Canvas Flow URL: ${runtime.publicUrl}`);
|
|
1064
|
+
if (!configExisted) {
|
|
1065
|
+
console.log('Created the default config.json.');
|
|
1066
|
+
console.log('Edit it with: canvas-flow config --edit');
|
|
1067
|
+
console.log('Show it with: canvas-flow config --show');
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
const shouldOpen = flags.open === true || (flags.open !== false && config.server.openBrowser === true);
|
|
1071
|
+
if (shouldOpen) {
|
|
1072
|
+
const timer = setTimeout(() => openBrowser(runtime.publicUrl), 1200);
|
|
1073
|
+
if (typeof timer.unref === 'function') timer.unref();
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
require(SERVER_ENTRY);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
async function main() {
|
|
1080
|
+
const args = parseArgs(process.argv.slice(2));
|
|
1081
|
+
if (args.flags.help || args.command === 'help') {
|
|
1082
|
+
printHelp();
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
const paths = resolvePaths(args.flags);
|
|
1087
|
+
if (args.command === 'init') {
|
|
1088
|
+
const result = createConfig(paths.configPath, args.flags.force === true);
|
|
1089
|
+
console.log(result.created ? `Created ${result.configPath}` : `Config already exists: ${result.configPath}`);
|
|
1090
|
+
if (args.flags.show === true) showConfig(paths.configPath);
|
|
1091
|
+
if (args.flags.edit === true) openConfigEditor(paths.configPath);
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
if (args.command === 'config') {
|
|
1096
|
+
if (args.flags.show === true) {
|
|
1097
|
+
showConfig(paths.configPath);
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
if (args.flags.edit === true) {
|
|
1101
|
+
openConfigEditor(paths.configPath);
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
console.log(paths.configPath);
|
|
1105
|
+
console.log('Use "canvas-flow config --edit" to open it, or "canvas-flow config --show" to print it.');
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
if (args.command === 'doctor') {
|
|
1110
|
+
await doctor(args.flags);
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
if (args.command === 'infra') {
|
|
1115
|
+
infra(args.positionals[0] || 'status', args.flags);
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
if (args.command === 'start' || args.command === 'run') {
|
|
1120
|
+
start(args.flags);
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
console.error(`Unknown command: ${args.command}`);
|
|
1125
|
+
printHelp();
|
|
1126
|
+
process.exitCode = 1;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
main().catch((error) => {
|
|
1130
|
+
console.error(error && error.stack ? error.stack : String(error));
|
|
1131
|
+
process.exitCode = 1;
|
|
1132
|
+
});
|