@arcadialdev/arcality 3.0.2 → 3.0.3
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 +12 -6
- package/bin/arcality.mjs +9 -8
- package/package.json +1 -1
- package/playwright.config.js +18 -16
- package/scripts/arcality-logs.mjs +167 -93
- package/scripts/gen-and-run.mjs +1706 -1158
- package/scripts/init.mjs +55 -19
- package/scripts/setup.mjs +120 -166
- package/src/KnowledgeService.ts +29 -20
- package/src/arcalityClient.mjs +33 -63
- package/src/configLoader.mjs +35 -39
- package/src/configManager.mjs +48 -39
- package/src/playwrightAssets.mjs +27 -0
- package/src/services/collectiveMemoryService.ts +37 -26
- package/tests/_helpers/ArcalityReporter.js +4 -1
- package/tests/_helpers/agentic-runner.bundle.spec.js +213 -140
package/src/arcalityClient.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// src/arcalityClient.mjs
|
|
2
2
|
// Client for communicating with Arcality Backend
|
|
3
|
-
// Supports arcality.config (primary) and .env (fallback)
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
3
|
+
// Supports arcality.config (primary) and .env (fallback)
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { getApiKey, getApiUrl, CONFIG_DIR } from './configLoader.mjs';
|
|
7
|
+
import { isRealProjectId } from './configManager.mjs';
|
|
8
8
|
|
|
9
9
|
// ── Backend config (internal, not user-configurable) ──
|
|
10
10
|
const DAILY_MISSION_LIMIT = 35;
|
|
@@ -97,11 +97,11 @@ function getEffectiveApiKey() {
|
|
|
97
97
|
* Gets the effective Project ID.
|
|
98
98
|
* Priority: arcality.config > ARCALITY_PROJECT_ID env var
|
|
99
99
|
*/
|
|
100
|
-
function loadProjectId() {
|
|
101
|
-
const localConfig = loadArcalityConfig();
|
|
102
|
-
|
|
103
|
-
return
|
|
104
|
-
}
|
|
100
|
+
function loadProjectId() {
|
|
101
|
+
const localConfig = loadArcalityConfig();
|
|
102
|
+
const projectId = localConfig?.projectId || process.env.ARCALITY_PROJECT_ID || null;
|
|
103
|
+
return isRealProjectId(projectId) ? projectId : null;
|
|
104
|
+
}
|
|
105
105
|
|
|
106
106
|
// ═══════════════════════════════════════════════════════
|
|
107
107
|
// PUBLIC API
|
|
@@ -126,11 +126,11 @@ export async function validateApiKey() {
|
|
|
126
126
|
const key = getEffectiveApiKey();
|
|
127
127
|
|
|
128
128
|
if (!key) {
|
|
129
|
-
return { valid: false, error: 'no_api_key', mode: '
|
|
129
|
+
return { valid: false, error: 'no_api_key', mode: 'live' };
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
if (!key.startsWith('arc_')) {
|
|
133
|
-
return { valid: false, error: 'invalid_format', mode: '
|
|
133
|
+
return { valid: false, error: 'invalid_format', mode: 'live' };
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
const apiBase = getEffectiveApiBase();
|
|
@@ -162,20 +162,12 @@ export async function validateApiKey() {
|
|
|
162
162
|
return { ...data, valid: true, mode: 'live' };
|
|
163
163
|
} catch (e) {
|
|
164
164
|
const reason = e?.name === 'AbortError' ? 'timeout (5s)' : (e?.message || 'unknown');
|
|
165
|
-
console.warn(
|
|
165
|
+
console.warn(`Backend not available (${apiBase}): ${reason}.`);
|
|
166
|
+
return { valid: false, error: 'backend_unavailable', mode: 'live', reason };
|
|
166
167
|
}
|
|
167
168
|
}
|
|
168
169
|
|
|
169
|
-
|
|
170
|
-
const dailyUsed = getLocalDailyUsage();
|
|
171
|
-
return {
|
|
172
|
-
valid: true,
|
|
173
|
-
mode: 'mock',
|
|
174
|
-
plan: 'internal',
|
|
175
|
-
daily_used: dailyUsed,
|
|
176
|
-
daily_limit: DAILY_MISSION_LIMIT,
|
|
177
|
-
remaining: Math.max(0, DAILY_MISSION_LIMIT - dailyUsed)
|
|
178
|
-
};
|
|
170
|
+
return { valid: false, error: 'backend_unavailable', mode: 'live' };
|
|
179
171
|
}
|
|
180
172
|
|
|
181
173
|
/**
|
|
@@ -192,7 +184,10 @@ export async function startMission(prompt, targetUrl) {
|
|
|
192
184
|
// ── LIVE mode ──
|
|
193
185
|
if (apiBase) {
|
|
194
186
|
try {
|
|
195
|
-
const projectId = process.env.ARCALITY_PROJECT_ID || loadArcalityConfig()?.projectId;
|
|
187
|
+
const projectId = process.env.ARCALITY_PROJECT_ID || loadArcalityConfig()?.projectId;
|
|
188
|
+
if (!isRealProjectId(projectId)) {
|
|
189
|
+
return { allowed: false, error: 'no_project_id' };
|
|
190
|
+
}
|
|
196
191
|
|
|
197
192
|
const controller = new AbortController();
|
|
198
193
|
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
@@ -221,38 +216,13 @@ export async function startMission(prompt, targetUrl) {
|
|
|
221
216
|
return { allowed: true, ...(await res.json()) };
|
|
222
217
|
} catch (e) {
|
|
223
218
|
const reason = e?.name === 'AbortError' ? 'timeout (5s)' : (e?.message || 'unknown');
|
|
224
|
-
console.warn(
|
|
219
|
+
console.warn(`startMission failed (${apiBase}): ${reason}.`);
|
|
220
|
+
return { allowed: false, error: 'backend_unavailable', reason };
|
|
225
221
|
}
|
|
226
222
|
}
|
|
227
223
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (dailyUsed >= DAILY_MISSION_LIMIT) {
|
|
232
|
-
const tomorrow = new Date();
|
|
233
|
-
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
234
|
-
tomorrow.setHours(0, 0, 0, 0);
|
|
235
|
-
|
|
236
|
-
return {
|
|
237
|
-
allowed: false,
|
|
238
|
-
error: 'mission_limit_exceeded',
|
|
239
|
-
daily_used: dailyUsed,
|
|
240
|
-
daily_limit: DAILY_MISSION_LIMIT,
|
|
241
|
-
remaining: 0,
|
|
242
|
-
resets_at: tomorrow.toISOString()
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const newCount = incrementLocalUsage();
|
|
247
|
-
return {
|
|
248
|
-
allowed: true,
|
|
249
|
-
mode: 'mock',
|
|
250
|
-
mission_id: `mock_${Date.now().toString(36)}`,
|
|
251
|
-
daily_used: newCount,
|
|
252
|
-
daily_limit: DAILY_MISSION_LIMIT,
|
|
253
|
-
remaining: DAILY_MISSION_LIMIT - newCount
|
|
254
|
-
};
|
|
255
|
-
}
|
|
224
|
+
return { allowed: false, error: 'backend_unavailable' };
|
|
225
|
+
}
|
|
256
226
|
|
|
257
227
|
/**
|
|
258
228
|
* Fetches missions from the backend API.
|
|
@@ -264,11 +234,10 @@ export async function fetchMissions(projectId, tags) {
|
|
|
264
234
|
const key = getEffectiveApiKey();
|
|
265
235
|
if (!key) return { success: false, error: 'no_api_key', missions: [] };
|
|
266
236
|
|
|
267
|
-
const apiBase = getEffectiveApiBase();
|
|
268
|
-
if (!apiBase) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
237
|
+
const apiBase = getEffectiveApiBase();
|
|
238
|
+
if (!apiBase) {
|
|
239
|
+
return { success: false, error: 'backend_unavailable', missions: [] };
|
|
240
|
+
}
|
|
272
241
|
|
|
273
242
|
try {
|
|
274
243
|
const url = new URL(`${apiBase}/api/v1/projects/${projectId}/missions`);
|
|
@@ -308,9 +277,10 @@ export async function fetchMissions(projectId, tags) {
|
|
|
308
277
|
* @param {'success'|'failed'|'cancelled'} result
|
|
309
278
|
* @param {object} usage - Optional usage statistics directly from the AI
|
|
310
279
|
*/
|
|
311
|
-
export async function endMission(missionId, result, usage = null, failReason = null, reportUrl = null, failReasoning = null) {
|
|
312
|
-
const apiBase = getEffectiveApiBase();
|
|
313
|
-
if (!apiBase
|
|
280
|
+
export async function endMission(missionId, result, usage = null, failReason = null, reportUrl = null, failReasoning = null) {
|
|
281
|
+
const apiBase = getEffectiveApiBase();
|
|
282
|
+
if (!apiBase) return { ok: false, error: 'backend_unavailable' };
|
|
283
|
+
if (!missionId) return { ok: false, error: 'no_mission_id' };
|
|
314
284
|
|
|
315
285
|
const key = getEffectiveApiKey();
|
|
316
286
|
try {
|
|
@@ -356,7 +326,7 @@ export async function createAdoTask(title, description) {
|
|
|
356
326
|
}
|
|
357
327
|
|
|
358
328
|
const projectId = loadProjectId();
|
|
359
|
-
if (!projectId) {
|
|
329
|
+
if (!isRealProjectId(projectId)) {
|
|
360
330
|
if (process.env.DEBUG) console.log(`[ADO] No hay Project ID configurado. Omitiendo creación de Task en Azure DevOps.`);
|
|
361
331
|
return { success: false, error: 'no_project_id' };
|
|
362
332
|
}
|
|
@@ -415,7 +385,7 @@ export async function createAdoTask(title, description) {
|
|
|
415
385
|
*/
|
|
416
386
|
export async function getEvidenceSasToken(missionId, projectId) {
|
|
417
387
|
const apiBase = getEffectiveApiBase();
|
|
418
|
-
if (!apiBase || !missionId) return null;
|
|
388
|
+
if (!apiBase || !missionId || !isRealProjectId(projectId)) return null;
|
|
419
389
|
|
|
420
390
|
const key = getEffectiveApiKey();
|
|
421
391
|
if (!key) return null;
|
package/src/configLoader.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// src/configLoader.mjs
|
|
2
2
|
// Configuration loader — supports arcality.config (primary) and .env / global config (fallback)
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import os from 'node:os';
|
|
6
|
-
import dotenv from 'dotenv';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import dotenv from 'dotenv';
|
|
7
|
+
import { isRealProjectId } from './configManager.mjs';
|
|
7
8
|
|
|
8
9
|
dotenv.config();
|
|
9
10
|
|
|
@@ -58,17 +59,22 @@ export function loadConfig() {
|
|
|
58
59
|
ARCALITY_PROJECT_ID: localConfig?.projectId || process.env.ARCALITY_PROJECT_ID,
|
|
59
60
|
};
|
|
60
61
|
|
|
61
|
-
if (!config.ARCALITY_API_KEY) {
|
|
62
|
-
throw new Error('ARCALITY_API_KEY is not defined. Run `arcality init` or set it in your .env file.');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
if (!config.ARCALITY_API_KEY) {
|
|
63
|
+
throw new Error('ARCALITY_API_KEY is not defined. Run `arcality init` or set it in your .env file.');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!isRealProjectId(config.ARCALITY_PROJECT_ID)) {
|
|
67
|
+
throw new Error('ARCALITY_PROJECT_ID is not connected to a real Arcality project. Run `arcality init`.');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return config;
|
|
71
|
+
}
|
|
67
72
|
|
|
68
|
-
export function loadProjectId() {
|
|
69
|
-
const localConfig = loadLocalArcalityConfig();
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
export function loadProjectId() {
|
|
74
|
+
const localConfig = loadLocalArcalityConfig();
|
|
75
|
+
const projectId = localConfig?.projectId || process.env.ARCALITY_PROJECT_ID;
|
|
76
|
+
return isRealProjectId(projectId) ? projectId : null;
|
|
77
|
+
}
|
|
72
78
|
|
|
73
79
|
|
|
74
80
|
/**
|
|
@@ -127,35 +133,25 @@ export function getApiKey() {
|
|
|
127
133
|
return { key: null, source: 'none' };
|
|
128
134
|
}
|
|
129
135
|
|
|
130
|
-
/**
|
|
131
|
-
* Loads
|
|
132
|
-
*/
|
|
133
|
-
export function setupProcessEnv() {
|
|
134
|
-
const config = loadGlobalConfig();
|
|
135
|
-
if (!config) return;
|
|
136
|
-
|
|
137
|
-
// Load Arcality API Key
|
|
138
|
-
getApiKey();
|
|
139
|
-
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
process.env.ANTHROPIC_API_KEY = config.anthropic_api_key;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Load default model if exists
|
|
146
|
-
if (config.model && !process.env.CLAUDE_MODEL) {
|
|
147
|
-
process.env.CLAUDE_MODEL = config.model;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Also inject arcality.config values into process.env for backward compat
|
|
151
|
-
const localConfig = loadLocalArcalityConfig();
|
|
136
|
+
/**
|
|
137
|
+
* Loads supported Arcality runtime values into process.env.
|
|
138
|
+
*/
|
|
139
|
+
export function setupProcessEnv() {
|
|
140
|
+
const config = loadGlobalConfig();
|
|
141
|
+
if (!config) return;
|
|
142
|
+
|
|
143
|
+
// Load Arcality API Key
|
|
144
|
+
getApiKey();
|
|
145
|
+
|
|
146
|
+
// Also inject arcality.config values into process.env for backward compat
|
|
147
|
+
const localConfig = loadLocalArcalityConfig();
|
|
152
148
|
if (localConfig) {
|
|
153
149
|
if (localConfig.apiKey && !process.env.ARCALITY_API_KEY) {
|
|
154
150
|
process.env.ARCALITY_API_KEY = localConfig.apiKey;
|
|
155
151
|
}
|
|
156
|
-
if (localConfig.projectId && !process.env.ARCALITY_PROJECT_ID) {
|
|
157
|
-
process.env.ARCALITY_PROJECT_ID = localConfig.projectId;
|
|
158
|
-
}
|
|
152
|
+
if (isRealProjectId(localConfig.projectId) && !process.env.ARCALITY_PROJECT_ID) {
|
|
153
|
+
process.env.ARCALITY_PROJECT_ID = localConfig.projectId;
|
|
154
|
+
}
|
|
159
155
|
if (localConfig.project?.baseUrl && !process.env.BASE_URL) {
|
|
160
156
|
process.env.BASE_URL = localConfig.project.baseUrl;
|
|
161
157
|
}
|
package/src/configManager.mjs
CHANGED
|
@@ -5,7 +5,16 @@
|
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
|
|
8
|
-
const CONFIG_FILENAME = 'arcality.config';
|
|
8
|
+
const CONFIG_FILENAME = 'arcality.config';
|
|
9
|
+
|
|
10
|
+
export function isRealProjectId(projectId) {
|
|
11
|
+
const id = String(projectId || '').trim();
|
|
12
|
+
if (!id) return false;
|
|
13
|
+
if (id === 'undefined' || id === 'null') return false;
|
|
14
|
+
if (/^(local|mock)_/i.test(id)) return false;
|
|
15
|
+
if (/^0{8}-0{4}-0{4}-0{4}-0{12}$/i.test(id)) return false;
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
9
18
|
|
|
10
19
|
/**
|
|
11
20
|
* Resolves the path to arcality.config in the project root.
|
|
@@ -59,21 +68,21 @@ export function saveProjectConfig(config, projectRoot) {
|
|
|
59
68
|
* @param {ArcalityConfig} config
|
|
60
69
|
* @returns {{ valid: boolean, missing: string[] }}
|
|
61
70
|
*/
|
|
62
|
-
export function validateConfig(config) {
|
|
63
|
-
const missing = [];
|
|
71
|
+
export function validateConfig(config) {
|
|
72
|
+
const missing = [];
|
|
64
73
|
|
|
65
74
|
if (!config) {
|
|
66
75
|
return { valid: false, missing: ['arcality.config file'] };
|
|
67
76
|
}
|
|
68
77
|
|
|
69
|
-
if (!config.apiKey) missing.push('apiKey');
|
|
70
|
-
if (!config.projectId) missing.push('projectId');
|
|
71
|
-
if (!config.project?.baseUrl) missing.push('project.baseUrl');
|
|
72
|
-
if (!config.auth?.username) missing.push('auth.username');
|
|
73
|
-
if (!config.auth?.password) missing.push('auth.password');
|
|
74
|
-
|
|
75
|
-
return { valid: missing.length === 0, missing };
|
|
76
|
-
}
|
|
78
|
+
if (!config.apiKey) missing.push('apiKey');
|
|
79
|
+
if (!isRealProjectId(config.projectId)) missing.push('projectId');
|
|
80
|
+
if (!config.project?.baseUrl) missing.push('project.baseUrl');
|
|
81
|
+
if (!config.auth?.username) missing.push('auth.username');
|
|
82
|
+
if (!config.auth?.password) missing.push('auth.password');
|
|
83
|
+
|
|
84
|
+
return { valid: missing.length === 0, missing };
|
|
85
|
+
}
|
|
77
86
|
|
|
78
87
|
/**
|
|
79
88
|
* Creates a fresh arcality.config structure.
|
|
@@ -84,30 +93,30 @@ export function createConfig({
|
|
|
84
93
|
apiKey,
|
|
85
94
|
organizationId,
|
|
86
95
|
projectId,
|
|
87
|
-
projectName,
|
|
88
|
-
baseUrl,
|
|
89
|
-
frameworkDetected,
|
|
90
|
-
username,
|
|
91
|
-
password,
|
|
92
|
-
arcalityVersion,
|
|
93
|
-
yamlOutputDir = './arcality',
|
|
94
|
-
}) {
|
|
95
|
-
return {
|
|
96
|
+
projectName,
|
|
97
|
+
baseUrl,
|
|
98
|
+
frameworkDetected,
|
|
99
|
+
username,
|
|
100
|
+
password,
|
|
101
|
+
arcalityVersion,
|
|
102
|
+
yamlOutputDir = './arcality',
|
|
103
|
+
}) {
|
|
104
|
+
return {
|
|
96
105
|
version: '1',
|
|
97
106
|
apiKey,
|
|
98
107
|
organizationId: organizationId || null,
|
|
99
108
|
projectId,
|
|
100
109
|
project: {
|
|
101
110
|
name: projectName,
|
|
102
|
-
baseUrl,
|
|
103
|
-
frameworkDetected: frameworkDetected || null,
|
|
104
|
-
},
|
|
105
|
-
auth: {
|
|
106
|
-
username,
|
|
107
|
-
password,
|
|
108
|
-
},
|
|
109
|
-
runtime: {
|
|
110
|
-
yamlOutputDir,
|
|
111
|
+
baseUrl,
|
|
112
|
+
frameworkDetected: frameworkDetected || null,
|
|
113
|
+
},
|
|
114
|
+
auth: {
|
|
115
|
+
username,
|
|
116
|
+
password,
|
|
117
|
+
},
|
|
118
|
+
runtime: {
|
|
119
|
+
yamlOutputDir,
|
|
111
120
|
reuseSuccessfulYamls: true,
|
|
112
121
|
singleConfigurationMode: true,
|
|
113
122
|
},
|
|
@@ -122,17 +131,17 @@ export function createConfig({
|
|
|
122
131
|
* with existing modules that read from process.env.
|
|
123
132
|
* @param {ArcalityConfig} config
|
|
124
133
|
*/
|
|
125
|
-
export function injectConfigToEnv(config) {
|
|
126
|
-
if (!config) return;
|
|
134
|
+
export function injectConfigToEnv(config) {
|
|
135
|
+
if (!config) return;
|
|
127
136
|
|
|
128
137
|
if (config.apiKey) process.env.ARCALITY_API_KEY = config.apiKey;
|
|
129
|
-
if (config.projectId) process.env.ARCALITY_PROJECT_ID = config.projectId;
|
|
130
|
-
if (config.project?.baseUrl && !process.env.BASE_URL) {
|
|
131
|
-
process.env.BASE_URL = config.project.baseUrl;
|
|
132
|
-
}
|
|
133
|
-
if (config.auth?.username) process.env.LOGIN_USER = config.auth.username;
|
|
134
|
-
if (config.auth?.password) process.env.LOGIN_PASSWORD = config.auth.password;
|
|
135
|
-
}
|
|
138
|
+
if (isRealProjectId(config.projectId)) process.env.ARCALITY_PROJECT_ID = config.projectId;
|
|
139
|
+
if (config.project?.baseUrl && !process.env.BASE_URL) {
|
|
140
|
+
process.env.BASE_URL = config.project.baseUrl;
|
|
141
|
+
}
|
|
142
|
+
if (config.auth?.username) process.env.LOGIN_USER = config.auth.username;
|
|
143
|
+
if (config.auth?.password) process.env.LOGIN_PASSWORD = config.auth.password;
|
|
144
|
+
}
|
|
136
145
|
|
|
137
146
|
/**
|
|
138
147
|
* Gets the YAML output directory from config, falling back to default.
|
|
@@ -166,7 +175,7 @@ export function ensureYamlOutputDir(config, projectRoot) {
|
|
|
166
175
|
* @property {string|null} organizationId
|
|
167
176
|
* @property {string} projectId
|
|
168
177
|
* @property {{ name: string, baseUrl: string, frameworkDetected: string|null }} project
|
|
169
|
-
* @property {{ username: string, password: string }} auth
|
|
178
|
+
* @property {{ username: string, password: string }} auth
|
|
170
179
|
* @property {{ yamlOutputDir: string, reuseSuccessfulYamls: boolean, singleConfigurationMode: boolean }} runtime
|
|
171
180
|
* @property {{ arcalityVersion: string }} meta
|
|
172
181
|
*/
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
export const PLAYWRIGHT_ASSETS = ['chromium', 'ffmpeg'];
|
|
4
|
+
|
|
5
|
+
export function getPlaywrightInstallCommand() {
|
|
6
|
+
return `npx playwright install ${PLAYWRIGHT_ASSETS.join(' ')}`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function installPlaywrightAssets({ cwd, stdio = 'inherit' } = {}) {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const child = spawn('npx', ['playwright', 'install', ...PLAYWRIGHT_ASSETS], {
|
|
12
|
+
stdio,
|
|
13
|
+
shell: true,
|
|
14
|
+
cwd,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
child.on('exit', code => {
|
|
18
|
+
if (code === 0) {
|
|
19
|
+
resolve();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
reject(new Error(`Playwright install failed with exit code ${code}`));
|
|
24
|
+
});
|
|
25
|
+
child.on('error', reject);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -16,25 +16,32 @@ const getPid = () => process.env.ARCALITY_PROJECT_ID || '';
|
|
|
16
16
|
const getOrgId = () => process.env.ARCALITY_ORG_ID;
|
|
17
17
|
const getMissionId = () => process.env.ARCALITY_MISSION_ID || EMPTY_GUID;
|
|
18
18
|
|
|
19
|
-
const EMPTY_GUID = '00000000-0000-0000-0000-000000000000';
|
|
20
|
-
|
|
21
|
-
let configWarningLogged = false;
|
|
22
|
-
|
|
23
|
-
function
|
|
19
|
+
const EMPTY_GUID = '00000000-0000-0000-0000-000000000000';
|
|
20
|
+
|
|
21
|
+
let configWarningLogged = false;
|
|
22
|
+
|
|
23
|
+
function hasRealProjectId(pid: string): boolean {
|
|
24
|
+
if (!pid || pid === EMPTY_GUID) return false;
|
|
25
|
+
if (/^(local|mock)_/i.test(pid)) return false;
|
|
26
|
+
if (pid === 'undefined' || pid === 'null') return false;
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isConfigured(): boolean {
|
|
24
31
|
const base = getBase();
|
|
25
32
|
const key = getKey();
|
|
26
33
|
const pid = getPid();
|
|
27
34
|
const orgId = getOrgId();
|
|
28
35
|
|
|
29
|
-
if (!base || !key || !pid ||
|
|
30
|
-
if (!configWarningLogged) {
|
|
31
|
-
const missing = [];
|
|
32
|
-
if (!base) missing.push('ARCALITY_API_URL');
|
|
33
|
-
if (!key) missing.push('ARCALITY_API_KEY');
|
|
34
|
-
if (!pid
|
|
36
|
+
if (!base || !key || !hasRealProjectId(pid) || !orgId) {
|
|
37
|
+
if (!configWarningLogged) {
|
|
38
|
+
const missing = [];
|
|
39
|
+
if (!base) missing.push('ARCALITY_API_URL');
|
|
40
|
+
if (!key) missing.push('ARCALITY_API_KEY');
|
|
41
|
+
if (!hasRealProjectId(pid)) missing.push('ARCALITY_PROJECT_ID');
|
|
35
42
|
if (!orgId) missing.push('ARCALITY_ORG_ID');
|
|
36
43
|
|
|
37
|
-
console.warn(`[CollectiveMemory] Service disabled. Missing: ${missing.join(', ')}`);
|
|
44
|
+
if (process.env.DEBUG) console.warn(`[CollectiveMemory] Service disabled. Missing: ${missing.join(', ')}`);
|
|
38
45
|
configWarningLogged = true;
|
|
39
46
|
}
|
|
40
47
|
return false;
|
|
@@ -191,8 +198,10 @@ export async function searchPromptPattern(prompt: string, limit: number = 5): Pr
|
|
|
191
198
|
limit: limit
|
|
192
199
|
};
|
|
193
200
|
|
|
194
|
-
|
|
195
|
-
|
|
201
|
+
const debugPatternSearch = process.env.DEBUG === 'true' || process.env.ARCALITY_DEBUG === 'true';
|
|
202
|
+
if (debugPatternSearch) {
|
|
203
|
+
console.log(`[PatternSearch] POST ${apiUrl} | prompt_chars=${prompt.length} | limit=${limit}`);
|
|
204
|
+
}
|
|
196
205
|
|
|
197
206
|
const res = await fetch(apiUrl, {
|
|
198
207
|
method: 'POST',
|
|
@@ -204,18 +213,20 @@ export async function searchPromptPattern(prompt: string, limit: number = 5): Pr
|
|
|
204
213
|
});
|
|
205
214
|
|
|
206
215
|
if (!res.ok) {
|
|
207
|
-
console.warn(`[PatternSearch] HTTP ${res.status} from ${apiUrl}`);
|
|
208
|
-
return [];
|
|
209
|
-
}
|
|
210
|
-
const data = await res.json();
|
|
211
|
-
const results = Array.isArray(data) ? data : (data.matches || data.results || []);
|
|
212
|
-
console.log(`[PatternSearch]
|
|
213
|
-
return results;
|
|
214
|
-
} catch (err: any) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
216
|
+
if (debugPatternSearch) console.warn(`[PatternSearch] HTTP ${res.status} from ${apiUrl}`);
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
const data = await res.json();
|
|
220
|
+
const results = Array.isArray(data) ? data : (data.matches || data.results || []);
|
|
221
|
+
if (debugPatternSearch) console.log(`[PatternSearch] ${results.length} patrones encontrados.`);
|
|
222
|
+
return results;
|
|
223
|
+
} catch (err: any) {
|
|
224
|
+
if (process.env.DEBUG === 'true' || process.env.ARCALITY_DEBUG === 'true') {
|
|
225
|
+
console.warn(`[PatternSearch] Error buscando patrones: ${err?.message || 'unknown'}`);
|
|
226
|
+
}
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
219
230
|
|
|
220
231
|
export async function savePromptPattern(patternData: any): Promise<boolean> {
|
|
221
232
|
if (!isConfigured()) return false;
|
|
@@ -46,13 +46,16 @@ class ArcalityReporter {
|
|
|
46
46
|
fs_1.default.mkdirSync(attachmentsDir, { recursive: true });
|
|
47
47
|
}
|
|
48
48
|
try {
|
|
49
|
+
let copied = false;
|
|
49
50
|
if (att.path && fs_1.default.existsSync(att.path)) {
|
|
50
51
|
fs_1.default.copyFileSync(att.path, destPath);
|
|
52
|
+
copied = true;
|
|
51
53
|
}
|
|
52
54
|
else if (att.body) {
|
|
53
55
|
fs_1.default.writeFileSync(destPath, att.body);
|
|
56
|
+
copied = true;
|
|
54
57
|
}
|
|
55
|
-
return { ...att, path: `attachments/${fileName}` };
|
|
58
|
+
return copied ? { ...att, path: `attachments/${fileName}` } : att;
|
|
56
59
|
}
|
|
57
60
|
catch (e) {
|
|
58
61
|
console.error(`Failed to process attachment ${att.name}:`, e);
|