@defai.digital/automatosx 5.6.35 → 5.7.2
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/CHANGELOG.md +142 -0
- package/README.md +44 -2
- package/dist/index.js +1129 -345
- package/examples/agents/.automatosx/abilities/accessibility.md +115 -0
- package/examples/agents/.automatosx/abilities/api-design.md +159 -0
- package/examples/agents/.automatosx/abilities/best-practices.md +102 -0
- package/examples/agents/.automatosx/abilities/caching-strategy.md +165 -0
- package/examples/agents/.automatosx/abilities/ci-cd.md +61 -0
- package/examples/agents/.automatosx/abilities/clean-code.md +398 -0
- package/examples/agents/.automatosx/abilities/code-generation.md +95 -0
- package/examples/agents/.automatosx/abilities/code-review.md +42 -0
- package/examples/agents/.automatosx/abilities/component-architecture.md +112 -0
- package/examples/agents/.automatosx/abilities/content-creation.md +97 -0
- package/examples/agents/.automatosx/abilities/data-modeling.md +171 -0
- package/examples/agents/.automatosx/abilities/data-validation.md +50 -0
- package/examples/agents/.automatosx/abilities/db-modeling.md +158 -0
- package/examples/agents/.automatosx/abilities/debugging.md +43 -0
- package/examples/agents/.automatosx/abilities/dependency-audit.md +60 -0
- package/examples/agents/.automatosx/abilities/design-patterns.md +437 -0
- package/examples/agents/.automatosx/abilities/design-system-implementation.md +126 -0
- package/examples/agents/.automatosx/abilities/documentation.md +54 -0
- package/examples/agents/.automatosx/abilities/error-analysis.md +107 -0
- package/examples/agents/.automatosx/abilities/etl-pipelines.md +44 -0
- package/examples/agents/.automatosx/abilities/feasibility-study.md +20 -0
- package/examples/agents/.automatosx/abilities/general-assistance.md +26 -0
- package/examples/agents/.automatosx/abilities/idea-evaluation.md +21 -0
- package/examples/agents/.automatosx/abilities/infra-as-code.md +57 -0
- package/examples/agents/.automatosx/abilities/job-orchestration.md +44 -0
- package/examples/agents/.automatosx/abilities/literature-review.md +19 -0
- package/examples/agents/.automatosx/abilities/logical-analysis.md +21 -0
- package/examples/agents/.automatosx/abilities/longform-report.md +25 -0
- package/examples/agents/.automatosx/abilities/mathematical-reasoning.md +170 -0
- package/examples/agents/.automatosx/abilities/mission-analysis.md +49 -0
- package/examples/agents/.automatosx/abilities/observability.md +61 -0
- package/examples/agents/.automatosx/abilities/orbital-mechanics.md +50 -0
- package/examples/agents/.automatosx/abilities/our-architecture-decisions.md +180 -0
- package/examples/agents/.automatosx/abilities/our-code-review-checklist.md +149 -0
- package/examples/agents/.automatosx/abilities/our-coding-standards.md +270 -0
- package/examples/agents/.automatosx/abilities/our-project-structure.md +183 -0
- package/examples/agents/.automatosx/abilities/performance-analysis.md +56 -0
- package/examples/agents/.automatosx/abilities/performance.md +80 -0
- package/examples/agents/.automatosx/abilities/problem-solving.md +50 -0
- package/examples/agents/.automatosx/abilities/propulsion-systems.md +50 -0
- package/examples/agents/.automatosx/abilities/quantum-algorithm-design.md +54 -0
- package/examples/agents/.automatosx/abilities/quantum-error-correction.md +56 -0
- package/examples/agents/.automatosx/abilities/quantum-frameworks-transpilation.md +53 -0
- package/examples/agents/.automatosx/abilities/quantum-noise-modeling.md +58 -0
- package/examples/agents/.automatosx/abilities/refactoring.md +223 -0
- package/examples/agents/.automatosx/abilities/release-strategy.md +58 -0
- package/examples/agents/.automatosx/abilities/risk-assessment.md +19 -0
- package/examples/agents/.automatosx/abilities/secrets-policy.md +61 -0
- package/examples/agents/.automatosx/abilities/secure-coding-review.md +51 -0
- package/examples/agents/.automatosx/abilities/security-audit.md +65 -0
- package/examples/agents/.automatosx/abilities/software-architecture.md +394 -0
- package/examples/agents/.automatosx/abilities/solid-principles.md +341 -0
- package/examples/agents/.automatosx/abilities/sql-optimization.md +84 -0
- package/examples/agents/.automatosx/abilities/state-management.md +96 -0
- package/examples/agents/.automatosx/abilities/task-planning.md +65 -0
- package/examples/agents/.automatosx/abilities/technical-writing.md +77 -0
- package/examples/agents/.automatosx/abilities/telemetry-diagnostics.md +51 -0
- package/examples/agents/.automatosx/abilities/testing.md +47 -0
- package/examples/agents/.automatosx/abilities/threat-modeling.md +49 -0
- package/examples/agents/.automatosx/abilities/troubleshooting.md +80 -0
- package/examples/agents/.automatosx/agents/aerospace-scientist.yaml +75 -0
- package/examples/agents/.automatosx/agents/backend.yaml +152 -0
- package/examples/agents/.automatosx/agents/ceo.yaml +63 -0
- package/examples/agents/.automatosx/agents/creative-marketer.yaml +121 -0
- package/examples/agents/.automatosx/agents/cto.yaml +72 -0
- package/examples/agents/.automatosx/agents/data-scientist.yaml +124 -0
- package/examples/agents/.automatosx/agents/data.yaml +77 -0
- package/examples/agents/.automatosx/agents/design.yaml +74 -0
- package/examples/agents/.automatosx/agents/devops.yaml +89 -0
- package/examples/agents/.automatosx/agents/frontend.yaml +139 -0
- package/examples/agents/.automatosx/agents/fullstack.yaml +151 -0
- package/examples/agents/.automatosx/agents/mobile.yaml +161 -0
- package/examples/agents/.automatosx/agents/product.yaml +72 -0
- package/examples/agents/.automatosx/agents/quality.yaml +79 -0
- package/examples/agents/.automatosx/agents/quantum-engineer.yaml +75 -0
- package/examples/agents/.automatosx/agents/researcher.yaml +71 -0
- package/examples/agents/.automatosx/agents/security.yaml +86 -0
- package/examples/agents/.automatosx/agents/stan.yaml +189 -0
- package/examples/agents/.automatosx/agents/writer.yaml +78 -0
- package/examples/agents/.automatosx/teams/business.yaml +56 -0
- package/examples/agents/.automatosx/teams/core.yaml +60 -0
- package/examples/agents/.automatosx/teams/design.yaml +58 -0
- package/examples/agents/.automatosx/teams/engineering.yaml +69 -0
- package/examples/agents/.automatosx/teams/research.yaml +56 -0
- package/examples/agents/.automatosx/templates/analyst.yaml +60 -0
- package/examples/agents/.automatosx/templates/assistant.yaml +48 -0
- package/examples/agents/.automatosx/templates/basic-agent.yaml +28 -0
- package/examples/agents/.automatosx/templates/code-reviewer.yaml +52 -0
- package/examples/agents/.automatosx/templates/debugger.yaml +63 -0
- package/examples/agents/.automatosx/templates/designer.yaml +69 -0
- package/examples/agents/.automatosx/templates/developer.yaml +60 -0
- package/examples/agents/.automatosx/templates/fullstack-developer.yaml +395 -0
- package/examples/agents/.automatosx/templates/qa-specialist.yaml +71 -0
- package/examples/agents/.claude/commands/ax-agent.md +37 -0
- package/examples/agents/.claude/commands/ax-clear.md +22 -0
- package/examples/agents/.claude/commands/ax-init.md +25 -0
- package/examples/agents/.claude/commands/ax-list.md +19 -0
- package/examples/agents/.claude/commands/ax-memory.md +25 -0
- package/examples/agents/.claude/commands/ax-status.md +24 -0
- package/examples/agents/.claude/commands/ax-update.md +28 -0
- package/examples/agents/.claude/mcp/automatosx.json +244 -0
- package/examples/agents/CLAUDE.md +262 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import * as actualFS from 'fs';
|
|
|
7
7
|
import { realpathSync as realpathSync$1, access, realpath, existsSync, readFileSync, constants, writeFileSync, readlinkSync, readdirSync, readdir as readdir$1, lstatSync, promises, mkdirSync } from 'fs';
|
|
8
8
|
import os2, { homedir, cpus } from 'os';
|
|
9
9
|
import { findUp } from 'find-up';
|
|
10
|
+
import { EventEmitter } from 'events';
|
|
10
11
|
import { exec, spawnSync } from 'child_process';
|
|
11
12
|
import yargs from 'yargs';
|
|
12
13
|
import { hideBin } from 'yargs/helpers';
|
|
@@ -17,7 +18,6 @@ import chalk27 from 'chalk';
|
|
|
17
18
|
import Table from 'cli-table3';
|
|
18
19
|
import * as sqliteVec from 'sqlite-vec';
|
|
19
20
|
import { Mutex } from 'async-mutex';
|
|
20
|
-
import { EventEmitter } from 'events';
|
|
21
21
|
import ora from 'ora';
|
|
22
22
|
import * as readline from 'readline';
|
|
23
23
|
import { promisify } from 'util';
|
|
@@ -490,6 +490,233 @@ var init_path_utils = __esm({
|
|
|
490
490
|
}
|
|
491
491
|
});
|
|
492
492
|
|
|
493
|
+
// src/utils/errors.ts
|
|
494
|
+
function toBaseError(error) {
|
|
495
|
+
if (error instanceof BaseError) {
|
|
496
|
+
return error;
|
|
497
|
+
}
|
|
498
|
+
if (error instanceof Error) {
|
|
499
|
+
return new BaseError(
|
|
500
|
+
error.message,
|
|
501
|
+
"E9999" /* UNKNOWN_ERROR */,
|
|
502
|
+
["Check error details for more information"],
|
|
503
|
+
{ originalError: error.name, stack: error.stack }
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
return new BaseError(
|
|
507
|
+
String(error),
|
|
508
|
+
"E9999" /* UNKNOWN_ERROR */,
|
|
509
|
+
["An unexpected error occurred"]
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
var BaseError, ConfigError, ProviderError;
|
|
513
|
+
var init_errors = __esm({
|
|
514
|
+
"src/utils/errors.ts"() {
|
|
515
|
+
init_esm_shims();
|
|
516
|
+
BaseError = class extends Error {
|
|
517
|
+
code;
|
|
518
|
+
suggestions;
|
|
519
|
+
context;
|
|
520
|
+
isOperational;
|
|
521
|
+
constructor(message, code, suggestions = [], context, isOperational = true) {
|
|
522
|
+
super(message);
|
|
523
|
+
this.name = this.constructor.name;
|
|
524
|
+
this.code = code;
|
|
525
|
+
this.suggestions = suggestions;
|
|
526
|
+
this.context = context;
|
|
527
|
+
this.isOperational = isOperational;
|
|
528
|
+
Error.captureStackTrace(this, this.constructor);
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Get formatted error message for display
|
|
532
|
+
*/
|
|
533
|
+
getFormattedMessage() {
|
|
534
|
+
let formatted = `[${this.code}] ${this.message}`;
|
|
535
|
+
if (this.suggestions.length > 0) {
|
|
536
|
+
formatted += "\n\nSuggestions:";
|
|
537
|
+
this.suggestions.forEach((suggestion, i) => {
|
|
538
|
+
formatted += `
|
|
539
|
+
${i + 1}. ${suggestion}`;
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
return formatted;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Get error details as object
|
|
546
|
+
*/
|
|
547
|
+
toJSON() {
|
|
548
|
+
return {
|
|
549
|
+
name: this.name,
|
|
550
|
+
code: this.code,
|
|
551
|
+
message: this.message,
|
|
552
|
+
suggestions: this.suggestions,
|
|
553
|
+
context: this.context,
|
|
554
|
+
isOperational: this.isOperational,
|
|
555
|
+
stack: this.stack
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
ConfigError = class _ConfigError extends BaseError {
|
|
560
|
+
constructor(message, code = "E1001" /* CONFIG_INVALID */, suggestions = [], context) {
|
|
561
|
+
super(message, code, suggestions, context);
|
|
562
|
+
}
|
|
563
|
+
static notFound(path6) {
|
|
564
|
+
return new _ConfigError(
|
|
565
|
+
`Configuration file not found: ${path6}`,
|
|
566
|
+
"E1000" /* CONFIG_NOT_FOUND */,
|
|
567
|
+
[
|
|
568
|
+
'Run "automatosx init" to create a new configuration',
|
|
569
|
+
"Specify a custom config path with --config option",
|
|
570
|
+
"Check that you are in a valid AutomatosX project directory"
|
|
571
|
+
],
|
|
572
|
+
{ path: path6 }
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
static invalid(reason, context) {
|
|
576
|
+
return new _ConfigError(
|
|
577
|
+
`Invalid configuration: ${reason}`,
|
|
578
|
+
"E1001" /* CONFIG_INVALID */,
|
|
579
|
+
[
|
|
580
|
+
"Check your automatosx.config.json for syntax errors",
|
|
581
|
+
"Validate against the schema in documentation",
|
|
582
|
+
'Reset to default with "automatosx init --force"'
|
|
583
|
+
],
|
|
584
|
+
context
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
static parseError(error, path6) {
|
|
588
|
+
return new _ConfigError(
|
|
589
|
+
`Failed to parse configuration: ${error.message}`,
|
|
590
|
+
"E1002" /* CONFIG_PARSE_ERROR */,
|
|
591
|
+
[
|
|
592
|
+
"Check JSON syntax in your config file",
|
|
593
|
+
"Use a JSON validator to find syntax errors",
|
|
594
|
+
'Reset to default with "automatosx init --force"'
|
|
595
|
+
],
|
|
596
|
+
{ path: path6, originalError: error.message }
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
ProviderError = class _ProviderError extends BaseError {
|
|
601
|
+
constructor(message, code = "E1301" /* PROVIDER_UNAVAILABLE */, suggestions = [], context) {
|
|
602
|
+
super(message, code, suggestions, context);
|
|
603
|
+
}
|
|
604
|
+
static notFound(providerName) {
|
|
605
|
+
return new _ProviderError(
|
|
606
|
+
`Provider "${providerName}" not found`,
|
|
607
|
+
"E1300" /* PROVIDER_NOT_FOUND */,
|
|
608
|
+
[
|
|
609
|
+
'Check available providers with "automatosx list providers"',
|
|
610
|
+
"Verify provider is enabled in automatosx.config.json",
|
|
611
|
+
"Install the provider CLI if not already installed"
|
|
612
|
+
],
|
|
613
|
+
{ providerName }
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
static unavailable(providerName, reason) {
|
|
617
|
+
const msg = reason ? `Provider "${providerName}" unavailable: ${reason}` : `Provider "${providerName}" is unavailable`;
|
|
618
|
+
return new _ProviderError(
|
|
619
|
+
msg,
|
|
620
|
+
"E1301" /* PROVIDER_UNAVAILABLE */,
|
|
621
|
+
[
|
|
622
|
+
"Check that the provider CLI is installed and in your PATH",
|
|
623
|
+
"Verify provider configuration in automatosx.config.json",
|
|
624
|
+
"Try running the provider CLI directly to diagnose issues",
|
|
625
|
+
'Check system status with "automatosx status --verbose"'
|
|
626
|
+
],
|
|
627
|
+
{ providerName, reason }
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
static timeout(providerName, timeoutMs) {
|
|
631
|
+
return new _ProviderError(
|
|
632
|
+
`Provider "${providerName}" timed out after ${timeoutMs}ms`,
|
|
633
|
+
"E1302" /* PROVIDER_TIMEOUT */,
|
|
634
|
+
[
|
|
635
|
+
"Increase timeout in automatosx.config.json",
|
|
636
|
+
"Try a simpler prompt or reduce context size",
|
|
637
|
+
"Check your network connection",
|
|
638
|
+
"Verify the provider service is responsive"
|
|
639
|
+
],
|
|
640
|
+
{ providerName, timeoutMs }
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
static noAvailableProviders() {
|
|
644
|
+
return new _ProviderError(
|
|
645
|
+
"No AI providers are available",
|
|
646
|
+
"E1306" /* PROVIDER_NO_AVAILABLE */,
|
|
647
|
+
[
|
|
648
|
+
"Install at least one provider CLI (Claude or Gemini)",
|
|
649
|
+
"Enable providers in automatosx.config.json",
|
|
650
|
+
'Check provider status with "automatosx status"',
|
|
651
|
+
"Verify provider CLIs are in your PATH"
|
|
652
|
+
]
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
static executionError(providerName, error) {
|
|
656
|
+
return new _ProviderError(
|
|
657
|
+
`Provider "${providerName}" execution failed: ${error.message}`,
|
|
658
|
+
"E1305" /* PROVIDER_EXEC_ERROR */,
|
|
659
|
+
[
|
|
660
|
+
"Check that the provider CLI is installed correctly",
|
|
661
|
+
"Verify you have necessary API keys configured",
|
|
662
|
+
"Try running the provider CLI directly to diagnose",
|
|
663
|
+
"Check error logs with --debug flag"
|
|
664
|
+
],
|
|
665
|
+
{ providerName, originalError: error.message }
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Create rate limit error with detailed context
|
|
670
|
+
* v5.7.0: Provider limit detection and rotation
|
|
671
|
+
*/
|
|
672
|
+
static rateLimit(providerName, limitWindow, resetAtMs, rawMessage, retryAfterSeconds) {
|
|
673
|
+
const resetDate = new Date(resetAtMs);
|
|
674
|
+
const hoursUntilReset = Math.ceil((resetAtMs - Date.now()) / (1e3 * 60 * 60));
|
|
675
|
+
const timeDesc = hoursUntilReset < 1 ? "less than 1 hour" : hoursUntilReset === 1 ? "1 hour" : `${hoursUntilReset} hours`;
|
|
676
|
+
return new _ProviderError(
|
|
677
|
+
`Provider "${providerName}" has hit its ${limitWindow} usage limit. Resets in ${timeDesc} at ${resetDate.toISOString()}`,
|
|
678
|
+
"E1303" /* PROVIDER_RATE_LIMIT */,
|
|
679
|
+
[
|
|
680
|
+
"AutomatosX will automatically switch to the next available provider",
|
|
681
|
+
`Wait until ${resetDate.toLocaleString()} for "${providerName}" to become available again`,
|
|
682
|
+
'Check current provider status with "ax provider limits"',
|
|
683
|
+
'Use "ax provider use <name>" to manually select a different provider'
|
|
684
|
+
],
|
|
685
|
+
{
|
|
686
|
+
providerName,
|
|
687
|
+
limitWindow,
|
|
688
|
+
resetAtMs,
|
|
689
|
+
rawMessage,
|
|
690
|
+
retryAfterSeconds
|
|
691
|
+
}
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Create error for all providers exhausted
|
|
696
|
+
* v5.7.0: Provider limit detection and rotation
|
|
697
|
+
*/
|
|
698
|
+
static allProvidersLimited(limitedProviders, soonestResetMs) {
|
|
699
|
+
const soonestResetDate = new Date(soonestResetMs);
|
|
700
|
+
const hoursUntilReset = Math.ceil((soonestResetMs - Date.now()) / (1e3 * 60 * 60));
|
|
701
|
+
return new _ProviderError(
|
|
702
|
+
`All providers have hit usage limits. Next provider resets in ${hoursUntilReset} hour(s) at ${soonestResetDate.toISOString()}`,
|
|
703
|
+
"E1306" /* PROVIDER_NO_AVAILABLE */,
|
|
704
|
+
[
|
|
705
|
+
`Wait until ${soonestResetDate.toLocaleString()} for providers to reset`,
|
|
706
|
+
'Check provider limits with "ax provider limits"',
|
|
707
|
+
'Use "ax provider use <name> --force" to override and try anyway',
|
|
708
|
+
"Consider spreading workload across different time periods"
|
|
709
|
+
],
|
|
710
|
+
{
|
|
711
|
+
limitedProviders,
|
|
712
|
+
soonestResetMs
|
|
713
|
+
}
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
|
|
493
720
|
// src/core/cache.ts
|
|
494
721
|
var TTLCache, ProviderResponseCache;
|
|
495
722
|
var init_cache = __esm({
|
|
@@ -868,8 +1095,8 @@ __export(path_resolver_exports, {
|
|
|
868
1095
|
PathResolver: () => PathResolver,
|
|
869
1096
|
detectProjectRoot: () => detectProjectRoot
|
|
870
1097
|
});
|
|
871
|
-
function isWindowsPath(
|
|
872
|
-
return /^[a-zA-Z]:[/\\]/.test(
|
|
1098
|
+
function isWindowsPath(path6) {
|
|
1099
|
+
return /^[a-zA-Z]:[/\\]/.test(path6);
|
|
873
1100
|
}
|
|
874
1101
|
async function detectProjectRoot(startDir = process.cwd()) {
|
|
875
1102
|
const gitDir = await findUp(".git", {
|
|
@@ -972,8 +1199,8 @@ var init_path_resolver = __esm({
|
|
|
972
1199
|
/**
|
|
973
1200
|
* Validate path is within allowed base directory
|
|
974
1201
|
*/
|
|
975
|
-
validatePath(
|
|
976
|
-
const normalized = normalizePath(resolvePath(
|
|
1202
|
+
validatePath(path6, baseDir) {
|
|
1203
|
+
const normalized = normalizePath(resolvePath(path6));
|
|
977
1204
|
const base = normalizePath(resolvePath(baseDir));
|
|
978
1205
|
const separator = "/";
|
|
979
1206
|
const pathWithSep = normalized + separator;
|
|
@@ -983,15 +1210,15 @@ var init_path_resolver = __esm({
|
|
|
983
1210
|
/**
|
|
984
1211
|
* Check if path is within allowed boundaries
|
|
985
1212
|
*/
|
|
986
|
-
isPathAllowed(
|
|
987
|
-
const boundary = this.checkBoundaries(
|
|
1213
|
+
isPathAllowed(path6) {
|
|
1214
|
+
const boundary = this.checkBoundaries(path6);
|
|
988
1215
|
return boundary === "agent_workspace" || boundary === "user_project";
|
|
989
1216
|
}
|
|
990
1217
|
/**
|
|
991
1218
|
* Check which boundary a path belongs to
|
|
992
1219
|
*/
|
|
993
|
-
checkBoundaries(
|
|
994
|
-
const normalized = resolvePath(
|
|
1220
|
+
checkBoundaries(path6) {
|
|
1221
|
+
const normalized = resolvePath(path6);
|
|
995
1222
|
if (this.validatePath(normalized, this.config.agentWorkspace)) {
|
|
996
1223
|
return "agent_workspace";
|
|
997
1224
|
}
|
|
@@ -1009,15 +1236,15 @@ var init_path_resolver = __esm({
|
|
|
1009
1236
|
/**
|
|
1010
1237
|
* Get relative path from project root
|
|
1011
1238
|
*/
|
|
1012
|
-
getRelativeToProject(
|
|
1013
|
-
const normalized = resolvePath(
|
|
1239
|
+
getRelativeToProject(path6) {
|
|
1240
|
+
const normalized = resolvePath(path6);
|
|
1014
1241
|
return normalizePath(getRelativePath(this.config.projectDir, normalized));
|
|
1015
1242
|
}
|
|
1016
1243
|
/**
|
|
1017
1244
|
* Get relative path from working directory
|
|
1018
1245
|
*/
|
|
1019
|
-
getRelativeToWorking(
|
|
1020
|
-
const normalized = resolvePath(
|
|
1246
|
+
getRelativeToWorking(path6) {
|
|
1247
|
+
const normalized = resolvePath(path6);
|
|
1021
1248
|
return normalizePath(getRelativePath(this.config.workingDir, normalized));
|
|
1022
1249
|
}
|
|
1023
1250
|
/**
|
|
@@ -1036,11 +1263,11 @@ var init_path_resolver = __esm({
|
|
|
1036
1263
|
* Validate path is within project boundaries
|
|
1037
1264
|
* @throws PathError if outside project
|
|
1038
1265
|
*/
|
|
1039
|
-
validateInProject(
|
|
1040
|
-
const boundary = this.checkBoundaries(
|
|
1266
|
+
validateInProject(path6) {
|
|
1267
|
+
const boundary = this.checkBoundaries(path6);
|
|
1041
1268
|
if (boundary === "outside_boundaries" || boundary === "system_restricted") {
|
|
1042
1269
|
throw new PathError("Path outside project directory", {
|
|
1043
|
-
path:
|
|
1270
|
+
path: path6,
|
|
1044
1271
|
projectDir: this.config.projectDir,
|
|
1045
1272
|
boundary
|
|
1046
1273
|
});
|
|
@@ -1064,6 +1291,347 @@ var init_path_resolver = __esm({
|
|
|
1064
1291
|
};
|
|
1065
1292
|
}
|
|
1066
1293
|
});
|
|
1294
|
+
function getProviderLimitManager(stateDirectory) {
|
|
1295
|
+
return ProviderLimitManager.getInstance(stateDirectory);
|
|
1296
|
+
}
|
|
1297
|
+
var ProviderLimitManager;
|
|
1298
|
+
var init_provider_limit_manager = __esm({
|
|
1299
|
+
"src/core/provider-limit-manager.ts"() {
|
|
1300
|
+
init_esm_shims();
|
|
1301
|
+
init_logger();
|
|
1302
|
+
ProviderLimitManager = class _ProviderLimitManager extends EventEmitter {
|
|
1303
|
+
static instance = null;
|
|
1304
|
+
stateFilePath;
|
|
1305
|
+
providerStates;
|
|
1306
|
+
manualOverride;
|
|
1307
|
+
providerConfigs;
|
|
1308
|
+
initialized = false;
|
|
1309
|
+
// Performance tracking
|
|
1310
|
+
metrics = {
|
|
1311
|
+
checksPerformed: 0,
|
|
1312
|
+
limitsRecorded: 0,
|
|
1313
|
+
limitsCleared: 0,
|
|
1314
|
+
cacheHits: 0
|
|
1315
|
+
};
|
|
1316
|
+
constructor(stateDirectory) {
|
|
1317
|
+
super();
|
|
1318
|
+
this.stateFilePath = path2.join(stateDirectory, "provider-limits.json");
|
|
1319
|
+
this.providerStates = /* @__PURE__ */ new Map();
|
|
1320
|
+
this.providerConfigs = /* @__PURE__ */ new Map();
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Get singleton instance
|
|
1324
|
+
*/
|
|
1325
|
+
static getInstance(stateDirectory = ".automatosx/state") {
|
|
1326
|
+
if (!_ProviderLimitManager.instance) {
|
|
1327
|
+
_ProviderLimitManager.instance = new _ProviderLimitManager(stateDirectory);
|
|
1328
|
+
}
|
|
1329
|
+
return _ProviderLimitManager.instance;
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Reset singleton instance (for testing)
|
|
1333
|
+
*/
|
|
1334
|
+
static resetInstance() {
|
|
1335
|
+
_ProviderLimitManager.instance = null;
|
|
1336
|
+
}
|
|
1337
|
+
/**
|
|
1338
|
+
* Initialize manager: load state from disk
|
|
1339
|
+
*/
|
|
1340
|
+
async initialize() {
|
|
1341
|
+
if (this.initialized) {
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
try {
|
|
1345
|
+
await this.loadState();
|
|
1346
|
+
this.initialized = true;
|
|
1347
|
+
logger.debug("ProviderLimitManager initialized", {
|
|
1348
|
+
stateFile: this.stateFilePath,
|
|
1349
|
+
trackedProviders: this.providerStates.size
|
|
1350
|
+
});
|
|
1351
|
+
} catch (error) {
|
|
1352
|
+
logger.warn("Failed to initialize ProviderLimitManager", {
|
|
1353
|
+
error: error.message
|
|
1354
|
+
});
|
|
1355
|
+
this.initialized = true;
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Register provider limit tracking configuration
|
|
1360
|
+
*/
|
|
1361
|
+
registerProvider(providerName, config) {
|
|
1362
|
+
this.providerConfigs.set(providerName, config);
|
|
1363
|
+
logger.debug("Provider limit tracking registered", {
|
|
1364
|
+
provider: providerName,
|
|
1365
|
+
window: config.window,
|
|
1366
|
+
enabled: config.enabled
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Record a limit hit for a provider
|
|
1371
|
+
*/
|
|
1372
|
+
async recordLimitHit(providerName, limitWindow, resetAtMs, metadata) {
|
|
1373
|
+
this.metrics.limitsRecorded++;
|
|
1374
|
+
const state = {
|
|
1375
|
+
status: "limited",
|
|
1376
|
+
window: limitWindow,
|
|
1377
|
+
detectedAtMs: Date.now(),
|
|
1378
|
+
resetAtMs,
|
|
1379
|
+
reason: metadata?.reason,
|
|
1380
|
+
rawMessage: metadata?.rawMessage,
|
|
1381
|
+
retryAfterSeconds: metadata?.retryAfterSeconds,
|
|
1382
|
+
manualHold: false
|
|
1383
|
+
};
|
|
1384
|
+
this.providerStates.set(providerName, state);
|
|
1385
|
+
await this.saveState();
|
|
1386
|
+
this.emit("limit-hit", {
|
|
1387
|
+
providerName,
|
|
1388
|
+
limitWindow,
|
|
1389
|
+
resetAtMs,
|
|
1390
|
+
reason: metadata?.reason
|
|
1391
|
+
});
|
|
1392
|
+
logger.warn("Provider limit hit recorded", {
|
|
1393
|
+
provider: providerName,
|
|
1394
|
+
window: limitWindow,
|
|
1395
|
+
resetAt: new Date(resetAtMs).toISOString(),
|
|
1396
|
+
reason: metadata?.reason
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
/**
|
|
1400
|
+
* Check if a provider is currently limited (O(1) operation)
|
|
1401
|
+
*/
|
|
1402
|
+
isProviderLimited(providerName, now = Date.now()) {
|
|
1403
|
+
this.metrics.checksPerformed++;
|
|
1404
|
+
if (this.manualOverride && this.manualOverride.provider === providerName) {
|
|
1405
|
+
return { isLimited: false };
|
|
1406
|
+
}
|
|
1407
|
+
const state = this.providerStates.get(providerName);
|
|
1408
|
+
if (!state || state.status !== "limited") {
|
|
1409
|
+
this.metrics.cacheHits++;
|
|
1410
|
+
return { isLimited: false };
|
|
1411
|
+
}
|
|
1412
|
+
if (now >= state.resetAtMs) {
|
|
1413
|
+
void this.clearLimit(providerName, "expired");
|
|
1414
|
+
return { isLimited: false };
|
|
1415
|
+
}
|
|
1416
|
+
const remainingMs = state.resetAtMs - now;
|
|
1417
|
+
return {
|
|
1418
|
+
isLimited: true,
|
|
1419
|
+
remainingMs,
|
|
1420
|
+
resetAtMs: state.resetAtMs,
|
|
1421
|
+
reason: state.reason
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
/**
|
|
1425
|
+
* Clear limit for a provider
|
|
1426
|
+
*/
|
|
1427
|
+
async clearLimit(providerName, reason = "manual") {
|
|
1428
|
+
const state = this.providerStates.get(providerName);
|
|
1429
|
+
if (!state || state.status !== "limited") {
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
this.metrics.limitsCleared++;
|
|
1433
|
+
this.providerStates.delete(providerName);
|
|
1434
|
+
await this.saveState();
|
|
1435
|
+
this.emit("limit-cleared", {
|
|
1436
|
+
providerName,
|
|
1437
|
+
reason
|
|
1438
|
+
});
|
|
1439
|
+
logger.info("Provider limit cleared", {
|
|
1440
|
+
provider: providerName,
|
|
1441
|
+
reason
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Refresh expired limits (background task)
|
|
1446
|
+
*/
|
|
1447
|
+
async refreshExpired() {
|
|
1448
|
+
const now = Date.now();
|
|
1449
|
+
const expiredProviders = [];
|
|
1450
|
+
for (const [providerName, state] of this.providerStates.entries()) {
|
|
1451
|
+
if (state.status === "limited" && now >= state.resetAtMs) {
|
|
1452
|
+
expiredProviders.push(providerName);
|
|
1453
|
+
await this.clearLimit(providerName, "expired");
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
if (expiredProviders.length > 0) {
|
|
1457
|
+
logger.debug("Expired provider limits refreshed", {
|
|
1458
|
+
providers: expiredProviders,
|
|
1459
|
+
count: expiredProviders.length
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
return expiredProviders;
|
|
1463
|
+
}
|
|
1464
|
+
/**
|
|
1465
|
+
* Set manual override for a specific provider
|
|
1466
|
+
*/
|
|
1467
|
+
async setManualOverride(provider, expiresAtMs) {
|
|
1468
|
+
this.manualOverride = {
|
|
1469
|
+
provider,
|
|
1470
|
+
setAtMs: Date.now(),
|
|
1471
|
+
expiresAtMs
|
|
1472
|
+
};
|
|
1473
|
+
await this.saveState();
|
|
1474
|
+
this.emit("override-set", {
|
|
1475
|
+
provider,
|
|
1476
|
+
expiresAtMs
|
|
1477
|
+
});
|
|
1478
|
+
logger.info("Manual provider override set", {
|
|
1479
|
+
provider,
|
|
1480
|
+
expiresAt: expiresAtMs ? new Date(expiresAtMs).toISOString() : "never"
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
/**
|
|
1484
|
+
* Clear manual override
|
|
1485
|
+
*/
|
|
1486
|
+
async clearManualOverride() {
|
|
1487
|
+
if (!this.manualOverride) {
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
const provider = this.manualOverride.provider;
|
|
1491
|
+
this.manualOverride = void 0;
|
|
1492
|
+
await this.saveState();
|
|
1493
|
+
this.emit("override-cleared", { provider });
|
|
1494
|
+
logger.info("Manual provider override cleared", { provider });
|
|
1495
|
+
}
|
|
1496
|
+
/**
|
|
1497
|
+
* Get current manual override
|
|
1498
|
+
*/
|
|
1499
|
+
getManualOverride() {
|
|
1500
|
+
if (this.manualOverride && this.manualOverride.expiresAtMs) {
|
|
1501
|
+
if (Date.now() >= this.manualOverride.expiresAtMs) {
|
|
1502
|
+
void this.clearManualOverride();
|
|
1503
|
+
return void 0;
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
return this.manualOverride;
|
|
1507
|
+
}
|
|
1508
|
+
/**
|
|
1509
|
+
* Get all provider states (for status commands)
|
|
1510
|
+
*/
|
|
1511
|
+
getAllStates() {
|
|
1512
|
+
return new Map(this.providerStates);
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Get next reset time across all providers
|
|
1516
|
+
*/
|
|
1517
|
+
getNextReset() {
|
|
1518
|
+
let soonest = null;
|
|
1519
|
+
for (const [providerName, state] of this.providerStates.entries()) {
|
|
1520
|
+
if (state.status === "limited") {
|
|
1521
|
+
if (!soonest || state.resetAtMs < soonest.resetAtMs) {
|
|
1522
|
+
soonest = { providerName, resetAtMs: state.resetAtMs };
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
return soonest;
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Calculate reset time for a provider based on its configuration
|
|
1530
|
+
*/
|
|
1531
|
+
calculateResetTime(providerName, limitWindow, retryAfterSeconds) {
|
|
1532
|
+
const config = this.providerConfigs.get(providerName);
|
|
1533
|
+
const now = Date.now();
|
|
1534
|
+
if (retryAfterSeconds) {
|
|
1535
|
+
return now + retryAfterSeconds * 1e3;
|
|
1536
|
+
}
|
|
1537
|
+
const resetHourUtc = config?.resetHourUtc ?? 0;
|
|
1538
|
+
if (limitWindow === "daily") {
|
|
1539
|
+
const tomorrow = /* @__PURE__ */ new Date();
|
|
1540
|
+
tomorrow.setUTCDate(tomorrow.getUTCDate() + 1);
|
|
1541
|
+
tomorrow.setUTCHours(resetHourUtc, 0, 0, 0);
|
|
1542
|
+
return tomorrow.getTime();
|
|
1543
|
+
}
|
|
1544
|
+
if (limitWindow === "weekly") {
|
|
1545
|
+
const nextWeek = /* @__PURE__ */ new Date();
|
|
1546
|
+
nextWeek.setUTCDate(nextWeek.getUTCDate() + 7);
|
|
1547
|
+
nextWeek.setUTCHours(resetHourUtc, 0, 0, 0);
|
|
1548
|
+
return nextWeek.getTime();
|
|
1549
|
+
}
|
|
1550
|
+
if (limitWindow === "custom" && config?.customResetMs) {
|
|
1551
|
+
return now + config.customResetMs;
|
|
1552
|
+
}
|
|
1553
|
+
return now + 24 * 60 * 60 * 1e3;
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1556
|
+
* Get performance metrics
|
|
1557
|
+
*/
|
|
1558
|
+
getMetrics() {
|
|
1559
|
+
return { ...this.metrics };
|
|
1560
|
+
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Serialize state for JSON export
|
|
1563
|
+
*/
|
|
1564
|
+
serialize() {
|
|
1565
|
+
const providers = {};
|
|
1566
|
+
for (const [name, state] of this.providerStates.entries()) {
|
|
1567
|
+
providers[name] = state;
|
|
1568
|
+
}
|
|
1569
|
+
return {
|
|
1570
|
+
schemaVersion: 1,
|
|
1571
|
+
providers,
|
|
1572
|
+
manualOverride: this.manualOverride
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
// ========================================
|
|
1576
|
+
// Private Methods: Persistence
|
|
1577
|
+
// ========================================
|
|
1578
|
+
/**
|
|
1579
|
+
* Load state from disk
|
|
1580
|
+
*/
|
|
1581
|
+
async loadState() {
|
|
1582
|
+
try {
|
|
1583
|
+
const data = await promises.readFile(this.stateFilePath, "utf-8");
|
|
1584
|
+
const parsed = JSON.parse(data);
|
|
1585
|
+
if (parsed.schemaVersion !== 1) {
|
|
1586
|
+
logger.warn("Unsupported state schema version", {
|
|
1587
|
+
version: parsed.schemaVersion
|
|
1588
|
+
});
|
|
1589
|
+
return;
|
|
1590
|
+
}
|
|
1591
|
+
for (const [name, state] of Object.entries(parsed.providers)) {
|
|
1592
|
+
this.providerStates.set(name, state);
|
|
1593
|
+
}
|
|
1594
|
+
if (parsed.manualOverride) {
|
|
1595
|
+
this.manualOverride = parsed.manualOverride;
|
|
1596
|
+
}
|
|
1597
|
+
logger.debug("Provider limit state loaded", {
|
|
1598
|
+
providers: this.providerStates.size,
|
|
1599
|
+
hasOverride: !!this.manualOverride
|
|
1600
|
+
});
|
|
1601
|
+
} catch (error) {
|
|
1602
|
+
if (error.code === "ENOENT") {
|
|
1603
|
+
logger.debug("No existing provider limit state found (new installation)");
|
|
1604
|
+
} else {
|
|
1605
|
+
throw error;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* Save state to disk (atomic write)
|
|
1611
|
+
*/
|
|
1612
|
+
async saveState() {
|
|
1613
|
+
try {
|
|
1614
|
+
const dir = path2.dirname(this.stateFilePath);
|
|
1615
|
+
await promises.mkdir(dir, { recursive: true });
|
|
1616
|
+
const state = this.serialize();
|
|
1617
|
+
const json = JSON.stringify(state, null, 2);
|
|
1618
|
+
const tempPath = `${this.stateFilePath}.tmp`;
|
|
1619
|
+
await promises.writeFile(tempPath, json, "utf-8");
|
|
1620
|
+
await promises.rename(tempPath, this.stateFilePath);
|
|
1621
|
+
logger.debug("Provider limit state saved", {
|
|
1622
|
+
file: this.stateFilePath,
|
|
1623
|
+
providers: this.providerStates.size
|
|
1624
|
+
});
|
|
1625
|
+
} catch (error) {
|
|
1626
|
+
logger.error("Failed to save provider limit state", {
|
|
1627
|
+
error: error.message,
|
|
1628
|
+
file: this.stateFilePath
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1634
|
+
});
|
|
1067
1635
|
|
|
1068
1636
|
// src/providers/retry-errors.ts
|
|
1069
1637
|
function containsErrorPattern(message, patterns) {
|
|
@@ -1189,10 +1757,10 @@ function findOnPathUnix(cmdBase) {
|
|
|
1189
1757
|
try {
|
|
1190
1758
|
const which = spawnSync("which", [cmdBase], { timeout: 3e3 });
|
|
1191
1759
|
if (which.status === 0) {
|
|
1192
|
-
const
|
|
1193
|
-
if (
|
|
1194
|
-
logger.debug("Found via which", { cmdBase, path:
|
|
1195
|
-
return { found: true, path:
|
|
1760
|
+
const path6 = which.stdout.toString().trim();
|
|
1761
|
+
if (path6) {
|
|
1762
|
+
logger.debug("Found via which", { cmdBase, path: path6 });
|
|
1763
|
+
return { found: true, path: path6 };
|
|
1196
1764
|
}
|
|
1197
1765
|
}
|
|
1198
1766
|
} catch (error) {
|
|
@@ -1314,6 +1882,25 @@ var init_provider_cache = __esm({
|
|
|
1314
1882
|
});
|
|
1315
1883
|
return entry.available;
|
|
1316
1884
|
}
|
|
1885
|
+
/**
|
|
1886
|
+
* Get cache entry with metadata (age, timestamp).
|
|
1887
|
+
* v5.7.2: Added for enhanced metrics tracking.
|
|
1888
|
+
*/
|
|
1889
|
+
getWithMetadata(providerName, ttl = 3e4) {
|
|
1890
|
+
const entry = this.cache.get(providerName);
|
|
1891
|
+
if (!entry) {
|
|
1892
|
+
return void 0;
|
|
1893
|
+
}
|
|
1894
|
+
const age = Date.now() - entry.timestamp;
|
|
1895
|
+
if (age >= ttl) {
|
|
1896
|
+
this.cache.delete(providerName);
|
|
1897
|
+
return void 0;
|
|
1898
|
+
}
|
|
1899
|
+
return {
|
|
1900
|
+
available: entry.available,
|
|
1901
|
+
age
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1317
1904
|
/**
|
|
1318
1905
|
* Set availability status for a provider.
|
|
1319
1906
|
*
|
|
@@ -1531,19 +2118,21 @@ var init_base_provider = __esm({
|
|
|
1531
2118
|
}
|
|
1532
2119
|
const ttl = this.calculateAdaptiveTTL();
|
|
1533
2120
|
try {
|
|
1534
|
-
const sharedCached = providerCache.
|
|
2121
|
+
const sharedCached = providerCache.getWithMetadata(this.config.name, ttl);
|
|
1535
2122
|
if (sharedCached !== void 0) {
|
|
1536
2123
|
this.cacheMetrics.availabilityHits++;
|
|
1537
2124
|
this.availabilityCacheMetrics.lastHit = Date.now();
|
|
2125
|
+
this.availabilityCacheMetrics.totalAge += sharedCached.age;
|
|
1538
2126
|
this.availabilityCacheMetrics.hitCount++;
|
|
1539
2127
|
logger.debug(`Using shared cache for ${this.config.name}`, {
|
|
1540
2128
|
provider: this.config.name,
|
|
1541
|
-
available: sharedCached,
|
|
2129
|
+
available: sharedCached.available,
|
|
1542
2130
|
source: "shared-cache",
|
|
2131
|
+
cacheAge: sharedCached.age,
|
|
1543
2132
|
cacheTTL: ttl,
|
|
1544
2133
|
cacheHits: this.cacheMetrics.availabilityHits
|
|
1545
2134
|
});
|
|
1546
|
-
return sharedCached;
|
|
2135
|
+
return sharedCached.available;
|
|
1547
2136
|
}
|
|
1548
2137
|
} catch (error) {
|
|
1549
2138
|
logger.warn(`Failed to read shared cache for ${this.config.name}, falling back to instance cache`, {
|
|
@@ -1712,25 +2301,25 @@ var init_base_provider = __esm({
|
|
|
1712
2301
|
* @param path File path to check
|
|
1713
2302
|
* @returns true if path exists and is valid
|
|
1714
2303
|
*/
|
|
1715
|
-
checkPathExists(
|
|
2304
|
+
checkPathExists(path6) {
|
|
1716
2305
|
try {
|
|
1717
|
-
if (
|
|
1718
|
-
logger.warn("Path traversal pattern detected (..)", { path:
|
|
2306
|
+
if (path6.includes("..")) {
|
|
2307
|
+
logger.warn("Path traversal pattern detected (..)", { path: path6 });
|
|
1719
2308
|
return false;
|
|
1720
2309
|
}
|
|
1721
|
-
if (
|
|
1722
|
-
logger.warn("Home directory shortcut detected (~)", { path:
|
|
2310
|
+
if (path6.startsWith("~") || path6.includes("~")) {
|
|
2311
|
+
logger.warn("Home directory shortcut detected (~)", { path: path6 });
|
|
1723
2312
|
return false;
|
|
1724
2313
|
}
|
|
1725
2314
|
const DANGEROUS_CHARS = /[;|&$`<>{}[\]'"\\]/;
|
|
1726
|
-
if (DANGEROUS_CHARS.test(
|
|
1727
|
-
logger.warn("Dangerous shell characters detected in path", { path:
|
|
2315
|
+
if (DANGEROUS_CHARS.test(path6)) {
|
|
2316
|
+
logger.warn("Dangerous shell characters detected in path", { path: path6 });
|
|
1728
2317
|
return false;
|
|
1729
2318
|
}
|
|
1730
|
-
return existsSync(
|
|
2319
|
+
return existsSync(path6);
|
|
1731
2320
|
} catch (error) {
|
|
1732
2321
|
logger.debug(`Error checking path existence`, {
|
|
1733
|
-
path:
|
|
2322
|
+
path: path6,
|
|
1734
2323
|
error: error.message
|
|
1735
2324
|
});
|
|
1736
2325
|
return false;
|
|
@@ -2444,6 +3033,8 @@ var init_claude_provider = __esm({
|
|
|
2444
3033
|
init_retry_errors();
|
|
2445
3034
|
init_environment();
|
|
2446
3035
|
init_logger();
|
|
3036
|
+
init_errors();
|
|
3037
|
+
init_provider_limit_manager();
|
|
2447
3038
|
ClaudeProvider = class extends BaseProvider {
|
|
2448
3039
|
constructor(config) {
|
|
2449
3040
|
super(config);
|
|
@@ -2659,11 +3250,8 @@ Details: ${errorMsg}`
|
|
|
2659
3250
|
`Authentication failed: Please check your Claude API credentials.
|
|
2660
3251
|
Details: ${errorMsg}`
|
|
2661
3252
|
));
|
|
2662
|
-
} else if (
|
|
2663
|
-
reject(
|
|
2664
|
-
`Rate limit exceeded: Please wait a moment and try again.
|
|
2665
|
-
Details: ${errorMsg}`
|
|
2666
|
-
));
|
|
3253
|
+
} else if (this.isRateLimitError(errorMsg)) {
|
|
3254
|
+
reject(this.createRateLimitError(errorMsg));
|
|
2667
3255
|
} else {
|
|
2668
3256
|
reject(new Error(`Claude CLI exited with code ${code}: ${errorMsg}`));
|
|
2669
3257
|
}
|
|
@@ -2754,6 +3342,41 @@ Try again or use --timeout option to increase the limit.`
|
|
|
2754
3342
|
supportsStreaming() {
|
|
2755
3343
|
return false;
|
|
2756
3344
|
}
|
|
3345
|
+
/**
|
|
3346
|
+
* Check if error message indicates a rate limit / usage limit error
|
|
3347
|
+
* v5.7.0: Provider limit detection patterns for Claude
|
|
3348
|
+
*/
|
|
3349
|
+
isRateLimitError(errorMsg) {
|
|
3350
|
+
const lowerMsg = errorMsg.toLowerCase();
|
|
3351
|
+
return lowerMsg.includes("rate limit") || lowerMsg.includes("quota") || lowerMsg.includes("limit for today") || lowerMsg.includes("limit for this week") || lowerMsg.includes("anthropicusagelimit") || lowerMsg.includes("usage limit") || lowerMsg.includes("exceeded") && (lowerMsg.includes("limit") || lowerMsg.includes("quota"));
|
|
3352
|
+
}
|
|
3353
|
+
/**
|
|
3354
|
+
* Create detailed rate limit error with automatic recording
|
|
3355
|
+
* v5.7.0: Provider limit detection and auto-rotation
|
|
3356
|
+
*/
|
|
3357
|
+
createRateLimitError(errorMsg) {
|
|
3358
|
+
const limitWindow = errorMsg.toLowerCase().includes("week") ? "weekly" : "daily";
|
|
3359
|
+
const limitManager = getProviderLimitManager();
|
|
3360
|
+
const resetAtMs = limitManager.calculateResetTime(
|
|
3361
|
+
this.config.name,
|
|
3362
|
+
limitWindow
|
|
3363
|
+
);
|
|
3364
|
+
void limitManager.recordLimitHit(
|
|
3365
|
+
this.config.name,
|
|
3366
|
+
limitWindow,
|
|
3367
|
+
resetAtMs,
|
|
3368
|
+
{
|
|
3369
|
+
reason: "usage_limit_exceeded",
|
|
3370
|
+
rawMessage: errorMsg
|
|
3371
|
+
}
|
|
3372
|
+
);
|
|
3373
|
+
return ProviderError.rateLimit(
|
|
3374
|
+
this.config.name,
|
|
3375
|
+
limitWindow,
|
|
3376
|
+
resetAtMs,
|
|
3377
|
+
errorMsg
|
|
3378
|
+
);
|
|
3379
|
+
}
|
|
2757
3380
|
};
|
|
2758
3381
|
}
|
|
2759
3382
|
});
|
|
@@ -2770,6 +3393,8 @@ var init_gemini_provider = __esm({
|
|
|
2770
3393
|
init_base_provider();
|
|
2771
3394
|
init_retry_errors();
|
|
2772
3395
|
init_logger();
|
|
3396
|
+
init_errors();
|
|
3397
|
+
init_provider_limit_manager();
|
|
2773
3398
|
GeminiProvider = class extends BaseProvider {
|
|
2774
3399
|
constructor(config) {
|
|
2775
3400
|
super(config);
|
|
@@ -2964,7 +3589,12 @@ This is a placeholder response. Set AUTOMATOSX_MOCK_PROVIDERS=false to use real
|
|
|
2964
3589
|
return;
|
|
2965
3590
|
}
|
|
2966
3591
|
if (code !== 0) {
|
|
2967
|
-
|
|
3592
|
+
const errorMsg = stderr || "No error message";
|
|
3593
|
+
if (this.isRateLimitError(errorMsg)) {
|
|
3594
|
+
reject(this.createRateLimitError(errorMsg));
|
|
3595
|
+
} else {
|
|
3596
|
+
reject(new Error(`Gemini CLI exited with code ${code}: ${errorMsg}`));
|
|
3597
|
+
}
|
|
2968
3598
|
} else {
|
|
2969
3599
|
resolve10({ content: stdout.trim() });
|
|
2970
3600
|
}
|
|
@@ -3028,6 +3658,44 @@ This is a placeholder response. Set AUTOMATOSX_MOCK_PROVIDERS=false to use real
|
|
|
3028
3658
|
supportsStreaming() {
|
|
3029
3659
|
return false;
|
|
3030
3660
|
}
|
|
3661
|
+
/**
|
|
3662
|
+
* Check if error message indicates a rate limit / usage limit error
|
|
3663
|
+
* v5.7.0: Provider limit detection patterns for Gemini
|
|
3664
|
+
*/
|
|
3665
|
+
isRateLimitError(errorMsg) {
|
|
3666
|
+
const lowerMsg = errorMsg.toLowerCase();
|
|
3667
|
+
return lowerMsg.includes("resource_exhausted") || // gRPC error code
|
|
3668
|
+
lowerMsg.includes("429") || // HTTP status
|
|
3669
|
+
lowerMsg.includes("ratelimitexceeded") || // Gemini API error
|
|
3670
|
+
lowerMsg.includes("quota exceeded") || lowerMsg.includes("quota") || lowerMsg.includes("rate limit") || lowerMsg.includes("too many requests");
|
|
3671
|
+
}
|
|
3672
|
+
/**
|
|
3673
|
+
* Create detailed rate limit error with automatic recording
|
|
3674
|
+
* v5.7.0: Provider limit detection and auto-rotation
|
|
3675
|
+
*/
|
|
3676
|
+
createRateLimitError(errorMsg) {
|
|
3677
|
+
const limitWindow = "daily";
|
|
3678
|
+
const limitManager = getProviderLimitManager();
|
|
3679
|
+
const resetAtMs = limitManager.calculateResetTime(
|
|
3680
|
+
this.config.name,
|
|
3681
|
+
limitWindow
|
|
3682
|
+
);
|
|
3683
|
+
void limitManager.recordLimitHit(
|
|
3684
|
+
this.config.name,
|
|
3685
|
+
limitWindow,
|
|
3686
|
+
resetAtMs,
|
|
3687
|
+
{
|
|
3688
|
+
reason: "quota_exceeded",
|
|
3689
|
+
rawMessage: errorMsg
|
|
3690
|
+
}
|
|
3691
|
+
);
|
|
3692
|
+
return ProviderError.rateLimit(
|
|
3693
|
+
this.config.name,
|
|
3694
|
+
limitWindow,
|
|
3695
|
+
resetAtMs,
|
|
3696
|
+
errorMsg
|
|
3697
|
+
);
|
|
3698
|
+
}
|
|
3031
3699
|
};
|
|
3032
3700
|
}
|
|
3033
3701
|
});
|
|
@@ -3044,6 +3712,8 @@ var init_openai_provider = __esm({
|
|
|
3044
3712
|
init_base_provider();
|
|
3045
3713
|
init_retry_errors();
|
|
3046
3714
|
init_logger();
|
|
3715
|
+
init_errors();
|
|
3716
|
+
init_provider_limit_manager();
|
|
3047
3717
|
OpenAIProvider = class extends BaseProvider {
|
|
3048
3718
|
constructor(config) {
|
|
3049
3719
|
super(config);
|
|
@@ -3243,7 +3913,12 @@ This is a placeholder response. Set AUTOMATOSX_MOCK_PROVIDERS=false to use real
|
|
|
3243
3913
|
return;
|
|
3244
3914
|
}
|
|
3245
3915
|
if (code !== 0) {
|
|
3246
|
-
|
|
3916
|
+
const errorMsg = stderr || "No error message";
|
|
3917
|
+
if (this.isRateLimitError(errorMsg)) {
|
|
3918
|
+
reject(this.createRateLimitError(errorMsg));
|
|
3919
|
+
} else {
|
|
3920
|
+
reject(new Error(`OpenAI CLI exited with code ${code}: ${errorMsg}`));
|
|
3921
|
+
}
|
|
3247
3922
|
} else {
|
|
3248
3923
|
resolve10({ content: stdout.trim() });
|
|
3249
3924
|
}
|
|
@@ -3524,6 +4199,43 @@ This is a placeholder streaming response.`;
|
|
|
3524
4199
|
});
|
|
3525
4200
|
});
|
|
3526
4201
|
}
|
|
4202
|
+
/**
|
|
4203
|
+
* Check if error message indicates a rate limit / usage limit error
|
|
4204
|
+
* v5.7.0: Provider limit detection patterns for OpenAI
|
|
4205
|
+
*/
|
|
4206
|
+
isRateLimitError(errorMsg) {
|
|
4207
|
+
const lowerMsg = errorMsg.toLowerCase();
|
|
4208
|
+
return lowerMsg.includes("rate_limit_exceeded") || // OpenAI API error type
|
|
4209
|
+
lowerMsg.includes("429") || // HTTP status
|
|
4210
|
+
lowerMsg.includes("quota exceeded") || lowerMsg.includes("insufficient_quota") || lowerMsg.includes("rate limit") || lowerMsg.includes("too many requests");
|
|
4211
|
+
}
|
|
4212
|
+
/**
|
|
4213
|
+
* Create detailed rate limit error with automatic recording
|
|
4214
|
+
* v5.7.0: Provider limit detection and auto-rotation
|
|
4215
|
+
*/
|
|
4216
|
+
createRateLimitError(errorMsg) {
|
|
4217
|
+
const limitWindow = "daily";
|
|
4218
|
+
const limitManager = getProviderLimitManager();
|
|
4219
|
+
const resetAtMs = limitManager.calculateResetTime(
|
|
4220
|
+
this.config.name,
|
|
4221
|
+
limitWindow
|
|
4222
|
+
);
|
|
4223
|
+
void limitManager.recordLimitHit(
|
|
4224
|
+
this.config.name,
|
|
4225
|
+
limitWindow,
|
|
4226
|
+
resetAtMs,
|
|
4227
|
+
{
|
|
4228
|
+
reason: "rate_limit_exceeded",
|
|
4229
|
+
rawMessage: errorMsg
|
|
4230
|
+
}
|
|
4231
|
+
);
|
|
4232
|
+
return ProviderError.rateLimit(
|
|
4233
|
+
this.config.name,
|
|
4234
|
+
limitWindow,
|
|
4235
|
+
resetAtMs,
|
|
4236
|
+
errorMsg
|
|
4237
|
+
);
|
|
4238
|
+
}
|
|
3527
4239
|
};
|
|
3528
4240
|
}
|
|
3529
4241
|
});
|
|
@@ -4062,6 +4774,14 @@ var GLOBAL_PROVIDER_DEFAULTS = {
|
|
|
4062
4774
|
forceKillDelay: 1e3,
|
|
4063
4775
|
// 1 second
|
|
4064
4776
|
cacheEnabled: true
|
|
4777
|
+
},
|
|
4778
|
+
limitTracking: {
|
|
4779
|
+
enabled: true,
|
|
4780
|
+
// Enabled by default
|
|
4781
|
+
window: "daily",
|
|
4782
|
+
// Daily reset by default
|
|
4783
|
+
resetHourUtc: 0
|
|
4784
|
+
// Midnight UTC
|
|
4065
4785
|
}
|
|
4066
4786
|
};
|
|
4067
4787
|
var DEFAULT_CONFIG = {
|
|
@@ -4082,7 +4802,13 @@ var DEFAULT_CONFIG = {
|
|
|
4082
4802
|
// v5.6.18: Circuit breaker, process management, and version detection
|
|
4083
4803
|
circuitBreaker: GLOBAL_PROVIDER_DEFAULTS.circuitBreaker,
|
|
4084
4804
|
processManagement: GLOBAL_PROVIDER_DEFAULTS.processManagement,
|
|
4085
|
-
versionDetection: GLOBAL_PROVIDER_DEFAULTS.versionDetection
|
|
4805
|
+
versionDetection: GLOBAL_PROVIDER_DEFAULTS.versionDetection,
|
|
4806
|
+
// v5.7.0: Usage limit tracking
|
|
4807
|
+
limitTracking: {
|
|
4808
|
+
...GLOBAL_PROVIDER_DEFAULTS.limitTracking,
|
|
4809
|
+
window: "weekly"
|
|
4810
|
+
// Claude Code has weekly limits
|
|
4811
|
+
}
|
|
4086
4812
|
// v5.0.5: Removed defaults - let provider CLI use optimal defaults
|
|
4087
4813
|
// Users can still set provider.defaults in config for specific needs
|
|
4088
4814
|
},
|
|
@@ -4101,7 +4827,10 @@ var DEFAULT_CONFIG = {
|
|
|
4101
4827
|
// v5.6.18: Circuit breaker, process management, and version detection
|
|
4102
4828
|
circuitBreaker: GLOBAL_PROVIDER_DEFAULTS.circuitBreaker,
|
|
4103
4829
|
processManagement: GLOBAL_PROVIDER_DEFAULTS.processManagement,
|
|
4104
|
-
versionDetection: GLOBAL_PROVIDER_DEFAULTS.versionDetection
|
|
4830
|
+
versionDetection: GLOBAL_PROVIDER_DEFAULTS.versionDetection,
|
|
4831
|
+
// v5.7.0: Usage limit tracking
|
|
4832
|
+
limitTracking: GLOBAL_PROVIDER_DEFAULTS.limitTracking
|
|
4833
|
+
// Gemini: daily limits
|
|
4105
4834
|
// v5.0.5: Removed defaults - let provider CLI use optimal defaults
|
|
4106
4835
|
},
|
|
4107
4836
|
"openai": {
|
|
@@ -4119,7 +4848,10 @@ var DEFAULT_CONFIG = {
|
|
|
4119
4848
|
// v5.6.18: Circuit breaker, process management, and version detection
|
|
4120
4849
|
circuitBreaker: GLOBAL_PROVIDER_DEFAULTS.circuitBreaker,
|
|
4121
4850
|
processManagement: GLOBAL_PROVIDER_DEFAULTS.processManagement,
|
|
4122
|
-
versionDetection: GLOBAL_PROVIDER_DEFAULTS.versionDetection
|
|
4851
|
+
versionDetection: GLOBAL_PROVIDER_DEFAULTS.versionDetection,
|
|
4852
|
+
// v5.7.0: Usage limit tracking
|
|
4853
|
+
limitTracking: GLOBAL_PROVIDER_DEFAULTS.limitTracking
|
|
4854
|
+
// OpenAI: daily limits
|
|
4123
4855
|
// v5.0.5: Removed defaults - let provider CLI use optimal defaults
|
|
4124
4856
|
}
|
|
4125
4857
|
},
|
|
@@ -4385,181 +5117,8 @@ var DEFAULT_CONFIG = {
|
|
|
4385
5117
|
}
|
|
4386
5118
|
};
|
|
4387
5119
|
|
|
4388
|
-
// src/utils/errors.ts
|
|
4389
|
-
init_esm_shims();
|
|
4390
|
-
var BaseError = class extends Error {
|
|
4391
|
-
code;
|
|
4392
|
-
suggestions;
|
|
4393
|
-
context;
|
|
4394
|
-
isOperational;
|
|
4395
|
-
constructor(message, code, suggestions = [], context, isOperational = true) {
|
|
4396
|
-
super(message);
|
|
4397
|
-
this.name = this.constructor.name;
|
|
4398
|
-
this.code = code;
|
|
4399
|
-
this.suggestions = suggestions;
|
|
4400
|
-
this.context = context;
|
|
4401
|
-
this.isOperational = isOperational;
|
|
4402
|
-
Error.captureStackTrace(this, this.constructor);
|
|
4403
|
-
}
|
|
4404
|
-
/**
|
|
4405
|
-
* Get formatted error message for display
|
|
4406
|
-
*/
|
|
4407
|
-
getFormattedMessage() {
|
|
4408
|
-
let formatted = `[${this.code}] ${this.message}`;
|
|
4409
|
-
if (this.suggestions.length > 0) {
|
|
4410
|
-
formatted += "\n\nSuggestions:";
|
|
4411
|
-
this.suggestions.forEach((suggestion, i) => {
|
|
4412
|
-
formatted += `
|
|
4413
|
-
${i + 1}. ${suggestion}`;
|
|
4414
|
-
});
|
|
4415
|
-
}
|
|
4416
|
-
return formatted;
|
|
4417
|
-
}
|
|
4418
|
-
/**
|
|
4419
|
-
* Get error details as object
|
|
4420
|
-
*/
|
|
4421
|
-
toJSON() {
|
|
4422
|
-
return {
|
|
4423
|
-
name: this.name,
|
|
4424
|
-
code: this.code,
|
|
4425
|
-
message: this.message,
|
|
4426
|
-
suggestions: this.suggestions,
|
|
4427
|
-
context: this.context,
|
|
4428
|
-
isOperational: this.isOperational,
|
|
4429
|
-
stack: this.stack
|
|
4430
|
-
};
|
|
4431
|
-
}
|
|
4432
|
-
};
|
|
4433
|
-
var ConfigError = class _ConfigError extends BaseError {
|
|
4434
|
-
constructor(message, code = "E1001" /* CONFIG_INVALID */, suggestions = [], context) {
|
|
4435
|
-
super(message, code, suggestions, context);
|
|
4436
|
-
}
|
|
4437
|
-
static notFound(path5) {
|
|
4438
|
-
return new _ConfigError(
|
|
4439
|
-
`Configuration file not found: ${path5}`,
|
|
4440
|
-
"E1000" /* CONFIG_NOT_FOUND */,
|
|
4441
|
-
[
|
|
4442
|
-
'Run "automatosx init" to create a new configuration',
|
|
4443
|
-
"Specify a custom config path with --config option",
|
|
4444
|
-
"Check that you are in a valid AutomatosX project directory"
|
|
4445
|
-
],
|
|
4446
|
-
{ path: path5 }
|
|
4447
|
-
);
|
|
4448
|
-
}
|
|
4449
|
-
static invalid(reason, context) {
|
|
4450
|
-
return new _ConfigError(
|
|
4451
|
-
`Invalid configuration: ${reason}`,
|
|
4452
|
-
"E1001" /* CONFIG_INVALID */,
|
|
4453
|
-
[
|
|
4454
|
-
"Check your automatosx.config.json for syntax errors",
|
|
4455
|
-
"Validate against the schema in documentation",
|
|
4456
|
-
'Reset to default with "automatosx init --force"'
|
|
4457
|
-
],
|
|
4458
|
-
context
|
|
4459
|
-
);
|
|
4460
|
-
}
|
|
4461
|
-
static parseError(error, path5) {
|
|
4462
|
-
return new _ConfigError(
|
|
4463
|
-
`Failed to parse configuration: ${error.message}`,
|
|
4464
|
-
"E1002" /* CONFIG_PARSE_ERROR */,
|
|
4465
|
-
[
|
|
4466
|
-
"Check JSON syntax in your config file",
|
|
4467
|
-
"Use a JSON validator to find syntax errors",
|
|
4468
|
-
'Reset to default with "automatosx init --force"'
|
|
4469
|
-
],
|
|
4470
|
-
{ path: path5, originalError: error.message }
|
|
4471
|
-
);
|
|
4472
|
-
}
|
|
4473
|
-
};
|
|
4474
|
-
var ProviderError = class _ProviderError extends BaseError {
|
|
4475
|
-
constructor(message, code = "E1301" /* PROVIDER_UNAVAILABLE */, suggestions = [], context) {
|
|
4476
|
-
super(message, code, suggestions, context);
|
|
4477
|
-
}
|
|
4478
|
-
static notFound(providerName) {
|
|
4479
|
-
return new _ProviderError(
|
|
4480
|
-
`Provider "${providerName}" not found`,
|
|
4481
|
-
"E1300" /* PROVIDER_NOT_FOUND */,
|
|
4482
|
-
[
|
|
4483
|
-
'Check available providers with "automatosx list providers"',
|
|
4484
|
-
"Verify provider is enabled in automatosx.config.json",
|
|
4485
|
-
"Install the provider CLI if not already installed"
|
|
4486
|
-
],
|
|
4487
|
-
{ providerName }
|
|
4488
|
-
);
|
|
4489
|
-
}
|
|
4490
|
-
static unavailable(providerName, reason) {
|
|
4491
|
-
const msg = reason ? `Provider "${providerName}" unavailable: ${reason}` : `Provider "${providerName}" is unavailable`;
|
|
4492
|
-
return new _ProviderError(
|
|
4493
|
-
msg,
|
|
4494
|
-
"E1301" /* PROVIDER_UNAVAILABLE */,
|
|
4495
|
-
[
|
|
4496
|
-
"Check that the provider CLI is installed and in your PATH",
|
|
4497
|
-
"Verify provider configuration in automatosx.config.json",
|
|
4498
|
-
"Try running the provider CLI directly to diagnose issues",
|
|
4499
|
-
'Check system status with "automatosx status --verbose"'
|
|
4500
|
-
],
|
|
4501
|
-
{ providerName, reason }
|
|
4502
|
-
);
|
|
4503
|
-
}
|
|
4504
|
-
static timeout(providerName, timeoutMs) {
|
|
4505
|
-
return new _ProviderError(
|
|
4506
|
-
`Provider "${providerName}" timed out after ${timeoutMs}ms`,
|
|
4507
|
-
"E1302" /* PROVIDER_TIMEOUT */,
|
|
4508
|
-
[
|
|
4509
|
-
"Increase timeout in automatosx.config.json",
|
|
4510
|
-
"Try a simpler prompt or reduce context size",
|
|
4511
|
-
"Check your network connection",
|
|
4512
|
-
"Verify the provider service is responsive"
|
|
4513
|
-
],
|
|
4514
|
-
{ providerName, timeoutMs }
|
|
4515
|
-
);
|
|
4516
|
-
}
|
|
4517
|
-
static noAvailableProviders() {
|
|
4518
|
-
return new _ProviderError(
|
|
4519
|
-
"No AI providers are available",
|
|
4520
|
-
"E1306" /* PROVIDER_NO_AVAILABLE */,
|
|
4521
|
-
[
|
|
4522
|
-
"Install at least one provider CLI (Claude or Gemini)",
|
|
4523
|
-
"Enable providers in automatosx.config.json",
|
|
4524
|
-
'Check provider status with "automatosx status"',
|
|
4525
|
-
"Verify provider CLIs are in your PATH"
|
|
4526
|
-
]
|
|
4527
|
-
);
|
|
4528
|
-
}
|
|
4529
|
-
static executionError(providerName, error) {
|
|
4530
|
-
return new _ProviderError(
|
|
4531
|
-
`Provider "${providerName}" execution failed: ${error.message}`,
|
|
4532
|
-
"E1305" /* PROVIDER_EXEC_ERROR */,
|
|
4533
|
-
[
|
|
4534
|
-
"Check that the provider CLI is installed correctly",
|
|
4535
|
-
"Verify you have necessary API keys configured",
|
|
4536
|
-
"Try running the provider CLI directly to diagnose",
|
|
4537
|
-
"Check error logs with --debug flag"
|
|
4538
|
-
],
|
|
4539
|
-
{ providerName, originalError: error.message }
|
|
4540
|
-
);
|
|
4541
|
-
}
|
|
4542
|
-
};
|
|
4543
|
-
function toBaseError(error) {
|
|
4544
|
-
if (error instanceof BaseError) {
|
|
4545
|
-
return error;
|
|
4546
|
-
}
|
|
4547
|
-
if (error instanceof Error) {
|
|
4548
|
-
return new BaseError(
|
|
4549
|
-
error.message,
|
|
4550
|
-
"E9999" /* UNKNOWN_ERROR */,
|
|
4551
|
-
["Check error details for more information"],
|
|
4552
|
-
{ originalError: error.name, stack: error.stack }
|
|
4553
|
-
);
|
|
4554
|
-
}
|
|
4555
|
-
return new BaseError(
|
|
4556
|
-
String(error),
|
|
4557
|
-
"E9999" /* UNKNOWN_ERROR */,
|
|
4558
|
-
["An unexpected error occurred"]
|
|
4559
|
-
);
|
|
4560
|
-
}
|
|
4561
|
-
|
|
4562
5120
|
// src/core/config.ts
|
|
5121
|
+
init_errors();
|
|
4563
5122
|
init_logger();
|
|
4564
5123
|
|
|
4565
5124
|
// src/utils/deep-merge.ts
|
|
@@ -4644,20 +5203,20 @@ var VALIDATION_LIMITS = {
|
|
|
4644
5203
|
MAX_CONCURRENT_AGENTS: 32
|
|
4645
5204
|
// Cap parallel agent execution to protect resources
|
|
4646
5205
|
};
|
|
4647
|
-
function isValidRelativePath(
|
|
4648
|
-
if (!
|
|
5206
|
+
function isValidRelativePath(path6) {
|
|
5207
|
+
if (!path6 || typeof path6 !== "string") {
|
|
4649
5208
|
return false;
|
|
4650
5209
|
}
|
|
4651
|
-
if (
|
|
5210
|
+
if (path6.startsWith("/")) {
|
|
4652
5211
|
return false;
|
|
4653
5212
|
}
|
|
4654
|
-
if (
|
|
5213
|
+
if (path6.includes("..")) {
|
|
4655
5214
|
return false;
|
|
4656
5215
|
}
|
|
4657
|
-
if (/^[a-zA-Z]:/.test(
|
|
5216
|
+
if (/^[a-zA-Z]:/.test(path6)) {
|
|
4658
5217
|
return false;
|
|
4659
5218
|
}
|
|
4660
|
-
if (
|
|
5219
|
+
if (path6.startsWith("\\\\")) {
|
|
4661
5220
|
return false;
|
|
4662
5221
|
}
|
|
4663
5222
|
return true;
|
|
@@ -5095,16 +5654,16 @@ async function loadConfigUncached(projectDir) {
|
|
|
5095
5654
|
});
|
|
5096
5655
|
return config;
|
|
5097
5656
|
}
|
|
5098
|
-
async function loadConfigFile(
|
|
5657
|
+
async function loadConfigFile(path6) {
|
|
5099
5658
|
try {
|
|
5100
|
-
const content = await readFile(
|
|
5659
|
+
const content = await readFile(path6, "utf-8");
|
|
5101
5660
|
if (content.length > VALIDATION_LIMITS.MAX_CONFIG_FILE_SIZE) {
|
|
5102
5661
|
throw ConfigError.parseError(
|
|
5103
5662
|
new Error(`Config file too large (max ${VALIDATION_LIMITS.MAX_CONFIG_FILE_SIZE / 1024}KB, got ${Math.ceil(content.length / 1024)}KB)`),
|
|
5104
|
-
|
|
5663
|
+
path6
|
|
5105
5664
|
);
|
|
5106
5665
|
}
|
|
5107
|
-
const ext2 = extname(
|
|
5666
|
+
const ext2 = extname(path6).toLowerCase();
|
|
5108
5667
|
let userConfig;
|
|
5109
5668
|
try {
|
|
5110
5669
|
if (ext2 === ".yaml" || ext2 === ".yml") {
|
|
@@ -5113,7 +5672,7 @@ async function loadConfigFile(path5) {
|
|
|
5113
5672
|
userConfig = JSON.parse(content);
|
|
5114
5673
|
}
|
|
5115
5674
|
} catch (parseError) {
|
|
5116
|
-
throw ConfigError.parseError(parseError,
|
|
5675
|
+
throw ConfigError.parseError(parseError, path6);
|
|
5117
5676
|
}
|
|
5118
5677
|
const config = mergeConfig(DEFAULT_CONFIG, userConfig);
|
|
5119
5678
|
if (config.execution && userConfig.execution?.maxConcurrentAgents === void 0) {
|
|
@@ -5129,35 +5688,35 @@ async function loadConfigFile(path5) {
|
|
|
5129
5688
|
if (validationErrors.length > 0) {
|
|
5130
5689
|
throw ConfigError.invalid(
|
|
5131
5690
|
validationErrors.join("; "),
|
|
5132
|
-
{ path:
|
|
5691
|
+
{ path: path6, errors: validationErrors }
|
|
5133
5692
|
);
|
|
5134
5693
|
}
|
|
5135
|
-
logger.info("Config loaded successfully", { path: normalizePath(
|
|
5694
|
+
logger.info("Config loaded successfully", { path: normalizePath(path6), format: ext2 });
|
|
5136
5695
|
return config;
|
|
5137
5696
|
} catch (error) {
|
|
5138
5697
|
if (error instanceof ConfigError) {
|
|
5139
5698
|
throw error;
|
|
5140
5699
|
}
|
|
5141
5700
|
if (error.code === "ENOENT") {
|
|
5142
|
-
throw ConfigError.notFound(
|
|
5701
|
+
throw ConfigError.notFound(path6);
|
|
5143
5702
|
}
|
|
5144
5703
|
if (error.code === "EACCES") {
|
|
5145
5704
|
throw new ConfigError(
|
|
5146
|
-
`Permission denied reading config: ${
|
|
5705
|
+
`Permission denied reading config: ${path6}`,
|
|
5147
5706
|
"E1002" /* CONFIG_PARSE_ERROR */,
|
|
5148
5707
|
[
|
|
5149
5708
|
"Check file permissions",
|
|
5150
5709
|
"Run with appropriate user privileges",
|
|
5151
5710
|
"Verify the file is accessible"
|
|
5152
5711
|
],
|
|
5153
|
-
{ path:
|
|
5712
|
+
{ path: path6, error: error.message }
|
|
5154
5713
|
);
|
|
5155
5714
|
}
|
|
5156
5715
|
throw new ConfigError(
|
|
5157
5716
|
`Failed to load config: ${error.message}`,
|
|
5158
5717
|
"E1002" /* CONFIG_PARSE_ERROR */,
|
|
5159
5718
|
["Check file format and permissions"],
|
|
5160
|
-
{ path:
|
|
5719
|
+
{ path: path6, originalError: error.message }
|
|
5161
5720
|
);
|
|
5162
5721
|
}
|
|
5163
5722
|
}
|
|
@@ -5535,16 +6094,16 @@ function validateConfig(config) {
|
|
|
5535
6094
|
}
|
|
5536
6095
|
return errors;
|
|
5537
6096
|
}
|
|
5538
|
-
async function saveConfigFile(
|
|
6097
|
+
async function saveConfigFile(path6, config) {
|
|
5539
6098
|
try {
|
|
5540
6099
|
const validationErrors = validateConfig(config);
|
|
5541
6100
|
if (validationErrors.length > 0) {
|
|
5542
6101
|
throw ConfigError.invalid(
|
|
5543
6102
|
validationErrors.join("; "),
|
|
5544
|
-
{ path:
|
|
6103
|
+
{ path: path6, errors: validationErrors }
|
|
5545
6104
|
);
|
|
5546
6105
|
}
|
|
5547
|
-
const ext2 = extname(
|
|
6106
|
+
const ext2 = extname(path6).toLowerCase();
|
|
5548
6107
|
let content;
|
|
5549
6108
|
if (ext2 === ".yaml" || ext2 === ".yml") {
|
|
5550
6109
|
content = dump(config, {
|
|
@@ -5556,29 +6115,29 @@ async function saveConfigFile(path5, config) {
|
|
|
5556
6115
|
} else {
|
|
5557
6116
|
content = JSON.stringify(config, null, 2);
|
|
5558
6117
|
}
|
|
5559
|
-
await writeFile(
|
|
5560
|
-
logger.info("Config saved successfully", { path: normalizePath(
|
|
6118
|
+
await writeFile(path6, content, "utf-8");
|
|
6119
|
+
logger.info("Config saved successfully", { path: normalizePath(path6), format: ext2 });
|
|
5561
6120
|
} catch (error) {
|
|
5562
6121
|
if (error instanceof ConfigError) {
|
|
5563
6122
|
throw error;
|
|
5564
6123
|
}
|
|
5565
6124
|
if (error.code === "EACCES") {
|
|
5566
6125
|
throw new ConfigError(
|
|
5567
|
-
`Permission denied writing config: ${
|
|
6126
|
+
`Permission denied writing config: ${path6}`,
|
|
5568
6127
|
"E1002" /* CONFIG_PARSE_ERROR */,
|
|
5569
6128
|
[
|
|
5570
6129
|
"Check file permissions",
|
|
5571
6130
|
"Run with appropriate user privileges",
|
|
5572
6131
|
"Verify the directory is writable"
|
|
5573
6132
|
],
|
|
5574
|
-
{ path:
|
|
6133
|
+
{ path: path6, error: error.message }
|
|
5575
6134
|
);
|
|
5576
6135
|
}
|
|
5577
6136
|
throw new ConfigError(
|
|
5578
6137
|
`Failed to save config: ${error.message}`,
|
|
5579
6138
|
"E1002" /* CONFIG_PARSE_ERROR */,
|
|
5580
6139
|
["Check file path and permissions"],
|
|
5581
|
-
{ path:
|
|
6140
|
+
{ path: path6, originalError: error.message }
|
|
5582
6141
|
);
|
|
5583
6142
|
}
|
|
5584
6143
|
}
|
|
@@ -5857,6 +6416,7 @@ init_logger();
|
|
|
5857
6416
|
|
|
5858
6417
|
// src/utils/error-formatter.ts
|
|
5859
6418
|
init_esm_shims();
|
|
6419
|
+
init_errors();
|
|
5860
6420
|
function formatError(error, options = {}) {
|
|
5861
6421
|
const {
|
|
5862
6422
|
verbose = false,
|
|
@@ -6314,10 +6874,10 @@ var configCommand = {
|
|
|
6314
6874
|
} else {
|
|
6315
6875
|
const projectConfig = resolve(process.cwd(), "automatosx.config.json");
|
|
6316
6876
|
const hiddenConfig = resolve(process.cwd(), ".automatosx", "config.json");
|
|
6317
|
-
const
|
|
6318
|
-
if (
|
|
6877
|
+
const fs5 = await import('fs');
|
|
6878
|
+
if (fs5.existsSync(projectConfig)) {
|
|
6319
6879
|
configPath = projectConfig;
|
|
6320
|
-
} else if (
|
|
6880
|
+
} else if (fs5.existsSync(hiddenConfig)) {
|
|
6321
6881
|
configPath = hiddenConfig;
|
|
6322
6882
|
} else {
|
|
6323
6883
|
configPath = projectConfig;
|
|
@@ -6364,9 +6924,9 @@ var configCommand = {
|
|
|
6364
6924
|
}
|
|
6365
6925
|
}
|
|
6366
6926
|
};
|
|
6367
|
-
async function checkExists(
|
|
6927
|
+
async function checkExists(path6) {
|
|
6368
6928
|
try {
|
|
6369
|
-
await access$1(
|
|
6929
|
+
await access$1(path6, constants.F_OK);
|
|
6370
6930
|
return true;
|
|
6371
6931
|
} catch {
|
|
6372
6932
|
return false;
|
|
@@ -6397,7 +6957,7 @@ async function validateConfigFile(config, verbose) {
|
|
|
6397
6957
|
}
|
|
6398
6958
|
console.log();
|
|
6399
6959
|
}
|
|
6400
|
-
async function resetConfig(
|
|
6960
|
+
async function resetConfig(path6, verbose) {
|
|
6401
6961
|
const { createRequire } = await import('module');
|
|
6402
6962
|
const require2 = createRequire(import.meta.url);
|
|
6403
6963
|
let version = "5.2.2";
|
|
@@ -6412,14 +6972,14 @@ async function resetConfig(path5, verbose) {
|
|
|
6412
6972
|
// Users should rely on IDE JSON Schema plugins that fetch from npm package
|
|
6413
6973
|
version
|
|
6414
6974
|
};
|
|
6415
|
-
await saveConfigFile(
|
|
6975
|
+
await saveConfigFile(path6, config);
|
|
6416
6976
|
printSuccess("Configuration reset to defaults");
|
|
6417
6977
|
if (verbose) {
|
|
6418
6978
|
console.log(chalk27.gray(`
|
|
6419
|
-
Config file: ${
|
|
6979
|
+
Config file: ${path6}
|
|
6420
6980
|
`));
|
|
6421
6981
|
}
|
|
6422
|
-
logger.info("Configuration reset", { path:
|
|
6982
|
+
logger.info("Configuration reset", { path: path6 });
|
|
6423
6983
|
}
|
|
6424
6984
|
async function listConfig(config, verbose) {
|
|
6425
6985
|
console.log(chalk27.bold.cyan("\n\u{1F4CB} AutomatosX Configuration\n"));
|
|
@@ -6498,7 +7058,7 @@ async function getConfig(config, key, verbose) {
|
|
|
6498
7058
|
}
|
|
6499
7059
|
}
|
|
6500
7060
|
}
|
|
6501
|
-
async function setConfig(
|
|
7061
|
+
async function setConfig(path6, config, key, value, verbose) {
|
|
6502
7062
|
let parsedValue = value;
|
|
6503
7063
|
try {
|
|
6504
7064
|
parsedValue = JSON.parse(value);
|
|
@@ -6517,20 +7077,20 @@ async function setConfig(path5, config, key, value, verbose) {
|
|
|
6517
7077
|
console.log();
|
|
6518
7078
|
process.exit(1);
|
|
6519
7079
|
}
|
|
6520
|
-
await saveConfigFile(
|
|
7080
|
+
await saveConfigFile(path6, config);
|
|
6521
7081
|
printSuccess(`Configuration updated: ${key} = ${value}`);
|
|
6522
7082
|
if (verbose) {
|
|
6523
7083
|
console.log(chalk27.gray(`
|
|
6524
|
-
Config file: ${
|
|
7084
|
+
Config file: ${path6}`));
|
|
6525
7085
|
console.log(chalk27.gray("Configuration validated successfully\n"));
|
|
6526
7086
|
}
|
|
6527
7087
|
logger.info("Configuration updated", { key, value });
|
|
6528
7088
|
}
|
|
6529
|
-
function getNestedValue(obj,
|
|
6530
|
-
return
|
|
7089
|
+
function getNestedValue(obj, path6) {
|
|
7090
|
+
return path6.split(".").reduce((current, key) => current?.[key], obj);
|
|
6531
7091
|
}
|
|
6532
|
-
function setNestedValue(obj,
|
|
6533
|
-
const keys =
|
|
7092
|
+
function setNestedValue(obj, path6, value) {
|
|
7093
|
+
const keys = path6.split(".");
|
|
6534
7094
|
const lastKey = keys.pop();
|
|
6535
7095
|
if (!lastKey) return false;
|
|
6536
7096
|
const target = keys.reduce((current, key) => {
|
|
@@ -6581,7 +7141,7 @@ var initCommand = {
|
|
|
6581
7141
|
let version = "5.1.2";
|
|
6582
7142
|
try {
|
|
6583
7143
|
const packageJson = JSON.parse(
|
|
6584
|
-
await import('fs/promises').then((
|
|
7144
|
+
await import('fs/promises').then((fs5) => fs5.readFile(join(packageRoot, "package.json"), "utf-8"))
|
|
6585
7145
|
);
|
|
6586
7146
|
version = packageJson.version;
|
|
6587
7147
|
} catch {
|
|
@@ -6724,9 +7284,9 @@ var initCommand = {
|
|
|
6724
7284
|
}
|
|
6725
7285
|
}
|
|
6726
7286
|
};
|
|
6727
|
-
async function checkExists2(
|
|
7287
|
+
async function checkExists2(path6) {
|
|
6728
7288
|
try {
|
|
6729
|
-
await access$1(
|
|
7289
|
+
await access$1(path6, constants.F_OK);
|
|
6730
7290
|
return true;
|
|
6731
7291
|
} catch {
|
|
6732
7292
|
return false;
|
|
@@ -7296,6 +7856,8 @@ init_logger();
|
|
|
7296
7856
|
// src/core/router.ts
|
|
7297
7857
|
init_esm_shims();
|
|
7298
7858
|
init_logger();
|
|
7859
|
+
init_errors();
|
|
7860
|
+
init_provider_limit_manager();
|
|
7299
7861
|
|
|
7300
7862
|
// src/utils/performance-markers.ts
|
|
7301
7863
|
init_esm_shims();
|
|
@@ -7476,6 +8038,10 @@ var Router = class {
|
|
|
7476
8038
|
this.penalizedProviders = /* @__PURE__ */ new Map();
|
|
7477
8039
|
this.providerCooldownMs = config.providerCooldownMs ?? 3e4;
|
|
7478
8040
|
this.cache = config.cache;
|
|
8041
|
+
const limitManager = getProviderLimitManager();
|
|
8042
|
+
void limitManager.initialize().catch((err) => {
|
|
8043
|
+
logger.warn("Failed to initialize ProviderLimitManager", { error: err.message });
|
|
8044
|
+
});
|
|
7479
8045
|
this.healthCheckIntervalMs = config.healthCheckInterval;
|
|
7480
8046
|
if (config.healthCheckInterval) {
|
|
7481
8047
|
this.startHealthChecks(config.healthCheckInterval);
|
|
@@ -7533,6 +8099,22 @@ var Router = class {
|
|
|
7533
8099
|
cacheEnabled: this.cache?.isEnabled ?? false
|
|
7534
8100
|
});
|
|
7535
8101
|
if (availableProviders.length === 0) {
|
|
8102
|
+
const limitManager2 = getProviderLimitManager();
|
|
8103
|
+
const allProviders = this.providers;
|
|
8104
|
+
const limitedProviders2 = [];
|
|
8105
|
+
for (const provider of allProviders) {
|
|
8106
|
+
const limitCheck = limitManager2.isProviderLimited(provider.name);
|
|
8107
|
+
if (limitCheck.isLimited && limitCheck.resetAtMs) {
|
|
8108
|
+
limitedProviders2.push({
|
|
8109
|
+
name: provider.name,
|
|
8110
|
+
resetAtMs: limitCheck.resetAtMs
|
|
8111
|
+
});
|
|
8112
|
+
}
|
|
8113
|
+
}
|
|
8114
|
+
if (limitedProviders2.length === allProviders.length && limitedProviders2.length > 0) {
|
|
8115
|
+
const soonestReset = Math.min(...limitedProviders2.map((p) => p.resetAtMs));
|
|
8116
|
+
throw ProviderError.allProvidersLimited(limitedProviders2, soonestReset);
|
|
8117
|
+
}
|
|
7536
8118
|
throw ProviderError.noAvailableProviders();
|
|
7537
8119
|
}
|
|
7538
8120
|
let lastError;
|
|
@@ -7600,20 +8182,47 @@ var Router = class {
|
|
|
7600
8182
|
return response;
|
|
7601
8183
|
} catch (error) {
|
|
7602
8184
|
lastError = error;
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
8185
|
+
const isRateLimitError = error instanceof ProviderError && error.code === "E1303" /* PROVIDER_RATE_LIMIT */;
|
|
8186
|
+
if (isRateLimitError) {
|
|
8187
|
+
const providerError = error;
|
|
8188
|
+
const limitManager2 = getProviderLimitManager();
|
|
8189
|
+
const resetAtMs = providerError.context?.resetAtMs;
|
|
8190
|
+
const limitWindow = providerError.context?.limitWindow;
|
|
8191
|
+
if (typeof resetAtMs === "number" && typeof limitWindow === "string") {
|
|
8192
|
+
await limitManager2.recordLimitHit(
|
|
8193
|
+
provider.name,
|
|
8194
|
+
limitWindow,
|
|
8195
|
+
resetAtMs,
|
|
8196
|
+
{
|
|
8197
|
+
reason: providerError.context?.reason || "usage_limit_exceeded",
|
|
8198
|
+
rawMessage: providerError.message
|
|
8199
|
+
}
|
|
8200
|
+
);
|
|
8201
|
+
}
|
|
8202
|
+
logger.warn("\u26A0\uFE0F Router: provider hit usage limit, auto-rotating", {
|
|
8203
|
+
provider: provider.name,
|
|
8204
|
+
attempt: attemptNumber,
|
|
8205
|
+
resetAtMs: providerError.context?.resetAtMs,
|
|
8206
|
+
willFallback: this.fallbackEnabled && attemptNumber < availableProviders.length
|
|
8207
|
+
});
|
|
8208
|
+
} else {
|
|
8209
|
+
logger.warn("\u274C Router: provider failed", {
|
|
8210
|
+
provider: provider.name,
|
|
8211
|
+
attempt: attemptNumber,
|
|
8212
|
+
error: lastError.message,
|
|
8213
|
+
willFallback: this.fallbackEnabled && attemptNumber < availableProviders.length
|
|
8214
|
+
});
|
|
8215
|
+
const penaltyExpiry = Date.now() + this.providerCooldownMs;
|
|
8216
|
+
this.penalizedProviders.set(provider.name, penaltyExpiry);
|
|
8217
|
+
logger.debug(`Provider ${provider.name} penalized until ${new Date(penaltyExpiry).toISOString()}`);
|
|
8218
|
+
}
|
|
7612
8219
|
if (!this.fallbackEnabled) {
|
|
7613
8220
|
throw lastError;
|
|
7614
8221
|
}
|
|
7615
8222
|
if (attemptNumber < availableProviders.length) {
|
|
8223
|
+
const reason = isRateLimitError ? "usage limit hit" : "error";
|
|
7616
8224
|
logger.info("\u{1F504} Router: fallback triggered", {
|
|
8225
|
+
reason,
|
|
7617
8226
|
failedProvider: provider.name,
|
|
7618
8227
|
nextProvider: availableProviders[attemptNumber]?.name,
|
|
7619
8228
|
remainingProviders: availableProviders.length - attemptNumber
|
|
@@ -7626,6 +8235,21 @@ var Router = class {
|
|
|
7626
8235
|
totalAttempts: attemptNumber,
|
|
7627
8236
|
lastError: lastError?.message
|
|
7628
8237
|
});
|
|
8238
|
+
const limitManager = getProviderLimitManager();
|
|
8239
|
+
const limitedProviders = [];
|
|
8240
|
+
for (const provider of availableProviders) {
|
|
8241
|
+
const limitCheck = limitManager.isProviderLimited(provider.name);
|
|
8242
|
+
if (limitCheck.isLimited && limitCheck.resetAtMs) {
|
|
8243
|
+
limitedProviders.push({
|
|
8244
|
+
name: provider.name,
|
|
8245
|
+
resetAtMs: limitCheck.resetAtMs
|
|
8246
|
+
});
|
|
8247
|
+
}
|
|
8248
|
+
}
|
|
8249
|
+
if (limitedProviders.length === availableProviders.length && limitedProviders.length > 0) {
|
|
8250
|
+
const soonestReset = Math.min(...limitedProviders.map((p) => p.resetAtMs));
|
|
8251
|
+
throw ProviderError.allProvidersLimited(limitedProviders, soonestReset);
|
|
8252
|
+
}
|
|
7629
8253
|
throw new ProviderError(
|
|
7630
8254
|
`All providers failed. Last error: ${lastError?.message || "Unknown error"}`,
|
|
7631
8255
|
"E1306" /* PROVIDER_NO_AVAILABLE */,
|
|
@@ -7640,10 +8264,12 @@ var Router = class {
|
|
|
7640
8264
|
}
|
|
7641
8265
|
/**
|
|
7642
8266
|
* Get available providers sorted by priority
|
|
7643
|
-
*
|
|
8267
|
+
* v5.7.0: Enhanced with provider limit detection
|
|
8268
|
+
* Filters out penalized providers (those in cooldown period) and limited providers (quota exceeded)
|
|
7644
8269
|
*/
|
|
7645
8270
|
async getAvailableProviders() {
|
|
7646
8271
|
const now = Date.now();
|
|
8272
|
+
const limitManager = getProviderLimitManager();
|
|
7647
8273
|
for (const [providerName, expiryTime] of this.penalizedProviders.entries()) {
|
|
7648
8274
|
if (now >= expiryTime) {
|
|
7649
8275
|
this.penalizedProviders.delete(providerName);
|
|
@@ -7652,6 +8278,13 @@ var Router = class {
|
|
|
7652
8278
|
}
|
|
7653
8279
|
const checks = this.providers.map(async (provider) => {
|
|
7654
8280
|
try {
|
|
8281
|
+
const limitCheck = limitManager.isProviderLimited(provider.name, now);
|
|
8282
|
+
if (limitCheck.isLimited) {
|
|
8283
|
+
const remainingSec = Math.ceil((limitCheck.remainingMs || 0) / 1e3);
|
|
8284
|
+
const remainingHours = Math.ceil(remainingSec / 3600);
|
|
8285
|
+
logger.debug(`Skipping limited provider ${provider.name} (resets in ${remainingHours}h)`);
|
|
8286
|
+
return null;
|
|
8287
|
+
}
|
|
7655
8288
|
if (this.penalizedProviders.has(provider.name)) {
|
|
7656
8289
|
const expiryTime = this.penalizedProviders.get(provider.name);
|
|
7657
8290
|
const remainingMs = expiryTime - now;
|
|
@@ -7707,6 +8340,14 @@ var Router = class {
|
|
|
7707
8340
|
const checkStartTime = Date.now();
|
|
7708
8341
|
this.healthCheckMetrics.checksPerformed++;
|
|
7709
8342
|
try {
|
|
8343
|
+
const limitManager = getProviderLimitManager();
|
|
8344
|
+
const restoredProviders = await limitManager.refreshExpired();
|
|
8345
|
+
if (restoredProviders.length > 0) {
|
|
8346
|
+
logger.info("\u2705 Router: provider limits auto-restored", {
|
|
8347
|
+
providers: restoredProviders,
|
|
8348
|
+
count: restoredProviders.length
|
|
8349
|
+
});
|
|
8350
|
+
}
|
|
7710
8351
|
const availabilityResults = await Promise.allSettled(
|
|
7711
8352
|
this.providers.map(async (provider) => {
|
|
7712
8353
|
const startTime = Date.now();
|
|
@@ -10218,6 +10859,7 @@ var WorkspaceManager = class _WorkspaceManager {
|
|
|
10218
10859
|
// src/agents/context-manager.ts
|
|
10219
10860
|
init_esm_shims();
|
|
10220
10861
|
init_logger();
|
|
10862
|
+
init_errors();
|
|
10221
10863
|
var PROVIDER_ALIASES = {
|
|
10222
10864
|
"claude": "claude-code",
|
|
10223
10865
|
"gemini": "gemini-cli",
|
|
@@ -12159,6 +12801,7 @@ var TimeoutManager = class {
|
|
|
12159
12801
|
|
|
12160
12802
|
// src/utils/timeout-validator.ts
|
|
12161
12803
|
init_esm_shims();
|
|
12804
|
+
init_errors();
|
|
12162
12805
|
var MIN_WARNING_THRESHOLD2 = 0.5;
|
|
12163
12806
|
var MAX_WARNING_THRESHOLD2 = 0.95;
|
|
12164
12807
|
function validateTimeoutConfig(config) {
|
|
@@ -12345,9 +12988,9 @@ var DependencyGraphBuilder = class {
|
|
|
12345
12988
|
detectCycles(graph) {
|
|
12346
12989
|
const visiting = /* @__PURE__ */ new Set();
|
|
12347
12990
|
const visited = /* @__PURE__ */ new Set();
|
|
12348
|
-
const visit = (nodeName,
|
|
12991
|
+
const visit = (nodeName, path6) => {
|
|
12349
12992
|
if (visiting.has(nodeName)) {
|
|
12350
|
-
throw new Error(`Circular dependency detected: ${[...
|
|
12993
|
+
throw new Error(`Circular dependency detected: ${[...path6, nodeName].join(" \u2192 ")}`);
|
|
12351
12994
|
}
|
|
12352
12995
|
if (visited.has(nodeName)) {
|
|
12353
12996
|
return;
|
|
@@ -12362,7 +13005,7 @@ var DependencyGraphBuilder = class {
|
|
|
12362
13005
|
}
|
|
12363
13006
|
visiting.add(nodeName);
|
|
12364
13007
|
for (const dependency of node.dependencies) {
|
|
12365
|
-
visit(dependency, [...
|
|
13008
|
+
visit(dependency, [...path6, nodeName]);
|
|
12366
13009
|
}
|
|
12367
13010
|
visiting.delete(nodeName);
|
|
12368
13011
|
visited.add(nodeName);
|
|
@@ -13950,12 +14593,12 @@ var ValidationError = class extends Error {
|
|
|
13950
14593
|
this.name = "ValidationError";
|
|
13951
14594
|
}
|
|
13952
14595
|
};
|
|
13953
|
-
function validatePathParameter(
|
|
13954
|
-
if (!
|
|
14596
|
+
function validatePathParameter(path6, paramName, projectRoot = process.cwd()) {
|
|
14597
|
+
if (!path6 || path6.trim() === "") {
|
|
13955
14598
|
throw new ValidationError(
|
|
13956
14599
|
`Invalid ${paramName}: path cannot be empty`,
|
|
13957
14600
|
-32602 /* InvalidParams */,
|
|
13958
|
-
{ path:
|
|
14601
|
+
{ path: path6, paramName }
|
|
13959
14602
|
);
|
|
13960
14603
|
}
|
|
13961
14604
|
const dangerousPatterns = [
|
|
@@ -13985,51 +14628,51 @@ function validatePathParameter(path5, paramName, projectRoot = process.cwd()) {
|
|
|
13985
14628
|
// Common data drive (Windows, alt format)
|
|
13986
14629
|
];
|
|
13987
14630
|
for (const pattern of dangerousPatterns) {
|
|
13988
|
-
if (
|
|
14631
|
+
if (path6.includes(pattern)) {
|
|
13989
14632
|
throw new ValidationError(
|
|
13990
14633
|
`Invalid ${paramName}: path contains dangerous pattern "${pattern}"`,
|
|
13991
14634
|
-32602 /* InvalidParams */,
|
|
13992
|
-
{ path:
|
|
14635
|
+
{ path: path6, paramName, pattern }
|
|
13993
14636
|
);
|
|
13994
14637
|
}
|
|
13995
14638
|
}
|
|
13996
|
-
if (isAbsolute(
|
|
14639
|
+
if (isAbsolute(path6)) {
|
|
13997
14640
|
throw new ValidationError(
|
|
13998
14641
|
`Invalid ${paramName}: absolute paths are not allowed`,
|
|
13999
14642
|
-32602 /* InvalidParams */,
|
|
14000
|
-
{ path:
|
|
14643
|
+
{ path: path6, paramName }
|
|
14001
14644
|
);
|
|
14002
14645
|
}
|
|
14003
14646
|
try {
|
|
14004
|
-
const resolvedPath = resolve(projectRoot,
|
|
14647
|
+
const resolvedPath = resolve(projectRoot, path6);
|
|
14005
14648
|
const normalizedRoot = resolve(projectRoot);
|
|
14006
14649
|
if (!resolvedPath.startsWith(normalizedRoot + sep) && resolvedPath !== normalizedRoot) {
|
|
14007
14650
|
throw new ValidationError(
|
|
14008
14651
|
`Invalid ${paramName}: path escapes project boundary`,
|
|
14009
14652
|
-32602 /* InvalidParams */,
|
|
14010
|
-
{ path:
|
|
14653
|
+
{ path: path6, paramName, projectRoot, resolvedPath }
|
|
14011
14654
|
);
|
|
14012
14655
|
}
|
|
14013
14656
|
} catch (error) {
|
|
14014
14657
|
throw new ValidationError(
|
|
14015
14658
|
`Invalid ${paramName}: path resolution failed`,
|
|
14016
14659
|
-32602 /* InvalidParams */,
|
|
14017
|
-
{ path:
|
|
14660
|
+
{ path: path6, paramName, error: String(error) }
|
|
14018
14661
|
);
|
|
14019
14662
|
}
|
|
14020
|
-
if (
|
|
14663
|
+
if (path6.includes("\0")) {
|
|
14021
14664
|
throw new ValidationError(
|
|
14022
14665
|
`Invalid ${paramName}: path contains null byte`,
|
|
14023
14666
|
-32602 /* InvalidParams */,
|
|
14024
|
-
{ path:
|
|
14667
|
+
{ path: path6, paramName }
|
|
14025
14668
|
);
|
|
14026
14669
|
}
|
|
14027
14670
|
const suspiciousChars = /[<>:|"]/;
|
|
14028
|
-
if (suspiciousChars.test(
|
|
14671
|
+
if (suspiciousChars.test(path6)) {
|
|
14029
14672
|
throw new ValidationError(
|
|
14030
14673
|
`Invalid ${paramName}: path contains invalid characters`,
|
|
14031
14674
|
-32602 /* InvalidParams */,
|
|
14032
|
-
{ path:
|
|
14675
|
+
{ path: path6, paramName }
|
|
14033
14676
|
);
|
|
14034
14677
|
}
|
|
14035
14678
|
}
|
|
@@ -14540,8 +15183,8 @@ function createMemoryExportHandler(deps) {
|
|
|
14540
15183
|
return async (input) => {
|
|
14541
15184
|
logger.info("[MCP] memory_export called", { input });
|
|
14542
15185
|
try {
|
|
14543
|
-
const { path:
|
|
14544
|
-
const absolutePath = resolveExportPath(deps.pathResolver,
|
|
15186
|
+
const { path: path6 } = input;
|
|
15187
|
+
const absolutePath = resolveExportPath(deps.pathResolver, path6);
|
|
14545
15188
|
const exported = await deps.memoryManager.exportToJSON(absolutePath);
|
|
14546
15189
|
const result = {
|
|
14547
15190
|
success: true,
|
|
@@ -14577,8 +15220,8 @@ function createMemoryImportHandler(deps) {
|
|
|
14577
15220
|
return async (input) => {
|
|
14578
15221
|
logger.info("[MCP] memory_import called", { input });
|
|
14579
15222
|
try {
|
|
14580
|
-
const { path:
|
|
14581
|
-
const absolutePath = resolveImportPath(deps.pathResolver,
|
|
15223
|
+
const { path: path6 } = input;
|
|
15224
|
+
const absolutePath = resolveImportPath(deps.pathResolver, path6);
|
|
14582
15225
|
const imported = await deps.memoryManager.importFromJSON(absolutePath);
|
|
14583
15226
|
const result = {
|
|
14584
15227
|
success: true,
|
|
@@ -15601,9 +16244,9 @@ var ProgressIndicator = class {
|
|
|
15601
16244
|
// src/cli/commands/memory.ts
|
|
15602
16245
|
var DEFAULT_DB_PATH = ".automatosx/memory/memory.db";
|
|
15603
16246
|
function getMemoryManager(dbPath) {
|
|
15604
|
-
const
|
|
16247
|
+
const path6 = dbPath || DEFAULT_DB_PATH;
|
|
15605
16248
|
return new LazyMemoryManager({
|
|
15606
|
-
dbPath: resolve(
|
|
16249
|
+
dbPath: resolve(path6),
|
|
15607
16250
|
maxEntries: 1e5,
|
|
15608
16251
|
autoCleanup: false,
|
|
15609
16252
|
trackAccess: true
|
|
@@ -19071,6 +19714,7 @@ init_claude_provider();
|
|
|
19071
19714
|
init_gemini_provider();
|
|
19072
19715
|
init_openai_provider();
|
|
19073
19716
|
init_logger();
|
|
19717
|
+
init_provider_limit_manager();
|
|
19074
19718
|
var VERSION = getVersion();
|
|
19075
19719
|
var statusCommand3 = {
|
|
19076
19720
|
command: "status",
|
|
@@ -19284,6 +19928,49 @@ var statusCommand3 = {
|
|
|
19284
19928
|
}
|
|
19285
19929
|
}
|
|
19286
19930
|
console.log();
|
|
19931
|
+
console.log(chalk27.cyan("Provider Limits:"));
|
|
19932
|
+
try {
|
|
19933
|
+
const limitManager = getProviderLimitManager();
|
|
19934
|
+
await limitManager.initialize();
|
|
19935
|
+
const limitStates = limitManager.getAllStates();
|
|
19936
|
+
const manualOverride = limitManager.getManualOverride();
|
|
19937
|
+
const now = Date.now();
|
|
19938
|
+
if (limitStates.size === 0 && !manualOverride) {
|
|
19939
|
+
console.log(chalk27.gray(" No limits detected. All providers available."));
|
|
19940
|
+
} else {
|
|
19941
|
+
if (limitStates.size > 0) {
|
|
19942
|
+
for (const [name, state] of limitStates.entries()) {
|
|
19943
|
+
const remainingMs = state.resetAtMs - now;
|
|
19944
|
+
const hours = Math.ceil(remainingMs / (1e3 * 60 * 60));
|
|
19945
|
+
console.log(chalk27.yellow(` \u26A0\uFE0F ${name}: LIMITED (resets in ${hours}h)`));
|
|
19946
|
+
if (argv2.verbose) {
|
|
19947
|
+
const resetDate = new Date(state.resetAtMs);
|
|
19948
|
+
console.log(chalk27.gray(` Window: ${state.window}`));
|
|
19949
|
+
console.log(chalk27.gray(` Resets: ${resetDate.toLocaleString()}`));
|
|
19950
|
+
if (state.reason) {
|
|
19951
|
+
console.log(chalk27.gray(` Reason: ${state.reason}`));
|
|
19952
|
+
}
|
|
19953
|
+
}
|
|
19954
|
+
}
|
|
19955
|
+
}
|
|
19956
|
+
if (manualOverride) {
|
|
19957
|
+
const expiresText = manualOverride.expiresAtMs ? `expires in ${Math.ceil((manualOverride.expiresAtMs - now) / (1e3 * 60))}m` : "no expiry";
|
|
19958
|
+
console.log(chalk27.cyan(` \u{1F527} Manual Override: ${manualOverride.provider} (${expiresText})`));
|
|
19959
|
+
}
|
|
19960
|
+
const availableCount = status.providers.filter((p) => {
|
|
19961
|
+
const isLimited = limitStates.has(p.name);
|
|
19962
|
+
const isOverridden = manualOverride && manualOverride.provider !== p.name;
|
|
19963
|
+
return p.available && !isLimited && !isOverridden;
|
|
19964
|
+
}).length;
|
|
19965
|
+
if (availableCount > 0) {
|
|
19966
|
+
console.log(chalk27.green(` \u2705 ${availableCount} provider(s) available`));
|
|
19967
|
+
}
|
|
19968
|
+
}
|
|
19969
|
+
} catch (error) {
|
|
19970
|
+
logger.warn("Failed to get provider limits", { error: error.message });
|
|
19971
|
+
console.log(chalk27.gray(" (Unable to load limit status)"));
|
|
19972
|
+
}
|
|
19973
|
+
console.log();
|
|
19287
19974
|
console.log(chalk27.cyan("ENV Variable Overrides:"));
|
|
19288
19975
|
const envVars = [
|
|
19289
19976
|
{ name: "CLAUDE_CLI", provider: "claude-code" },
|
|
@@ -19535,11 +20222,11 @@ async function getCurrentVersion() {
|
|
|
19535
20222
|
return result.dependencies["@defai.digital/automatosx"]?.version || "unknown";
|
|
19536
20223
|
} catch (error) {
|
|
19537
20224
|
const { readFile: readFile9 } = await import('fs/promises');
|
|
19538
|
-
const { dirname:
|
|
20225
|
+
const { dirname: dirname14, join: join27 } = await import('path');
|
|
19539
20226
|
const { fileURLToPath: fileURLToPath9 } = await import('url');
|
|
19540
20227
|
const __filename6 = fileURLToPath9(import.meta.url);
|
|
19541
|
-
const __dirname7 =
|
|
19542
|
-
const pkgPath =
|
|
20228
|
+
const __dirname7 = dirname14(__filename6);
|
|
20229
|
+
const pkgPath = join27(__dirname7, "../../../package.json");
|
|
19543
20230
|
const content = await readFile9(pkgPath, "utf-8");
|
|
19544
20231
|
const pkg = JSON.parse(content);
|
|
19545
20232
|
return pkg.version;
|
|
@@ -22263,11 +22950,11 @@ var qmarksTestNoExtDot = ([$0]) => {
|
|
|
22263
22950
|
return (f) => f.length === len && f !== "." && f !== "..";
|
|
22264
22951
|
};
|
|
22265
22952
|
var defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
|
|
22266
|
-
var
|
|
22953
|
+
var path5 = {
|
|
22267
22954
|
win32: { sep: "\\" },
|
|
22268
22955
|
posix: { sep: "/" }
|
|
22269
22956
|
};
|
|
22270
|
-
var sep2 = defaultPlatform === "win32" ?
|
|
22957
|
+
var sep2 = defaultPlatform === "win32" ? path5.win32.sep : path5.posix.sep;
|
|
22271
22958
|
minimatch.sep = sep2;
|
|
22272
22959
|
var GLOBSTAR = Symbol("globstar **");
|
|
22273
22960
|
minimatch.GLOBSTAR = GLOBSTAR;
|
|
@@ -25470,12 +26157,12 @@ var PathBase = class {
|
|
|
25470
26157
|
/**
|
|
25471
26158
|
* Get the Path object referenced by the string path, resolved from this Path
|
|
25472
26159
|
*/
|
|
25473
|
-
resolve(
|
|
25474
|
-
if (!
|
|
26160
|
+
resolve(path6) {
|
|
26161
|
+
if (!path6) {
|
|
25475
26162
|
return this;
|
|
25476
26163
|
}
|
|
25477
|
-
const rootPath = this.getRootString(
|
|
25478
|
-
const dir =
|
|
26164
|
+
const rootPath = this.getRootString(path6);
|
|
26165
|
+
const dir = path6.substring(rootPath.length);
|
|
25479
26166
|
const dirParts = dir.split(this.splitSep);
|
|
25480
26167
|
const result = rootPath ? this.getRoot(rootPath).#resolveParts(dirParts) : this.#resolveParts(dirParts);
|
|
25481
26168
|
return result;
|
|
@@ -26227,8 +26914,8 @@ var PathWin32 = class _PathWin32 extends PathBase {
|
|
|
26227
26914
|
/**
|
|
26228
26915
|
* @internal
|
|
26229
26916
|
*/
|
|
26230
|
-
getRootString(
|
|
26231
|
-
return win32.parse(
|
|
26917
|
+
getRootString(path6) {
|
|
26918
|
+
return win32.parse(path6).root;
|
|
26232
26919
|
}
|
|
26233
26920
|
/**
|
|
26234
26921
|
* @internal
|
|
@@ -26274,8 +26961,8 @@ var PathPosix = class _PathPosix extends PathBase {
|
|
|
26274
26961
|
/**
|
|
26275
26962
|
* @internal
|
|
26276
26963
|
*/
|
|
26277
|
-
getRootString(
|
|
26278
|
-
return
|
|
26964
|
+
getRootString(path6) {
|
|
26965
|
+
return path6.startsWith("/") ? "/" : "";
|
|
26279
26966
|
}
|
|
26280
26967
|
/**
|
|
26281
26968
|
* @internal
|
|
@@ -26324,8 +27011,8 @@ var PathScurryBase = class {
|
|
|
26324
27011
|
*
|
|
26325
27012
|
* @internal
|
|
26326
27013
|
*/
|
|
26327
|
-
constructor(cwd = process.cwd(), pathImpl, sep4, { nocase, childrenCacheSize = 16 * 1024, fs:
|
|
26328
|
-
this.#fs = fsFromOption(
|
|
27014
|
+
constructor(cwd = process.cwd(), pathImpl, sep4, { nocase, childrenCacheSize = 16 * 1024, fs: fs5 = defaultFS } = {}) {
|
|
27015
|
+
this.#fs = fsFromOption(fs5);
|
|
26329
27016
|
if (cwd instanceof URL || cwd.startsWith("file://")) {
|
|
26330
27017
|
cwd = fileURLToPath(cwd);
|
|
26331
27018
|
}
|
|
@@ -26364,11 +27051,11 @@ var PathScurryBase = class {
|
|
|
26364
27051
|
/**
|
|
26365
27052
|
* Get the depth of a provided path, string, or the cwd
|
|
26366
27053
|
*/
|
|
26367
|
-
depth(
|
|
26368
|
-
if (typeof
|
|
26369
|
-
|
|
27054
|
+
depth(path6 = this.cwd) {
|
|
27055
|
+
if (typeof path6 === "string") {
|
|
27056
|
+
path6 = this.cwd.resolve(path6);
|
|
26370
27057
|
}
|
|
26371
|
-
return
|
|
27058
|
+
return path6.depth();
|
|
26372
27059
|
}
|
|
26373
27060
|
/**
|
|
26374
27061
|
* Return the cache of child entries. Exposed so subclasses can create
|
|
@@ -26855,9 +27542,9 @@ var PathScurryBase = class {
|
|
|
26855
27542
|
process2();
|
|
26856
27543
|
return results;
|
|
26857
27544
|
}
|
|
26858
|
-
chdir(
|
|
27545
|
+
chdir(path6 = this.cwd) {
|
|
26859
27546
|
const oldCwd = this.cwd;
|
|
26860
|
-
this.cwd = typeof
|
|
27547
|
+
this.cwd = typeof path6 === "string" ? this.cwd.resolve(path6) : path6;
|
|
26861
27548
|
this.cwd[setAsCwd](oldCwd);
|
|
26862
27549
|
}
|
|
26863
27550
|
};
|
|
@@ -26883,8 +27570,8 @@ var PathScurryWin32 = class extends PathScurryBase {
|
|
|
26883
27570
|
/**
|
|
26884
27571
|
* @internal
|
|
26885
27572
|
*/
|
|
26886
|
-
newRoot(
|
|
26887
|
-
return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs:
|
|
27573
|
+
newRoot(fs5) {
|
|
27574
|
+
return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs5 });
|
|
26888
27575
|
}
|
|
26889
27576
|
/**
|
|
26890
27577
|
* Return true if the provided path string is an absolute path
|
|
@@ -26912,8 +27599,8 @@ var PathScurryPosix = class extends PathScurryBase {
|
|
|
26912
27599
|
/**
|
|
26913
27600
|
* @internal
|
|
26914
27601
|
*/
|
|
26915
|
-
newRoot(
|
|
26916
|
-
return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs:
|
|
27602
|
+
newRoot(fs5) {
|
|
27603
|
+
return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs5 });
|
|
26917
27604
|
}
|
|
26918
27605
|
/**
|
|
26919
27606
|
* Return true if the provided path string is an absolute path
|
|
@@ -27219,8 +27906,8 @@ var MatchRecord = class {
|
|
|
27219
27906
|
}
|
|
27220
27907
|
// match, absolute, ifdir
|
|
27221
27908
|
entries() {
|
|
27222
|
-
return [...this.store.entries()].map(([
|
|
27223
|
-
|
|
27909
|
+
return [...this.store.entries()].map(([path6, n]) => [
|
|
27910
|
+
path6,
|
|
27224
27911
|
!!(n & 2),
|
|
27225
27912
|
!!(n & 1)
|
|
27226
27913
|
]);
|
|
@@ -27425,9 +28112,9 @@ var GlobUtil = class {
|
|
|
27425
28112
|
signal;
|
|
27426
28113
|
maxDepth;
|
|
27427
28114
|
includeChildMatches;
|
|
27428
|
-
constructor(patterns,
|
|
28115
|
+
constructor(patterns, path6, opts) {
|
|
27429
28116
|
this.patterns = patterns;
|
|
27430
|
-
this.path =
|
|
28117
|
+
this.path = path6;
|
|
27431
28118
|
this.opts = opts;
|
|
27432
28119
|
this.#sep = !opts.posix && opts.platform === "win32" ? "\\" : "/";
|
|
27433
28120
|
this.includeChildMatches = opts.includeChildMatches !== false;
|
|
@@ -27446,11 +28133,11 @@ var GlobUtil = class {
|
|
|
27446
28133
|
});
|
|
27447
28134
|
}
|
|
27448
28135
|
}
|
|
27449
|
-
#ignored(
|
|
27450
|
-
return this.seen.has(
|
|
28136
|
+
#ignored(path6) {
|
|
28137
|
+
return this.seen.has(path6) || !!this.#ignore?.ignored?.(path6);
|
|
27451
28138
|
}
|
|
27452
|
-
#childrenIgnored(
|
|
27453
|
-
return !!this.#ignore?.childrenIgnored?.(
|
|
28139
|
+
#childrenIgnored(path6) {
|
|
28140
|
+
return !!this.#ignore?.childrenIgnored?.(path6);
|
|
27454
28141
|
}
|
|
27455
28142
|
// backpressure mechanism
|
|
27456
28143
|
pause() {
|
|
@@ -27665,8 +28352,8 @@ var GlobUtil = class {
|
|
|
27665
28352
|
};
|
|
27666
28353
|
var GlobWalker = class extends GlobUtil {
|
|
27667
28354
|
matches = /* @__PURE__ */ new Set();
|
|
27668
|
-
constructor(patterns,
|
|
27669
|
-
super(patterns,
|
|
28355
|
+
constructor(patterns, path6, opts) {
|
|
28356
|
+
super(patterns, path6, opts);
|
|
27670
28357
|
}
|
|
27671
28358
|
matchEmit(e) {
|
|
27672
28359
|
this.matches.add(e);
|
|
@@ -27703,8 +28390,8 @@ var GlobWalker = class extends GlobUtil {
|
|
|
27703
28390
|
};
|
|
27704
28391
|
var GlobStream = class extends GlobUtil {
|
|
27705
28392
|
results;
|
|
27706
|
-
constructor(patterns,
|
|
27707
|
-
super(patterns,
|
|
28393
|
+
constructor(patterns, path6, opts) {
|
|
28394
|
+
super(patterns, path6, opts);
|
|
27708
28395
|
this.results = new Minipass({
|
|
27709
28396
|
signal: this.signal,
|
|
27710
28397
|
objectMode: true
|
|
@@ -28329,8 +29016,8 @@ var ConfigManager = class {
|
|
|
28329
29016
|
* @returns User configuration or empty object if not found
|
|
28330
29017
|
*/
|
|
28331
29018
|
async readUserConfig() {
|
|
28332
|
-
const
|
|
28333
|
-
return this.readConfig(
|
|
29019
|
+
const path6 = getUserConfigPath();
|
|
29020
|
+
return this.readConfig(path6, "user");
|
|
28334
29021
|
}
|
|
28335
29022
|
/**
|
|
28336
29023
|
* Read project-level Gemini CLI configuration
|
|
@@ -28338,8 +29025,8 @@ var ConfigManager = class {
|
|
|
28338
29025
|
* @returns Project configuration or empty object if not found
|
|
28339
29026
|
*/
|
|
28340
29027
|
async readProjectConfig() {
|
|
28341
|
-
const
|
|
28342
|
-
return this.readConfig(
|
|
29028
|
+
const path6 = getProjectConfigPath();
|
|
29029
|
+
return this.readConfig(path6, "project");
|
|
28343
29030
|
}
|
|
28344
29031
|
/**
|
|
28345
29032
|
* Read configuration from a specific path with caching
|
|
@@ -28349,7 +29036,7 @@ var ConfigManager = class {
|
|
|
28349
29036
|
* @returns Configuration object
|
|
28350
29037
|
* @private
|
|
28351
29038
|
*/
|
|
28352
|
-
async readConfig(
|
|
29039
|
+
async readConfig(path6, scope) {
|
|
28353
29040
|
const cached = this.cache.get(scope);
|
|
28354
29041
|
if (cached && Date.now() - cached.timestamp < this.ttl) {
|
|
28355
29042
|
return cached.data;
|
|
@@ -28360,13 +29047,13 @@ var ConfigManager = class {
|
|
|
28360
29047
|
}
|
|
28361
29048
|
const readOperation = (async () => {
|
|
28362
29049
|
try {
|
|
28363
|
-
const exists = await fileExists(
|
|
29050
|
+
const exists = await fileExists(path6);
|
|
28364
29051
|
if (!exists) {
|
|
28365
29052
|
const emptyConfig = {};
|
|
28366
29053
|
this.cache.set(scope, { data: emptyConfig, timestamp: Date.now() });
|
|
28367
29054
|
return emptyConfig;
|
|
28368
29055
|
}
|
|
28369
|
-
const config = await readJsonFile(
|
|
29056
|
+
const config = await readJsonFile(path6);
|
|
28370
29057
|
this.cache.set(scope, { data: config, timestamp: Date.now() });
|
|
28371
29058
|
return config;
|
|
28372
29059
|
} catch (error) {
|
|
@@ -28375,8 +29062,8 @@ var ConfigManager = class {
|
|
|
28375
29062
|
}
|
|
28376
29063
|
throw new GeminiCLIError(
|
|
28377
29064
|
"INVALID_CONFIG" /* INVALID_CONFIG */,
|
|
28378
|
-
`Failed to read configuration from ${
|
|
28379
|
-
{ path:
|
|
29065
|
+
`Failed to read configuration from ${path6}`,
|
|
29066
|
+
{ path: path6, originalError: error }
|
|
28380
29067
|
);
|
|
28381
29068
|
} finally {
|
|
28382
29069
|
this.pendingReads.delete(scope);
|
|
@@ -29044,8 +29731,8 @@ var CommandTranslator = class {
|
|
|
29044
29731
|
paths.push(getProjectCommandsPath());
|
|
29045
29732
|
}
|
|
29046
29733
|
}
|
|
29047
|
-
for (const
|
|
29048
|
-
const discovered = await this.scanDirectory(
|
|
29734
|
+
for (const path6 of paths) {
|
|
29735
|
+
const discovered = await this.scanDirectory(path6);
|
|
29049
29736
|
commands.push(...discovered);
|
|
29050
29737
|
}
|
|
29051
29738
|
return commands;
|
|
@@ -29086,8 +29773,8 @@ var CommandTranslator = class {
|
|
|
29086
29773
|
const results = [];
|
|
29087
29774
|
for (const name of commandNames) {
|
|
29088
29775
|
try {
|
|
29089
|
-
const
|
|
29090
|
-
results.push(
|
|
29776
|
+
const path6 = await this.importCommand(name, outputDir, options);
|
|
29777
|
+
results.push(path6);
|
|
29091
29778
|
} catch (error) {
|
|
29092
29779
|
if (error instanceof GeminiCLIError) {
|
|
29093
29780
|
console.error(`Failed to import ${name}: ${error.message}`);
|
|
@@ -29973,12 +30660,109 @@ var geminiCommand = {
|
|
|
29973
30660
|
}
|
|
29974
30661
|
};
|
|
29975
30662
|
|
|
30663
|
+
// src/cli/commands/provider-limits.ts
|
|
30664
|
+
init_esm_shims();
|
|
30665
|
+
init_provider_limit_manager();
|
|
30666
|
+
init_logger();
|
|
30667
|
+
function formatDuration(ms) {
|
|
30668
|
+
const hours = Math.floor(ms / (1e3 * 60 * 60));
|
|
30669
|
+
const minutes = Math.floor(ms % (1e3 * 60 * 60) / (1e3 * 60));
|
|
30670
|
+
if (hours > 0) {
|
|
30671
|
+
if (minutes > 0) {
|
|
30672
|
+
return `${hours}h ${minutes}m`;
|
|
30673
|
+
}
|
|
30674
|
+
return `${hours}h`;
|
|
30675
|
+
}
|
|
30676
|
+
if (minutes > 0) {
|
|
30677
|
+
return `${minutes}m`;
|
|
30678
|
+
}
|
|
30679
|
+
return "< 1m";
|
|
30680
|
+
}
|
|
30681
|
+
var providerLimitsCommand = {
|
|
30682
|
+
command: "provider-limits",
|
|
30683
|
+
aliases: ["pl", "limits"],
|
|
30684
|
+
describe: "Show current provider limit status",
|
|
30685
|
+
builder: (yargs2) => {
|
|
30686
|
+
return yargs2.option("json", {
|
|
30687
|
+
describe: "Output in JSON format",
|
|
30688
|
+
type: "boolean",
|
|
30689
|
+
default: false
|
|
30690
|
+
}).example("$0 provider-limits", "Show provider limits").example("$0 provider-limits --json", "Show limits in JSON format");
|
|
30691
|
+
},
|
|
30692
|
+
handler: async (argv2) => {
|
|
30693
|
+
try {
|
|
30694
|
+
const limitManager = getProviderLimitManager();
|
|
30695
|
+
await limitManager.initialize();
|
|
30696
|
+
const states = limitManager.getAllStates();
|
|
30697
|
+
const manualOverride = limitManager.getManualOverride();
|
|
30698
|
+
const now = Date.now();
|
|
30699
|
+
if (argv2.json) {
|
|
30700
|
+
const output = {
|
|
30701
|
+
limits: Array.from(states.entries()).map(([name, state]) => ({
|
|
30702
|
+
provider: name,
|
|
30703
|
+
status: state.status,
|
|
30704
|
+
window: state.window,
|
|
30705
|
+
detectedAtMs: state.detectedAtMs,
|
|
30706
|
+
resetAtMs: state.resetAtMs,
|
|
30707
|
+
remainingMs: state.resetAtMs - now,
|
|
30708
|
+
reason: state.reason,
|
|
30709
|
+
manualHold: state.manualHold
|
|
30710
|
+
})),
|
|
30711
|
+
manualOverride: manualOverride || null,
|
|
30712
|
+
timestamp: Date.now()
|
|
30713
|
+
};
|
|
30714
|
+
console.log(JSON.stringify(output, null, 2));
|
|
30715
|
+
} else {
|
|
30716
|
+
console.log();
|
|
30717
|
+
console.log(chalk27.bold("\u{1F4CA} Provider Limits Status"));
|
|
30718
|
+
console.log();
|
|
30719
|
+
if (states.size === 0) {
|
|
30720
|
+
console.log(chalk27.green(" \u2705 No limits detected. All providers available."));
|
|
30721
|
+
} else {
|
|
30722
|
+
for (const [name, state] of states.entries()) {
|
|
30723
|
+
const resetDate = new Date(state.resetAtMs);
|
|
30724
|
+
const remainingMs = state.resetAtMs - now;
|
|
30725
|
+
const remainingStr = formatDuration(remainingMs);
|
|
30726
|
+
console.log(chalk27.yellow(` \u26A0\uFE0F ${name}:`));
|
|
30727
|
+
console.log(` Status: ${state.status}`);
|
|
30728
|
+
console.log(` Window: ${state.window}`);
|
|
30729
|
+
console.log(` Resets: ${resetDate.toLocaleString()} (${remainingStr})`);
|
|
30730
|
+
if (state.reason) {
|
|
30731
|
+
console.log(` Reason: ${state.reason}`);
|
|
30732
|
+
}
|
|
30733
|
+
console.log();
|
|
30734
|
+
}
|
|
30735
|
+
}
|
|
30736
|
+
if (manualOverride) {
|
|
30737
|
+
console.log(chalk27.cyan(` \u{1F527} Manual Override: ${manualOverride.provider}`));
|
|
30738
|
+
if (manualOverride.expiresAtMs) {
|
|
30739
|
+
const expiresDate = new Date(manualOverride.expiresAtMs);
|
|
30740
|
+
const expiresIn = formatDuration(manualOverride.expiresAtMs - now);
|
|
30741
|
+
console.log(` Expires: ${expiresDate.toLocaleString()} (${expiresIn})`);
|
|
30742
|
+
} else {
|
|
30743
|
+
console.log(` Expires: Never`);
|
|
30744
|
+
}
|
|
30745
|
+
console.log();
|
|
30746
|
+
}
|
|
30747
|
+
console.log(chalk27.gray(' Use "ax status" for detailed provider information.'));
|
|
30748
|
+
console.log();
|
|
30749
|
+
}
|
|
30750
|
+
} catch (error) {
|
|
30751
|
+
logger.error("Failed to get provider limits", {
|
|
30752
|
+
error: error.message
|
|
30753
|
+
});
|
|
30754
|
+
console.error(chalk27.red(`Error: ${error.message}`));
|
|
30755
|
+
process.exit(1);
|
|
30756
|
+
}
|
|
30757
|
+
}
|
|
30758
|
+
};
|
|
30759
|
+
|
|
29976
30760
|
// src/cli/index.ts
|
|
29977
30761
|
installExitHandlers();
|
|
29978
30762
|
var VERSION2 = getVersion();
|
|
29979
30763
|
globalTracker.mark("cli_start");
|
|
29980
30764
|
globalTracker.mark("yargs_parse_start");
|
|
29981
|
-
var argv = await yargs(hideBin(process.argv)).scriptName("automatosx").usage("$0 <command> [options]").usage("\nAI Agent Orchestration Platform").example("$0 init", "Initialize project").example("$0 agent create backend --template developer", "Create agent from template").example("$0 agent list", "List all agents").example('$0 run assistant "Hello"', "Run assistant agent").example('$0 run backend "task" --interactive', "Run with interactive checkpoints").example("$0 resume <run-id>", "Resume from checkpoint").example("$0 runs list", "List checkpoint runs").example('$0 session create "Build API" backend', "Create multi-agent session").example("$0 session list", "List all sessions").example("$0 workspace stats", "Show workspace statistics").example("$0 list agents", "List available agents").example('$0 memory search "topic"', "Search memory").example("$0 cache status", "View cache statistics").example("$0 config --list", "View configuration").example("$0 mcp", "Start MCP server for Claude Code").example("$0 update", "Update to latest version").example("$0 gemini status", "Show Gemini CLI integration status").option("debug", {
|
|
30765
|
+
var argv = await yargs(hideBin(process.argv)).scriptName("automatosx").usage("$0 <command> [options]").usage("\nAI Agent Orchestration Platform").example("$0 init", "Initialize project").example("$0 agent create backend --template developer", "Create agent from template").example("$0 agent list", "List all agents").example('$0 run assistant "Hello"', "Run assistant agent").example('$0 run backend "task" --interactive', "Run with interactive checkpoints").example("$0 resume <run-id>", "Resume from checkpoint").example("$0 runs list", "List checkpoint runs").example('$0 session create "Build API" backend', "Create multi-agent session").example("$0 session list", "List all sessions").example("$0 workspace stats", "Show workspace statistics").example("$0 list agents", "List available agents").example('$0 memory search "topic"', "Search memory").example("$0 cache status", "View cache statistics").example("$0 config --list", "View configuration").example("$0 provider-limits", "Show provider usage limits").example("$0 mcp", "Start MCP server for Claude Code").example("$0 update", "Update to latest version").example("$0 gemini status", "Show Gemini CLI integration status").option("debug", {
|
|
29982
30766
|
alias: "D",
|
|
29983
30767
|
type: "boolean",
|
|
29984
30768
|
description: "Enable debug mode with verbose output",
|
|
@@ -29993,7 +30777,7 @@ var argv = await yargs(hideBin(process.argv)).scriptName("automatosx").usage("$0
|
|
|
29993
30777
|
type: "string",
|
|
29994
30778
|
description: "Path to custom config file",
|
|
29995
30779
|
global: true
|
|
29996
|
-
}).command(initCommand).command(agentCommand).command(listCommand).command(runCommand).command(resumeCommand).command(runsCommand).command(sessionCommand).command(workspaceCommand).command(cacheCommand).command(configCommand).command(statusCommand3).command(memoryCommand).command(mcpCommand).command(geminiCommand).command(updateCommand).demandCommand(1, "You must provide a command. Run --help for usage.").help().version(VERSION2).alias("h", "help").alias("v", "version").strict().wrap(Math.min(120, yargs().terminalWidth())).parse();
|
|
30780
|
+
}).command(initCommand).command(agentCommand).command(listCommand).command(runCommand).command(resumeCommand).command(runsCommand).command(sessionCommand).command(workspaceCommand).command(cacheCommand).command(configCommand).command(statusCommand3).command(memoryCommand).command(mcpCommand).command(geminiCommand).command(providerLimitsCommand).command(updateCommand).demandCommand(1, "You must provide a command. Run --help for usage.").help().version(VERSION2).alias("h", "help").alias("v", "version").strict().wrap(Math.min(120, yargs().terminalWidth())).parse();
|
|
29997
30781
|
globalTracker.mark("yargs_parse_end");
|
|
29998
30782
|
globalTracker.measure("yargs_parsing", "yargs_parse_start", "yargs_parse_end");
|
|
29999
30783
|
globalTracker.mark("options_setup_start");
|