@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.
Files changed (79) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +130 -0
  3. package/dist/TreeExecutor.d.ts +113 -0
  4. package/dist/TreeExecutor.d.ts.map +1 -0
  5. package/dist/TreeExecutor.js +113 -0
  6. package/dist/TreeExecutor.js.map +1 -0
  7. package/dist/checkpoint/CheckpointManager.d.ts +18 -0
  8. package/dist/checkpoint/CheckpointManager.d.ts.map +1 -0
  9. package/dist/checkpoint/CheckpointManager.js +156 -0
  10. package/dist/checkpoint/CheckpointManager.js.map +1 -0
  11. package/dist/checkpoint/index.d.ts +5 -0
  12. package/dist/checkpoint/index.d.ts.map +1 -0
  13. package/dist/checkpoint/index.js +5 -0
  14. package/dist/checkpoint/index.js.map +1 -0
  15. package/dist/execution/CommandValidator.d.ts +25 -0
  16. package/dist/execution/CommandValidator.d.ts.map +1 -0
  17. package/dist/execution/CommandValidator.js +129 -0
  18. package/dist/execution/CommandValidator.js.map +1 -0
  19. package/dist/execution/DependencyChecker.d.ts +47 -0
  20. package/dist/execution/DependencyChecker.d.ts.map +1 -0
  21. package/dist/execution/DependencyChecker.js +95 -0
  22. package/dist/execution/DependencyChecker.js.map +1 -0
  23. package/dist/execution/DynamicTaskPool.d.ts +118 -0
  24. package/dist/execution/DynamicTaskPool.d.ts.map +1 -0
  25. package/dist/execution/DynamicTaskPool.js +658 -0
  26. package/dist/execution/DynamicTaskPool.js.map +1 -0
  27. package/dist/execution/RecoveryManager.d.ts +89 -0
  28. package/dist/execution/RecoveryManager.d.ts.map +1 -0
  29. package/dist/execution/RecoveryManager.js +592 -0
  30. package/dist/execution/RecoveryManager.js.map +1 -0
  31. package/dist/execution/ResourceMonitor.d.ts +73 -0
  32. package/dist/execution/ResourceMonitor.d.ts.map +1 -0
  33. package/dist/execution/ResourceMonitor.js +148 -0
  34. package/dist/execution/ResourceMonitor.js.map +1 -0
  35. package/dist/execution/Scheduler.d.ts +36 -0
  36. package/dist/execution/Scheduler.d.ts.map +1 -0
  37. package/dist/execution/Scheduler.js +83 -0
  38. package/dist/execution/Scheduler.js.map +1 -0
  39. package/dist/execution/TreeExecutionAdapter.d.ts +45 -0
  40. package/dist/execution/TreeExecutionAdapter.d.ts.map +1 -0
  41. package/dist/execution/TreeExecutionAdapter.js +249 -0
  42. package/dist/execution/TreeExecutionAdapter.js.map +1 -0
  43. package/dist/index.d.ts +29 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +25 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/tree.d.ts +13 -0
  48. package/dist/tree.d.ts.map +1 -0
  49. package/dist/tree.js +2453 -0
  50. package/dist/tree.js.map +1 -0
  51. package/dist/types/config.d.ts +172 -0
  52. package/dist/types/config.d.ts.map +1 -0
  53. package/dist/types/config.js +2 -0
  54. package/dist/types/config.js.map +1 -0
  55. package/dist/types/index.d.ts +6 -0
  56. package/dist/types/index.d.ts.map +1 -0
  57. package/dist/types/index.js +6 -0
  58. package/dist/types/index.js.map +1 -0
  59. package/dist/types/parallelExecution.d.ts +108 -0
  60. package/dist/types/parallelExecution.d.ts.map +1 -0
  61. package/dist/types/parallelExecution.js +2 -0
  62. package/dist/types/parallelExecution.js.map +1 -0
  63. package/dist/util/commandStubs.d.ts +22 -0
  64. package/dist/util/commandStubs.d.ts.map +1 -0
  65. package/dist/util/commandStubs.js +49 -0
  66. package/dist/util/commandStubs.js.map +1 -0
  67. package/dist/util/logger.d.ts +14 -0
  68. package/dist/util/logger.d.ts.map +1 -0
  69. package/dist/util/logger.js +15 -0
  70. package/dist/util/logger.js.map +1 -0
  71. package/dist/util/mutex.d.ts +38 -0
  72. package/dist/util/mutex.d.ts.map +1 -0
  73. package/dist/util/mutex.js +101 -0
  74. package/dist/util/mutex.js.map +1 -0
  75. package/dist/util/treeUtils.d.ts +46 -0
  76. package/dist/util/treeUtils.d.ts.map +1 -0
  77. package/dist/util/treeUtils.js +74 -0
  78. package/dist/util/treeUtils.js.map +1 -0
  79. 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":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAUzB;;GAEG;AACH,MAAM,OAAO,eAAe;IAChB,cAAc,CAAS;IACvB,kBAAkB,GAAW,CAAC,CAAC;IAC/B,OAAO,CAAkB;IACzB,iBAAiB,GAAa,EAAE,CAAC;IACjC,MAAM,GAAG,SAAS,EAAE,CAAC;IAE7B,oDAAoD;IACnC,wBAAwB,GAAG,CAAC,CAAC;IAE9C,YAAY,cAAsB;QAC9B,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG;YACX,eAAe,EAAE,CAAC;YAClB,kBAAkB,EAAE,CAAC;YACrB,gBAAgB,EAAE,CAAC;YACnB,aAAa,EAAE,CAAC;SACnB,CAAC;IACN,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,QAAgB,CAAC;QACzB,0BAA0B;QAC1B,IAAI,IAAI,CAAC,kBAAkB,GAAG,KAAK,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;YACxD,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,mCAAmC;QACnC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAEzB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACK,iBAAiB;QACrB,IAAI,CAAC;YACD,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;YAC/B,MAAM,WAAW,GAAG,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG,GAAG,CAAC;YAE/C,IAAI,WAAW,GAAG,IAAI,CAAC,wBAAwB,EAAE,CAAC;gBAC9C,MAAM,MAAM,GAAG,CAAC,OAAO,GAAG,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBAC3D,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,2DAA2D,MAAM,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC;YAC3J,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,4DAA4D;YAC5D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,KAAK,EAAE,CAAC,CAAC;QACjE,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,QAAQ,CAAC,QAAgB,CAAC;QACtB,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,IAAI,CAAC,kBAAkB,IAAI,KAAK,CAAC;QACjC,IAAI,CAAC,OAAO,CAAC,gBAAgB,IAAI,KAAK,CAAC;QACvC,IAAI,CAAC,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,CACnC,IAAI,CAAC,OAAO,CAAC,eAAe,EAC5B,IAAI,CAAC,kBAAkB,CAC1B,CAAC;QAEF,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACrD,IAAI,CAAC,wBAAwB,EAAE,CAAC;QAEhC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,KAAK,aAAa,IAAI,CAAC,kBAAkB,IAAI,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;QAEpG,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,OAAO,CAAC,QAAgB,CAAC;QACrB,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC,CAAC;QACvE,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,KAAK,CAAC;QAEpC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACrD,IAAI,CAAC,wBAAwB,EAAE,CAAC;QAEhC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,KAAK,aAAa,IAAI,CAAC,kBAAkB,IAAI,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;IACvG,CAAC;IAED;;OAEG;IACH,iBAAiB;QACb,OAAO,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,kBAAkB,CAAC;IACzD,CAAC;IAED;;OAEG;IACH,qBAAqB;QACjB,OAAO,IAAI,CAAC,kBAAkB,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,iBAAiB;QACb,OAAO,IAAI,CAAC,cAAc,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,UAAU;QACN,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,cAAc;QACV,IAAI,IAAI,CAAC,cAAc,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,GAAG,CAAC;IACjE,CAAC;IAED;;OAEG;IACH,eAAe;QACX,OAAO,IAAI,CAAC,kBAAkB,IAAI,IAAI,CAAC,cAAc,CAAC;IAC1D,CAAC;IAED;;OAEG;IACH,MAAM;QACF,OAAO,IAAI,CAAC,kBAAkB,KAAK,CAAC,CAAC;IACzC,CAAC;IAED;;OAEG;IACK,wBAAwB;QAC5B,IAAI,IAAI,CAAC,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,OAAO,CAAC,kBAAkB,GAAG,CAAC,CAAC;YACpC,OAAO;QACX,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC9D,IAAI,CAAC,OAAO,CAAC,kBAAkB,GAAG,GAAG,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC;IAC1E,CAAC;IAED;;OAEG;IACH,KAAK;QACD,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,GAAG;YACX,eAAe,EAAE,CAAC;YAClB,kBAAkB,EAAE,CAAC;YACrB,gBAAgB,EAAE,CAAC;YACnB,aAAa,EAAE,CAAC;SACnB,CAAC;IACN,CAAC;CACJ"}
@@ -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