github-issue-tower-defence-management 1.104.4 → 1.106.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 (101) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +8 -7
  3. package/bin/adapter/entry-points/cli/index.js +21 -13
  4. package/bin/adapter/entry-points/cli/index.js.map +1 -1
  5. package/bin/adapter/entry-points/console/{consoleServer.js → webServer.js} +11 -11
  6. package/bin/adapter/entry-points/console/webServer.js.map +1 -0
  7. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +54 -2
  8. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
  9. package/bin/adapter/entry-points/handlers/dashboardRowWriter.js +34 -0
  10. package/bin/adapter/entry-points/handlers/dashboardRowWriter.js.map +1 -0
  11. package/bin/adapter/entry-points/handlers/machineStatusWriter.js +60 -0
  12. package/bin/adapter/entry-points/handlers/machineStatusWriter.js.map +1 -0
  13. package/bin/adapter/entry-points/handlers/staleTmuxSessionCleaner.js +18 -0
  14. package/bin/adapter/entry-points/handlers/staleTmuxSessionCleaner.js.map +1 -0
  15. package/bin/adapter/entry-points/handlers/tokenStatusWriter.js +98 -0
  16. package/bin/adapter/entry-points/handlers/tokenStatusWriter.js.map +1 -0
  17. package/bin/adapter/proxy/RateLimitCache.js +3 -0
  18. package/bin/adapter/proxy/RateLimitCache.js.map +1 -1
  19. package/bin/adapter/repositories/NodeTmuxSessionRepository.js +22 -0
  20. package/bin/adapter/repositories/NodeTmuxSessionRepository.js.map +1 -1
  21. package/bin/adapter/repositories/ProcHostMetricsRepository.js +136 -0
  22. package/bin/adapter/repositories/ProcHostMetricsRepository.js.map +1 -0
  23. package/bin/adapter/repositories/ProcTakeOwnershipSpawnRepository.js +131 -0
  24. package/bin/adapter/repositories/ProcTakeOwnershipSpawnRepository.js.map +1 -0
  25. package/bin/domain/entities/LiveTmuxSession.js +3 -0
  26. package/bin/domain/entities/LiveTmuxSession.js.map +1 -0
  27. package/bin/domain/usecases/StaleTmuxSessionKillUseCase.js +58 -0
  28. package/bin/domain/usecases/StaleTmuxSessionKillUseCase.js.map +1 -0
  29. package/bin/domain/usecases/adapter-interfaces/TakeOwnershipSpawnRepository.js +3 -0
  30. package/bin/domain/usecases/adapter-interfaces/TakeOwnershipSpawnRepository.js.map +1 -0
  31. package/bin/domain/usecases/dashboard/GenerateDashboardRowUseCase.js +38 -0
  32. package/bin/domain/usecases/dashboard/GenerateDashboardRowUseCase.js.map +1 -0
  33. package/bin/domain/usecases/dashboard/GenerateTokenStatusUseCase.js +115 -0
  34. package/bin/domain/usecases/dashboard/GenerateTokenStatusUseCase.js.map +1 -0
  35. package/package.json +1 -1
  36. package/src/adapter/entry-points/cli/index.test.ts +74 -21
  37. package/src/adapter/entry-points/cli/index.ts +146 -136
  38. package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +2 -2
  39. package/src/adapter/entry-points/console/{consoleServer.test.ts → webServer.test.ts} +32 -32
  40. package/src/adapter/entry-points/console/{consoleServer.ts → webServer.ts} +15 -17
  41. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +69 -0
  42. package/src/adapter/entry-points/handlers/dashboardRowWriter.test.ts +111 -0
  43. package/src/adapter/entry-points/handlers/dashboardRowWriter.ts +51 -0
  44. package/src/adapter/entry-points/handlers/machineStatusWriter.test.ts +100 -0
  45. package/src/adapter/entry-points/handlers/machineStatusWriter.ts +79 -0
  46. package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.test.ts +170 -0
  47. package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.ts +40 -0
  48. package/src/adapter/entry-points/handlers/tokenStatusWriter.test.ts +176 -0
  49. package/src/adapter/entry-points/handlers/tokenStatusWriter.ts +139 -0
  50. package/src/adapter/proxy/RateLimitCache.test.ts +32 -0
  51. package/src/adapter/proxy/RateLimitCache.ts +6 -0
  52. package/src/adapter/repositories/NodeTmuxSessionRepository.test.ts +81 -0
  53. package/src/adapter/repositories/NodeTmuxSessionRepository.ts +35 -0
  54. package/src/adapter/repositories/ProcHostMetricsRepository.test.ts +135 -0
  55. package/src/adapter/repositories/ProcHostMetricsRepository.ts +136 -0
  56. package/src/adapter/repositories/ProcTakeOwnershipSpawnRepository.test.ts +130 -0
  57. package/src/adapter/repositories/ProcTakeOwnershipSpawnRepository.ts +118 -0
  58. package/src/domain/entities/LiveTmuxSession.ts +4 -0
  59. package/src/domain/usecases/StaleTmuxSessionKillUseCase.test.ts +286 -0
  60. package/src/domain/usecases/StaleTmuxSessionKillUseCase.ts +102 -0
  61. package/src/domain/usecases/adapter-interfaces/TakeOwnershipSpawnRepository.ts +8 -0
  62. package/src/domain/usecases/adapter-interfaces/TmuxSessionRepository.ts +4 -0
  63. package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.test.ts +159 -0
  64. package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.ts +72 -0
  65. package/src/domain/usecases/dashboard/GenerateTokenStatusUseCase.test.ts +209 -0
  66. package/src/domain/usecases/dashboard/GenerateTokenStatusUseCase.ts +195 -0
  67. package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.test.ts +6 -0
  68. package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
  69. package/types/adapter/entry-points/console/{consoleServer.d.ts → webServer.d.ts} +7 -7
  70. package/types/adapter/entry-points/console/webServer.d.ts.map +1 -0
  71. package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
  72. package/types/adapter/entry-points/handlers/dashboardRowWriter.d.ts +15 -0
  73. package/types/adapter/entry-points/handlers/dashboardRowWriter.d.ts.map +1 -0
  74. package/types/adapter/entry-points/handlers/machineStatusWriter.d.ts +16 -0
  75. package/types/adapter/entry-points/handlers/machineStatusWriter.d.ts.map +1 -0
  76. package/types/adapter/entry-points/handlers/staleTmuxSessionCleaner.d.ts +12 -0
  77. package/types/adapter/entry-points/handlers/staleTmuxSessionCleaner.d.ts.map +1 -0
  78. package/types/adapter/entry-points/handlers/tokenStatusWriter.d.ts +21 -0
  79. package/types/adapter/entry-points/handlers/tokenStatusWriter.d.ts.map +1 -0
  80. package/types/adapter/proxy/RateLimitCache.d.ts +2 -0
  81. package/types/adapter/proxy/RateLimitCache.d.ts.map +1 -1
  82. package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts +3 -0
  83. package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts.map +1 -1
  84. package/types/adapter/repositories/ProcHostMetricsRepository.d.ts +23 -0
  85. package/types/adapter/repositories/ProcHostMetricsRepository.d.ts.map +1 -0
  86. package/types/adapter/repositories/ProcTakeOwnershipSpawnRepository.d.ts +11 -0
  87. package/types/adapter/repositories/ProcTakeOwnershipSpawnRepository.d.ts.map +1 -0
  88. package/types/domain/entities/LiveTmuxSession.d.ts +5 -0
  89. package/types/domain/entities/LiveTmuxSession.d.ts.map +1 -0
  90. package/types/domain/usecases/StaleTmuxSessionKillUseCase.d.ts +19 -0
  91. package/types/domain/usecases/StaleTmuxSessionKillUseCase.d.ts.map +1 -0
  92. package/types/domain/usecases/adapter-interfaces/TakeOwnershipSpawnRepository.d.ts +8 -0
  93. package/types/domain/usecases/adapter-interfaces/TakeOwnershipSpawnRepository.d.ts.map +1 -0
  94. package/types/domain/usecases/adapter-interfaces/TmuxSessionRepository.d.ts +3 -0
  95. package/types/domain/usecases/adapter-interfaces/TmuxSessionRepository.d.ts.map +1 -1
  96. package/types/domain/usecases/dashboard/GenerateDashboardRowUseCase.d.ts +19 -0
  97. package/types/domain/usecases/dashboard/GenerateDashboardRowUseCase.d.ts.map +1 -0
  98. package/types/domain/usecases/dashboard/GenerateTokenStatusUseCase.d.ts +53 -0
  99. package/types/domain/usecases/dashboard/GenerateTokenStatusUseCase.d.ts.map +1 -0
  100. package/bin/adapter/entry-points/console/consoleServer.js.map +0 -1
  101. package/types/adapter/entry-points/console/consoleServer.d.ts.map +0 -1
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StaleTmuxSessionKillUseCase = exports.DEFAULT_IDLE_THRESHOLD_SECONDS = exports.DEFAULT_EXCLUDED_STATUS = void 0;
4
+ const WorkflowStatus_1 = require("../entities/WorkflowStatus");
5
+ const InTmuxByHumanSessionReconcileUseCase_1 = require("./intmux/InTmuxByHumanSessionReconcileUseCase");
6
+ exports.DEFAULT_EXCLUDED_STATUS = WorkflowStatus_1.IN_TMUX_STATUS_NAME;
7
+ exports.DEFAULT_IDLE_THRESHOLD_SECONDS = 24 * 60 * 60;
8
+ class StaleTmuxSessionKillUseCase {
9
+ constructor(issueRepository, tmuxSessionRepository) {
10
+ this.issueRepository = issueRepository;
11
+ this.tmuxSessionRepository = tmuxSessionRepository;
12
+ this.run = async (params) => {
13
+ const liveSessions = await this.tmuxSessionRepository.listLiveSessionsWithActivity();
14
+ const openIssues = await this.issueRepository.getAllOpened(params.project, params.allowCacheMinutes);
15
+ const issueBySessionName = new Map();
16
+ for (const issue of openIssues) {
17
+ issueBySessionName.set((0, InTmuxByHumanSessionReconcileUseCase_1.toTmuxSessionName)(issue.url), issue);
18
+ }
19
+ const nowEpochSeconds = Math.floor(params.now.getTime() / 1000);
20
+ const killCandidates = [];
21
+ for (const session of liveSessions) {
22
+ const reason = this.evaluateKillReason(session, issueBySessionName.get(session.sessionName) ?? null, nowEpochSeconds, params.excludedStatus, params.idleThresholdSeconds);
23
+ if (reason !== null) {
24
+ killCandidates.push({ sessionName: session.sessionName, reason });
25
+ }
26
+ }
27
+ console.log(`Stale tmux session cleanup: ${killCandidates.length} kill candidate(s) of ${liveSessions.length} live session(s).`);
28
+ for (const candidate of killCandidates) {
29
+ console.log(`Kill candidate: ${candidate.sessionName} (${candidate.reason})`);
30
+ }
31
+ for (const candidate of killCandidates) {
32
+ await this.tmuxSessionRepository.killSession(candidate.sessionName);
33
+ console.log(`Killed tmux session: ${candidate.sessionName} (${candidate.reason})`);
34
+ }
35
+ };
36
+ this.evaluateKillReason = (session, issue, nowEpochSeconds, excludedStatus, idleThresholdSeconds) => {
37
+ if (issue !== null) {
38
+ if (issue.status !== excludedStatus) {
39
+ return `mapped to open issue ${issue.url} with status "${issue.status ?? 'null'}" which is not the excluded status "${excludedStatus}"`;
40
+ }
41
+ if (issue.nextActionDate !== null) {
42
+ return `mapped to open issue ${issue.url} which has a next action date set`;
43
+ }
44
+ if (issue.nextActionHour !== null) {
45
+ return `mapped to open issue ${issue.url} which has a next action hour set`;
46
+ }
47
+ return null;
48
+ }
49
+ const idleSeconds = nowEpochSeconds - session.activityEpochSeconds;
50
+ if (idleSeconds >= idleThresholdSeconds) {
51
+ return `maps to no open issue and has been idle for ${idleSeconds} seconds (threshold ${idleThresholdSeconds} seconds)`;
52
+ }
53
+ return null;
54
+ };
55
+ }
56
+ }
57
+ exports.StaleTmuxSessionKillUseCase = StaleTmuxSessionKillUseCase;
58
+ //# sourceMappingURL=StaleTmuxSessionKillUseCase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StaleTmuxSessionKillUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/StaleTmuxSessionKillUseCase.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=TakeOwnershipSpawnRepository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TakeOwnershipSpawnRepository.js","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/TakeOwnershipSpawnRepository.ts"],"names":[],"mappings":""}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GenerateDashboardRowUseCase = void 0;
4
+ const WorkflowStatus_1 = require("../../entities/WorkflowStatus");
5
+ const WORKFLOW_BLOCKER_STORY_MARKER = 'workflow blocker';
6
+ class GenerateDashboardRowUseCase {
7
+ constructor() {
8
+ this.run = (input) => {
9
+ const { issues, assigneeLogin } = input;
10
+ const mine = (issue) => issue.isClosed === false && issue.assignees.includes(assigneeLogin);
11
+ const actionable = (issue) => mine(issue) &&
12
+ issue.dependedIssueUrls.length === 0 &&
13
+ issue.nextActionDate === null &&
14
+ issue.nextActionHour === null;
15
+ const countActionableWithStatus = (statusName) => issues.filter((issue) => issue.status === statusName && actionable(issue))
16
+ .length;
17
+ const countMineWithStatus = (statusName) => issues.filter((issue) => mine(issue) && issue.status === statusName)
18
+ .length;
19
+ return {
20
+ unread: countActionableWithStatus(WorkflowStatus_1.DEFAULT_STATUS_NAME),
21
+ todo: countActionableWithStatus(WorkflowStatus_1.TODO_STATUS_NAME),
22
+ qc: countActionableWithStatus(WorkflowStatus_1.AWAITING_QUALITY_CHECK_STATUS_NAME),
23
+ fail: countMineWithStatus(WorkflowStatus_1.FAILED_PREPARATION_STATUS_NAME),
24
+ pr: countMineWithStatus(WorkflowStatus_1.PREPARATION_STATUS_NAME),
25
+ ws: countActionableWithStatus(WorkflowStatus_1.AWAITING_WORKSPACE_STATUS_NAME),
26
+ dep: issues.filter((issue) => mine(issue) &&
27
+ issue.status === WorkflowStatus_1.AWAITING_WORKSPACE_STATUS_NAME &&
28
+ issue.dependedIssueUrls.length > 0).length,
29
+ blocker: issues.filter((issue) => mine(issue) &&
30
+ (issue.story ?? '')
31
+ .toLowerCase()
32
+ .includes(WORKFLOW_BLOCKER_STORY_MARKER)).length,
33
+ };
34
+ };
35
+ }
36
+ }
37
+ exports.GenerateDashboardRowUseCase = GenerateDashboardRowUseCase;
38
+ //# sourceMappingURL=GenerateDashboardRowUseCase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GenerateDashboardRowUseCase.js","sourceRoot":"","sources":["../../../../src/domain/usecases/dashboard/GenerateDashboardRowUseCase.ts"],"names":[],"mappings":";;;AACA,kEAOuC;AAkBvC,MAAM,6BAA6B,GAAG,kBAAkB,CAAC;AAEzD,MAAa,2BAA2B;IAAxC;QACE,QAAG,GAAG,CAAC,KAAgC,EAAgB,EAAE;YACvD,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC;YAExC,MAAM,IAAI,GAAG,CAAC,KAAY,EAAW,EAAE,CACrC,KAAK,CAAC,QAAQ,KAAK,KAAK,IAAI,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;YAEtE,MAAM,UAAU,GAAG,CAAC,KAAY,EAAW,EAAE,CAC3C,IAAI,CAAC,KAAK,CAAC;gBACX,KAAK,CAAC,iBAAiB,CAAC,MAAM,KAAK,CAAC;gBACpC,KAAK,CAAC,cAAc,KAAK,IAAI;gBAC7B,KAAK,CAAC,cAAc,KAAK,IAAI,CAAC;YAEhC,MAAM,yBAAyB,GAAG,CAAC,UAAkB,EAAU,EAAE,CAC/D,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,UAAU,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;iBACvE,MAAM,CAAC;YAEZ,MAAM,mBAAmB,GAAG,CAAC,UAAkB,EAAU,EAAE,CACzD,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU,CAAC;iBACjE,MAAM,CAAC;YAEZ,OAAO;gBACL,MAAM,EAAE,yBAAyB,CAAC,oCAAmB,CAAC;gBACtD,IAAI,EAAE,yBAAyB,CAAC,iCAAgB,CAAC;gBACjD,EAAE,EAAE,yBAAyB,CAAC,mDAAkC,CAAC;gBACjE,IAAI,EAAE,mBAAmB,CAAC,+CAA8B,CAAC;gBACzD,EAAE,EAAE,mBAAmB,CAAC,wCAAuB,CAAC;gBAChD,EAAE,EAAE,yBAAyB,CAAC,+CAA8B,CAAC;gBAC7D,GAAG,EAAE,MAAM,CAAC,MAAM,CAChB,CAAC,KAAK,EAAE,EAAE,CACR,IAAI,CAAC,KAAK,CAAC;oBACX,KAAK,CAAC,MAAM,KAAK,+CAA8B;oBAC/C,KAAK,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,CACrC,CAAC,MAAM;gBACR,OAAO,EAAE,MAAM,CAAC,MAAM,CACpB,CAAC,KAAK,EAAE,EAAE,CACR,IAAI,CAAC,KAAK,CAAC;oBACX,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;yBAChB,WAAW,EAAE;yBACb,QAAQ,CAAC,6BAA6B,CAAC,CAC7C,CAAC,MAAM;aACT,CAAC;QACJ,CAAC,CAAC;IACJ,CAAC;CAAA;AA3CD,kEA2CC"}
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GenerateTokenStatusUseCase = exports.judgeTokenColor = void 0;
4
+ const HIGH_UTILIZATION_THRESHOLD = 0.7;
5
+ const ALLOWED_WARNING_STATUS = 'allowed_warning';
6
+ const judgeTokenColor = (decision) => {
7
+ if (decision === null || decision.partial) {
8
+ return 'Y';
9
+ }
10
+ const fiveHourUtilization = decision.fiveHourUtilization ?? 0;
11
+ const sevenDayUtilization = decision.sevenDayUtilization ?? 0;
12
+ const fiveHourExhausted = fiveHourUtilization >= 1.0 || decision.fiveHourRejected;
13
+ const generalSevenDayRejected = decision.sevenDayRejected;
14
+ const bothModelsSevenDayRejected = decision.sevenDaySonnetRejected && decision.sevenDayOpusRejected;
15
+ const noModelUsable = decision.blocked ||
16
+ fiveHourExhausted ||
17
+ generalSevenDayRejected ||
18
+ bothModelsSevenDayRejected;
19
+ if (noModelUsable) {
20
+ return 'K';
21
+ }
22
+ if (decision.unifiedStatus === ALLOWED_WARNING_STATUS) {
23
+ return 'Y';
24
+ }
25
+ if (fiveHourUtilization >= HIGH_UTILIZATION_THRESHOLD ||
26
+ sevenDayUtilization >= HIGH_UTILIZATION_THRESHOLD) {
27
+ return 'Y';
28
+ }
29
+ return 'G';
30
+ };
31
+ exports.judgeTokenColor = judgeTokenColor;
32
+ class GenerateTokenStatusUseCase {
33
+ constructor() {
34
+ this.run = (input) => {
35
+ const { tokens, prepCountByToken, humCountByToken, nowEpochSeconds } = input;
36
+ return tokens.map((tokenInput) => {
37
+ const decision = this.toDecision(tokenInput.snapshot, nowEpochSeconds);
38
+ const normalized = this.normalizeWindows(tokenInput.snapshot, nowEpochSeconds);
39
+ return {
40
+ name: tokenInput.name,
41
+ fiveHourUtilizationPercent: normalized.fiveHourUtilizationPercent,
42
+ fiveHourResetSeconds: normalized.fiveHourResetSeconds,
43
+ sevenDayUtilizationPercent: normalized.sevenDayUtilizationPercent,
44
+ sevenDayResetSeconds: normalized.sevenDayResetSeconds,
45
+ color: (0, exports.judgeTokenColor)(decision),
46
+ prep: prepCountByToken.get(tokenInput.token) ?? 0,
47
+ hum: humCountByToken.get(tokenInput.token) ?? 0,
48
+ };
49
+ });
50
+ };
51
+ this.normalizeWindows = (snapshot, nowEpochSeconds) => {
52
+ if (snapshot === null || !snapshot.hasWindowData) {
53
+ return {
54
+ fiveHourUtilizationPercent: null,
55
+ fiveHourResetSeconds: null,
56
+ sevenDayUtilizationPercent: null,
57
+ sevenDayResetSeconds: null,
58
+ };
59
+ }
60
+ const fiveHourReset = snapshot.fiveHourReset > 0 ? snapshot.fiveHourReset : null;
61
+ const sevenDayReset = snapshot.sevenDayReset > 0 ? snapshot.sevenDayReset : null;
62
+ const fiveHourExpired = fiveHourReset !== null && fiveHourReset < nowEpochSeconds;
63
+ const sevenDayExpired = sevenDayReset !== null && sevenDayReset < nowEpochSeconds;
64
+ return {
65
+ fiveHourUtilizationPercent: fiveHourExpired
66
+ ? 0
67
+ : Math.trunc(snapshot.fiveHourUtilization * 100),
68
+ fiveHourResetSeconds: fiveHourReset === null
69
+ ? null
70
+ : Math.max(0, fiveHourReset - nowEpochSeconds),
71
+ sevenDayUtilizationPercent: sevenDayExpired
72
+ ? 0
73
+ : Math.trunc(snapshot.sevenDayUtilization * 100),
74
+ sevenDayResetSeconds: sevenDayReset === null
75
+ ? null
76
+ : Math.max(0, sevenDayReset - nowEpochSeconds),
77
+ };
78
+ };
79
+ this.toDecision = (snapshot, nowEpochSeconds) => {
80
+ if (snapshot === null) {
81
+ return null;
82
+ }
83
+ if (!snapshot.hasWindowData) {
84
+ return {
85
+ fiveHourUtilization: null,
86
+ sevenDayUtilization: null,
87
+ fiveHourRejected: snapshot.fiveHourRejected,
88
+ sevenDayRejected: snapshot.sevenDayRejected,
89
+ blocked: snapshot.blocked,
90
+ unifiedStatus: snapshot.unifiedStatus,
91
+ sevenDaySonnetRejected: snapshot.sevenDaySonnetRejected,
92
+ sevenDayOpusRejected: snapshot.sevenDayOpusRejected,
93
+ partial: true,
94
+ };
95
+ }
96
+ const fiveHourExpired = snapshot.fiveHourReset > 0 && snapshot.fiveHourReset < nowEpochSeconds;
97
+ const sevenDayExpired = snapshot.sevenDayReset > 0 && snapshot.sevenDayReset < nowEpochSeconds;
98
+ return {
99
+ fiveHourUtilization: fiveHourExpired ? 0 : snapshot.fiveHourUtilization,
100
+ sevenDayUtilization: sevenDayExpired ? 0 : snapshot.sevenDayUtilization,
101
+ fiveHourRejected: fiveHourExpired ? false : snapshot.fiveHourRejected,
102
+ sevenDayRejected: sevenDayExpired ? false : snapshot.sevenDayRejected,
103
+ blocked: snapshot.blocked,
104
+ unifiedStatus: snapshot.unifiedStatus,
105
+ sevenDaySonnetRejected: snapshot.sevenDaySonnetRejected ||
106
+ (sevenDayExpired ? false : snapshot.sevenDayRejected),
107
+ sevenDayOpusRejected: snapshot.sevenDayOpusRejected ||
108
+ (sevenDayExpired ? false : snapshot.sevenDayRejected),
109
+ partial: false,
110
+ };
111
+ };
112
+ }
113
+ }
114
+ exports.GenerateTokenStatusUseCase = GenerateTokenStatusUseCase;
115
+ //# sourceMappingURL=GenerateTokenStatusUseCase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GenerateTokenStatusUseCase.js","sourceRoot":"","sources":["../../../../src/domain/usecases/dashboard/GenerateTokenStatusUseCase.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-issue-tower-defence-management",
3
- "version": "1.104.4",
3
+ "version": "1.106.0",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {
@@ -73,23 +73,22 @@ jest.mock('../handlers/HandleScheduledEventUseCaseHandler', () => ({
73
73
  handle: jest.fn().mockResolvedValue(null),
74
74
  })),
75
75
  }));
