@igoruehara/canvas-flow 0.1.9 → 0.1.11

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
@@ -1,10 +1,11 @@
1
1
  # Canvas Flow
2
2
 
3
- Canvas Flow e uma aplicacao web local para criar, testar e executar fluxos de IA multiagente em formato visual. O pacote npm sobe frontend e backend juntos em um unico comando.
3
+ Canvas Flow é uma aplicação web local para criar, testar e executar agentes de IA multiagente em formato visual, com canais prontos para WhatsApp e Web Widget. O pacote npm sobe frontend e backend juntos em um único comando.
4
4
 
5
5
  Use quando quiser:
6
6
 
7
- - criar automacoes com agentes de IA;
7
+ - criar agentes de IA para atendimento e automação;
8
+ - publicar agentes nos canais WhatsApp e Web Widget;
8
9
  - conectar prompts, ferramentas, webhooks e documentos;
9
10
  - testar fluxos localmente antes de publicar;
10
11
  - usar RAG com documentos;
@@ -42,7 +43,7 @@ http://localhost:3333
42
43
  - execucao local de frontend e API no mesmo processo Node;
43
44
  - configuracao de provedores de IA pela UI ou pelo `config.json`;
44
45
  - RAG com Milvus/Zilliz ou Azure AI Search;
45
- - entrada por API, webhook, web widget e canais configurados no app;
46
+ - canais de entrada para WhatsApp, Web Widget, API e webhook;
46
47
  - chaves de API para consumir fluxos publicados;
47
48
  - validacao do ambiente com `doctor`.
48
49
 
@@ -465,6 +465,19 @@ function joinCorsOrigins(config, publicUrl, port) {
465
465
  ].join(',');
466
466
  }
467
467
 
468
+ function isLoopbackUrl(value) {
469
+ try {
470
+ const hostname = new URL(value).hostname.toLowerCase();
471
+ return hostname === 'localhost'
472
+ || hostname === '::1'
473
+ || hostname === '[::1]'
474
+ || hostname === '0.0.0.0'
475
+ || hostname.startsWith('127.');
476
+ } catch {
477
+ return false;
478
+ }
479
+ }
480
+
468
481
  function applyEnvironment(config, paths, flags) {
469
482
  const port = Number(flags.port || config.server.port || 3333);
470
483
  const publicUrl = String(flags['public-url'] || config.server.publicUrl || `http://localhost:${port}`).replace(/\/$/, '');
@@ -515,11 +528,18 @@ function applyEnvironment(config, paths, flags) {
515
528
  setEnv('MONGO_SERVER_SELECTION_TIMEOUT_MS', config.database.mongoServerSelectionTimeoutMs);
516
529
  setEnv('MONGO_CONNECT_TIMEOUT_MS', config.database.mongoConnectTimeoutMs);
517
530
 
518
- setBoolEnv('CANVAS_FLOW_LOGIN', asBool(config.auth.login));
531
+ const loginRequired = asBool(config.auth.login);
532
+ const exposeApiTokenToFrontend = config.auth.exposeApiTokenToFrontend === true
533
+ || (!loginRequired && isLoopbackUrl(publicUrl));
534
+ setBoolEnv('CANVAS_FLOW_LOGIN', loginRequired);
519
535
  setEnv('CANVAS_FLOW_LOGIN_TTL_HOURS', config.auth.loginTtlHours);
520
536
  setEnv('CANVAS_FLOW_LOGIN_THROTTLE_WINDOW_MS', config.auth.loginThrottleWindowMs || 600000);
521
537
  setEnv('CANVAS_FLOW_LOGIN_MAX_ATTEMPTS', config.auth.loginMaxAttempts || 8);
522
538
  setEnv('CANVAS_FLOW_API_TOKEN', config.auth.apiToken);
539
+ delete process.env.CANVAS_FLOW_FRONTEND_API_TOKEN;
540
+ if (!loginRequired && exposeApiTokenToFrontend) {
541
+ setEnv('CANVAS_FLOW_FRONTEND_API_TOKEN', config.auth.apiToken);
542
+ }
523
543
  setEnv('CANVAS_FLOW_JWT_SECRET', config.auth.jwtSecret);
524
544
  setEnv('CANVAS_FLOW_MEDIA_PROXY_SECRET', config.auth.mediaProxySecret);
525
545
  setEnv('CANVAS_FLOW_MEDIA_PROXY_TTL_SECONDS', config.auth.mediaProxyTtlSeconds);
@@ -815,6 +835,59 @@ function sleep(ms) {
815
835
  return new Promise((resolve) => setTimeout(resolve, ms));
816
836
  }
817
837
 
838
+ async function checkHttpOk(url, timeoutMs = 1200) {
839
+ const controller = new AbortController();
840
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
841
+ try {
842
+ const response = await fetch(url, { signal: controller.signal });
843
+ return response.ok;
844
+ } catch {
845
+ return false;
846
+ } finally {
847
+ clearTimeout(timer);
848
+ }
849
+ }
850
+
851
+ function startStartupStatus(publicUrl, options = {}) {
852
+ const healthUrl = `${String(publicUrl || '').replace(/\/$/, '')}/health`;
853
+ const startedAt = Date.now();
854
+ let stopped = false;
855
+
856
+ console.log('Canvas Flow startup: loading application bundle...');
857
+ console.log(`Canvas Flow startup: waiting for backend health at ${healthUrl}`);
858
+
859
+ const interval = setInterval(() => {
860
+ const elapsedSeconds = Math.max(1, Math.round((Date.now() - startedAt) / 1000));
861
+ console.log(`Canvas Flow startup: still starting (${elapsedSeconds}s elapsed)...`);
862
+ }, 2500);
863
+
864
+ const stop = () => {
865
+ if (stopped) return;
866
+ stopped = true;
867
+ clearInterval(interval);
868
+ };
869
+
870
+ void (async () => {
871
+ const deadline = Date.now() + 90000;
872
+ while (!stopped && Date.now() < deadline) {
873
+ if (await checkHttpOk(healthUrl)) {
874
+ stop();
875
+ console.log(`Canvas Flow ready: ${publicUrl}`);
876
+ if (options.openBrowser) openBrowser(publicUrl);
877
+ return;
878
+ }
879
+ await sleep(500);
880
+ }
881
+
882
+ if (!stopped) {
883
+ stop();
884
+ console.log('Canvas Flow startup: health check is still pending. Keep this terminal open and watch the backend logs above.');
885
+ }
886
+ })();
887
+
888
+ return { stop };
889
+ }
890
+
818
891
  function mongoConnectionOptions(config) {
819
892
  return {
820
893
  serverSelectionTimeoutMS: Number(config.database?.mongoServerSelectionTimeoutMs || 8000),
@@ -1147,12 +1220,13 @@ async function start(flags) {
1147
1220
  }
1148
1221
 
1149
1222
  const shouldOpen = flags.open === true || (flags.open !== false && config.server.openBrowser === true);
1150
- if (shouldOpen) {
1151
- const timer = setTimeout(() => openBrowser(runtime.publicUrl), 1200);
1152
- if (typeof timer.unref === 'function') timer.unref();
1223
+ const startupStatus = startStartupStatus(runtime.publicUrl, { openBrowser: shouldOpen });
1224
+ try {
1225
+ require(SERVER_ENTRY);
1226
+ } catch (error) {
1227
+ startupStatus.stop();
1228
+ throw error;
1153
1229
  }
1154
-
1155
- require(SERVER_ENTRY);
1156
1230
  }
1157
1231
 
1158
1232
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@igoruehara/canvas-flow",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
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": {