@igoruehara/canvas-flow 0.1.12 → 0.1.14

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/README.md CHANGED
@@ -219,6 +219,21 @@ npx @igoruehara/canvas-flow@latest --home C:\canvas-flow-data --open
219
219
 
220
220
  Tambem e possivel configurar provedores pela propria aplicacao depois que ela estiver rodando. Nesse caso, a configuracao fica salva no MongoDB.
221
221
 
222
+ ## WhatsApp Oficial E Open Source
223
+
224
+ O onboarding da Meta tem dois modos principais:
225
+
226
+ - **Sinergy gerenciado / Coexistence**: usa o preset da Sinergy. Funciona quando
227
+ o onboarding roda por uma URL fixa da Sinergy ou por dominios autorizados no
228
+ app Meta da Sinergy.
229
+ - **Self-hosted / app Meta proprio**: use quando cada usuario hospeda o Canvas
230
+ Flow no proprio dominio. O usuario precisa informar o proprio App ID,
231
+ Configuration ID e App Secret, alem de cadastrar o dominio HTTPS/redirect URI
232
+ no app Meta dele.
233
+
234
+ Use o modo manual quando o cliente ja tiver WABA ID, Phone Number ID e access
235
+ token existentes.
236
+
222
237
  `providers.milvus`: opcional para RAG com Milvus/Zilliz. Milvus local via Docker pode rodar sem token. Milvus remoto normalmente precisa de `token` ou `username/password`.
223
238
 