76
- import type { StartConsoleServerOptions } from '../console/consoleServer';
76
+ import type { StartWebServerOptions } from '../console/webServer';
77
77
 
78
- const mockStartConsoleServer = jest
79
- .fn<Promise<unknown>, [StartConsoleServerOptions]>()
78
+ const mockStartWebServer = jest
79
+ .fn<Promise<unknown>, [StartWebServerOptions]>()
80
80
  .mockResolvedValue({
81
81
  close: jest.fn(),
82
82
  address: jest.fn().mockReturnValue({ port: 9981 }),
83
83
  });
84
- jest.mock('../console/consoleServer', () => {
84
+ jest.mock('../console/webServer', () => {
85
85
  const actual: Record<string, unknown> = jest.requireActual(
86
- '../console/consoleServer',
86
+ '../console/webServer',
87
87
  );
88
88
  return {
89
89
  ...actual,
90
- startConsoleServer: (
91
- options: StartConsoleServerOptions,
92
- ): Promise<unknown> => mockStartConsoleServer(options),
90
+ startWebServer: (options: StartWebServerOptions): Promise<unknown> =>
91
+ mockStartWebServer(options),
93
92
  };
94
93
  });
95
94
 
@@ -1677,10 +1676,10 @@ mysteryKey: 'value'
1677
1676
  });
