@eldrforge/tree-execution 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +130 -0
- package/dist/TreeExecutor.d.ts +113 -0
- package/dist/TreeExecutor.d.ts.map +1 -0
- package/dist/TreeExecutor.js +113 -0
- package/dist/TreeExecutor.js.map +1 -0
- package/dist/checkpoint/CheckpointManager.d.ts +18 -0
- package/dist/checkpoint/CheckpointManager.d.ts.map +1 -0
- package/dist/checkpoint/CheckpointManager.js +156 -0
- package/dist/checkpoint/CheckpointManager.js.map +1 -0
- package/dist/checkpoint/index.d.ts +5 -0
- package/dist/checkpoint/index.d.ts.map +1 -0
- package/dist/checkpoint/index.js +5 -0
- package/dist/checkpoint/index.js.map +1 -0
- package/dist/execution/CommandValidator.d.ts +25 -0
- package/dist/execution/CommandValidator.d.ts.map +1 -0
- package/dist/execution/CommandValidator.js +129 -0
- package/dist/execution/CommandValidator.js.map +1 -0
- package/dist/execution/DependencyChecker.d.ts +47 -0
- package/dist/execution/DependencyChecker.d.ts.map +1 -0
- package/dist/execution/DependencyChecker.js +95 -0
- package/dist/execution/DependencyChecker.js.map +1 -0
- package/dist/execution/DynamicTaskPool.d.ts +118 -0
- package/dist/execution/DynamicTaskPool.d.ts.map +1 -0
- package/dist/execution/DynamicTaskPool.js +658 -0
- package/dist/execution/DynamicTaskPool.js.map +1 -0
- package/dist/execution/RecoveryManager.d.ts +89 -0
- package/dist/execution/RecoveryManager.d.ts.map +1 -0
- package/dist/execution/RecoveryManager.js +592 -0
- package/dist/execution/RecoveryManager.js.map +1 -0
- package/dist/execution/ResourceMonitor.d.ts +73 -0
- package/dist/execution/ResourceMonitor.d.ts.map +1 -0
- package/dist/execution/ResourceMonitor.js +148 -0
- package/dist/execution/ResourceMonitor.js.map +1 -0
- package/dist/execution/Scheduler.d.ts +36 -0
- package/dist/execution/Scheduler.d.ts.map +1 -0
- package/dist/execution/Scheduler.js +83 -0
- package/dist/execution/Scheduler.js.map +1 -0
- package/dist/execution/TreeExecutionAdapter.d.ts +45 -0
- package/dist/execution/TreeExecutionAdapter.d.ts.map +1 -0
- package/dist/execution/TreeExecutionAdapter.js +249 -0
- package/dist/execution/TreeExecutionAdapter.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/tree.d.ts +13 -0
- package/dist/tree.d.ts.map +1 -0
- package/dist/tree.js +2453 -0
- package/dist/tree.js.map +1 -0
- package/dist/types/config.d.ts +172 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +2 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/parallelExecution.d.ts +108 -0
- package/dist/types/parallelExecution.d.ts.map +1 -0
- package/dist/types/parallelExecution.js +2 -0
- package/dist/types/parallelExecution.js.map +1 -0
- package/dist/util/commandStubs.d.ts +22 -0
- package/dist/util/commandStubs.d.ts.map +1 -0
- package/dist/util/commandStubs.js +49 -0
- package/dist/util/commandStubs.js.map +1 -0
- package/dist/util/logger.d.ts +14 -0
- package/dist/util/logger.d.ts.map +1 -0
- package/dist/util/logger.js +15 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/mutex.d.ts +38 -0
- package/dist/util/mutex.d.ts.map +1 -0
- package/dist/util/mutex.js +101 -0
- package/dist/util/mutex.js.map +1 -0
- package/dist/util/treeUtils.d.ts +46 -0
- package/dist/util/treeUtils.d.ts.map +1 -0
- package/dist/util/treeUtils.js +74 -0
- package/dist/util/treeUtils.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export interface ResourceMetrics {
|
|
2
|
+
peakConcurrency: number;
|
|
3
|
+
averageConcurrency: number;
|
|
4
|
+
totalAllocations: number;
|
|
5
|
+
totalReleases: number;
|
|
6
|
+
freeMemoryBytes?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* ResourceMonitor manages concurrency limits and tracks resource utilization
|
|
10
|
+
*/
|
|
11
|
+
export declare class ResourceMonitor {
|
|
12
|
+
private maxConcurrency;
|
|
13
|
+
private currentConcurrency;
|
|
14
|
+
private metrics;
|
|
15
|
+
private allocationHistory;
|
|
16
|
+
private logger;
|
|
17
|
+
private readonly MEMORY_THRESHOLD_PERCENT;
|
|
18
|
+
constructor(maxConcurrency: number);
|
|
19
|
+
/**
|
|
20
|
+
* Check if we can allocate N slots
|
|
21
|
+
*/
|
|
22
|
+
canAllocate(count?: number): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Log a warning if system memory is low
|
|
25
|
+
*/
|
|
26
|
+
private checkSystemMemory;
|
|
27
|
+
/**
|
|
28
|
+
* Allocate resource slots
|
|
29
|
+
* @returns true if allocation succeeded, false if not enough slots available
|
|
30
|
+
*/
|
|
31
|
+
allocate(count?: number): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Release resource slots
|
|
34
|
+
*/
|
|
35
|
+
release(count?: number): void;
|
|
36
|
+
/**
|
|
37
|
+
* Get number of available slots
|
|
38
|
+
*/
|
|
39
|
+
getAvailableSlots(): number;
|
|
40
|
+
/**
|
|
41
|
+
* Get current concurrency level
|
|
42
|
+
*/
|
|
43
|
+
getCurrentConcurrency(): number;
|
|
44
|
+
/**
|
|
45
|
+
* Get maximum concurrency limit
|
|
46
|
+
*/
|
|
47
|
+
getMaxConcurrency(): number;
|
|
48
|
+
/**
|
|
49
|
+
* Get resource utilization metrics
|
|
50
|
+
*/
|
|
51
|
+
getMetrics(): ResourceMetrics;
|
|
52
|
+
/**
|
|
53
|
+
* Get utilization percentage (0-100)
|
|
54
|
+
*/
|
|
55
|
+
getUtilization(): number;
|
|
56
|
+
/**
|
|
57
|
+
* Check if resources are fully utilized
|
|
58
|
+
*/
|
|
59
|
+
isFullyUtilized(): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Check if resources are idle
|
|
62
|
+
*/
|
|
63
|
+
isIdle(): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Update average concurrency calculation
|
|
66
|
+
*/
|
|
67
|
+
private updateAverageConcurrency;
|
|
68
|
+
/**
|
|
69
|
+
* Reset metrics (useful for testing)
|
|
70
|
+
*/
|
|
71
|
+
reset(): void;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=ResourceMonitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResourceMonitor.d.ts","sourceRoot":"","sources":["../../src/execution/ResourceMonitor.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,qBAAa,eAAe;IACxB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,iBAAiB,CAAgB;IACzC,OAAO,CAAC,MAAM,CAAe;IAG7B,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAK;gBAElC,cAAc,EAAE,MAAM;IAUlC;;OAEG;IACH,WAAW,CAAC,KAAK,GAAE,MAAU,GAAG,OAAO;IAYvC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAgBzB;;;OAGG;IACH,QAAQ,CAAC,KAAK,GAAE,MAAU,GAAG,OAAO;IAoBpC;;OAEG;IACH,OAAO,CAAC,KAAK,GAAE,MAAU,GAAG,IAAI;IAUhC;;OAEG;IACH,iBAAiB,IAAI,MAAM;IAI3B;;OAEG;IACH,qBAAqB,IAAI,MAAM;IAI/B;;OAEG;IACH,iBAAiB,IAAI,MAAM;IAI3B;;OAEG;IACH,UAAU,IAAI,eAAe;IAI7B;;OAEG;IACH,cAAc,IAAI,MAAM;IAKxB;;OAEG;IACH,eAAe,IAAI,OAAO;IAI1B;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAUhC;;OAEG;IACH,KAAK,IAAI,IAAI;CAUhB"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { getLogger } from '../util/logger.js';
|
|
2
|
+
import * as os from 'os';
|
|
3
|
+
/**
|
|
4
|
+
* ResourceMonitor manages concurrency limits and tracks resource utilization
|
|
5
|
+
*/
|
|
6
|
+
export class ResourceMonitor {
|
|
7
|
+
maxConcurrency;
|
|
8
|
+
currentConcurrency = 0;
|
|
9
|
+
metrics;
|
|
10
|
+
allocationHistory = [];
|
|
11
|
+
logger = getLogger();
|
|
12
|
+
// Memory threshold: warn if free memory is below 5%
|
|
13
|
+
MEMORY_THRESHOLD_PERCENT = 5;
|
|
14
|
+
constructor(maxConcurrency) {
|
|
15
|
+
this.maxConcurrency = maxConcurrency;
|
|
16
|
+
this.metrics = {
|
|
17
|
+
peakConcurrency: 0,
|
|
18
|
+
averageConcurrency: 0,
|
|
19
|
+
totalAllocations: 0,
|
|
20
|
+
totalReleases: 0
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Check if we can allocate N slots
|
|
25
|
+
*/
|
|
26
|
+
canAllocate(count = 1) {
|
|
27
|
+
// Check concurrency limit
|
|
28
|
+
if (this.currentConcurrency + count > this.maxConcurrency) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
// Check system memory (soft check)
|
|
32
|
+
this.checkSystemMemory();
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Log a warning if system memory is low
|
|
37
|
+
*/
|
|
38
|
+
checkSystemMemory() {
|
|
39
|
+
try {
|
|
40
|
+
const freeMem = os.freemem();
|
|
41
|
+
const totalMem = os.totalmem();
|
|
42
|
+
const freePercent = (freeMem / totalMem) * 100;
|
|
43
|
+
if (freePercent < this.MEMORY_THRESHOLD_PERCENT) {
|
|
44
|
+
const freeGB = (freeMem / (1024 * 1024 * 1024)).toFixed(2);
|
|
45
|
+
this.logger.warn(`SYSTEM_MEMORY_LOW: System memory is running low | Free: ${freeGB}GB (${freePercent.toFixed(1)}%) | Action: Proceeding with caution`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
// Ignore errors in memory check to avoid blocking execution
|
|
50
|
+
this.logger.debug(`Failed to check system memory: ${error}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Allocate resource slots
|
|
55
|
+
* @returns true if allocation succeeded, false if not enough slots available
|
|
56
|
+
*/
|
|
57
|
+
allocate(count = 1) {
|
|
58
|
+
if (!this.canAllocate(count)) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
this.currentConcurrency += count;
|
|
62
|
+
this.metrics.totalAllocations += count;
|
|
63
|
+
this.metrics.peakConcurrency = Math.max(this.metrics.peakConcurrency, this.currentConcurrency);
|
|
64
|
+
this.allocationHistory.push(this.currentConcurrency);
|
|
65
|
+
this.updateAverageConcurrency();
|
|
66
|
+
this.logger.debug(`Allocated ${count} slot(s) (${this.currentConcurrency}/${this.maxConcurrency})`);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Release resource slots
|
|
71
|
+
*/
|
|
72
|
+
release(count = 1) {
|
|
73
|
+
this.currentConcurrency = Math.max(0, this.currentConcurrency - count);
|
|
74
|
+
this.metrics.totalReleases += count;
|
|
75
|
+
this.allocationHistory.push(this.currentConcurrency);
|
|
76
|
+
this.updateAverageConcurrency();
|
|
77
|
+
this.logger.debug(`Released ${count} slot(s) (${this.currentConcurrency}/${this.maxConcurrency})`);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get number of available slots
|
|
81
|
+
*/
|
|
82
|
+
getAvailableSlots() {
|
|
83
|
+
return this.maxConcurrency - this.currentConcurrency;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get current concurrency level
|
|
87
|
+
*/
|
|
88
|
+
getCurrentConcurrency() {
|
|
89
|
+
return this.currentConcurrency;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get maximum concurrency limit
|
|
93
|
+
*/
|
|
94
|
+
getMaxConcurrency() {
|
|
95
|
+
return this.maxConcurrency;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get resource utilization metrics
|
|
99
|
+
*/
|
|
100
|
+
getMetrics() {
|
|
101
|
+
return { ...this.metrics };
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get utilization percentage (0-100)
|
|
105
|
+
*/
|
|
106
|
+
getUtilization() {
|
|
107
|
+
if (this.maxConcurrency === 0)
|
|
108
|
+
return 0;
|
|
109
|
+
return (this.currentConcurrency / this.maxConcurrency) * 100;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Check if resources are fully utilized
|
|
113
|
+
*/
|
|
114
|
+
isFullyUtilized() {
|
|
115
|
+
return this.currentConcurrency >= this.maxConcurrency;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Check if resources are idle
|
|
119
|
+
*/
|
|
120
|
+
isIdle() {
|
|
121
|
+
return this.currentConcurrency === 0;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Update average concurrency calculation
|
|
125
|
+
*/
|
|
126
|
+
updateAverageConcurrency() {
|
|
127
|
+
if (this.allocationHistory.length === 0) {
|
|
128
|
+
this.metrics.averageConcurrency = 0;
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const sum = this.allocationHistory.reduce((a, b) => a + b, 0);
|
|
132
|
+
this.metrics.averageConcurrency = sum / this.allocationHistory.length;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Reset metrics (useful for testing)
|
|
136
|
+
*/
|
|
137
|
+
reset() {
|
|
138
|
+
this.currentConcurrency = 0;
|
|
139
|
+
this.allocationHistory = [];
|
|
140
|
+
this.metrics = {
|
|
141
|
+
peakConcurrency: 0,
|
|
142
|
+
averageConcurrency: 0,
|
|
143
|
+
totalAllocations: 0,
|
|
144
|
+
totalReleases: 0
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=ResourceMonitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResourceMonitor.js","sourceRoot":"","sources":["../../src/execution/ResourceMonitor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { DependencyGraph } from '@eldrforge/tree-core';
|
|
2
|
+
import type { ExecutionState } from '../types/index.js';
|
|
3
|
+
import { DependencyChecker } from './DependencyChecker.js';
|
|
4
|
+
/**
|
|
5
|
+
* Scheduler determines which packages to execute next based on priority
|
|
6
|
+
*/
|
|
7
|
+
export declare class Scheduler {
|
|
8
|
+
private graph;
|
|
9
|
+
private checker;
|
|
10
|
+
constructor(graph: DependencyGraph, checker: DependencyChecker);
|
|
11
|
+
/**
|
|
12
|
+
* Get next packages to schedule based on available slots
|
|
13
|
+
* Returns packages sorted by priority (highest priority first)
|
|
14
|
+
*/
|
|
15
|
+
getNext(availableSlots: number, state: ExecutionState): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Calculate priority score for a package
|
|
18
|
+
* Higher score = higher priority = execute sooner
|
|
19
|
+
*
|
|
20
|
+
* Priority factors:
|
|
21
|
+
* 1. Number of dependents (more = higher priority, unblocks more packages)
|
|
22
|
+
* 2. Depth in dependency tree (shallower = higher priority, enables earlier unlocking)
|
|
23
|
+
* 3. Retry attempts (fewer = higher priority, give fresh packages a chance)
|
|
24
|
+
*/
|
|
25
|
+
calculatePriority(packageName: string, state: ExecutionState): number;
|
|
26
|
+
/**
|
|
27
|
+
* Get estimated completion order (for progress reporting)
|
|
28
|
+
*/
|
|
29
|
+
getEstimatedOrder(state: ExecutionState): string[];
|
|
30
|
+
/**
|
|
31
|
+
* Predict which packages will become ready next
|
|
32
|
+
* Useful for pre-loading or progress estimation
|
|
33
|
+
*/
|
|
34
|
+
predictNextReady(state: ExecutionState): string[];
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=Scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Scheduler.d.ts","sourceRoot":"","sources":["../../src/execution/Scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D;;GAEG;AACH,qBAAa,SAAS;IAClB,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,OAAO,CAAoB;gBAEvB,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,iBAAiB;IAK9D;;;OAGG;IACH,OAAO,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,EAAE;IAchE;;;;;;;;OAQG;IACH,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM;IA2BrE;;OAEG;IACH,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,EAAE;IAQlD;;;OAGG;IACH,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,EAAE;CAwBpD"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduler determines which packages to execute next based on priority
|
|
3
|
+
*/
|
|
4
|
+
export class Scheduler {
|
|
5
|
+
graph;
|
|
6
|
+
checker;
|
|
7
|
+
constructor(graph, checker) {
|
|
8
|
+
this.graph = graph;
|
|
9
|
+
this.checker = checker;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Get next packages to schedule based on available slots
|
|
13
|
+
* Returns packages sorted by priority (highest priority first)
|
|
14
|
+
*/
|
|
15
|
+
getNext(availableSlots, state) {
|
|
16
|
+
if (availableSlots <= 0 || state.ready.length === 0) {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
// Sort ready packages by priority
|
|
20
|
+
const sorted = [...state.ready].sort((a, b) => this.calculatePriority(b, state) - this.calculatePriority(a, state));
|
|
21
|
+
// Return top N packages that fit in available slots
|
|
22
|
+
return sorted.slice(0, availableSlots);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Calculate priority score for a package
|
|
26
|
+
* Higher score = higher priority = execute sooner
|
|
27
|
+
*
|
|
28
|
+
* Priority factors:
|
|
29
|
+
* 1. Number of dependents (more = higher priority, unblocks more packages)
|
|
30
|
+
* 2. Depth in dependency tree (shallower = higher priority, enables earlier unlocking)
|
|
31
|
+
* 3. Retry attempts (fewer = higher priority, give fresh packages a chance)
|
|
32
|
+
*/
|
|
33
|
+
calculatePriority(packageName, state) {
|
|
34
|
+
let score = 0;
|
|
35
|
+
// Factor 1: More dependents = higher priority (weight: 100)
|
|
36
|
+
// Packages that unblock many others should run first
|
|
37
|
+
const dependentCount = this.checker.getDependentCount(packageName);
|
|
38
|
+
score += dependentCount * 100;
|
|
39
|
+
// Factor 2: Depth penalty (weight: -10)
|
|
40
|
+
// Prefer packages closer to the root of the dependency tree
|
|
41
|
+
const depth = this.checker.getDepth(packageName);
|
|
42
|
+
score -= depth * 10;
|
|
43
|
+
// Factor 3: Retry penalty (weight: -50)
|
|
44
|
+
// Packages that have failed before get lower priority
|
|
45
|
+
const retries = state.failed.filter(f => f.name === packageName).length;
|
|
46
|
+
score -= retries * 50;
|
|
47
|
+
// Factor 4: Bonus for packages with no dependents (leaf nodes)
|
|
48
|
+
// These are usually final deliverables and good to complete early for feedback
|
|
49
|
+
if (!this.checker.hasDependents(packageName)) {
|
|
50
|
+
score += 5;
|
|
51
|
+
}
|
|
52
|
+
return score;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get estimated completion order (for progress reporting)
|
|
56
|
+
*/
|
|
57
|
+
getEstimatedOrder(state) {
|
|
58
|
+
const allPending = [...state.pending, ...state.ready];
|
|
59
|
+
return allPending.sort((a, b) => this.calculatePriority(b, state) - this.calculatePriority(a, state));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Predict which packages will become ready next
|
|
63
|
+
* Useful for pre-loading or progress estimation
|
|
64
|
+
*/
|
|
65
|
+
predictNextReady(state) {
|
|
66
|
+
const predictions = [];
|
|
67
|
+
// Look at currently running packages
|
|
68
|
+
const runningNames = state.running.map(r => r.name);
|
|
69
|
+
// For each running package, see which pending packages only depend on it
|
|
70
|
+
for (const runningPkg of runningNames) {
|
|
71
|
+
for (const pendingPkg of state.pending) {
|
|
72
|
+
const deps = this.checker.getDependencies(pendingPkg);
|
|
73
|
+
// Check if this pending package will be ready when running completes
|
|
74
|
+
const blockedBy = Array.from(deps).filter(dep => !state.completed.includes(dep) && dep !== runningPkg);
|
|
75
|
+
if (blockedBy.length === 0) {
|
|
76
|
+
predictions.push(pendingPkg);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return predictions;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=Scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Scheduler.js","sourceRoot":"","sources":["../../src/execution/Scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { DynamicTaskPool, PoolConfig } from './DynamicTaskPool.js';
|
|
2
|
+
import type { PackageInfo } from '@eldrforge/tree-core';
|
|
3
|
+
import type { TreeExecutionConfig } from '../types/config.js';
|
|
4
|
+
/**
|
|
5
|
+
* ExecutePackageFunction type matches the signature of tree.ts executePackage
|
|
6
|
+
*/
|
|
7
|
+
export type ExecutePackageFunction = (packageName: string, packageInfo: PackageInfo, commandToRun: string, runConfig: TreeExecutionConfig, isDryRun: boolean, index: number, total: number, allPackageNames: Set<string>, isBuiltInCommand?: boolean) => Promise<{
|
|
8
|
+
success: boolean;
|
|
9
|
+
error?: any;
|
|
10
|
+
isTimeoutError?: boolean;
|
|
11
|
+
skippedNoChanges?: boolean;
|
|
12
|
+
logFile?: string;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* TreeExecutionAdapter bridges DynamicTaskPool with tree.ts executePackage
|
|
16
|
+
*/
|
|
17
|
+
export declare class TreeExecutionAdapter {
|
|
18
|
+
private pool;
|
|
19
|
+
private executePackageFn;
|
|
20
|
+
private config;
|
|
21
|
+
private startedCount;
|
|
22
|
+
private completedCount;
|
|
23
|
+
constructor(config: PoolConfig, executePackageFn: ExecutePackageFunction);
|
|
24
|
+
/**
|
|
25
|
+
* Create wrapper that adapts tree.ts executePackage to DynamicTaskPool format
|
|
26
|
+
*/
|
|
27
|
+
private createExecutePackageWrapper;
|
|
28
|
+
/**
|
|
29
|
+
* Execute parallel execution
|
|
30
|
+
*/
|
|
31
|
+
execute(): Promise<import("../types/parallelExecution.js").ExecutionResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Get the underlying task pool for event listeners
|
|
34
|
+
*/
|
|
35
|
+
getPool(): DynamicTaskPool;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create progress logger that listens to pool events
|
|
39
|
+
*/
|
|
40
|
+
export declare function createParallelProgressLogger(pool: DynamicTaskPool, config: TreeExecutionConfig): void;
|
|
41
|
+
/**
|
|
42
|
+
* Format parallel execution result for display
|
|
43
|
+
*/
|
|
44
|
+
export declare function formatParallelResult(result: any): string;
|
|
45
|
+
//# sourceMappingURL=TreeExecutionAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TreeExecutionAdapter.d.ts","sourceRoot":"","sources":["../../src/execution/TreeExecutionAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAI9D;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,CACjC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,mBAAmB,EAC9B,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,EAC5B,gBAAgB,CAAC,EAAE,OAAO,KACzB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,GAAG,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAExH;;GAEG;AACH,qBAAa,oBAAoB;IAC7B,OAAO,CAAC,IAAI,CAAkB;IAC9B,OAAO,CAAC,gBAAgB,CAAyB;IACjD,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,cAAc,CAAa;gBAEvB,MAAM,EAAE,UAAU,EAAE,gBAAgB,EAAE,sBAAsB;IAgBxE;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAqDnC;;OAEG;IACG,OAAO;IAIb;;OAEG;IACH,OAAO,IAAI,eAAe;CAG7B;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAuErG;AAoCD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,CAuFxD"}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { DynamicTaskPool } from './DynamicTaskPool.js';
|
|
2
|
+
import { getLogger } from '../util/logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* TreeExecutionAdapter bridges DynamicTaskPool with tree.ts executePackage
|
|
5
|
+
*/
|
|
6
|
+
export class TreeExecutionAdapter {
|
|
7
|
+
pool;
|
|
8
|
+
executePackageFn;
|
|
9
|
+
config;
|
|
10
|
+
startedCount = 0;
|
|
11
|
+
completedCount = 0;
|
|
12
|
+
constructor(config, executePackageFn) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
this.executePackageFn = executePackageFn;
|
|
15
|
+
// Create custom pool that uses our execute function
|
|
16
|
+
this.pool = new DynamicTaskPool(config);
|
|
17
|
+
// Track completion count for progress display
|
|
18
|
+
this.pool.on('package:completed', () => {
|
|
19
|
+
this.completedCount++;
|
|
20
|
+
});
|
|
21
|
+
// Override the executePackage method to use tree.ts function
|
|
22
|
+
this.pool.executePackage = this.createExecutePackageWrapper();
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Create wrapper that adapts tree.ts executePackage to DynamicTaskPool format
|
|
26
|
+
*/
|
|
27
|
+
createExecutePackageWrapper() {
|
|
28
|
+
return async (packageName, _signal) => {
|
|
29
|
+
const packageInfo = this.config.graph.packages.get(packageName);
|
|
30
|
+
if (!packageInfo) {
|
|
31
|
+
throw new Error(`Package not found: ${packageName}`);
|
|
32
|
+
}
|
|
33
|
+
const allPackageNames = new Set(this.config.graph.packages.keys());
|
|
34
|
+
const isDryRun = this.config.config.dryRun || false;
|
|
35
|
+
const isBuiltInCommand = !this.config.command.startsWith('npm') &&
|
|
36
|
+
!this.config.command.includes('&&');
|
|
37
|
+
// Increment started count and use it as index for progress display
|
|
38
|
+
const currentIndex = this.startedCount++;
|
|
39
|
+
// Call tree.ts executePackage
|
|
40
|
+
const startTime = Date.now();
|
|
41
|
+
const result = await this.executePackageFn(packageName, packageInfo, this.config.command, this.config.config, isDryRun, currentIndex, // Use incremented started count for proper [N/Total] display
|
|
42
|
+
this.config.graph.packages.size, allPackageNames, isBuiltInCommand);
|
|
43
|
+
const duration = Date.now() - startTime;
|
|
44
|
+
if (!result.success) {
|
|
45
|
+
// Attach logFile path to error for better error reporting
|
|
46
|
+
const error = result.error || new Error('Package execution failed');
|
|
47
|
+
error.logFilePath = result.logFile;
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
// Check if this was a "no changes" skip (result will have skippedNoChanges flag)
|
|
51
|
+
const skippedNoChanges = result.skippedNoChanges || false;
|
|
52
|
+
return {
|
|
53
|
+
success: true,
|
|
54
|
+
duration,
|
|
55
|
+
// Extract published version if available (from output or state)
|
|
56
|
+
publishedVersion: undefined,
|
|
57
|
+
stdout: undefined,
|
|
58
|
+
stderr: undefined,
|
|
59
|
+
skippedNoChanges
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Execute parallel execution
|
|
65
|
+
*/
|
|
66
|
+
async execute() {
|
|
67
|
+
return await this.pool.execute();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get the underlying task pool for event listeners
|
|
71
|
+
*/
|
|
72
|
+
getPool() {
|
|
73
|
+
return this.pool;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create progress logger that listens to pool events
|
|
78
|
+
*/
|
|
79
|
+
export function createParallelProgressLogger(pool, config) {
|
|
80
|
+
const logger = getLogger();
|
|
81
|
+
const startTime = Date.now();
|
|
82
|
+
let completedCount = 0;
|
|
83
|
+
let totalPackages = 0;
|
|
84
|
+
pool.on('execution:started', ({ totalPackages: total }) => {
|
|
85
|
+
totalPackages = total;
|
|
86
|
+
logger.info(`\nPARALLEL_EXECUTION_STARTING: Initiating parallel package execution | Package Count: ${total} | Mode: parallel | Strategy: dependency-aware`);
|
|
87
|
+
});
|
|
88
|
+
pool.on('package:started', ({ packageName }) => {
|
|
89
|
+
if (config.verbose || config.debug) {
|
|
90
|
+
logger.info(`PACKAGE_STARTED: Package execution initiated | Package: ${packageName} | Status: running`);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
pool.on('package:completed', ({ packageName, result }) => {
|
|
94
|
+
completedCount++;
|
|
95
|
+
const percent = Math.round((completedCount / totalPackages) * 100);
|
|
96
|
+
const elapsed = Math.round((Date.now() - startTime) / 1000);
|
|
97
|
+
if (config.debug) {
|
|
98
|
+
logger.info(`PACKAGE_COMPLETED: Package execution finished successfully | Package: ${packageName} | Duration: ${result.duration}ms | Progress: ${completedCount}/${totalPackages} (${percent}%) | Elapsed: ${elapsed}s`);
|
|
99
|
+
}
|
|
100
|
+
else if (config.verbose) {
|
|
101
|
+
logger.info(`PACKAGE_COMPLETED: Package execution finished | Package: ${packageName} | Progress: ${completedCount}/${totalPackages}`);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// Minimal output
|
|
105
|
+
logger.info(`PROGRESS: [${completedCount}/${totalPackages}] Package completed: ${packageName}`);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
pool.on('package:failed', ({ packageName, error }) => {
|
|
109
|
+
logger.error(`PACKAGE_FAILED: Package execution failed | Package: ${packageName} | Error: ${error.message} | Status: error`);
|
|
110
|
+
});
|
|
111
|
+
pool.on('package:retrying', ({ packageName, attemptNumber }) => {
|
|
112
|
+
logger.warn(`PACKAGE_RETRYING: Retrying package execution | Package: ${packageName} | Attempt: ${attemptNumber} | Status: retrying`);
|
|
113
|
+
});
|
|
114
|
+
pool.on('package:skipped', ({ packageName, reason }) => {
|
|
115
|
+
logger.warn(`PACKAGE_SKIPPED: Package skipped due to dependency failure | Package: ${packageName} | Reason: ${reason} | Status: skipped`);
|
|
116
|
+
});
|
|
117
|
+
pool.on('package:skipped-no-changes', ({ packageName }) => {
|
|
118
|
+
if (config.verbose || config.debug) {
|
|
119
|
+
logger.info(`PACKAGE_SKIPPED_NO_CHANGES: Package skipped due to no code changes | Package: ${packageName} | Reason: no-code-changes | Status: skipped`);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
pool.on('checkpoint:saved', () => {
|
|
123
|
+
if (config.debug) {
|
|
124
|
+
logger.debug('CHECKPOINT_SAVED: Execution checkpoint saved | Purpose: Recovery support | Action: State persisted to disk');
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
pool.on('execution:completed', ({ result }) => {
|
|
128
|
+
const totalTime = Math.round((Date.now() - startTime) / 1000);
|
|
129
|
+
logger.info(`\nPARALLEL_EXECUTION_COMPLETED: Parallel execution finished | Duration: ${totalTime}s | Status: completed`);
|
|
130
|
+
if (config.verbose || config.debug) {
|
|
131
|
+
logger.info(`\nEXECUTION_METRICS: Performance and execution statistics:`);
|
|
132
|
+
logger.info(` METRIC_TOTAL_PACKAGES: ${result.totalPackages}`);
|
|
133
|
+
logger.info(` METRIC_COMPLETED: ${result.completed.length} packages successfully completed`);
|
|
134
|
+
logger.info(` METRIC_SKIPPED_NO_CHANGES: ${result.skippedNoChanges.length} packages skipped (no changes)`);
|
|
135
|
+
logger.info(` METRIC_SKIPPED_DEPENDENCIES: ${result.skipped.length} packages skipped (dependency failures)`);
|
|
136
|
+
logger.info(` METRIC_FAILED: ${result.failed.length} packages failed`);
|
|
137
|
+
logger.info(` METRIC_PEAK_CONCURRENCY: ${result.metrics.peakConcurrency} packages running simultaneously`);
|
|
138
|
+
logger.info(` METRIC_AVG_CONCURRENCY: ${result.metrics.averageConcurrency.toFixed(1)} average concurrent packages`);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Simple error summary formatter (inline implementation)
|
|
144
|
+
*/
|
|
145
|
+
function createErrorSummary(failed) {
|
|
146
|
+
const lines = [];
|
|
147
|
+
lines.push('ā Error Details:');
|
|
148
|
+
for (const pkg of failed) {
|
|
149
|
+
lines.push(`\n Package: ${pkg.name}`);
|
|
150
|
+
if (pkg.error) {
|
|
151
|
+
lines.push(` Error: ${pkg.error}`);
|
|
152
|
+
}
|
|
153
|
+
if (pkg.logFile) {
|
|
154
|
+
lines.push(` Log: ${pkg.logFile}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return lines;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Simple recovery guidance formatter (inline implementation)
|
|
161
|
+
*/
|
|
162
|
+
function createRecoveryGuidance(hasRetriable, hasPermanent) {
|
|
163
|
+
const lines = [];
|
|
164
|
+
if (hasRetriable) {
|
|
165
|
+
lines.push('\nš” Some failures may be temporary (network issues, etc.)');
|
|
166
|
+
lines.push(' Consider retrying with: kodrdriv tree publish --continue');
|
|
167
|
+
}
|
|
168
|
+
if (hasPermanent) {
|
|
169
|
+
lines.push('\nā ļø Some failures appear to be permanent');
|
|
170
|
+
lines.push(' Review and fix the issues before retrying');
|
|
171
|
+
}
|
|
172
|
+
return lines;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Format parallel execution result for display
|
|
176
|
+
*/
|
|
177
|
+
export function formatParallelResult(result) {
|
|
178
|
+
const lines = [];
|
|
179
|
+
// Separator line
|
|
180
|
+
lines.push('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
181
|
+
lines.push('š Publish Summary');
|
|
182
|
+
lines.push('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
183
|
+
// Detailed status breakdown by category
|
|
184
|
+
if (result.completed.length > 0) {
|
|
185
|
+
lines.push(`ā
Published (${result.completed.length}):`);
|
|
186
|
+
for (const pkg of result.completed) {
|
|
187
|
+
lines.push(` - ${pkg}`);
|
|
188
|
+
}
|
|
189
|
+
lines.push('');
|
|
190
|
+
}
|
|
191
|
+
if (result.skippedNoChanges.length > 0) {
|
|
192
|
+
lines.push(`āļø Skipped (${result.skippedNoChanges.length}) - no code changes:`);
|
|
193
|
+
for (const pkg of result.skippedNoChanges) {
|
|
194
|
+
lines.push(` - ${pkg}`);
|
|
195
|
+
}
|
|
196
|
+
lines.push('');
|
|
197
|
+
}
|
|
198
|
+
if (result.failed.length > 0) {
|
|
199
|
+
lines.push(`ā Failed (${result.failed.length}):`);
|
|
200
|
+
for (const pkg of result.failed) {
|
|
201
|
+
lines.push(` - ${typeof pkg === 'string' ? pkg : pkg.name}`);
|
|
202
|
+
}
|
|
203
|
+
lines.push('');
|
|
204
|
+
}
|
|
205
|
+
if (result.skipped.length > 0) {
|
|
206
|
+
lines.push(`ā Skipped due to dependencies (${result.skipped.length}):`);
|
|
207
|
+
for (const pkg of result.skipped) {
|
|
208
|
+
lines.push(` - ${pkg}`);
|
|
209
|
+
}
|
|
210
|
+
lines.push('');
|
|
211
|
+
}
|
|
212
|
+
// Summary line
|
|
213
|
+
lines.push('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
214
|
+
// Calculate success rate
|
|
215
|
+
const totalProcessed = result.completed.length + result.failed.length + result.skippedNoChanges.length;
|
|
216
|
+
const successRate = totalProcessed > 0 ? Math.round((result.completed.length / totalProcessed) * 100) : 0;
|
|
217
|
+
// Format elapsed time
|
|
218
|
+
const totalTimeMs = result.metrics?.totalDuration || 0;
|
|
219
|
+
const minutes = Math.floor(totalTimeMs / 60000);
|
|
220
|
+
const seconds = Math.floor((totalTimeMs % 60000) / 1000);
|
|
221
|
+
const timeStr = minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
|
|
222
|
+
lines.push(`Total time: ${timeStr}`);
|
|
223
|
+
lines.push(`Success rate: ${successRate}% (${result.completed.length}/${totalProcessed} packages processed)`);
|
|
224
|
+
if (result.metrics?.peakConcurrency) {
|
|
225
|
+
lines.push(`Peak concurrency: ${result.metrics.peakConcurrency} packages`);
|
|
226
|
+
}
|
|
227
|
+
lines.push('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
228
|
+
// Failed packages with formatted error summary
|
|
229
|
+
if (result.failed.length > 0) {
|
|
230
|
+
lines.push('');
|
|
231
|
+
const errorLines = createErrorSummary(result.failed);
|
|
232
|
+
lines.push(...errorLines);
|
|
233
|
+
// Next steps for failures
|
|
234
|
+
lines.push('\nš Next steps:');
|
|
235
|
+
lines.push('1. Review the errors above for each failed package');
|
|
236
|
+
lines.push('2. Fix the issues in the failed packages');
|
|
237
|
+
lines.push('3. Retry the publish command');
|
|
238
|
+
if (result.skipped.length > 0) {
|
|
239
|
+
lines.push('\nNote: Once failed packages are fixed, their dependent packages will also be published.');
|
|
240
|
+
}
|
|
241
|
+
// Recovery guidance
|
|
242
|
+
const hasRetriable = result.failed.some((f) => f.isRetriable);
|
|
243
|
+
const hasPermanent = result.failed.some((f) => !f.isRetriable);
|
|
244
|
+
const recoveryLines = createRecoveryGuidance(hasRetriable, hasPermanent);
|
|
245
|
+
lines.push(...recoveryLines);
|
|
246
|
+
}
|
|
247
|
+
return lines.join('\n');
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=TreeExecutionAdapter.js.map
|