@gowelle/stint-agent 1.2.38 → 1.2.40
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/dist/{StatusDashboard-YI6HJLZS.js → StatusDashboard-NGRPLDM7.js} +2 -2
- package/dist/api-QIIGJ7R2.js +7 -0
- package/dist/{chunk-SE4UOLIV.js → chunk-3AS45O54.js} +20 -2
- package/dist/{chunk-ZBVVGIPE.js → chunk-RZM4P35S.js} +69 -11
- package/dist/{chunk-FJO77ALZ.js → chunk-UBNAWBEL.js} +1 -1
- package/dist/{chunk-LJLCMCK6.js → chunk-XH2UAPVA.js} +1 -1
- package/dist/daemon/runner.js +178 -7
- package/dist/index.js +103 -20
- package/package.json +5 -1
- package/dist/api-DVQBJAYO.js +0 -7
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
gitService,
|
|
3
3
|
projectService,
|
|
4
4
|
validatePidFile
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UBNAWBEL.js";
|
|
6
6
|
import {
|
|
7
7
|
authService
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-XH2UAPVA.js";
|
|
9
9
|
|
|
10
10
|
// src/components/StatusDashboard.tsx
|
|
11
11
|
import { useState, useEffect } from "react";
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
authService,
|
|
3
3
|
config,
|
|
4
4
|
logger
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-XH2UAPVA.js";
|
|
6
6
|
|
|
7
7
|
// src/utils/circuit-breaker.ts
|
|
8
8
|
var CircuitBreaker = class {
|
|
@@ -100,7 +100,7 @@ var CircuitBreaker = class {
|
|
|
100
100
|
};
|
|
101
101
|
|
|
102
102
|
// src/services/api.ts
|
|
103
|
-
var AGENT_VERSION = "1.2.
|
|
103
|
+
var AGENT_VERSION = "1.2.40";
|
|
104
104
|
var ApiServiceImpl = class {
|
|
105
105
|
sessionId = null;
|
|
106
106
|
circuitBreaker = new CircuitBreaker({
|
|
@@ -501,6 +501,24 @@ var ApiServiceImpl = class {
|
|
|
501
501
|
);
|
|
502
502
|
}
|
|
503
503
|
}
|
|
504
|
+
/**
|
|
505
|
+
* Get tasks for a project, optionally filtered by branch
|
|
506
|
+
* @param projectId - Project ID
|
|
507
|
+
* @param branch - Optional branch name to filter by
|
|
508
|
+
* @returns Array of tasks
|
|
509
|
+
*/
|
|
510
|
+
async getProjectTasks(projectId, branch) {
|
|
511
|
+
logger.info("api", `Fetching tasks for project ${projectId}`);
|
|
512
|
+
const queryParams = new URLSearchParams();
|
|
513
|
+
if (branch) {
|
|
514
|
+
queryParams.append("branch", branch);
|
|
515
|
+
}
|
|
516
|
+
const queryString = queryParams.toString() ? `?${queryParams.toString()}` : "";
|
|
517
|
+
const response = await this.request(`/api/agent/projects/${projectId}/tasks${queryString}`);
|
|
518
|
+
const tasks = response.data;
|
|
519
|
+
logger.info("api", `Found ${tasks.length} tasks`);
|
|
520
|
+
return tasks;
|
|
521
|
+
}
|
|
504
522
|
};
|
|
505
523
|
var apiService = new ApiServiceImpl();
|
|
506
524
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
apiService
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-3AS45O54.js";
|
|
4
4
|
import {
|
|
5
5
|
gitService,
|
|
6
6
|
projectService
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-UBNAWBEL.js";
|
|
8
8
|
import {
|
|
9
9
|
authService,
|
|
10
10
|
config,
|
|
11
11
|
logger
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-XH2UAPVA.js";
|
|
13
13
|
|
|
14
14
|
// src/utils/notify.ts
|
|
15
15
|
import notifier from "node-notifier";
|
|
@@ -613,10 +613,12 @@ var WebSocketServiceImpl = class {
|
|
|
613
613
|
echo = null;
|
|
614
614
|
userId = null;
|
|
615
615
|
reconnectAttempts = 0;
|
|
616
|
-
maxReconnectAttempts =
|
|
616
|
+
maxReconnectAttempts = -1;
|
|
617
|
+
// -1 = infinite reconnection
|
|
617
618
|
reconnectTimer = null;
|
|
618
619
|
isManualDisconnect = false;
|
|
619
620
|
currentPusherClient = null;
|
|
621
|
+
lastSuccessfulConnection = null;
|
|
620
622
|
// Event handlers
|
|
621
623
|
commitApprovedHandlers = [];
|
|
622
624
|
commitPendingHandlers = [];
|
|
@@ -793,6 +795,7 @@ var WebSocketServiceImpl = class {
|
|
|
793
795
|
"\u2705 Connected to Broadcaster via Sanctum"
|
|
794
796
|
);
|
|
795
797
|
writeStatus({ connected: true });
|
|
798
|
+
this.lastSuccessfulConnection = /* @__PURE__ */ new Date();
|
|
796
799
|
this.reconnectAttempts = 0;
|
|
797
800
|
this.isManualDisconnect = false;
|
|
798
801
|
safeResolve();
|
|
@@ -908,7 +911,7 @@ var WebSocketServiceImpl = class {
|
|
|
908
911
|
"websocket",
|
|
909
912
|
`Commit ${commit.id} marked as large, fetching full details...`
|
|
910
913
|
);
|
|
911
|
-
const { apiService: apiService2 } = await import("./api-
|
|
914
|
+
const { apiService: apiService2 } = await import("./api-QIIGJ7R2.js");
|
|
912
915
|
const fullCommit = await apiService2.getCommit(commit.id);
|
|
913
916
|
commit = {
|
|
914
917
|
...commit,
|
|
@@ -1015,19 +1018,30 @@ var WebSocketServiceImpl = class {
|
|
|
1015
1018
|
clearTimeout(this.reconnectTimer);
|
|
1016
1019
|
this.reconnectTimer = null;
|
|
1017
1020
|
}
|
|
1018
|
-
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
1021
|
+
if (this.maxReconnectAttempts === -1 || this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
1019
1022
|
const delay = this.getReconnectDelay();
|
|
1020
1023
|
this.reconnectAttempts++;
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`
|
|
1024
|
-
);
|
|
1024
|
+
const attemptInfo = this.maxReconnectAttempts === -1 ? `attempt ${this.reconnectAttempts}` : `attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts}`;
|
|
1025
|
+
logger.info("websocket", `Reconnecting in ${delay}ms (${attemptInfo})`);
|
|
1025
1026
|
this.reconnectTimer = setTimeout(async () => {
|
|
1026
1027
|
try {
|
|
1028
|
+
const token = await authService.getToken();
|
|
1029
|
+
if (!token) {
|
|
1030
|
+
logger.error(
|
|
1031
|
+
"websocket",
|
|
1032
|
+
"Cannot reconnect: authentication token expired or missing"
|
|
1033
|
+
);
|
|
1034
|
+
this.handleDisconnect();
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1027
1037
|
await this.connect();
|
|
1028
1038
|
if (this.userId) {
|
|
1029
1039
|
await this.subscribeToUserChannel(this.userId);
|
|
1030
1040
|
}
|
|
1041
|
+
logger.success(
|
|
1042
|
+
"websocket",
|
|
1043
|
+
`Reconnected successfully after ${this.reconnectAttempts} attempts`
|
|
1044
|
+
);
|
|
1031
1045
|
} catch (error) {
|
|
1032
1046
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1033
1047
|
if (errorMessage.includes("disconnected during connection")) {
|
|
@@ -1044,12 +1058,56 @@ var WebSocketServiceImpl = class {
|
|
|
1044
1058
|
logger.error("websocket", "Max reconnection attempts reached");
|
|
1045
1059
|
}
|
|
1046
1060
|
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Force a reconnection attempt - used by health monitor
|
|
1063
|
+
* Resets attempt counter and triggers immediate reconnection
|
|
1064
|
+
*/
|
|
1065
|
+
async forceReconnect() {
|
|
1066
|
+
logger.info("websocket", "Force reconnect requested by health monitor");
|
|
1067
|
+
this.reconnectAttempts = 0;
|
|
1068
|
+
if (this.reconnectTimer) {
|
|
1069
|
+
clearTimeout(this.reconnectTimer);
|
|
1070
|
+
this.reconnectTimer = null;
|
|
1071
|
+
}
|
|
1072
|
+
if (this.echo) {
|
|
1073
|
+
this.currentPusherClient = null;
|
|
1074
|
+
this.echo.disconnect();
|
|
1075
|
+
this.echo = null;
|
|
1076
|
+
}
|
|
1077
|
+
try {
|
|
1078
|
+
await this.connect();
|
|
1079
|
+
if (this.userId) {
|
|
1080
|
+
await this.subscribeToUserChannel(this.userId);
|
|
1081
|
+
}
|
|
1082
|
+
} catch (error) {
|
|
1083
|
+
logger.error("websocket", "Force reconnect failed", error);
|
|
1084
|
+
this.handleDisconnect();
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Get the timestamp of last successful connection
|
|
1089
|
+
* Used by health monitor to detect stale connections
|
|
1090
|
+
*/
|
|
1091
|
+
getLastSuccessfulConnection() {
|
|
1092
|
+
return this.lastSuccessfulConnection;
|
|
1093
|
+
}
|
|
1047
1094
|
/**
|
|
1048
1095
|
* Get reconnect delay with exponential backoff and jitter
|
|
1049
1096
|
* Jitter prevents thundering herd problem when many clients reconnect simultaneously
|
|
1097
|
+
* Caps at 5 minutes for long-running daemon resilience
|
|
1050
1098
|
*/
|
|
1051
1099
|
getReconnectDelay() {
|
|
1052
|
-
const delays = [
|
|
1100
|
+
const delays = [
|
|
1101
|
+
1e3,
|
|
1102
|
+
2e3,
|
|
1103
|
+
4e3,
|
|
1104
|
+
8e3,
|
|
1105
|
+
16e3,
|
|
1106
|
+
3e4,
|
|
1107
|
+
6e4,
|
|
1108
|
+
12e4,
|
|
1109
|
+
3e5
|
|
1110
|
+
];
|
|
1053
1111
|
const index = Math.min(this.reconnectAttempts, delays.length - 1);
|
|
1054
1112
|
const baseDelay = delays[index];
|
|
1055
1113
|
const jitter = baseDelay * (Math.random() * 0.3);
|
|
@@ -346,7 +346,7 @@ var AuthServiceImpl = class {
|
|
|
346
346
|
return null;
|
|
347
347
|
}
|
|
348
348
|
try {
|
|
349
|
-
const { apiService } = await import("./api-
|
|
349
|
+
const { apiService } = await import("./api-QIIGJ7R2.js");
|
|
350
350
|
const user = await apiService.getCurrentUser();
|
|
351
351
|
logger.info("auth", `Token validated for user: ${user.email}`);
|
|
352
352
|
return user;
|
package/dist/daemon/runner.js
CHANGED
|
@@ -3,20 +3,20 @@ import {
|
|
|
3
3
|
commitQueue,
|
|
4
4
|
notify,
|
|
5
5
|
websocketService
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-RZM4P35S.js";
|
|
7
7
|
import {
|
|
8
8
|
apiService
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-3AS45O54.js";
|
|
10
10
|
import {
|
|
11
11
|
gitService,
|
|
12
12
|
projectService,
|
|
13
13
|
removePidFile,
|
|
14
14
|
writePidFile
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-UBNAWBEL.js";
|
|
16
16
|
import {
|
|
17
17
|
authService,
|
|
18
18
|
logger
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-XH2UAPVA.js";
|
|
20
20
|
|
|
21
21
|
// src/daemon/runner.ts
|
|
22
22
|
import "dotenv/config";
|
|
@@ -338,6 +338,146 @@ async function syncPendingCommits() {
|
|
|
338
338
|
}
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
+
// src/daemon/health.ts
|
|
342
|
+
var HealthMonitor = class {
|
|
343
|
+
checkInterval = null;
|
|
344
|
+
startTime = null;
|
|
345
|
+
lastSuccessfulHeartbeat = null;
|
|
346
|
+
consecutiveFailures = 0;
|
|
347
|
+
maxConsecutiveFailures = 3;
|
|
348
|
+
checkIntervalMs = 9e4;
|
|
349
|
+
// 90 seconds
|
|
350
|
+
/**
|
|
351
|
+
* Start the health monitor
|
|
352
|
+
*/
|
|
353
|
+
start() {
|
|
354
|
+
if (this.checkInterval) {
|
|
355
|
+
logger.warn("health", "Health monitor already running");
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
this.startTime = /* @__PURE__ */ new Date();
|
|
359
|
+
this.consecutiveFailures = 0;
|
|
360
|
+
logger.info("health", "Starting health monitor (90s interval)");
|
|
361
|
+
this.checkInterval = setInterval(() => {
|
|
362
|
+
this.performHealthCheck().catch((error) => {
|
|
363
|
+
logger.error("health", "Health check error", error);
|
|
364
|
+
});
|
|
365
|
+
}, this.checkIntervalMs);
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Stop the health monitor
|
|
369
|
+
*/
|
|
370
|
+
stop() {
|
|
371
|
+
if (this.checkInterval) {
|
|
372
|
+
clearInterval(this.checkInterval);
|
|
373
|
+
this.checkInterval = null;
|
|
374
|
+
logger.info("health", "Health monitor stopped");
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Record a successful heartbeat
|
|
379
|
+
* Called by the daemon after each successful API heartbeat
|
|
380
|
+
*/
|
|
381
|
+
recordHeartbeat() {
|
|
382
|
+
this.lastSuccessfulHeartbeat = /* @__PURE__ */ new Date();
|
|
383
|
+
if (this.consecutiveFailures > 0) {
|
|
384
|
+
logger.info(
|
|
385
|
+
"health",
|
|
386
|
+
`Health restored after ${this.consecutiveFailures} consecutive failures`
|
|
387
|
+
);
|
|
388
|
+
this.consecutiveFailures = 0;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Get current health status
|
|
393
|
+
*/
|
|
394
|
+
getStatus() {
|
|
395
|
+
const websocketConnected = websocketService.isConnected();
|
|
396
|
+
const lastSuccessfulConnection = websocketService.getLastSuccessfulConnection();
|
|
397
|
+
const heartbeatStale = this.isHeartbeatStale();
|
|
398
|
+
const healthy = websocketConnected && !heartbeatStale;
|
|
399
|
+
return {
|
|
400
|
+
healthy,
|
|
401
|
+
websocketConnected,
|
|
402
|
+
lastSuccessfulHeartbeat: this.lastSuccessfulHeartbeat,
|
|
403
|
+
lastSuccessfulConnection,
|
|
404
|
+
consecutiveFailures: this.consecutiveFailures,
|
|
405
|
+
uptime: this.startTime ? Math.floor((Date.now() - this.startTime.getTime()) / 1e3) : 0
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Perform a health check and trigger recovery if needed
|
|
410
|
+
*/
|
|
411
|
+
async performHealthCheck() {
|
|
412
|
+
const status = this.getStatus();
|
|
413
|
+
logger.debug(
|
|
414
|
+
"health",
|
|
415
|
+
`Health check: connected=${status.websocketConnected}, heartbeat_stale=${this.isHeartbeatStale()}, failures=${this.consecutiveFailures}`
|
|
416
|
+
);
|
|
417
|
+
if (!status.healthy) {
|
|
418
|
+
this.consecutiveFailures++;
|
|
419
|
+
logger.warn(
|
|
420
|
+
"health",
|
|
421
|
+
`Unhealthy state detected (${this.consecutiveFailures}/${this.maxConsecutiveFailures})`
|
|
422
|
+
);
|
|
423
|
+
if (this.consecutiveFailures >= this.maxConsecutiveFailures) {
|
|
424
|
+
await this.triggerRecovery();
|
|
425
|
+
}
|
|
426
|
+
} else {
|
|
427
|
+
if (this.consecutiveFailures > 0) {
|
|
428
|
+
logger.info("health", "Health restored");
|
|
429
|
+
this.consecutiveFailures = 0;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Check if heartbeat is stale (no successful heartbeat in 3 minutes)
|
|
435
|
+
*/
|
|
436
|
+
isHeartbeatStale() {
|
|
437
|
+
if (!this.lastSuccessfulHeartbeat) {
|
|
438
|
+
if (this.startTime) {
|
|
439
|
+
const sinceSart = Date.now() - this.startTime.getTime();
|
|
440
|
+
return sinceSart > 12e4;
|
|
441
|
+
}
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
const staleThreshold = 18e4;
|
|
445
|
+
const timeSinceHeartbeat = Date.now() - this.lastSuccessfulHeartbeat.getTime();
|
|
446
|
+
return timeSinceHeartbeat > staleThreshold;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Trigger recovery actions
|
|
450
|
+
*/
|
|
451
|
+
async triggerRecovery() {
|
|
452
|
+
logger.warn(
|
|
453
|
+
"health",
|
|
454
|
+
"Triggering recovery due to persistent unhealthy state"
|
|
455
|
+
);
|
|
456
|
+
this.consecutiveFailures = 0;
|
|
457
|
+
if (!websocketService.isConnected()) {
|
|
458
|
+
logger.info("health", "Forcing WebSocket reconnection...");
|
|
459
|
+
try {
|
|
460
|
+
await websocketService.forceReconnect();
|
|
461
|
+
logger.success("health", "WebSocket reconnection initiated");
|
|
462
|
+
} catch (error) {
|
|
463
|
+
logger.error(
|
|
464
|
+
"health",
|
|
465
|
+
"Failed to force WebSocket reconnection",
|
|
466
|
+
error
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
try {
|
|
471
|
+
await apiService.heartbeat();
|
|
472
|
+
this.recordHeartbeat();
|
|
473
|
+
logger.success("health", "API session verified");
|
|
474
|
+
} catch (error) {
|
|
475
|
+
logger.error("health", "API session check failed", error);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
var healthMonitor = new HealthMonitor();
|
|
480
|
+
|
|
341
481
|
// src/daemon/index.ts
|
|
342
482
|
var heartbeatInterval = null;
|
|
343
483
|
var isShuttingDown = false;
|
|
@@ -456,6 +596,7 @@ Priority: ${suggestion.priority}`,
|
|
|
456
596
|
});
|
|
457
597
|
setupSignalHandlers();
|
|
458
598
|
startHeartbeat();
|
|
599
|
+
healthMonitor.start();
|
|
459
600
|
logger.info("daemon", "Starting file watcher...");
|
|
460
601
|
fileWatcher.start();
|
|
461
602
|
logger.info("daemon", "Daemon started successfully");
|
|
@@ -498,6 +639,7 @@ function startHeartbeat() {
|
|
|
498
639
|
if (isShuttingDown) return;
|
|
499
640
|
try {
|
|
500
641
|
await apiService.heartbeat();
|
|
642
|
+
healthMonitor.recordHeartbeat();
|
|
501
643
|
logger.debug("daemon", "Heartbeat sent successfully");
|
|
502
644
|
} catch (error) {
|
|
503
645
|
logger.error("daemon", "Heartbeat failed", error);
|
|
@@ -527,6 +669,7 @@ async function shutdown(reason) {
|
|
|
527
669
|
isShuttingDown = true;
|
|
528
670
|
shutdownReason = reason;
|
|
529
671
|
logger.info("daemon", "Shutting down daemon...");
|
|
672
|
+
healthMonitor.stop();
|
|
530
673
|
stopHeartbeat();
|
|
531
674
|
try {
|
|
532
675
|
fileWatcher.stop();
|
|
@@ -556,7 +699,35 @@ async function shutdown(reason) {
|
|
|
556
699
|
|
|
557
700
|
// src/daemon/runner.ts
|
|
558
701
|
writePidFile(process.pid);
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
702
|
+
var startupAttempts = 0;
|
|
703
|
+
var maxStartupAttempts = 5;
|
|
704
|
+
async function startWithRetry() {
|
|
705
|
+
while (startupAttempts < maxStartupAttempts) {
|
|
706
|
+
try {
|
|
707
|
+
startupAttempts++;
|
|
708
|
+
await startDaemon();
|
|
709
|
+
logger.warn("daemon-runner", "Daemon exited unexpectedly, restarting...");
|
|
710
|
+
} catch (error) {
|
|
711
|
+
logger.error(
|
|
712
|
+
"daemon-runner",
|
|
713
|
+
`Daemon startup failed (attempt ${startupAttempts}/${maxStartupAttempts})`,
|
|
714
|
+
error
|
|
715
|
+
);
|
|
716
|
+
if (startupAttempts >= maxStartupAttempts) {
|
|
717
|
+
logger.error("daemon-runner", "Max startup attempts reached, exiting");
|
|
718
|
+
process.exit(1);
|
|
719
|
+
}
|
|
720
|
+
const delay = Math.min(5e3 * Math.pow(2, startupAttempts - 1), 6e4);
|
|
721
|
+
logger.info("daemon-runner", `Retrying in ${delay}ms...`);
|
|
722
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
process.on("uncaughtException", (error) => {
|
|
727
|
+
logger.error("daemon-runner", "Uncaught exception", error);
|
|
728
|
+
});
|
|
729
|
+
process.on("unhandledRejection", (reason) => {
|
|
730
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
731
|
+
logger.error("daemon-runner", "Unhandled rejection", error);
|
|
562
732
|
});
|
|
733
|
+
startWithRetry();
|
package/dist/index.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
commitQueue,
|
|
4
4
|
websocketService
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-RZM4P35S.js";
|
|
6
6
|
import {
|
|
7
7
|
apiService
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-3AS45O54.js";
|
|
9
9
|
import {
|
|
10
10
|
getPidFilePath,
|
|
11
11
|
gitService,
|
|
@@ -14,14 +14,14 @@ import {
|
|
|
14
14
|
projectService,
|
|
15
15
|
spawnDetached,
|
|
16
16
|
validatePidFile
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-UBNAWBEL.js";
|
|
18
18
|
import {
|
|
19
19
|
__commonJS,
|
|
20
20
|
__toESM,
|
|
21
21
|
authService,
|
|
22
22
|
config,
|
|
23
23
|
logger
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-XH2UAPVA.js";
|
|
25
25
|
|
|
26
26
|
// node_modules/semver/internal/constants.js
|
|
27
27
|
var require_constants = __commonJS({
|
|
@@ -1274,7 +1274,7 @@ var require_range = __commonJS({
|
|
|
1274
1274
|
var require_comparator = __commonJS({
|
|
1275
1275
|
"node_modules/semver/classes/comparator.js"(exports, module) {
|
|
1276
1276
|
"use strict";
|
|
1277
|
-
var ANY =
|
|
1277
|
+
var ANY = Symbol("SemVer ANY");
|
|
1278
1278
|
var Comparator = class _Comparator {
|
|
1279
1279
|
static get ANY() {
|
|
1280
1280
|
return ANY;
|
|
@@ -1955,8 +1955,8 @@ var require_semver2 = __commonJS({
|
|
|
1955
1955
|
|
|
1956
1956
|
// src/index.ts
|
|
1957
1957
|
import "dotenv/config";
|
|
1958
|
-
import { Command } from "commander";
|
|
1959
|
-
import
|
|
1958
|
+
import { Command as Command2 } from "commander";
|
|
1959
|
+
import chalk16 from "chalk";
|
|
1960
1960
|
|
|
1961
1961
|
// src/commands/login.ts
|
|
1962
1962
|
import open from "open";
|
|
@@ -2657,7 +2657,7 @@ function registerStatusCommand(program2) {
|
|
|
2657
2657
|
try {
|
|
2658
2658
|
const { render } = await import("ink");
|
|
2659
2659
|
const { createElement } = await import("react");
|
|
2660
|
-
const { StatusDashboard } = await import("./StatusDashboard-
|
|
2660
|
+
const { StatusDashboard } = await import("./StatusDashboard-NGRPLDM7.js");
|
|
2661
2661
|
render(createElement(StatusDashboard, { cwd }));
|
|
2662
2662
|
return;
|
|
2663
2663
|
} catch (error) {
|
|
@@ -4845,22 +4845,104 @@ ${chalk14.bold("Config file:")} ${chalk14.cyan(configPath)}
|
|
|
4845
4845
|
});
|
|
4846
4846
|
}
|
|
4847
4847
|
|
|
4848
|
+
// src/commands/tasks.ts
|
|
4849
|
+
import { Command } from "commander";
|
|
4850
|
+
import chalk15 from "chalk";
|
|
4851
|
+
import ora14 from "ora";
|
|
4852
|
+
var tasksCommand = new Command("tasks").description("List tasks for the current project").option("-b, --branch <branch>", "Filter tasks by specific branch").option("-a, --all", "Show all tasks (ignore current branch context)").action(async (options) => {
|
|
4853
|
+
try {
|
|
4854
|
+
const spinner = ora14("Checking project context...").start();
|
|
4855
|
+
const cwd = process.cwd();
|
|
4856
|
+
const isRepo = await gitService.isRepo(cwd);
|
|
4857
|
+
if (!isRepo) {
|
|
4858
|
+
spinner.fail("Not a git repository");
|
|
4859
|
+
console.log(
|
|
4860
|
+
chalk15.yellow("Please run this command from within a git repository.")
|
|
4861
|
+
);
|
|
4862
|
+
return;
|
|
4863
|
+
}
|
|
4864
|
+
const linkedProject = await projectService.getLinkedProject(cwd);
|
|
4865
|
+
if (!linkedProject) {
|
|
4866
|
+
spinner.fail("Project not linked");
|
|
4867
|
+
console.log(
|
|
4868
|
+
chalk15.yellow(
|
|
4869
|
+
"This repository is not linked to a Stint project.\nRun 'stint link' to link it first."
|
|
4870
|
+
)
|
|
4871
|
+
);
|
|
4872
|
+
return;
|
|
4873
|
+
}
|
|
4874
|
+
let filterBranch;
|
|
4875
|
+
if (options.all) {
|
|
4876
|
+
filterBranch = void 0;
|
|
4877
|
+
} else if (options.branch) {
|
|
4878
|
+
filterBranch = options.branch;
|
|
4879
|
+
} else {
|
|
4880
|
+
try {
|
|
4881
|
+
filterBranch = await gitService.getCurrentBranch(cwd);
|
|
4882
|
+
} catch (_error) {
|
|
4883
|
+
spinner.warn("Could not detect current branch, showing all tasks");
|
|
4884
|
+
}
|
|
4885
|
+
}
|
|
4886
|
+
spinner.text = "Fetching tasks...";
|
|
4887
|
+
const tasks = await apiService.getProjectTasks(
|
|
4888
|
+
linkedProject.projectId,
|
|
4889
|
+
filterBranch
|
|
4890
|
+
);
|
|
4891
|
+
spinner.stop();
|
|
4892
|
+
if (tasks.length === 0) {
|
|
4893
|
+
console.log(chalk15.gray("No tasks found."));
|
|
4894
|
+
if (filterBranch) {
|
|
4895
|
+
console.log(chalk15.gray(`(Filtered by branch: ${filterBranch})`));
|
|
4896
|
+
}
|
|
4897
|
+
return;
|
|
4898
|
+
}
|
|
4899
|
+
console.log(
|
|
4900
|
+
chalk15.bold(
|
|
4901
|
+
`
|
|
4902
|
+
Tasks for project ${chalk15.cyan(linkedProject.projectId)}`
|
|
4903
|
+
)
|
|
4904
|
+
);
|
|
4905
|
+
if (filterBranch) {
|
|
4906
|
+
console.log(chalk15.gray(`Branch: ${filterBranch}`));
|
|
4907
|
+
}
|
|
4908
|
+
console.log("");
|
|
4909
|
+
tasks.forEach((task) => {
|
|
4910
|
+
const priorityColor = task.priority === "urgent" ? chalk15.red : task.priority === "high" ? chalk15.yellow : task.priority === "medium" ? chalk15.blue : chalk15.gray;
|
|
4911
|
+
const statusIcon = task.status === "done" ? "\u2705" : task.status === "in_progress" ? "\u{1F6A7}" : task.status === "todo" ? "\u{1F4C5}" : "\u{1F4C2}";
|
|
4912
|
+
console.log(
|
|
4913
|
+
`${statusIcon} ${chalk15.white.bold(task.title)} ${priorityColor(
|
|
4914
|
+
`[${task.priority}]`
|
|
4915
|
+
)} ${chalk15.gray(`(${task.identifier})`)}`
|
|
4916
|
+
);
|
|
4917
|
+
if (task.description) {
|
|
4918
|
+
const desc = task.description.split("\n")[0].substring(0, 80) + (task.description.length > 80 ? "..." : "");
|
|
4919
|
+
console.log(` ${chalk15.gray(desc)}`);
|
|
4920
|
+
}
|
|
4921
|
+
console.log("");
|
|
4922
|
+
});
|
|
4923
|
+
} catch (error) {
|
|
4924
|
+
ora14().stop();
|
|
4925
|
+
logger.error("cli", "Failed to fetch tasks", error);
|
|
4926
|
+
process.exit(1);
|
|
4927
|
+
}
|
|
4928
|
+
});
|
|
4929
|
+
|
|
4848
4930
|
// src/index.ts
|
|
4849
|
-
var AGENT_VERSION = "1.2.
|
|
4850
|
-
var program = new
|
|
4931
|
+
var AGENT_VERSION = "1.2.40";
|
|
4932
|
+
var program = new Command2();
|
|
4851
4933
|
program.name("stint").description("Stint Agent - Local daemon for Stint Project Assistant").version(AGENT_VERSION, "-v, --version", "output the current version").addHelpText(
|
|
4852
4934
|
"after",
|
|
4853
4935
|
`
|
|
4854
|
-
${
|
|
4855
|
-
${
|
|
4856
|
-
${
|
|
4857
|
-
${
|
|
4858
|
-
${
|
|
4859
|
-
${
|
|
4860
|
-
${
|
|
4936
|
+
${chalk16.bold("Examples:")}
|
|
4937
|
+
${chalk16.cyan("$")} stint login ${chalk16.gray("# Authenticate with Stint")}
|
|
4938
|
+
${chalk16.cyan("$")} stint install ${chalk16.gray("# Install agent to run on startup")}
|
|
4939
|
+
${chalk16.cyan("$")} stint link ${chalk16.gray("# Link current directory to a project")}
|
|
4940
|
+
${chalk16.cyan("$")} stint daemon start ${chalk16.gray("# Start background daemon")}
|
|
4941
|
+
${chalk16.cyan("$")} stint status ${chalk16.gray("# Check status")}
|
|
4942
|
+
${chalk16.cyan("$")} stint commits ${chalk16.gray("# List pending commits")}
|
|
4861
4943
|
|
|
4862
|
-
${
|
|
4863
|
-
For more information, visit: ${
|
|
4944
|
+
${chalk16.bold("Documentation:")}
|
|
4945
|
+
For more information, visit: ${chalk16.blue("https://stint.codes/docs")}
|
|
4864
4946
|
`
|
|
4865
4947
|
);
|
|
4866
4948
|
registerLoginCommand(program);
|
|
@@ -4877,6 +4959,7 @@ registerUninstallCommand(program);
|
|
|
4877
4959
|
registerUpdateCommand(program);
|
|
4878
4960
|
registerDoctorCommand(program);
|
|
4879
4961
|
registerConfigCommand(program);
|
|
4962
|
+
program.addCommand(tasksCommand);
|
|
4880
4963
|
program.exitOverride();
|
|
4881
4964
|
try {
|
|
4882
4965
|
await program.parseAsync(process.argv);
|
|
@@ -4884,7 +4967,7 @@ try {
|
|
|
4884
4967
|
const commanderError = error;
|
|
4885
4968
|
if (commanderError.code !== "commander.help" && commanderError.code !== "commander.version" && commanderError.code !== "commander.helpDisplayed") {
|
|
4886
4969
|
logger.error("cli", "Command execution failed", error);
|
|
4887
|
-
console.error(
|
|
4970
|
+
console.error(chalk16.red(`
|
|
4888
4971
|
\u2716 Error: ${error.message}
|
|
4889
4972
|
`));
|
|
4890
4973
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gowelle/stint-agent",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.40",
|
|
4
4
|
"description": "Local agent for Stint - Project Assistant",
|
|
5
5
|
"author": "Gowelle John <gowelle.john@icloud.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,6 +33,8 @@
|
|
|
33
33
|
"build": "tsup",
|
|
34
34
|
"dev": "tsup --watch",
|
|
35
35
|
"lint": "eslint src",
|
|
36
|
+
"format": "prettier --write src",
|
|
37
|
+
"format:check": "prettier --check src",
|
|
36
38
|
"test": "vitest",
|
|
37
39
|
"test:update": "node --loader ts-node/esm scripts/test-update.ts",
|
|
38
40
|
"prepublishOnly": "npm run build"
|
|
@@ -65,6 +67,8 @@
|
|
|
65
67
|
"@typescript-eslint/parser": "^8.50.0",
|
|
66
68
|
"@vitest/coverage-v8": "^4.0.16",
|
|
67
69
|
"eslint": "^8.56.0",
|
|
70
|
+
"eslint-config-prettier": "^10.1.8",
|
|
71
|
+
"prettier": "^3.7.4",
|
|
68
72
|
"ts-node": "^10.9.2",
|
|
69
73
|
"tsup": "^8.0.1",
|
|
70
74
|
"typescript": "^5.3.3",
|