1678
1677
  });
1679
1678
 
1680
- describe('serveConsole', () => {
1679
+ describe('serveWeb', () => {
1681
1680
  it('should appear in the CLI help output', () => {
1682
1681
  const helpText = program.helpInformation();
1683
- expect(helpText).toContain('serveConsole');
1682
+ expect(helpText).toContain('serveWeb');
1684
1683
  });
1685
1684
 
1686
1685
  it('should start the server on the default port 9981 when --port is omitted', async () => {
@@ -1690,13 +1689,13 @@ mysteryKey: 'value'
1690
1689
  await program.parseAsync([
1691
1690
  'node',
1692
1691
  'test',
1693
- 'serveConsole',
1692
+ 'serveWeb',
1694
1693
  '--configFilePath',
1695
1694
  configFilePath,
1696
1695
  ]);
1697
1696
 
1698
- expect(mockStartConsoleServer).toHaveBeenCalledTimes(1);
1699
- const callArg = mockStartConsoleServer.mock.calls[0][0];
1697
+ expect(mockStartWebServer).toHaveBeenCalledTimes(1);
1698
+ const callArg = mockStartWebServer.mock.calls[0][0];
1700
1699
  expect(callArg.port).toBe(9981);
1701
1700
  expect(callArg.accessToken).toBe('config-token');
1702
1701
  expect(callArg.consoleDataOutputDir).toBeNull();
@@ -1717,7 +1716,7 @@ mysteryKey: 'value'
1717
1716
  await program.parseAsync([
1718
1717
  'node',
1719
1718
  'test',
1720
- 'serveConsole',
1719
+ 'serveWeb',
1721
1720
  '--configFilePath',
1722
1721
  configFilePath,
1723
1722
  '--port',
@@ -1726,7 +1725,7 @@ mysteryKey: 'value'
1726
1725
  '/tmp/console-data',
1727
1726
  ]);
1728
1727
 
1729
- const callArg = mockStartConsoleServer.mock.calls[0][0];
1728
+ const callArg = mockStartWebServer.mock.calls[0][0];
1730
1729
  expect(callArg.port).toBe(12345);
1731
1730
  expect(callArg.consoleDataOutputDir).toBe('/tmp/console-data');
1732
1731
 
@@ -1746,7 +1745,7 @@ mysteryKey: 'value'
1746
1745
  program.parseAsync([
1747
1746
  'node',
1748
1747
  'test',
1749
- 'serveConsole',
1748
+ 'serveWeb',
1750
1749
  '--configFilePath',
1751
1750
  configFilePath,
1752
1751
  ]),
@@ -1774,7 +1773,7 @@ mysteryKey: 'value'
1774
1773
  program.parseAsync([
1775
1774
  'node',
1776
1775
  'test',
1777
- 'serveConsole',
1776
+ 'serveWeb',
1778
1777
  '--configFilePath',
1779
1778
  configFilePath,
1780
1779
  '--port',
@@ -1795,12 +1794,12 @@ mysteryKey: 'value'
1795
1794
  await program.parseAsync([
1796
1795
  'node',
1797
1796
  'test',
1798
- 'serveConsole',
1797
+ 'serveWeb',
1799
1798
  '--configFilePath',
1800
1799
  configFilePath,
1801
1800
  ]);
1802
1801
 
1803
- const callArg = mockStartConsoleServer.mock.calls[0][0];
1802
+ const callArg = mockStartWebServer.mock.calls[0][0];
1804
1803
  expect(callArg.issueRepository).not.toBeNull();
1805
1804
  expect(callArg.issueTitleStateCache).not.toBeNull();
1806
1805
 
@@ -1833,7 +1832,7 @@ mysteryKey: 'value'
1833
1832
  program.parseAsync([
1834
1833
  'node',
1835
1834
  'test',
1836
- 'serveConsole',
1835
+ 'serveWeb',
1837
1836
  '--configFilePath',
1838
1837
  configFilePath,
1839
1838
  ]),
@@ -1865,7 +1864,7 @@ mysteryKey: 'value'
1865
1864
  program.parseAsync([
1866
1865
  'node',
1867
1866
  'test',
1868
- 'serveConsole',
1867
+ 'serveWeb',
1869
1868
  '--configFilePath',
1870
1869
  configFilePath,
1871
1870
  ]),
@@ -1879,4 +1878,58 @@ mysteryKey: 'value'
1879
1878
  processExitSpy.mockRestore();
1880
1879
  });
1881
1880
  });
1881
+
1882
+ describe('serveConsole (deprecated alias)', () => {
1883
+ it('should appear in the CLI help output', () => {
1884
+ const helpText = program.helpInformation();
1885
+ expect(helpText).toContain('serveConsole');
1886
+ });
1887
+
1888
+ it('should route to the same handler as serveWeb on the default port 9981', async () => {
1889
+ writeConfig({ ...defaultConfig, consoleAccessToken: 'config-token' });
1890
+ const logSpy = jest.spyOn(console, 'log').mockImplementation();
1891
+
1892
+ await program.parseAsync([
1893
+ 'node',
1894
+ 'test',
1895
+ 'serveConsole',
1896
+ '--configFilePath',
1897
+ configFilePath,
1898
+ ]);
1899
+
1900
+ expect(mockStartWebServer).toHaveBeenCalledTimes(1);
1901
+ const callArg = mockStartWebServer.mock.calls[0][0];
1902
+ expect(callArg.port).toBe(9981);
1903
+ expect(callArg.accessToken).toBe('config-token');
1904
+ expect(callArg.consoleDataOutputDir).toBeNull();
1905
+ expect(callArg.uiDistDir).toBe(
1906
+ path.join(__dirname, '..', 'console', 'ui-dist'),
1907
+ );
1908
+
1909
+ logSpy.mockRestore();
1910
+ });
1911
+
1912
+ it('should use the provided --port and --consoleDataOutputDir', async () => {
1913
+ writeConfig({ ...defaultConfig, consoleAccessToken: 'config-token' });
1914
+ const logSpy = jest.spyOn(console, 'log').mockImplementation();
1915
+
1916
+ await program.parseAsync([
1917
+ 'node',
1918
+ 'test',
1919
+ 'serveConsole',
1920
+ '--configFilePath',
1921
+ configFilePath,
1922
+ '--port',
1923
+ '12345',
1924
+ '--consoleDataOutputDir',
1925
+ '/tmp/console-data',
1926
+ ]);
1927
+
1928
+ const callArg = mockStartWebServer.mock.calls[0][0];
1929
+ expect(callArg.port).toBe(12345);
1930
+ expect(callArg.consoleDataOutputDir).toBe('/tmp/console-data');
1931
+
1932
+ logSpy.mockRestore();
1933
+ });
1934
+ });
1882
1935
  });