224
239
  ```json
@@ -19,6 +19,62 @@ const INFRA_COMPOSE_FILE = path.join(PACKAGE_ROOT, 'templates', 'docker-compose.
19
19
  const INFRA_PROJECT_NAME = 'canvas-flow';
20
20
  const INFRA_BASE_SERVICES = ['mongo'];
21
21
  const INFRA_FULL_SERVICES = ['mongo', 'etcd', 'minio', 'milvus'];
22
+ const SINERGY_WHATSAPP_COEXISTENCE_PRESET = {
23
+ embeddedSignupAppId: '617497366521622',
24
+ embeddedSignupConfigId: '1952866105586018',
25
+ embeddedSignupSessionInfoVersion: '3',
26
+ embeddedSignupVersion: 'v3',
27
+ };
28
+
29
+ const STARTUP_BANNER = [
30
+ ' ______ ________ ',
31
+ ' / ____/___ _____ _ ______ ______/ ____/ /___ _ __ ',
32
+ " / / / __ '/ __ \\ | / / __ '/ ___/ /_ / / __ \\ | /| / / ",
33
+ '/ /___/ /_/ / / / / |/ / /_/ (__ ) __/ / / /_/ / |/ |/ / ',
34
+ '\\____/\\__,_/_/ /_/|___/\\__,_/____/_/ /_/\\____/|__/|__/ ',
35
+ ].join('\n');
36
+
37
+ function envFlagEnabled(name) {
38
+ return ['1', 'true', 'yes', 'sim', 'on'].includes(String(process.env[name] || '').trim().toLowerCase());
39
+ }
40
+
41
+ function shouldUseAnsiColor() {
42
+ if (process.env.NO_COLOR) return false;
43
+ return Boolean(process.stdout.isTTY || process.env.FORCE_COLOR);
44
+ }
45
+
46
+ function colorAnsi(text, code) {
47
+ return shouldUseAnsiColor() ? `\x1b[${code}m${text}\x1b[0m` : text;
48
+ }
49
+
50
+ function shouldPrintStartupBanner(flags = {}) {
51
+ if (flags.banner === false || envFlagEnabled('CANVAS_FLOW_NO_BANNER')) return false;
52
+ if (process.env.CI && !envFlagEnabled('CANVAS_FLOW_BANNER')) return false;
53
+ return Boolean(process.stdout.isTTY || envFlagEnabled('CANVAS_FLOW_BANNER'));
54
+ }
55
+
56
+ function boxLine(text, width = 74) {
57
+ const safeText = String(text || '').slice(0, width - 4);
58
+ return `| ${safeText.padEnd(width - 4, ' ')} |`;
59
+ }
60
+
61
+ function printStartupBanner(flags = {}) {
62
+ if (!shouldPrintStartupBanner(flags)) return;
63
+
64
+ const border = '+'.padEnd(75, '-') + '+';
65
+ const box = [
66
+ border,
67
+ boxLine('Canvas Flow standalone runtime'),
68
+ boxLine('Tip: use --with-docker for local Mongo, or --full for Mongo + Milvus.'),
69
+ boxLine('Docs: https://igoruehara.github.io/canvas-flow/'),
70
+ border,
71
+ ].join('\n');
72
+
73
+ console.log('');
74
+ console.log(colorAnsi(STARTUP_BANNER.trimEnd(), '95'));
75
+ console.log(colorAnsi(box, '36'));
76
+ console.log('');
77
+ }
22
78
 
23
79
  function printHelp() {
24
80
  console.log(`
@@ -44,6 +100,7 @@ Options:
44
100
  --public-url <url> Override server.publicUrl
45
101
  --open Open the browser after starting
46
102
  --no-open Do not open the browser
103
+ --no-banner Do not print the startup banner
47
104
  --with-docker Start local Docker infrastructure before Canvas Flow
48
105
  --full Include Milvus, MinIO and etcd with Docker infrastructure
49
106
  --show Show config content with "init" or "config"
@@ -267,6 +324,7 @@ function baseConfig() {
267
324
  whatsapp: {
268
325
  provider: 'meta',
269
326
  deliveryMode: 'provider',
327
+ onboardingMode: 'manual',
270
328
  autoReply: true,
271
329
  verifyToken: '',
272
330
  accessToken: '',
@@ -274,6 +332,16 @@ function baseConfig() {
274
332
  wabaId: '',
275
333
  phoneNumberId: '',
276
334
  graphApiVersion: 'v20.0',
335
+ coexistenceEnabled: false,
336
+ syncMessageEchoes: true,
337
+ syncHistory: false,
338
+ embeddedSignupAppId: SINERGY_WHATSAPP_COEXISTENCE_PRESET.embeddedSignupAppId,
339
+ embeddedSignupConfigId: SINERGY_WHATSAPP_COEXISTENCE_PRESET.embeddedSignupConfigId,
340
+ embeddedSignupAppSecret: '',
341
+ embeddedSignupSolutionId: '',
342
+ embeddedSignupFeatureType: '',
343
+ embeddedSignupSessionInfoVersion: SINERGY_WHATSAPP_COEXISTENCE_PRESET.embeddedSignupSessionInfoVersion,
344
+ embeddedSignupVersion: SINERGY_WHATSAPP_COEXISTENCE_PRESET.embeddedSignupVersion,
277
345
  blipContractId: '',
278
346
  blipAuthorizationKey: '',
279
347
  sinchProjectId: '',
@@ -350,6 +418,19 @@ function mergeConfig(defaults, overrides) {
350
418
  return output;
351
419
  }
352
420
 
421
+ function applyWhatsappCoexistenceDefaults(config) {
422
+ const whatsapp = config?.providers?.whatsapp;
423
+ if (!isPlainObject(whatsapp)) return false;
424
+
425
+ let changed = false;
426
+ for (const [key, value] of Object.entries(SINERGY_WHATSAPP_COEXISTENCE_PRESET)) {
427
+ if (String(whatsapp[key] || '').trim()) continue;
428
+ whatsapp[key] = value;
429
+ changed = true;
430
+ }
431
+ return changed;
432
+ }
433
+
353
434
  function writeJson(filePath, value) {
354
435
  fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
355
436
  }
@@ -388,6 +469,9 @@ function loadConfig(configPath) {
388
469
 
389
470
  const config = mergeConfig(baseConfig(), parsed);
390
471
  let changed = false;
472
+ if (applyWhatsappCoexistenceDefaults(config)) {
473
+ changed = true;
474
+ }
391
475
  if (!config.auth.apiToken) {
392
476
  config.auth.apiToken = randomSecret('cf_master_');
393
477
  changed = true;
@@ -478,6 +562,17 @@ function isLoopbackUrl(value) {
478
562
  }
479
563
  }
480
564
 
565
+ function normalizeProviderName(value) {
566
+ const text = String(value || '').trim().toLowerCase();
567
+ if (text === 'azure' || text === 'azure_openai' || text === 'azure-openai' || text === 'azureopenai') {
568
+ return 'azure';
569
+ }
570
+ if (['openai', 'gemini', 'claude', 'grok', 'bedrock'].includes(text)) {
571
+ return text;
572
+ }
573
+ return 'openai';
574
+ }
575
+
481
576
  function applyEnvironment(config, paths, flags) {
482
577
  const port = Number(flags.port || config.server.port || 3333);
483
578
  const publicUrl = String(flags['public-url'] || config.server.publicUrl || `http://localhost:${port}`).replace(/\/$/, '');
@@ -486,7 +581,7 @@ function applyEnvironment(config, paths, flags) {
486
581
  const claude = config.providers.claude || {};
487
582
  const grok = config.providers.grok || {};
488
583
  const bedrock = config.providers.bedrock || {};
489
- const azureOpenAI = config.providers.azureOpenAI || {};
584
+ const azureOpenAI = config.providers.azureOpenAI || config.providers.azureOpenai || {};
490
585
  const milvus = config.providers.milvus || {};
491
586
  const azureBlob = config.providers.azureBlob || {};
492
587
  const azureSearch = config.providers.azureSearch || {};
@@ -502,6 +597,9 @@ function applyEnvironment(config, paths, flags) {
502
597
  const httpBatch = config.httpBatch || {};
503
598
  const agentOps = config.agentOps || {};
504
599
  const aws = config.aws || {};
600
+ const configuredProvider = normalizeProviderName(openai.provider);
601
+ const azureOpenAIEnabled = configuredProvider === 'azure' || asBool(azureOpenAI.enabled);
602
+ const openaiProvider = azureOpenAIEnabled ? 'azure' : configuredProvider;
505
603
 
506
604
  setEnv('CANVAS_FLOW_HOME', paths.homeDir);
507
605
  setEnv('CANVAS_FLOW_CONFIG_FILE', paths.configPath);
@@ -562,7 +660,7 @@ function applyEnvironment(config, paths, flags) {
562
660
  setEnv('CANVAS_FLOW_MAX_STEP_VISITS', config.runtime.maxStepVisits || 10);
563
661
  setEnv('CANVAS_FLOW_PROVIDER_CACHE_MS', config.runtime.providerCacheMs || 10000);
564
662
 
565
- setEnv('OPENAI_PROVIDER', openai.provider || 'openai');
663
+ setEnv('OPENAI_PROVIDER', openaiProvider);
566
664
  setEnv('LLM_PROVIDER', openai.llmProvider);
567
665
  setEnv('OPENAI_API_KEY', openai.apiKey);
568
666
  setEnv('OPENAI_CHAT_MODEL', openai.chatModel);
@@ -593,7 +691,7 @@ function applyEnvironment(config, paths, flags) {
593
691
  setEnv('BEDROCK_CHAT_MODEL', bedrock.chatModel || 'anthropic.claude-sonnet-4-6');
594
692
  setEnv('BEDROCK_MODEL', bedrock.chatModel || 'anthropic.claude-sonnet-4-6');
595
693
 
596
- setBoolEnv('AZURE_OPENAI_ENABLED', asBool(azureOpenAI.enabled));
694
+ setBoolEnv('AZURE_OPENAI_ENABLED', azureOpenAIEnabled);
597
695
  setEnv('AZURE_OPENAI_API_KEY', azureOpenAI.apiKey);
598
696
  setEnv('AZURE_OPENAI_ENDPOINT', azureOpenAI.endpoint || azureOpenAI.apiBasePath);
599
697
  setEnv('AZURE_OPENAI_API_BASE_PATH', azureOpenAI.apiBasePath || azureOpenAI.endpoint);
@@ -650,6 +748,7 @@ function applyEnvironment(config, paths, flags) {
650
748
 
651
749
  setEnv('WHATSAPP_PROVIDER', whatsapp.provider || 'meta');
652
750
  setEnv('WHATSAPP_DELIVERY_MODE', whatsapp.deliveryMode || 'provider');
751
+ setEnv('WHATSAPP_ONBOARDING_MODE', whatsapp.onboardingMode || 'manual');
653
752
  setBoolEnv('WHATSAPP_AUTO_REPLY', whatsapp.autoReply !== false);
654
753
  setEnv('WHATSAPP_VERIFY_TOKEN', whatsapp.verifyToken);
655
754
  setEnv('WHATSAPP_ACCESS_TOKEN', whatsapp.accessToken);
@@ -657,6 +756,16 @@ function applyEnvironment(config, paths, flags) {
657
756
  setEnv('WHATSAPP_WABA_ID', whatsapp.wabaId || whatsapp.businessAccountId);
658
757
  setEnv('WHATSAPP_PHONE_NUMBER_ID', whatsapp.phoneNumberId);
659
758
  setEnv('WHATSAPP_GRAPH_API_VERSION', whatsapp.graphApiVersion || 'v20.0');
759
+ setBoolEnv('WHATSAPP_COEXISTENCE_ENABLED', whatsapp.coexistenceEnabled === true || whatsapp.onboardingMode === 'coexistence');
760
+ setBoolEnv('WHATSAPP_SYNC_MESSAGE_ECHOES', whatsapp.syncMessageEchoes !== false);
761
+ setBoolEnv('WHATSAPP_SYNC_HISTORY', whatsapp.syncHistory === true);
762
+ setEnv('WHATSAPP_EMBEDDED_SIGNUP_APP_ID', whatsapp.embeddedSignupAppId);
763
+ setEnv('WHATSAPP_EMBEDDED_SIGNUP_CONFIG_ID', whatsapp.embeddedSignupConfigId);
764
+ setEnv('WHATSAPP_EMBEDDED_SIGNUP_APP_SECRET', whatsapp.embeddedSignupAppSecret);
765
+ setEnv('WHATSAPP_EMBEDDED_SIGNUP_SOLUTION_ID', whatsapp.embeddedSignupSolutionId);
766
+ setEnv('WHATSAPP_EMBEDDED_SIGNUP_FEATURE_TYPE', whatsapp.embeddedSignupFeatureType);
767
+ setEnv('WHATSAPP_EMBEDDED_SIGNUP_SESSION_INFO_VERSION', whatsapp.embeddedSignupSessionInfoVersion || '3');
768
+ setEnv('WHATSAPP_EMBEDDED_SIGNUP_VERSION', whatsapp.embeddedSignupVersion);
660
769
  setEnv('BLIP_CONTRACT_ID', whatsapp.blipContractId);
661
770
  setEnv('BLIP_AUTHORIZATION_KEY', whatsapp.blipAuthorizationKey);
662
771
  setEnv('SINCH_PROJECT_ID', whatsapp.sinchProjectId);
@@ -1266,6 +1375,7 @@ async function waitForMongo(config, flags, paths, progress) {
1266
1375
  }
1267
1376
 
1268
1377
  async function start(flags) {
1378
+ printStartupBanner(flags);
1269
1379
  const progress = createStartupProgress();
1270
1380
  let startupStatus;
1271
1381
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@igoruehara/canvas-flow",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "Standalone npm launcher for Canvas Flow multi-agent GenAI workflows.",
5
5
  "homepage": "https://github.com/igoruehara/canvas-flow#readme",
6
6
  "repository": {