@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.
Files changed (283) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +152 -0
  3. package/bin/canvas-flow.js +1132 -0
  4. package/package.json +68 -0
  5. package/public/assets/index-PCQkqMUe.css +1 -0
  6. package/public/assets/index-qV8twxcq.js +767 -0
  7. package/public/index.html +13 -0
  8. package/server/api-key/api-key-connect-provider.d.ts +104 -0
  9. package/server/api-key/api-key-connect-provider.js +14 -0
  10. package/server/api-key/api-key-connect-provider.js.map +1 -0
  11. package/server/api-key/api-key-constants-model.d.ts +2 -0
  12. package/server/api-key/api-key-constants-model.js +6 -0
  13. package/server/api-key/api-key-constants-model.js.map +1 -0
  14. package/server/api-key/api-key-controller.d.ts +12 -0
  15. package/server/api-key/api-key-controller.js +86 -0
  16. package/server/api-key/api-key-controller.js.map +1 -0
  17. package/server/api-key/api-key-module.d.ts +2 -0
  18. package/server/api-key/api-key-module.js +27 -0
  19. package/server/api-key/api-key-module.js.map +1 -0
  20. package/server/api-key/api-key-schema.d.ts +72 -0
  21. package/server/api-key/api-key-schema.js +98 -0
  22. package/server/api-key/api-key-schema.js.map +1 -0
  23. package/server/api-key/api-key-service.d.ts +45 -0
  24. package/server/api-key/api-key-service.js +151 -0
  25. package/server/api-key/api-key-service.js.map +1 -0
  26. package/server/api-key/dto/create-api-key.dto.d.ts +8 -0
  27. package/server/api-key/dto/create-api-key.dto.js +7 -0
  28. package/server/api-key/dto/create-api-key.dto.js.map +1 -0
  29. package/server/app.module.d.ts +2 -0
  30. package/server/app.module.js +53 -0
  31. package/server/app.module.js.map +1 -0
  32. package/server/auth/auth-connect-provider.d.ts +140 -0
  33. package/server/auth/auth-connect-provider.js +20 -0
  34. package/server/auth/auth-connect-provider.js.map +1 -0
  35. package/server/auth/auth-constants-model.d.ts +4 -0
  36. package/server/auth/auth-constants-model.js +8 -0
  37. package/server/auth/auth-constants-model.js.map +1 -0
  38. package/server/auth/auth-controller.d.ts +25 -0
  39. package/server/auth/auth-controller.js +96 -0
  40. package/server/auth/auth-controller.js.map +1 -0
  41. package/server/auth/auth-module.d.ts +2 -0
  42. package/server/auth/auth-module.js +26 -0
  43. package/server/auth/auth-module.js.map +1 -0
  44. package/server/auth/auth-organization-schema.d.ts +44 -0
  45. package/server/auth/auth-organization-schema.js +62 -0
  46. package/server/auth/auth-organization-schema.js.map +1 -0
  47. package/server/auth/auth-schema.d.ts +56 -0
  48. package/server/auth/auth-schema.js +77 -0
  49. package/server/auth/auth-schema.js.map +1 -0
  50. package/server/auth/auth-service.d.ts +64 -0
  51. package/server/auth/auth-service.js +343 -0
  52. package/server/auth/auth-service.js.map +1 -0
  53. package/server/canvas-flow/canvas-flow-connect-provider.d.ts +278 -0
  54. package/server/canvas-flow/canvas-flow-connect-provider.js +24 -0
  55. package/server/canvas-flow/canvas-flow-connect-provider.js.map +1 -0
  56. package/server/canvas-flow/canvas-flow-constants-model.d.ts +6 -0
  57. package/server/canvas-flow/canvas-flow-constants-model.js +10 -0
  58. package/server/canvas-flow/canvas-flow-constants-model.js.map +1 -0
  59. package/server/canvas-flow/canvas-flow-controller.d.ts +98 -0
  60. package/server/canvas-flow/canvas-flow-controller.js +423 -0
  61. package/server/canvas-flow/canvas-flow-controller.js.map +1 -0
  62. package/server/canvas-flow/canvas-flow-module.d.ts +2 -0
  63. package/server/canvas-flow/canvas-flow-module.js +27 -0
  64. package/server/canvas-flow/canvas-flow-module.js.map +1 -0
  65. package/server/canvas-flow/canvas-flow-schema.d.ts +192 -0
  66. package/server/canvas-flow/canvas-flow-schema.js +239 -0
  67. package/server/canvas-flow/canvas-flow-schema.js.map +1 -0
  68. package/server/canvas-flow/canvas-flow-service.d.ts +250 -0
  69. package/server/canvas-flow/canvas-flow-service.js +1681 -0
  70. package/server/canvas-flow/canvas-flow-service.js.map +1 -0
  71. package/server/canvas-flow/dto/create-canvas-flow.dto.d.ts +11 -0
  72. package/server/canvas-flow/dto/create-canvas-flow.dto.js +61 -0
  73. package/server/canvas-flow/dto/create-canvas-flow.dto.js.map +1 -0
  74. package/server/canvas-flow/dto/update-canvas-flow.dto.d.ts +10 -0
  75. package/server/canvas-flow/dto/update-canvas-flow.dto.js +56 -0
  76. package/server/canvas-flow/dto/update-canvas-flow.dto.js.map +1 -0
  77. package/server/constants-global.d.ts +1 -0
  78. package/server/constants-global.js +5 -0
  79. package/server/constants-global.js.map +1 -0
  80. package/server/database/database.module.d.ts +2 -0
  81. package/server/database/database.module.js +23 -0
  82. package/server/database/database.module.js.map +1 -0
  83. package/server/database/database.providers.d.ts +7 -0
  84. package/server/database/database.providers.js +26 -0
  85. package/server/database/database.providers.js.map +1 -0
  86. package/server/documents/documents-connect-provider.d.ts +140 -0
  87. package/server/documents/documents-connect-provider.js +14 -0
  88. package/server/documents/documents-connect-provider.js.map +1 -0
  89. package/server/documents/documents-constants-model.d.ts +2 -0
  90. package/server/documents/documents-constants-model.js +6 -0
  91. package/server/documents/documents-constants-model.js.map +1 -0
  92. package/server/documents/documents-controller.d.ts +16 -0
  93. package/server/documents/documents-controller.js +117 -0
  94. package/server/documents/documents-controller.js.map +1 -0
  95. package/server/documents/documents-module.d.ts +2 -0
  96. package/server/documents/documents-module.js +27 -0
  97. package/server/documents/documents-module.js.map +1 -0
  98. package/server/documents/documents-schema.d.ts +96 -0
  99. package/server/documents/documents-schema.js +38 -0
  100. package/server/documents/documents-schema.js.map +1 -0
  101. package/server/documents/documents-service.d.ts +164 -0
  102. package/server/documents/documents-service.js +1417 -0
  103. package/server/documents/documents-service.js.map +1 -0
  104. package/server/flow-tag/flow-tag-connect-provider.d.ts +146 -0
  105. package/server/flow-tag/flow-tag-connect-provider.js +14 -0
  106. package/server/flow-tag/flow-tag-connect-provider.js.map +1 -0
  107. package/server/flow-tag/flow-tag-constants-model.d.ts +2 -0
  108. package/server/flow-tag/flow-tag-constants-model.js +6 -0
  109. package/server/flow-tag/flow-tag-constants-model.js.map +1 -0
  110. package/server/flow-tag/flow-tag-module.d.ts +2 -0
  111. package/server/flow-tag/flow-tag-module.js +24 -0
  112. package/server/flow-tag/flow-tag-module.js.map +1 -0
  113. package/server/flow-tag/flow-tag-schema.d.ts +100 -0
  114. package/server/flow-tag/flow-tag-schema.js +131 -0
  115. package/server/flow-tag/flow-tag-schema.js.map +1 -0
  116. package/server/flow-tag/flow-tag-service.d.ts +77 -0
  117. package/server/flow-tag/flow-tag-service.js +156 -0
  118. package/server/flow-tag/flow-tag-service.js.map +1 -0
  119. package/server/health.controller.d.ts +7 -0
  120. package/server/health.controller.js +33 -0
  121. package/server/health.controller.js.map +1 -0
  122. package/server/http-batch/http-batch-controller.d.ts +345 -0
  123. package/server/http-batch/http-batch-controller.js +40 -0
  124. package/server/http-batch/http-batch-controller.js.map +1 -0
  125. package/server/http-batch/http-batch-module.d.ts +2 -0
  126. package/server/http-batch/http-batch-module.js +25 -0
  127. package/server/http-batch/http-batch-module.js.map +1 -0
  128. package/server/http-batch/http-batch-service.d.ts +381 -0
  129. package/server/http-batch/http-batch-service.js +268 -0
  130. package/server/http-batch/http-batch-service.js.map +1 -0
  131. package/server/lambda.d.ts +2 -0
  132. package/server/lambda.js +115 -0
  133. package/server/lambda.js.map +1 -0
  134. package/server/llm/openai-provider.d.ts +8 -0
  135. package/server/llm/openai-provider.js +256 -0
  136. package/server/llm/openai-provider.js.map +1 -0
  137. package/server/main.d.ts +1 -0
  138. package/server/main.js +80 -0
  139. package/server/main.js.map +1 -0
  140. package/server/mcp-oauth/mcp-oauth-connect-provider.d.ts +164 -0
  141. package/server/mcp-oauth/mcp-oauth-connect-provider.js +14 -0
  142. package/server/mcp-oauth/mcp-oauth-connect-provider.js.map +1 -0
  143. package/server/mcp-oauth/mcp-oauth-constants-model.d.ts +2 -0
  144. package/server/mcp-oauth/mcp-oauth-constants-model.js +6 -0
  145. package/server/mcp-oauth/mcp-oauth-constants-model.js.map +1 -0
  146. package/server/mcp-oauth/mcp-oauth-controller.d.ts +66 -0
  147. package/server/mcp-oauth/mcp-oauth-controller.js +166 -0
  148. package/server/mcp-oauth/mcp-oauth-controller.js.map +1 -0
  149. package/server/mcp-oauth/mcp-oauth-module.d.ts +2 -0
  150. package/server/mcp-oauth/mcp-oauth-module.js +27 -0
  151. package/server/mcp-oauth/mcp-oauth-module.js.map +1 -0
  152. package/server/mcp-oauth/mcp-oauth-schema.d.ts +112 -0
  153. package/server/mcp-oauth/mcp-oauth-schema.js +148 -0
  154. package/server/mcp-oauth/mcp-oauth-schema.js.map +1 -0
  155. package/server/mcp-oauth/mcp-oauth-service.d.ts +189 -0
  156. package/server/mcp-oauth/mcp-oauth-service.js +545 -0
  157. package/server/mcp-oauth/mcp-oauth-service.js.map +1 -0
  158. package/server/memory/memory-connect-provider.d.ts +200 -0
  159. package/server/memory/memory-connect-provider.js +26 -0
  160. package/server/memory/memory-connect-provider.js.map +1 -0
  161. package/server/memory/memory-constants-model.d.ts +6 -0
  162. package/server/memory/memory-constants-model.js +10 -0
  163. package/server/memory/memory-constants-model.js.map +1 -0
  164. package/server/memory/memory-controller.d.ts +15 -0
  165. package/server/memory/memory-controller.js +53 -0
  166. package/server/memory/memory-controller.js.map +1 -0
  167. package/server/memory/memory-history-schema.d.ts +48 -0
  168. package/server/memory/memory-history-schema.js +62 -0
  169. package/server/memory/memory-history-schema.js.map +1 -0
  170. package/server/memory/memory-module.d.ts +2 -0
  171. package/server/memory/memory-module.js +26 -0
  172. package/server/memory/memory-module.js.map +1 -0
  173. package/server/memory/memory-schema.d.ts +48 -0
  174. package/server/memory/memory-schema.js +62 -0
  175. package/server/memory/memory-schema.js.map +1 -0
  176. package/server/memory/memory-service.d.ts +134 -0
  177. package/server/memory/memory-service.js +317 -0
  178. package/server/memory/memory-service.js.map +1 -0
  179. package/server/memory/memory-trace-history-schema.d.ts +48 -0
  180. package/server/memory/memory-trace-history-schema.js +62 -0
  181. package/server/memory/memory-trace-history-schema.js.map +1 -0
  182. package/server/observability/observability.d.ts +3 -0
  183. package/server/observability/observability.js +62 -0
  184. package/server/observability/observability.js.map +1 -0
  185. package/server/production-guard.d.ts +9 -0
  186. package/server/production-guard.js +105 -0
  187. package/server/production-guard.js.map +1 -0
  188. package/server/provider-config/provider-config-connect-provider.d.ts +44 -0
  189. package/server/provider-config/provider-config-connect-provider.js +14 -0
  190. package/server/provider-config/provider-config-connect-provider.js.map +1 -0
  191. package/server/provider-config/provider-config-constants-model.d.ts +3 -0
  192. package/server/provider-config/provider-config-constants-model.js +7 -0
  193. package/server/provider-config/provider-config-constants-model.js.map +1 -0
  194. package/server/provider-config/provider-config-controller.d.ts +23 -0
  195. package/server/provider-config/provider-config-controller.js +80 -0
  196. package/server/provider-config/provider-config-controller.js.map +1 -0
  197. package/server/provider-config/provider-config-module.d.ts +2 -0
  198. package/server/provider-config/provider-config-module.js +27 -0
  199. package/server/provider-config/provider-config-module.js.map +1 -0
  200. package/server/provider-config/provider-config-schema.d.ts +32 -0
  201. package/server/provider-config/provider-config-schema.js +46 -0
  202. package/server/provider-config/provider-config-schema.js.map +1 -0
  203. package/server/provider-config/provider-config-service.d.ts +178 -0
  204. package/server/provider-config/provider-config-service.js +689 -0
  205. package/server/provider-config/provider-config-service.js.map +1 -0
  206. package/server/queue/queue-job-connect-provider.d.ts +128 -0
  207. package/server/queue/queue-job-connect-provider.js +14 -0
  208. package/server/queue/queue-job-connect-provider.js.map +1 -0
  209. package/server/queue/queue-job-constants-model.d.ts +2 -0
  210. package/server/queue/queue-job-constants-model.js +6 -0
  211. package/server/queue/queue-job-constants-model.js.map +1 -0
  212. package/server/queue/queue-job-schema.d.ts +88 -0
  213. package/server/queue/queue-job-schema.js +119 -0
  214. package/server/queue/queue-job-schema.js.map +1 -0
  215. package/server/queue/queue-lock-connect-provider.d.ts +44 -0
  216. package/server/queue/queue-lock-connect-provider.js +14 -0
  217. package/server/queue/queue-lock-connect-provider.js.map +1 -0
  218. package/server/queue/queue-lock-constants-model.d.ts +2 -0
  219. package/server/queue/queue-lock-constants-model.js +6 -0
  220. package/server/queue/queue-lock-constants-model.js.map +1 -0
  221. package/server/queue/queue-lock-schema.d.ts +32 -0
  222. package/server/queue/queue-lock-schema.js +47 -0
  223. package/server/queue/queue-lock-schema.js.map +1 -0
  224. package/server/queue/queue-message-dedupe-connect-provider.d.ts +116 -0
  225. package/server/queue/queue-message-dedupe-connect-provider.js +14 -0
  226. package/server/queue/queue-message-dedupe-connect-provider.js.map +1 -0
  227. package/server/queue/queue-message-dedupe-constants-model.d.ts +2 -0
  228. package/server/queue/queue-message-dedupe-constants-model.js +6 -0
  229. package/server/queue/queue-message-dedupe-constants-model.js.map +1 -0
  230. package/server/queue/queue-message-dedupe-schema.d.ts +80 -0
  231. package/server/queue/queue-message-dedupe-schema.js +108 -0
  232. package/server/queue/queue-message-dedupe-schema.js.map +1 -0
  233. package/server/queue/queue-module.d.ts +2 -0
  234. package/server/queue/queue-module.js +33 -0
  235. package/server/queue/queue-module.js.map +1 -0
  236. package/server/queue/queue-rate-limit-connect-provider.d.ts +56 -0
  237. package/server/queue/queue-rate-limit-connect-provider.js +14 -0
  238. package/server/queue/queue-rate-limit-connect-provider.js.map +1 -0
  239. package/server/queue/queue-rate-limit-constants-model.d.ts +2 -0
  240. package/server/queue/queue-rate-limit-constants-model.js +6 -0
  241. package/server/queue/queue-rate-limit-constants-model.js.map +1 -0
  242. package/server/queue/queue-rate-limit-schema.d.ts +40 -0
  243. package/server/queue/queue-rate-limit-schema.js +57 -0
  244. package/server/queue/queue-rate-limit-schema.js.map +1 -0
  245. package/server/queue/sqs-transition-service.d.ts +123 -0
  246. package/server/queue/sqs-transition-service.js +442 -0
  247. package/server/queue/sqs-transition-service.js.map +1 -0
  248. package/server/rag/rag-controller.d.ts +167 -0
  249. package/server/rag/rag-controller.js +232 -0
  250. package/server/rag/rag-controller.js.map +1 -0
  251. package/server/rag/rag-module.d.ts +2 -0
  252. package/server/rag/rag-module.js +30 -0
  253. package/server/rag/rag-module.js.map +1 -0
  254. package/server/rag/rag-service.d.ts +361 -0
  255. package/server/rag/rag-service.js +2864 -0
  256. package/server/rag/rag-service.js.map +1 -0
  257. package/server/runner/flow-templates.d.ts +55 -0
  258. package/server/runner/flow-templates.js +388 -0
  259. package/server/runner/flow-templates.js.map +1 -0
  260. package/server/runner/langgraph-runtime.service.d.ts +77 -0
  261. package/server/runner/langgraph-runtime.service.js +221 -0
  262. package/server/runner/langgraph-runtime.service.js.map +1 -0
  263. package/server/runner/runner-controller.d.ts +1044 -0
  264. package/server/runner/runner-controller.js +751 -0
  265. package/server/runner/runner-controller.js.map +1 -0
  266. package/server/runner/runner-module.d.ts +2 -0
  267. package/server/runner/runner-module.js +37 -0
  268. package/server/runner/runner-module.js.map +1 -0
  269. package/server/runner/runner-queue-processor.d.ts +29 -0
  270. package/server/runner/runner-queue-processor.js +259 -0
  271. package/server/runner/runner-queue-processor.js.map +1 -0
  272. package/server/runner/runner-service.d.ts +1761 -0
  273. package/server/runner/runner-service.js +14256 -0
  274. package/server/runner/runner-service.js.map +1 -0
  275. package/server/scripts/migrate-canvas-flow-versions.d.ts +1 -0
  276. package/server/scripts/migrate-canvas-flow-versions.js +72 -0
  277. package/server/scripts/migrate-canvas-flow-versions.js.map +1 -0
  278. package/server/scripts/migrate-mcp-oauth-user-scope.d.ts +1 -0
  279. package/server/scripts/migrate-mcp-oauth-user-scope.js +95 -0
  280. package/server/scripts/migrate-mcp-oauth-user-scope.js.map +1 -0
  281. package/templates/config.example.json +204 -0
  282. package/templates/config.production.example.json +206 -0
  283. 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
+ });