@arcadialdev/arcality 2.3.4 → 2.4.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcadialdev/arcality",
3
- "version": "2.3.4",
3
+ "version": "2.4.19",
4
4
  "description": "AI-powered QA testing tool — Autonomous web testing agent by Arcadial",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -9,7 +9,8 @@ import path from "node:path";
9
9
  import { createRequire } from 'node:module';
10
10
  import { spawn, exec } from "node:child_process";
11
11
  import chalk from "chalk";
12
- import { fileURLToPath } from 'url';
12
+ import { fileURLToPath, pathToFileURL } from 'url';
13
+ const require = createRequire(import.meta.url);
13
14
  import figlet from "figlet";
14
15
  import { intro, outro, select, text, spinner, note, isCancel, cancel } from '@clack/prompts';
15
16
  import { load as loadYaml } from 'js-yaml';
@@ -303,21 +304,34 @@ function sanitizeFilename(name, fallback) {
303
304
  }
304
305
 
305
306
  function run(cmd, args, options = {}) {
307
+ const isDebug = process.argv.includes('--debug');
308
+ const cwd = options.cwd || process.cwd();
309
+
306
310
  return new Promise((resolve, reject) => {
311
+ // NODE_PATH: let Node resolve modules from both the arcality package and user's project
307
312
  const appNodeModules = path.join(PROJECT_ROOT, 'node_modules');
313
+ const userNodeModules = path.join(ORIGINAL_CWD, 'node_modules');
308
314
  const existingNodePath = process.env.NODE_PATH || '';
309
- const newNodePath = existingNodePath ? `${appNodeModules}${path.delimiter}${existingNodePath}` : appNodeModules;
315
+ const newNodePath = [appNodeModules, userNodeModules, existingNodePath].filter(Boolean).join(path.delimiter);
316
+
317
+ const env = { ...options.env || process.env, NODE_PATH: newNodePath };
318
+ if (isDebug) console.log(chalk.gray(`\n[DEBUG] CWD: ${cwd}`));
319
+ if (isDebug) console.log(chalk.gray(`[DEBUG] Running: ${cmd} ${args.join(' ')}`));
310
320
 
321
+ // Use standard cross-platform spawn. When shell is false, Node's child_process
322
+ // properly wraps arguments with spaces in quotes natively for CreateProcessW on Windows.
311
323
  const p = spawn(cmd, args, {
312
- stdio: ["ignore", "pipe", "pipe"],
313
- shell: false, // IMPORTANT: shell:false prevents Windows CMD from splitting paths with spaces
314
- windowsHide: true,
315
- env: { ...options.env || process.env, NODE_PATH: newNodePath },
324
+ stdio: isDebug ? ['inherit', 'inherit', 'inherit'] : ['ignore', 'pipe', 'pipe'],
325
+ shell: false,
326
+ cwd,
327
+ env,
316
328
  });
317
329
 
318
330
  let outputBuffer = '';
319
- p.stdout.on('data', (data) => {
331
+ const handleData = (data) => {
320
332
  const str = data.toString();
333
+ outputBuffer += str;
334
+
321
335
  if (str.includes('>>ARCALITY_STATUS>>')) {
322
336
  const lines = str.split('\n');
323
337
  for (const line of lines) {
@@ -331,15 +345,19 @@ function run(cmd, args, options = {}) {
331
345
  } else {
332
346
  process.stdout.write(data);
333
347
  }
334
- outputBuffer += str;
335
- });
348
+ };
336
349
 
337
- p.stderr.on('data', (data) => {
338
- process.stderr.write(data);
339
- outputBuffer += data.toString();
340
- });
350
+ if (!isDebug && p.stdout) {
351
+ p.stdout.on('data', handleData);
352
+ }
353
+ if (!isDebug && p.stderr) {
354
+ p.stderr.on('data', (data) => {
355
+ process.stderr.write(data);
356
+ outputBuffer += data.toString();
357
+ });
358
+ }
341
359
 
342
- p.on("exit", (code) => {
360
+ p.on('exit', (code) => {
343
361
  const lastRunLog = path.join(LOGS_DIR, 'last-run.log');
344
362
  try {
345
363
  if (!fs.existsSync(LOGS_DIR)) fs.mkdirSync(LOGS_DIR, { recursive: true });
@@ -347,7 +365,15 @@ function run(cmd, args, options = {}) {
347
365
  } catch (e) { }
348
366
 
349
367
  if (code === 0) resolve();
350
- else reject(new Error(`Command failed (${code}): ${cmd} ${args.join(" ")}`));
368
+ else {
369
+ if (isDebug) console.error(chalk.red(`\n[DEBUG] Process exited with code ${code}`));
370
+ reject(new Error(`Process exited with code ${code}`));
371
+ }
372
+ });
373
+
374
+ p.on('error', (err) => {
375
+ if (isDebug) console.error(chalk.red(`\n[DEBUG] Process failed to spawn: ${err.message}`));
376
+ reject(err);
351
377
  });
352
378
  });
353
379
  }
@@ -398,6 +424,8 @@ async function main() {
398
424
  if (sIndex !== -1) { initialSmartMode = true; argv.splice(sIndex, 1); }
399
425
  const aIndex = argv.findIndex(a => a === '--agent');
400
426
  if (aIndex !== -1) { initialAgentMode = true; argv.splice(aIndex, 1); }
427
+ const debugIndex = argv.findIndex(a => a === '--debug');
428
+ if (debugIndex !== -1) { argv.splice(debugIndex, 1); } // Handled by run() naturally now
401
429
 
402
430
  const promptArg = argv.join(" ").trim();
403
431
  let firstRun = true;
@@ -575,7 +603,10 @@ async function main() {
575
603
  }
576
604
 
577
605
  if (agentMode) {
578
- const dotEnv = parseDotEnvFile(path.join(PROJECT_ROOT, '.env'));
606
+ // Load environment from BOTH the user's project and the library's root
607
+ const userDotEnv = parseDotEnvFile(path.join(ORIGINAL_CWD, '.env'));
608
+ const libDotEnv = parseDotEnvFile(path.join(PROJECT_ROOT, '.env'));
609
+ const dotEnv = { ...userDotEnv, ...libDotEnv };
579
610
 
580
611
  // ── API Key and Quota Validation ──
581
612
  const { validateApiKey, startMission: requestMission } = await import('../src/arcalityClient.mjs');
@@ -688,14 +719,21 @@ async function main() {
688
719
  }
689
720
 
690
721
  const s = spinner();
691
- s.start(`🤖 [AGENT MODE] Starting mission: "${prompt}" ${chalk.gray(`(${mission.daily_used}/${mission.daily_limit} today)`)}`);
722
+ if (!process.argv.includes('--debug')) {
723
+ s.start(`🤖 [AGENT MODE] Starting mission: "${prompt}" ${chalk.gray(`(${mission.daily_used}/${mission.daily_limit} today)`)}`);
724
+ } else {
725
+ console.log(chalk.cyan(`\n🤖 [AGENT MODE] Starting mission: "${prompt}" ${chalk.gray(`(${mission.daily_used}/${mission.daily_limit} today)`)}`));
726
+ }
692
727
 
693
728
  const finalProjectId = process.env.ARCALITY_PROJECT_ID || selectedProjectId;
694
729
 
695
- // Build env for the runner — merge arcality.config values
730
+ // Build env for the runner — merge arcality.config values.
731
+ // ARCALITY_API_URL must be explicit — it comes from the library's internal config,
732
+ // not from the user's project .env, so we inject it directly via getApiUrl().
696
733
  const mergedEnv = {
697
734
  ...dotEnv,
698
735
  ...process.env,
736
+ ARCALITY_API_URL: getApiUrl(), // Internal URL — always injected explicitly
699
737
  ARCALITY_PROJECT_ID: finalProjectId,
700
738
  BASE_URL: projectConfig?.project?.baseUrl || dotEnv.BASE_URL || process.env.BASE_URL,
701
739
  LOGIN_USER: projectConfig?.auth?.username || dotEnv.LOGIN_USER || process.env.LOGIN_USER,
@@ -712,33 +750,50 @@ async function main() {
712
750
  console.log(chalk.gray(`\n>> ARCALITY_PROJECT_ID: ${finalProjectId}`));
713
751
  }
714
752
 
715
- // Resolve playwright CLI using Node's module resolution algorithm.
716
- // This handles BOTH cases:
717
- // - Global install: playwright is inside arcality's own node_modules
718
- // - Local install: playwright is hoisted to the project's root node_modules
753
+ // ── Resolve execution paths ──
754
+ // tsx and playwright are HOISTED to ORIGINAL_CWD/node_modules when installed via npm.
755
+ // We run from ORIGINAL_CWD so relative paths for playwright work.
756
+ // For the test file and config (inside the arcality package), we use absolute paths.
757
+ //
758
+ // KEY: tsx loader uses file:// URL (pathToFileURL) so spaces encode as %20 — no quoting needed.
759
+
760
+ const testFile = path.join(PROJECT_ROOT, 'tests', '_helpers', 'agentic-runner.spec.ts');
761
+ const configFile = path.join(PROJECT_ROOT, 'playwright.config.js');
762
+
763
+ // Resolve playwright CLI relative to PROJECT_ROOT exactly as Node handles module resolution.
764
+ // This prevents the "did not expect test() to be called here" singleton duplication error.
719
765
  let playwrightCli;
720
766
  try {
721
- const arcalityRequire = createRequire(path.join(PROJECT_ROOT, 'package.json'));
722
- playwrightCli = arcalityRequire.resolve('playwright/cli');
723
- } catch {
724
- // Fallback: try local hoisted path (project_root/../../playwright)
725
- const hoistedPath = path.join(PROJECT_ROOT, '..', '..', 'playwright', 'cli.js');
726
- if (fs.existsSync(hoistedPath)) {
727
- playwrightCli = hoistedPath;
728
- } else {
729
- playwrightCli = path.join(PROJECT_ROOT, 'node_modules', 'playwright', 'cli.js');
767
+ // Find the core @playwright/test that the local project uses
768
+ const testRunnerPath = require.resolve('@playwright/test', { paths: [PROJECT_ROOT] });
769
+ // Target the cli.js file strictly inside its parallel playwright dependency
770
+ playwrightCli = path.join(testRunnerPath, '..', '..', '..', 'playwright', 'cli.js');
771
+ if (!fs.existsSync(playwrightCli)) {
772
+ throw new Error(`CLI not found exactly at: ${playwrightCli}`);
730
773
  }
774
+ } catch (e) {
775
+ console.error(chalk.red('\n[FATAL] Playwright core not found! Make sure playwright is properly installed.'));
776
+ console.error(chalk.red(e.message));
777
+ process.exit(1);
731
778
  }
732
- const configFile = path.join(PROJECT_ROOT, 'playwright.config.js');
779
+
733
780
  try {
734
- // IMPORTANT: Playwright's internal `.ts` transpiler intentionally ignores everything inside `node_modules`.
735
- // Because Arcality tests run from inside `node_modules/@arcadialdev/arcality`, Playwright refuses to compile them.
736
- // We use `--import tsx` to force Node to transpile TypeScript globally regardless of path.
737
- await run("node", ["--import", "tsx", playwrightCli, "test", path.join(PROJECT_ROOT, "tests/_helpers/agentic-runner.spec.ts"), "--headed", `--config=${configFile}`], {
781
+ // By switching cwd to PROJECT_ROOT, playwright's internal regex test match
782
+ // perfectly matches relative paths without breaking on Windows backslashes.
783
+ await run('node', [
784
+ '--no-warnings',
785
+ playwrightCli,
786
+ 'test',
787
+ 'tests/_helpers/agentic-runner.spec.ts', // RELATIVE path prevents regex errors
788
+ '--headed',
789
+ '--config', 'playwright.config.js'
790
+ ], {
791
+ cwd: PROJECT_ROOT, // Run from the tool's true root!
738
792
  env: mergedEnv,
739
- onStatus: (msg) => s.message(msg)
793
+ onStatus: (msg) => { if (!process.argv.includes('--debug')) s.message(msg); else console.log(chalk.gray(`>> Status: ${msg}`)); }
740
794
  });
741
- s.stop(chalk.green('✅ Mission completed successfully.'));
795
+ if (!process.argv.includes('--debug')) s.stop(chalk.green('✅ Mission completed successfully.'));
796
+ else console.log(chalk.green('✅ Mission completed successfully.'));
742
797
 
743
798
  // ── End Mission ──
744
799
  const { endMission } = await import('../src/arcalityClient.mjs');
@@ -792,7 +847,11 @@ async function main() {
792
847
  }
793
848
  }
794
849
  } catch (e) {
795
- s.stop(chalk.red('❌ Mission could not be completed.'));
850
+ s.stop(chalk.red(`❌ Mission could not be completed.`));
851
+ console.error(chalk.red(`\nError Details: ${e.message}`));
852
+ if (e.stack && process.argv.includes('--debug')) {
853
+ console.error(chalk.gray(e.stack));
854
+ }
796
855
 
797
856
  // End mission with failure
798
857
  try {
@@ -49,6 +49,23 @@ export class KnowledgeService {
49
49
  this.projectId = process.env.ARCALITY_PROJECT_ID || null;
50
50
  }
51
51
 
52
+ /**
53
+ * Fetch wrapper with a 10-second timeout to prevent hanging.
54
+ * All KnowledgeService calls are telemetry/secondary — they must NEVER block the agent.
55
+ */
56
+ private async fetchWithTimeout(url: string, options: RequestInit = {}, timeoutMs = 10000): Promise<Response> {
57
+ const controller = new AbortController();
58
+ const id = setTimeout(() => controller.abort(), timeoutMs);
59
+ try {
60
+ const res = await fetch(url, { ...options, signal: controller.signal });
61
+ clearTimeout(id);
62
+ return res;
63
+ } catch (err: any) {
64
+ clearTimeout(id);
65
+ throw err;
66
+ }
67
+ }
68
+
52
69
  public static getInstance(): KnowledgeService {
53
70
  if (!KnowledgeService.instance) {
54
71
  KnowledgeService.instance = new KnowledgeService();
@@ -72,7 +89,7 @@ export class KnowledgeService {
72
89
  */
73
90
  public async getProjects(): Promise<ProjectDto[]> {
74
91
  try {
75
- const res = await fetch(`${this.apiBase}/api/v1/projects`, {
92
+ const res = await this.fetchWithTimeout(`${this.apiBase}/api/v1/projects`, {
76
93
  headers: { 'x-api-key': this.apiKey }
77
94
  });
78
95
  if (!res.ok) return [];
@@ -86,7 +103,7 @@ export class KnowledgeService {
86
103
 
87
104
  public async createProject(name: string, baseUrl: string): Promise<ProjectDto | null> {
88
105
  try {
89
- const res = await fetch(`${this.apiBase}/api/v1/projects`, {
106
+ const res = await this.fetchWithTimeout(`${this.apiBase}/api/v1/projects`, {
90
107
  method: 'POST',
91
108
  headers: {
92
109
  'Content-Type': 'application/json',
@@ -112,7 +129,7 @@ export class KnowledgeService {
112
129
  }
113
130
 
114
131
  try {
115
- const res = await fetch(`${this.apiBase}/api/v1/portal/ingest`, {
132
+ const res = await this.fetchWithTimeout(`${this.apiBase}/api/v1/portal/ingest`, {
116
133
  method: 'POST',
117
134
  headers: {
118
135
  'Content-Type': 'application/json',
@@ -141,10 +158,10 @@ export class KnowledgeService {
141
158
  if (!pid || pid === 'undefined') return null;
142
159
  try {
143
160
  const pathUrl = new URL(url).pathname;
144
- // Para queries de GET solemos usar snake_case si el server es estricto
145
- const res = await fetch(`${this.apiBase}/api/v1/portal/context?project_id=${pid}&path=${encodeURIComponent(pathUrl)}`, {
146
- headers: { 'x-api-key': this.apiKey }
147
- });
161
+ const res = await this.fetchWithTimeout(
162
+ `${this.apiBase}/api/v1/portal/context?project_id=${pid}&path=${encodeURIComponent(pathUrl)}`,
163
+ { headers: { 'x-api-key': this.apiKey } }
164
+ );
148
165
  if (!res.ok) return null;
149
166
  return await res.json();
150
167
  } catch (e: any) {
@@ -167,6 +167,12 @@ export function setupProcessEnv() {
167
167
  process.env.LOGIN_PASSWORD = localConfig.auth.password;
168
168
  }
169
169
  }
170
+ // SIEMPRE garantizar que la URL interna de la API esté en process.env.
171
+ // Esta URL es interna de la herramienta, no configurable por el usuario.
172
+ // Proviene del .env de la librería o usa el valor por defecto.
173
+ if (!process.env.ARCALITY_API_URL) {
174
+ process.env.ARCALITY_API_URL = 'https://arcalityqadev.arcadial.lat';
175
+ }
170
176
  }
171
177
 
172
178
  /**
@@ -53,9 +53,33 @@ class ArcalityReporter {
53
53
  }
54
54
  });
55
55
 
56
+ if (result.status !== 'passed') {
57
+ console.log(`\n⚠️ [TEST END] ${test.title} ended with status: ${result.status}`);
58
+ if (result.error) console.log(`Error: ${result.error?.message || result.error?.stack || 'Unknown'}`);
59
+ }
60
+
56
61
  this.results.push({ test, result, stepsHtml, attachments: processedAttachments });
57
62
  }
58
63
 
64
+ onTestBegin(test) {
65
+ console.log(`▶️ [TEST BEGIN] ${test.title}`);
66
+ }
67
+
68
+ onStdErr(chunk, test, result) {
69
+ console.error(`[STDERR] ${chunk.toString()}`);
70
+ }
71
+
72
+ onStdOut(chunk, test, result) {
73
+ console.log(`[STDOUT] ${chunk.toString()}`);
74
+ }
75
+
76
+ onError(error) {
77
+ console.log(`\n❌ [FATAL GLOBAL ERROR]`);
78
+ console.log(error.message || error.stack || JSON.stringify(error));
79
+ if (error.stack) console.log(error.stack);
80
+ if (error.value) console.log(error.value);
81
+ }
82
+
59
83
  async onEnd(result) {
60
84
  this.totalDuration = result.duration;
61
85
  const htmlContent = this.generateHtml();
@@ -66,6 +66,14 @@ class ArcalityReporter implements Reporter {
66
66
  }
67
67
  });
68
68
 
69
+ if (result.status === 'failed' || result.error) {
70
+ console.log(`\n❌ [TEST ENGINE FAILURE] ${test.title} failed:`);
71
+ console.log(`${result.error?.message || result.error?.stack || 'Unknown error'}\n`);
72
+ if (result.error?.stack) {
73
+ console.log(result.error.stack);
74
+ }
75
+ }
76
+
69
77
  this.results.push({ test, result, stepsHtml, attachments: processedAttachments });
70
78
  }
71
79
 
@@ -540,12 +540,13 @@ ${best.steps.slice(0, 15).map((s: any) => ` - ${s}`).join('\n')}
540
540
  }
541
541
 
542
542
  async askIA(prompt: string, history: string[] = []): Promise<AgentAction> {
543
- // MODO BRIDGE: Si existe una URL de portal, delegamos la decisión al servidor (SaaS)
544
- if (process.env.ARCALITY_PORTAL_URL) {
545
- return this.askBridge(prompt, history);
546
- }
543
+ // El modo proxy (ARCALITY_API_URL) está integrado en el bucle de fetch de abajo.
544
+ // Si ARCALITY_API_URL está definido, se enruta automáticamente a /api/v1/ai/proxy.
545
+ // Si no, requiere ANTHROPIC_API_KEY para llamada directa.
547
546
 
548
- if (!process.env.ANTHROPIC_API_KEY) throw new Error("ANTHROPIC_API_KEY missing");
547
+ if (!process.env.ARCALITY_API_URL && !process.env.ANTHROPIC_API_KEY) {
548
+ throw new Error("ANTHROPIC_API_KEY missing and ARCALITY_API_URL not configured");
549
+ }
549
550
 
550
551
  const state = await this.getPageState();
551
552
  const screenshot = await this.page.screenshot({ type: 'png' });
@@ -827,6 +828,9 @@ ${history.slice(-25).join('\n') || 'None'}`
827
828
  ? `${process.env.ARCALITY_API_URL}/api/v1/ai/proxy`
828
829
  : "https://api.anthropic.com/v1/messages";
829
830
 
831
+ // Log the endpoint being called on every turn so we can debug connectivity
832
+ console.log(`>>ARCALITY_STATUS>> 📡 Llamando a: ${endpointUrl} (turno ${turn + 1})`);
833
+
830
834
  const headers: Record<string, string> = {
831
835
  "Content-Type": "application/json"
832
836
  };
@@ -838,26 +842,43 @@ ${history.slice(-25).join('\n') || 'None'}`
838
842
  headers["anthropic-version"] = "2023-06-01";
839
843
  }
840
844
 
841
- response = await fetch(endpointUrl, {
842
- method: "POST",
843
- headers,
844
- body: JSON.stringify({
845
- model: process.env.CLAUDE_MODEL || "claude-3-5-sonnet-20241022",
846
- max_tokens: 4096,
847
- system: systemPromptBlocks,
848
- tools: rawTools.map(t => ({
849
- name: t.name,
850
- description: t.description,
851
- input_schema: (t as any).parameters || (t as any).input_schema
852
- })),
853
- messages: anthropicMessages,
854
- temperature: 0.2
855
- })
856
- });
845
+ // AbortController: 90s timeout to prevent silent hangs
846
+ const controller = new AbortController();
847
+ const timeoutId = setTimeout(() => controller.abort(), 90000);
848
+
849
+ try {
850
+ response = await fetch(endpointUrl, {
851
+ method: "POST",
852
+ headers,
853
+ signal: controller.signal,
854
+ body: JSON.stringify({
855
+ model: process.env.CLAUDE_MODEL || "claude-3-5-sonnet-20241022",
856
+ max_tokens: 4096,
857
+ system: systemPromptBlocks,
858
+ tools: rawTools.map(t => ({
859
+ name: t.name,
860
+ description: t.description,
861
+ input_schema: (t as any).parameters || (t as any).input_schema
862
+ })),
863
+ messages: anthropicMessages,
864
+ temperature: 0.2
865
+ })
866
+ });
867
+ } catch (fetchErr: any) {
868
+ clearTimeout(timeoutId);
869
+ if (fetchErr.name === 'AbortError') {
870
+ throw new Error(`Arcality Brain Timeout: No response from ${endpointUrl} after 90s`);
871
+ }
872
+ throw new Error(`Arcality Brain Network Error: ${fetchErr.message}`);
873
+ }
874
+ clearTimeout(timeoutId);
857
875
 
858
876
  if (response.ok) break;
859
877
 
860
878
  const errorText = await response.text();
879
+ // Always log the error body so the developer can see what went wrong
880
+ console.log(`>>ARCALITY_STATUS>> ❌ API Error ${response.status}: ${errorText.substring(0, 500)}`);
881
+
861
882
  const isTransient = response.status === 529 || response.status === 429 || response.status === 503;
862
883
 
863
884
  if (isTransient && retryCount < maxRetries) {
@@ -868,7 +889,7 @@ ${history.slice(-25).join('\n') || 'None'}`
868
889
  continue;
869
890
  }
870
891
 
871
- throw new Error(`Arcality Brain Error: ${errorText}`);
892
+ throw new Error(`Arcality Brain Error ${response.status}: ${errorText}`);
872
893
  }
873
894
 
874
895
  const data: any = await response.json();
@@ -1445,63 +1466,7 @@ ${history.slice(-25).join('\n') || 'None'}`
1445
1466
  /**
1446
1467
  * MODO BRIDGE: Envía la percepción de la CLI hacia el Portal Web (SaaS)
1447
1468
  * para que el servidor decida la acción usando su propia infraestructura e IP.
1448
- */
1449
- private async askBridge(prompt: string, history: string[] = []): Promise<AgentAction> {
1450
- console.log(`>>ARCALITY_STATUS>> 🌐 Conectando con el cerebro de Arcality (Modo SaaS)...`);
1451
-
1452
- const state = await this.getPageState();
1453
- const screenshot = await this.page.screenshot({ type: 'png' });
1454
-
1455
- // Payload enriquecido para el portal
1456
- const payload = {
1457
- mission: prompt,
1458
- history: history,
1459
- percept: {
1460
- url: state.url,
1461
- title: state.title,
1462
- components: state.components,
1463
- screenshot: screenshot.toString('base64'),
1464
- logs: this.logs
1465
- },
1466
- context: {
1467
- version: "1.0.1",
1468
- model: process.env.CLAUDE_MODEL || "claude-3-5-sonnet-20241022",
1469
- config: process.env.ACTIVE_CONFIG || "Default"
1470
- }
1471
- };
1472
-
1473
- try {
1474
- const baseUrl = process.env.ARCALITY_PORTAL_URL || "";
1475
- const url = baseUrl.endsWith('/')
1476
- ? baseUrl + 'api/v1/decide'
1477
- : baseUrl + '/api/v1/decide';
1478
-
1479
- const response = await fetch(url, {
1480
- method: 'POST',
1481
- headers: {
1482
- 'Content-Type': 'application/json',
1483
- 'x-arcality-cli-version': '1.0.1'
1484
- },
1485
- body: JSON.stringify(payload)
1486
- });
1487
-
1488
- if (!response.ok) {
1489
- const errorText = await response.text();
1490
- throw new Error(`SaaS Bridge Error (${response.status}): ${errorText}`);
1491
- }
1492
-
1493
- const decision = await response.json() as AgentAction;
1494
- return decision;
1495
-
1496
- } catch (err: any) {
1497
- console.error(chalk.red(`\n❌ Error en el Bridge de Arcality: ${err.message}`));
1498
- console.log(chalk.yellow(`\n⚠️ Reintentando modo local (Direct Claude)...`));
1499
-
1500
- // Fallback al modo local si el bridge falla por red o configuración
1501
- delete process.env.ARCALITY_PORTAL_URL;
1502
- return this.askIA(prompt, history);
1503
- }
1504
- }
1469
+ // askBridge eliminado — proxy mode ahora está integrado directamente en askIA.
1505
1470
 
1506
1471
  /**
1507
1472
  * QA Skill: Summarizes the mission execution into a clean, human-friendly English YAML format.