@arcadialdev/arcality 2.3.4 → 2.4.20
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
package/scripts/gen-and-run.mjs
CHANGED
|
@@ -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 =
|
|
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: [
|
|
313
|
-
shell: false,
|
|
314
|
-
|
|
315
|
-
env
|
|
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
|
-
|
|
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
|
-
|
|
335
|
-
});
|
|
348
|
+
};
|
|
336
349
|
|
|
337
|
-
p.
|
|
338
|
-
|
|
339
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
716
|
-
//
|
|
717
|
-
//
|
|
718
|
-
//
|
|
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
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
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
|
-
|
|
779
|
+
|
|
733
780
|
try {
|
|
734
|
-
//
|
|
735
|
-
//
|
|
736
|
-
|
|
737
|
-
|
|
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(
|
|
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 {
|
package/src/KnowledgeService.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
145
|
-
|
|
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) {
|
package/src/configLoader.mjs
CHANGED
|
@@ -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
|
-
//
|
|
544
|
-
|
|
545
|
-
|
|
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.
|
|
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
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
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.
|