@arela/uploader 1.1.3 → 1.2.0
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/.claude/worktrees/agent-multi-profile/.env.template +224 -0
- package/.claude/worktrees/agent-multi-profile/.prettierrc +13 -0
- package/.claude/worktrees/agent-multi-profile/README.md +405 -0
- package/.claude/worktrees/agent-multi-profile/package-lock.json +7096 -0
- package/.claude/worktrees/agent-multi-profile/package.json +78 -0
- package/.claude/worktrees/agent-multi-profile/scripts/cleanup-ds-store.js +109 -0
- package/.claude/worktrees/agent-multi-profile/scripts/cleanup-system-files.js +69 -0
- package/.claude/worktrees/agent-multi-profile/scripts/scoring-compare.js +243 -0
- package/.claude/worktrees/agent-multi-profile/scripts/scoring-phase4-check.js +96 -0
- package/.claude/worktrees/agent-multi-profile/scripts/tests/phase-7-features.test.js +415 -0
- package/.claude/worktrees/agent-multi-profile/scripts/tests/signal-handling.test.js +275 -0
- package/.claude/worktrees/agent-multi-profile/scripts/tests/smart-watch-integration.test.js +554 -0
- package/.claude/worktrees/agent-multi-profile/scripts/tests/watch-service-integration.test.js +584 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/AgentCommand.js +229 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/AgentInitCommand.js +316 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/DatastageCommand.js +164 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/GDriveSyncCommand.js +475 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/IdentifyCommand.js +708 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/PollWorkerCommand.js +169 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/PropagateCommand.js +636 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/PushCommand.js +743 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/ScanCommand.js +722 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/UploadCommand.js +587 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/WatchCommand.js +1342 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/WorkerCommand.js +337 -0
- package/.claude/worktrees/agent-multi-profile/src/config/config.js +862 -0
- package/.claude/worktrees/agent-multi-profile/src/document-type-shared.js +131 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/_pedimento-shared-extractors.js +348 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/doda-pdf.js +121 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/doda-xml.js +118 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/factura-inter-agencia.js +186 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/facturas-comerciales.js +233 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/pedimento-completo-xml.js +372 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/pedimento-completo.js +108 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/pedimento-simplificado.js +76 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/proforma.js +29 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/support-document.js +200 -0
- package/.claude/worktrees/agent-multi-profile/src/errors/ErrorHandler.js +278 -0
- package/.claude/worktrees/agent-multi-profile/src/errors/ErrorTypes.js +104 -0
- package/.claude/worktrees/agent-multi-profile/src/file-detection.js +338 -0
- package/.claude/worktrees/agent-multi-profile/src/index.js +890 -0
- package/.claude/worktrees/agent-multi-profile/src/scoring/db-matcher-adapter.js +98 -0
- package/.claude/worktrees/agent-multi-profile/src/scoring/matchers-seed.js +386 -0
- package/.claude/worktrees/agent-multi-profile/src/scoring/scoring-engine.js +251 -0
- package/.claude/worktrees/agent-multi-profile/src/services/AdvancedFilterService.js +505 -0
- package/.claude/worktrees/agent-multi-profile/src/services/AutoProcessingService.js +749 -0
- package/.claude/worktrees/agent-multi-profile/src/services/BenchmarkingService.js +381 -0
- package/.claude/worktrees/agent-multi-profile/src/services/DatabaseService.js +2173 -0
- package/.claude/worktrees/agent-multi-profile/src/services/DatastageApiService.js +240 -0
- package/.claude/worktrees/agent-multi-profile/src/services/ErrorMonitor.js +275 -0
- package/.claude/worktrees/agent-multi-profile/src/services/GoogleDriveService.js +217 -0
- package/.claude/worktrees/agent-multi-profile/src/services/LoggingService.js +649 -0
- package/.claude/worktrees/agent-multi-profile/src/services/MonitoringService.js +401 -0
- package/.claude/worktrees/agent-multi-profile/src/services/PerformanceOptimizer.js +511 -0
- package/.claude/worktrees/agent-multi-profile/src/services/PipelineApiService.js +274 -0
- package/.claude/worktrees/agent-multi-profile/src/services/PipelineJobRunner.js +389 -0
- package/.claude/worktrees/agent-multi-profile/src/services/ProfileManager.js +164 -0
- package/.claude/worktrees/agent-multi-profile/src/services/ReportingService.js +511 -0
- package/.claude/worktrees/agent-multi-profile/src/services/ScanApiService.js +775 -0
- package/.claude/worktrees/agent-multi-profile/src/services/SignalHandler.js +255 -0
- package/.claude/worktrees/agent-multi-profile/src/services/SmartWatchDatabaseService.js +527 -0
- package/.claude/worktrees/agent-multi-profile/src/services/WatchService.js +783 -0
- package/.claude/worktrees/agent-multi-profile/src/services/upload/ApiUploadService.js +676 -0
- package/.claude/worktrees/agent-multi-profile/src/services/upload/BaseUploadService.js +36 -0
- package/.claude/worktrees/agent-multi-profile/src/services/upload/MultiApiUploadService.js +233 -0
- package/.claude/worktrees/agent-multi-profile/src/services/upload/SupabaseUploadService.js +148 -0
- package/.claude/worktrees/agent-multi-profile/src/services/upload/UploadServiceFactory.js +100 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/CleanupManager.js +262 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/FileOperations.js +192 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/FileSanitizer.js +99 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/PathDetector.js +198 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/PathNormalizer.js +274 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/WatchEventHandler.js +522 -0
- package/.claude/worktrees/agent-multi-profile/supabase/migrations/001_create_initial_schema.sql +366 -0
- package/.claude/worktrees/agent-multi-profile/supabase/migrations/002_align_with_arela_api_schema.sql +145 -0
- package/.claude/worktrees/agent-multi-profile/tests/commands/IdentifyCommand.test.js +570 -0
- package/.claude/worktrees/agent-multi-profile/tests/commands/PropagateCommand.test.js +568 -0
- package/.claude/worktrees/agent-multi-profile/tests/commands/PushCommand.test.js +754 -0
- package/.claude/worktrees/agent-multi-profile/tests/commands/ScanCommand.test.js +382 -0
- package/.claude/worktrees/agent-multi-profile/tests/unit/PathAndTableNameGeneration.test.js +1211 -0
- package/.claude/worktrees/agent-multi-profile/tests/unit/factura-inter-agencia.test.js +218 -0
- package/.claude/worktrees/agent-multi-profile/tests/unit/pedimento-completo-xml-matcher.test.js +271 -0
- package/.claude/worktrees/agent-multi-profile/tests/unit/pedimento-simplificado-matcher.test.js +185 -0
- package/.claude/worktrees/agent-multi-profile/tests/unit/scoring-engine.test.js +221 -0
- package/README.md +65 -0
- package/package.json +1 -1
- package/src/commands/AgentCommand.js +210 -0
- package/src/commands/AgentInitCommand.js +316 -0
- package/src/commands/PollWorkerCommand.js +11 -322
- package/src/config/config.js +44 -6
- package/src/document-type-shared.js +1 -1
- package/src/document-types/pedimento-completo.js +8 -1
- package/src/file-detection.js +9 -0
- package/src/index.js +55 -0
- package/src/scoring/scoring-engine.js +1 -1
- package/src/services/LoggingService.js +35 -0
- package/src/services/PipelineApiService.js +7 -1
- package/src/services/PipelineJobRunner.js +389 -0
- package/src/services/ProfileManager.js +164 -0
package/src/config/config.js
CHANGED
|
@@ -37,10 +37,10 @@ class Config {
|
|
|
37
37
|
const __dirname = path.dirname(__filename);
|
|
38
38
|
const packageJsonPath = path.resolve(__dirname, '../../package.json');
|
|
39
39
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
40
|
-
return packageJson.version || '1.
|
|
40
|
+
return packageJson.version || '1.2.0';
|
|
41
41
|
} catch (error) {
|
|
42
42
|
console.warn('⚠️ Could not read package.json version, using fallback');
|
|
43
|
-
return '1.
|
|
43
|
+
return '1.2.0';
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -126,7 +126,7 @@ class Config {
|
|
|
126
126
|
* @param {string} target - API target: 'default', 'agencia', or 'cliente'
|
|
127
127
|
*/
|
|
128
128
|
setApiTarget(target) {
|
|
129
|
-
const validTargets = ['default',
|
|
129
|
+
const validTargets = ['default', ...Object.keys(this.api.targets)];
|
|
130
130
|
if (!validTargets.includes(target.toLowerCase())) {
|
|
131
131
|
throw new Error(
|
|
132
132
|
`Invalid API target '${target}'. Must be one of: ${validTargets.join(', ')}`,
|
|
@@ -143,7 +143,7 @@ class Config {
|
|
|
143
143
|
* @param {string} targetTarget - Target API target (for writing data)
|
|
144
144
|
*/
|
|
145
145
|
setCrossTenantTargets(sourceTarget, targetTarget) {
|
|
146
|
-
const validTargets = ['default',
|
|
146
|
+
const validTargets = ['default', ...Object.keys(this.api.targets)];
|
|
147
147
|
|
|
148
148
|
if (!validTargets.includes(sourceTarget.toLowerCase())) {
|
|
149
149
|
throw new Error(
|
|
@@ -171,6 +171,41 @@ class Config {
|
|
|
171
171
|
);
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Upsert an API target in memory (does not touch process.env).
|
|
176
|
+
* Used by agent mode to register per-profile targets at runtime.
|
|
177
|
+
* @param {string} name - Target name, e.g. 'cliente', 'agencia'
|
|
178
|
+
* @param {{baseUrl: string, token: string}} targetConfig
|
|
179
|
+
*/
|
|
180
|
+
setTargetConfig(name, { baseUrl, token }) {
|
|
181
|
+
this.api.targets[name.toLowerCase()] = { baseUrl, token };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Apply an agent profile: API targets, server id and env overrides.
|
|
186
|
+
* Idempotent — the agent calls it before every poll/job of the profile so the
|
|
187
|
+
* singleton always reflects the ACTIVE profile (jobs reference targets by
|
|
188
|
+
* symbolic name: 'agencia', 'cliente').
|
|
189
|
+
* @param {Object} profile - Normalized profile from ProfileManager
|
|
190
|
+
*/
|
|
191
|
+
applyProfile(profile) {
|
|
192
|
+
this.api.baseUrl = profile.url;
|
|
193
|
+
this.api.token = profile.token;
|
|
194
|
+
for (const [name, targetConfig] of Object.entries(profile.targets)) {
|
|
195
|
+
this.setTargetConfig(name, targetConfig);
|
|
196
|
+
}
|
|
197
|
+
if (profile.serverId) {
|
|
198
|
+
process.env.ARELA_SERVER_ID = profile.serverId;
|
|
199
|
+
}
|
|
200
|
+
for (const [key, value] of Object.entries(profile.env || {})) {
|
|
201
|
+
process.env[key] = String(value);
|
|
202
|
+
}
|
|
203
|
+
// Clear cross-tenant state left over from a previous profile's job
|
|
204
|
+
this.api.sourceTarget = null;
|
|
205
|
+
this.api.targetTarget = null;
|
|
206
|
+
this.api.activeTarget = 'default';
|
|
207
|
+
}
|
|
208
|
+
|
|
174
209
|
/**
|
|
175
210
|
* Get source API config for cross-tenant operations
|
|
176
211
|
* @returns {Object} Source API configuration
|
|
@@ -780,12 +815,15 @@ class Config {
|
|
|
780
815
|
}
|
|
781
816
|
|
|
782
817
|
/**
|
|
783
|
-
* Reload upload and
|
|
784
|
-
* Must be called after modifying env vars at runtime (e.g.,
|
|
818
|
+
* Reload upload, scan, push and performance config from current process.env
|
|
819
|
+
* values. Must be called after modifying env vars at runtime (e.g.,
|
|
820
|
+
* PipelineJobRunner applying a job's scanConfig/tuning overrides).
|
|
785
821
|
*/
|
|
786
822
|
reloadScanConfig() {
|
|
787
823
|
this.upload = this.#loadUploadConfig();
|
|
788
824
|
this.scan = this.#loadScanConfig();
|
|
825
|
+
this.push = this.#loadPushConfig();
|
|
826
|
+
this.performance = this.#loadPerformanceConfig();
|
|
789
827
|
}
|
|
790
828
|
|
|
791
829
|
/**
|
|
@@ -100,7 +100,7 @@ export function extractDocumentFields(source, fileExtension, filePath) {
|
|
|
100
100
|
|
|
101
101
|
// Resolve final type if the definition supports it (e.g., pedimento_simplificado vs proforma)
|
|
102
102
|
const resolvedType = docType.resolveType
|
|
103
|
-
? docType.resolveType(fields)
|
|
103
|
+
? docType.resolveType(fields, filePath)
|
|
104
104
|
: docType.type;
|
|
105
105
|
|
|
106
106
|
console.log(` → Resolved type: ${resolvedType}`);
|
|
@@ -74,8 +74,15 @@ export const pedimentoCompletoDefinition = {
|
|
|
74
74
|
* - R1 rectifications require fechaPagoRectificacion
|
|
75
75
|
* - Everything else requires paymentDate
|
|
76
76
|
* No payment evidence ⇒ proforma_completo.
|
|
77
|
+
*
|
|
78
|
+
* Exception: the "CoveFact" annex (…-CoveFact.pdf) doesn't always print the
|
|
79
|
+
* payment section, so the payment-based demotion misfires and the file gets
|
|
80
|
+
* stuck as proforma_completo (NON_PUSHABLE) forever — the CLI never
|
|
81
|
+
* re-identifies rows that already have a detected_type. The filename is the
|
|
82
|
+
* payment-independent signal for this variant.
|
|
77
83
|
*/
|
|
78
|
-
resolveType: (fields) => {
|
|
84
|
+
resolveType: (fields, filePath) => {
|
|
85
|
+
if (/covefact/i.test(filePath ?? '')) return 'pedimento_completo';
|
|
79
86
|
const clavePedimento =
|
|
80
87
|
fields?.find((f) => f.name === 'clavePedimento')?.value ?? null;
|
|
81
88
|
const paymentDate =
|
package/src/file-detection.js
CHANGED
|
@@ -56,6 +56,15 @@ function composeArelaPath(
|
|
|
56
56
|
return null;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
// The CoveFact annex resolves to pedimento_completo without payment
|
|
60
|
+
// evidence (see pedimento-completo.js resolveType), so it must never anchor
|
|
61
|
+
// an operation on its own: with no self-composed path it only becomes
|
|
62
|
+
// pushable when a PAID sibling pedimento propagates the folder's
|
|
63
|
+
// arela_path. Unpaid operations therefore never reach Arela through it.
|
|
64
|
+
if (/covefact/i.test(filePath ?? '')) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
const rfc = fields?.find((f) => f.name === 'rfc')?.value;
|
|
60
69
|
let patente = fields?.find((f) => f.name === 'patente')?.value;
|
|
61
70
|
const aduana = fields?.find((f) => f.name === 'aduanaEntradaSalida')?.value;
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
|
|
4
|
+
import agentCommand from './commands/AgentCommand.js';
|
|
5
|
+
import agentInitCommand from './commands/AgentInitCommand.js';
|
|
4
6
|
import datastageCommand from './commands/DatastageCommand.js';
|
|
5
7
|
import gdriveSyncCommand from './commands/GDriveSyncCommand.js';
|
|
6
8
|
import identifyCommand from './commands/IdentifyCommand.js';
|
|
@@ -646,6 +648,59 @@ class ArelaUploaderCLI {
|
|
|
646
648
|
}
|
|
647
649
|
});
|
|
648
650
|
|
|
651
|
+
// ============================================================================
|
|
652
|
+
// AGENT MODE - one process polling N tenant APIs (replaces N worker terminals)
|
|
653
|
+
// ============================================================================
|
|
654
|
+
|
|
655
|
+
const agent = this.program
|
|
656
|
+
.command('agent')
|
|
657
|
+
.description(
|
|
658
|
+
'🤖 Multi-profile polling agent: one process for all tenant APIs (profiles.json)',
|
|
659
|
+
);
|
|
660
|
+
|
|
661
|
+
agent
|
|
662
|
+
.command('run', { isDefault: true })
|
|
663
|
+
.description('Run the agent (default: `arela agent` runs this)')
|
|
664
|
+
.option(
|
|
665
|
+
'--config <path>',
|
|
666
|
+
'Path to profiles.json (default: ~/.arela/profiles.json)',
|
|
667
|
+
)
|
|
668
|
+
.option('--interval <ms>', 'Base poll interval per idle round')
|
|
669
|
+
.option(
|
|
670
|
+
'--profile <names>',
|
|
671
|
+
'Comma-separated subset of profiles to run (debugging)',
|
|
672
|
+
)
|
|
673
|
+
.action(async (options) => {
|
|
674
|
+
try {
|
|
675
|
+
await agentCommand.execute(options);
|
|
676
|
+
} catch (error) {
|
|
677
|
+
this.errorHandler.handleFatalError(error, { command: 'agent' });
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
agent
|
|
682
|
+
.command('init')
|
|
683
|
+
.description(
|
|
684
|
+
'Generate profiles.json + pipeline_config seeds from legacy per-folder .env files',
|
|
685
|
+
)
|
|
686
|
+
.requiredOption(
|
|
687
|
+
'--from <dirs...>',
|
|
688
|
+
'Folders containing the legacy .env files (one per RFC×source)',
|
|
689
|
+
)
|
|
690
|
+
.option(
|
|
691
|
+
'--server-id <id>',
|
|
692
|
+
'Machine server id (default: ARELA_SERVER_ID from first .env, else hostname)',
|
|
693
|
+
)
|
|
694
|
+
.option('--out <dir>', 'Output directory', '~/.arela')
|
|
695
|
+
.option('--force', 'Overwrite an existing profiles.json')
|
|
696
|
+
.action(async (options) => {
|
|
697
|
+
try {
|
|
698
|
+
await agentInitCommand.execute(options.from, options);
|
|
699
|
+
} catch (error) {
|
|
700
|
+
this.errorHandler.handleFatalError(error, { command: 'agent init' });
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
|
|
649
704
|
// Version command (already handled by program.version())
|
|
650
705
|
|
|
651
706
|
// Help command
|
|
@@ -225,7 +225,7 @@ export function classifyDocument(matchers, { source, extension, filePath }) {
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
const resolvedType = def.resolveType
|
|
228
|
-
? def.resolveType(fields)
|
|
228
|
+
? def.resolveType(fields, filePath)
|
|
229
229
|
: def.documentType;
|
|
230
230
|
const pedimento = def.extractNumPedimento
|
|
231
231
|
? def.extractNumPedimento(source, fields, filePath)
|
|
@@ -17,6 +17,7 @@ export class LoggingService {
|
|
|
17
17
|
this.bufferSize = appConfig.performance.logBufferSize;
|
|
18
18
|
this.flushInterval = appConfig.performance.logFlushInterval;
|
|
19
19
|
this.lastFlushTime = Date.now();
|
|
20
|
+
this.context = null;
|
|
20
21
|
|
|
21
22
|
// Event tracking (new in Fase 5)
|
|
22
23
|
this.uploadEvents = [];
|
|
@@ -50,6 +51,15 @@ export class LoggingService {
|
|
|
50
51
|
});
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Prefix a message with the active context (agent profile name), so both the
|
|
56
|
+
* console echo and the file entry stay grep-able per profile
|
|
57
|
+
* @private
|
|
58
|
+
*/
|
|
59
|
+
#withContext(message) {
|
|
60
|
+
return this.context ? `[${this.context}] ${message}` : message;
|
|
61
|
+
}
|
|
62
|
+
|
|
53
63
|
/**
|
|
54
64
|
* Write a log message
|
|
55
65
|
* @param {string} message - Log message
|
|
@@ -80,6 +90,7 @@ export class LoggingService {
|
|
|
80
90
|
* @param {string} message - Message to log
|
|
81
91
|
*/
|
|
82
92
|
info(message) {
|
|
93
|
+
message = this.#withContext(message);
|
|
83
94
|
this.writeLog(message, 'info');
|
|
84
95
|
// Echo to console: run banners, stats and the 🧩 observability lines were
|
|
85
96
|
// file-only, so operators watching the terminal never saw them (the v1
|
|
@@ -92,6 +103,7 @@ export class LoggingService {
|
|
|
92
103
|
* @param {string} message - Message to log
|
|
93
104
|
*/
|
|
94
105
|
warn(message) {
|
|
106
|
+
message = this.#withContext(message);
|
|
95
107
|
this.writeLog(message, 'warn');
|
|
96
108
|
console.warn(`⚠️ ${message}`);
|
|
97
109
|
}
|
|
@@ -101,6 +113,7 @@ export class LoggingService {
|
|
|
101
113
|
* @param {string} message - Message to log
|
|
102
114
|
*/
|
|
103
115
|
error(message) {
|
|
116
|
+
message = this.#withContext(message);
|
|
104
117
|
this.writeLog(message, 'error');
|
|
105
118
|
console.error(`❌ ${message}`);
|
|
106
119
|
}
|
|
@@ -111,6 +124,7 @@ export class LoggingService {
|
|
|
111
124
|
*/
|
|
112
125
|
debug(message) {
|
|
113
126
|
if (this.isVerbose) {
|
|
127
|
+
message = this.#withContext(message);
|
|
114
128
|
this.writeLog(message, 'debug');
|
|
115
129
|
console.log(`🔍 ${message}`);
|
|
116
130
|
}
|
|
@@ -122,6 +136,7 @@ export class LoggingService {
|
|
|
122
136
|
*/
|
|
123
137
|
verbose(message) {
|
|
124
138
|
if (this.isVerbose) {
|
|
139
|
+
message = this.#withContext(message);
|
|
125
140
|
this.writeLog(message, 'verbose');
|
|
126
141
|
console.log(message);
|
|
127
142
|
}
|
|
@@ -132,6 +147,7 @@ export class LoggingService {
|
|
|
132
147
|
* @param {string} message - Message to log
|
|
133
148
|
*/
|
|
134
149
|
success(message) {
|
|
150
|
+
message = this.#withContext(message);
|
|
135
151
|
this.writeLog(message, 'success');
|
|
136
152
|
console.log(`✅ ${message}`);
|
|
137
153
|
}
|
|
@@ -173,6 +189,25 @@ export class LoggingService {
|
|
|
173
189
|
return this.logFilePath;
|
|
174
190
|
}
|
|
175
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Redirect logging to a different file (agent mode uses ~/.arela/arela-agent.log
|
|
194
|
+
* instead of the cwd-bound arela-upload.log). Flushes pending entries first.
|
|
195
|
+
* @param {string} filePath - Absolute path to the new log file
|
|
196
|
+
*/
|
|
197
|
+
setLogFilePath(filePath) {
|
|
198
|
+
this.flush();
|
|
199
|
+
this.logFilePath = filePath;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Set a context prefix added to every file log entry (e.g. the active agent
|
|
204
|
+
* profile name, so one shared log stays grep-able per profile).
|
|
205
|
+
* @param {string|null} prefix - Context name, or null to clear
|
|
206
|
+
*/
|
|
207
|
+
setContext(prefix) {
|
|
208
|
+
this.context = prefix || null;
|
|
209
|
+
}
|
|
210
|
+
|
|
176
211
|
/**
|
|
177
212
|
* Check if verbose logging is enabled
|
|
178
213
|
* @returns {boolean} True if verbose logging is enabled
|
|
@@ -146,9 +146,12 @@ export class PipelineApiService {
|
|
|
146
146
|
/**
|
|
147
147
|
* Get the next available job for this server
|
|
148
148
|
* @param {string} serverId - Server identifier
|
|
149
|
+
* @param {Object} [options]
|
|
150
|
+
* @param {boolean} [options.throwOnError=false] - Rethrow request failures
|
|
151
|
+
* instead of swallowing them (agent mode needs this to back off a dead API)
|
|
149
152
|
* @returns {Promise<Object|null>} Job data or null if no jobs available
|
|
150
153
|
*/
|
|
151
|
-
async getNextJob(serverId) {
|
|
154
|
+
async getNextJob(serverId, { throwOnError = false } = {}) {
|
|
152
155
|
try {
|
|
153
156
|
const result = await this.#request(
|
|
154
157
|
'GET',
|
|
@@ -156,6 +159,9 @@ export class PipelineApiService {
|
|
|
156
159
|
);
|
|
157
160
|
return result;
|
|
158
161
|
} catch (error) {
|
|
162
|
+
if (throwOnError) {
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
159
165
|
logger.error(`❌ Failed to get next job: ${error.message}`);
|
|
160
166
|
return null;
|
|
161
167
|
}
|