github-issue-tower-defence-management 1.105.0 → 1.107.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/CHANGELOG.md +14 -0
- package/README.md +5 -0
- package/bin/adapter/entry-points/cli/index.js +18 -1
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/console/dashboardComposeService.js +178 -0
- package/bin/adapter/entry-points/console/dashboardComposeService.js.map +1 -0
- package/bin/adapter/entry-points/console/webServer.js +29 -7
- package/bin/adapter/entry-points/console/webServer.js.map +1 -1
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +13 -0
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/staleTmuxSessionCleaner.js +18 -0
- package/bin/adapter/entry-points/handlers/staleTmuxSessionCleaner.js.map +1 -0
- package/bin/adapter/repositories/NodeTmuxSessionRepository.js +22 -0
- package/bin/adapter/repositories/NodeTmuxSessionRepository.js.map +1 -1
- package/bin/domain/entities/LiveTmuxSession.js +3 -0
- package/bin/domain/entities/LiveTmuxSession.js.map +1 -0
- package/bin/domain/usecases/StaleTmuxSessionKillUseCase.js +58 -0
- package/bin/domain/usecases/StaleTmuxSessionKillUseCase.js.map +1 -0
- package/bin/domain/usecases/dashboard/ComposeDashboardUseCase.js +161 -0
- package/bin/domain/usecases/dashboard/ComposeDashboardUseCase.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/cli/index.ts +36 -2
- package/src/adapter/entry-points/console/dashboardComposeService.test.ts +443 -0
- package/src/adapter/entry-points/console/dashboardComposeService.ts +200 -0
- package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +2 -0
- package/src/adapter/entry-points/console/webServer.test.ts +255 -54
- package/src/adapter/entry-points/console/webServer.ts +43 -10
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +17 -0
- package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.test.ts +170 -0
- package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.ts +40 -0
- package/src/adapter/repositories/NodeTmuxSessionRepository.test.ts +81 -0
- package/src/adapter/repositories/NodeTmuxSessionRepository.ts +35 -0
- package/src/domain/entities/LiveTmuxSession.ts +4 -0
- package/src/domain/usecases/StaleTmuxSessionKillUseCase.test.ts +286 -0
- package/src/domain/usecases/StaleTmuxSessionKillUseCase.ts +102 -0
- package/src/domain/usecases/adapter-interfaces/TmuxSessionRepository.ts +4 -0
- package/src/domain/usecases/dashboard/ComposeDashboardUseCase.test.ts +405 -0
- package/src/domain/usecases/dashboard/ComposeDashboardUseCase.ts +220 -0
- package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.test.ts +6 -0
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
- package/types/adapter/entry-points/console/dashboardComposeService.d.ts +9 -0
- package/types/adapter/entry-points/console/dashboardComposeService.d.ts.map +1 -0
- package/types/adapter/entry-points/console/webServer.d.ts +4 -0
- package/types/adapter/entry-points/console/webServer.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/staleTmuxSessionCleaner.d.ts +12 -0
- package/types/adapter/entry-points/handlers/staleTmuxSessionCleaner.d.ts.map +1 -0
- package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts +3 -0
- package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts.map +1 -1
- package/types/domain/entities/LiveTmuxSession.d.ts +5 -0
- package/types/domain/entities/LiveTmuxSession.d.ts.map +1 -0
- package/types/domain/usecases/StaleTmuxSessionKillUseCase.d.ts +19 -0
- package/types/domain/usecases/StaleTmuxSessionKillUseCase.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/TmuxSessionRepository.d.ts +3 -0
- package/types/domain/usecases/adapter-interfaces/TmuxSessionRepository.d.ts.map +1 -1
- package/types/domain/usecases/dashboard/ComposeDashboardUseCase.d.ts +28 -0
- package/types/domain/usecases/dashboard/ComposeDashboardUseCase.d.ts.map +1 -0
|
@@ -14,6 +14,22 @@ class NodeTmuxSessionRepository {
|
|
|
14
14
|
.map((line) => line.trim())
|
|
15
15
|
.filter((line) => line.length > 0);
|
|
16
16
|
};
|
|
17
|
+
this.listLiveSessionsWithActivity = async () => {
|
|
18
|
+
const { stdout, exitCode } = await this.localCommandRunner.runCommand('tmux', ['list-sessions', '-F', '#{session_name} #{session_activity}']);
|
|
19
|
+
if (exitCode !== 0) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
return stdout
|
|
23
|
+
.split('\n')
|
|
24
|
+
.map((line) => line.trim())
|
|
25
|
+
.filter((line) => line.length > 0)
|
|
26
|
+
.map((line) => {
|
|
27
|
+
const separatorIndex = line.lastIndexOf(' ');
|
|
28
|
+
const sessionName = line.slice(0, separatorIndex);
|
|
29
|
+
const activityEpochSeconds = Number(line.slice(separatorIndex + 1));
|
|
30
|
+
return { sessionName, activityEpochSeconds };
|
|
31
|
+
});
|
|
32
|
+
};
|
|
17
33
|
this.listInteractiveProcessCommandLines = async () => {
|
|
18
34
|
const { stdout, exitCode } = await this.localCommandRunner.runCommand('ps', ['-eo', 'args=']);
|
|
19
35
|
if (exitCode !== 0) {
|
|
@@ -39,6 +55,12 @@ class NodeTmuxSessionRepository {
|
|
|
39
55
|
issueUrl,
|
|
40
56
|
]);
|
|
41
57
|
};
|
|
58
|
+
this.killSession = async (sessionName) => {
|
|
59
|
+
const { stderr, exitCode } = await this.localCommandRunner.runCommand('tmux', ['kill-session', '-t', sessionName]);
|
|
60
|
+
if (exitCode !== 0) {
|
|
61
|
+
throw new Error(`Failed to kill tmux session "${sessionName}": exit code ${exitCode}${stderr ? `: ${stderr}` : ''}`);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
42
64
|
}
|
|
43
65
|
}
|
|
44
66
|
exports.NodeTmuxSessionRepository = NodeTmuxSessionRepository;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeTmuxSessionRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/NodeTmuxSessionRepository.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"NodeTmuxSessionRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/NodeTmuxSessionRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LiveTmuxSession.js","sourceRoot":"","sources":["../../../src/domain/entities/LiveTmuxSession.ts"],"names":[],"mappings":""}
|
|
@@ -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,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ComposeDashboardUseCase = exports.formatTokenRowLine = exports.formatProjectRowLine = exports.formatProjectHeaderLine = exports.formatMachineStatusLine = exports.formatResetCountdown = exports.roundHalfToEven = exports.PROJECT_ROW_WIDTH_BUDGET = void 0;
|
|
4
|
+
exports.PROJECT_ROW_WIDTH_BUDGET = 32;
|
|
5
|
+
const PROJECT_COLUMNS = [
|
|
6
|
+
{ header: 'unr', key: 'unread' },
|
|
7
|
+
{ header: 'tdo', key: 'todo' },
|
|
8
|
+
{ header: 'aqc', key: 'qc' },
|
|
9
|
+
{ header: 'fal', key: 'fail' },
|
|
10
|
+
{ header: 'prp', key: 'pr' },
|
|
11
|
+
{ header: 'aws', key: 'ws' },
|
|
12
|
+
{ header: 'dep', key: 'dep' },
|
|
13
|
+
];
|
|
14
|
+
const PROJECT_COLUMN_WIDTH = 3;
|
|
15
|
+
const SEVERITY_BLANK = ' ';
|
|
16
|
+
const TOKEN_COLOR_DOT = {
|
|
17
|
+
G: '🟢',
|
|
18
|
+
Y: '🟡',
|
|
19
|
+
K: '⚪',
|
|
20
|
+
};
|
|
21
|
+
const padEnd = (value, width, fill) => {
|
|
22
|
+
let result = value;
|
|
23
|
+
while (result.length < width) {
|
|
24
|
+
result = result + fill;
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
const padStart = (value, width) => {
|
|
29
|
+
let result = value;
|
|
30
|
+
while (result.length < width) {
|
|
31
|
+
result = ' ' + result;
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
const padStartZero = (value, width) => {
|
|
36
|
+
let result = value;
|
|
37
|
+
while (result.length < width) {
|
|
38
|
+
result = '0' + result;
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
const roundHalfToEven = (value) => {
|
|
43
|
+
const floor = Math.floor(value);
|
|
44
|
+
const difference = value - floor;
|
|
45
|
+
if (difference < 0.5) {
|
|
46
|
+
return floor;
|
|
47
|
+
}
|
|
48
|
+
if (difference > 0.5) {
|
|
49
|
+
return floor + 1;
|
|
50
|
+
}
|
|
51
|
+
return floor % 2 === 0 ? floor : floor + 1;
|
|
52
|
+
};
|
|
53
|
+
exports.roundHalfToEven = roundHalfToEven;
|
|
54
|
+
const formatResetCountdown = (totalSeconds) => {
|
|
55
|
+
if (totalSeconds < 0) {
|
|
56
|
+
return '0d00h00';
|
|
57
|
+
}
|
|
58
|
+
const whole = Math.trunc(totalSeconds);
|
|
59
|
+
const days = Math.trunc(whole / 86400);
|
|
60
|
+
const afterDays = whole % 86400;
|
|
61
|
+
const hours = Math.trunc(afterDays / 3600);
|
|
62
|
+
const minutes = Math.trunc((afterDays % 3600) / 60);
|
|
63
|
+
return `${days}d${padStartZero(String(hours), 2)}h${padStartZero(String(minutes), 2)}`;
|
|
64
|
+
};
|
|
65
|
+
exports.formatResetCountdown = formatResetCountdown;
|
|
66
|
+
const formatMachineStatusLine = (machineStatus) => {
|
|
67
|
+
const memText = machineStatus !== null && machineStatus.memPct !== null
|
|
68
|
+
? `${machineStatus.memPct}%`
|
|
69
|
+
: '?%';
|
|
70
|
+
const cpuText = machineStatus !== null && machineStatus.cpuPct !== null
|
|
71
|
+
? `${machineStatus.cpuPct}%`
|
|
72
|
+
: '?%';
|
|
73
|
+
const load = machineStatus !== null ? machineStatus.load : null;
|
|
74
|
+
const oneMinute = load === null ? '?' : String((0, exports.roundHalfToEven)(load[0]));
|
|
75
|
+
const fiveMinute = load === null ? '?' : String((0, exports.roundHalfToEven)(load[1]));
|
|
76
|
+
const fifteenMinute = load === null ? '?' : String((0, exports.roundHalfToEven)(load[2]));
|
|
77
|
+
const cycle = machineStatus !== null && machineStatus.cycleMinutes !== null
|
|
78
|
+
? `cy${machineStatus.cycleMinutes}`
|
|
79
|
+
: 'cy-';
|
|
80
|
+
return `M${memText} C${cpuText} LA ${oneMinute} ${fiveMinute} ${fifteenMinute} ${cycle}`;
|
|
81
|
+
};
|
|
82
|
+
exports.formatMachineStatusLine = formatMachineStatusLine;
|
|
83
|
+
const capThreeDigits = (value) => value > 999 ? '999' : String(value);
|
|
84
|
+
const formatProjectHeaderLine = () => {
|
|
85
|
+
const head = padEnd('pj', 4, ' ');
|
|
86
|
+
const columns = PROJECT_COLUMNS.map((column) => ' ' + padStart(column.header, PROJECT_COLUMN_WIDTH)).join('');
|
|
87
|
+
return head + columns;
|
|
88
|
+
};
|
|
89
|
+
exports.formatProjectHeaderLine = formatProjectHeaderLine;
|
|
90
|
+
const severityDot = (row) => {
|
|
91
|
+
if (row.blocker >= 2) {
|
|
92
|
+
return '🔴';
|
|
93
|
+
}
|
|
94
|
+
if (row.blocker === 1) {
|
|
95
|
+
return '🟣';
|
|
96
|
+
}
|
|
97
|
+
if (row.unread >= 10 || row.qc >= 15 || row.fail >= 5) {
|
|
98
|
+
return '🟠';
|
|
99
|
+
}
|
|
100
|
+
if (row.unread >= 5 || row.qc >= 10 || row.fail >= 3) {
|
|
101
|
+
return '🟡';
|
|
102
|
+
}
|
|
103
|
+
return '🟢';
|
|
104
|
+
};
|
|
105
|
+
const formatProjectRowLine = (project) => {
|
|
106
|
+
const mark = project.row === null ? SEVERITY_BLANK : severityDot(project.row);
|
|
107
|
+
const cells = PROJECT_COLUMNS.map((column) => {
|
|
108
|
+
const cell = project.row === null ? '--' : capThreeDigits(project.row[column.key]);
|
|
109
|
+
return ' ' + padStart(cell, PROJECT_COLUMN_WIDTH);
|
|
110
|
+
}).join('');
|
|
111
|
+
return mark + padEnd(project.code, 2, ' ') + cells;
|
|
112
|
+
};
|
|
113
|
+
exports.formatProjectRowLine = formatProjectRowLine;
|
|
114
|
+
const formatUtilization = (percent) => padStart(percent === null ? '?' : `${percent}%`, 4);
|
|
115
|
+
const formatReset = (resetSeconds) => resetSeconds === null ? '?' : (0, exports.formatResetCountdown)(resetSeconds);
|
|
116
|
+
const tokenSortKey = (token) => token.sevenDayResetSeconds === null
|
|
117
|
+
? [1, 0]
|
|
118
|
+
: [0, token.sevenDayResetSeconds];
|
|
119
|
+
const sortTokens = (tokens) => tokens
|
|
120
|
+
.map((token, index) => ({ token, index }))
|
|
121
|
+
.sort((left, right) => {
|
|
122
|
+
const leftKey = tokenSortKey(left.token);
|
|
123
|
+
const rightKey = tokenSortKey(right.token);
|
|
124
|
+
if (leftKey[0] !== rightKey[0]) {
|
|
125
|
+
return leftKey[0] - rightKey[0];
|
|
126
|
+
}
|
|
127
|
+
if (leftKey[1] !== rightKey[1]) {
|
|
128
|
+
return leftKey[1] - rightKey[1];
|
|
129
|
+
}
|
|
130
|
+
return left.index - right.index;
|
|
131
|
+
})
|
|
132
|
+
.map((entry) => entry.token);
|
|
133
|
+
const formatTokenRowLine = (token) => {
|
|
134
|
+
const dot = TOKEN_COLOR_DOT[token.color];
|
|
135
|
+
const name = padEnd(token.name, 4, '_');
|
|
136
|
+
const fiveHourUtilization = formatUtilization(token.fiveHourUtilizationPercent);
|
|
137
|
+
const fiveHourReset = formatReset(token.fiveHourResetSeconds);
|
|
138
|
+
const sevenDayUtilization = formatUtilization(token.sevenDayUtilizationPercent);
|
|
139
|
+
const sevenDayReset = formatReset(token.sevenDayResetSeconds);
|
|
140
|
+
const prep = String(token.prep);
|
|
141
|
+
const hum = String(token.hum);
|
|
142
|
+
return `${dot}${name} ${fiveHourUtilization} ${fiveHourReset} ${sevenDayUtilization} ${sevenDayReset} ${prep} ${hum}`;
|
|
143
|
+
};
|
|
144
|
+
exports.formatTokenRowLine = formatTokenRowLine;
|
|
145
|
+
const wrapLine = (line) => `<tt>${line.replace(/ /g, ' ')}</tt><br>`;
|
|
146
|
+
class ComposeDashboardUseCase {
|
|
147
|
+
constructor() {
|
|
148
|
+
this.run = (input) => {
|
|
149
|
+
const statsLine = (0, exports.formatMachineStatusLine)(input.machineStatus);
|
|
150
|
+
const projectLines = [
|
|
151
|
+
(0, exports.formatProjectHeaderLine)(),
|
|
152
|
+
...input.projects.map((project) => (0, exports.formatProjectRowLine)(project)),
|
|
153
|
+
];
|
|
154
|
+
const tokenLines = sortTokens(input.tokens).map((token) => (0, exports.formatTokenRowLine)(token));
|
|
155
|
+
const lines = [statsLine, ...projectLines, '', ...tokenLines];
|
|
156
|
+
return lines.map((line) => wrapLine(line)).join('\n') + '\n';
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
exports.ComposeDashboardUseCase = ComposeDashboardUseCase;
|
|
161
|
+
//# sourceMappingURL=ComposeDashboardUseCase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ComposeDashboardUseCase.js","sourceRoot":"","sources":["../../../../src/domain/usecases/dashboard/ComposeDashboardUseCase.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -32,7 +32,11 @@ import { GitHubIssueCommentRepository } from '../../repositories/GitHubIssueComm
|
|
|
32
32
|
import { FetchWebhookRepository } from '../../repositories/FetchWebhookRepository';
|
|
33
33
|
import { RevertOrphanedPreparationUseCase } from '../../../domain/usecases/RevertOrphanedPreparationUseCase';
|
|
34
34
|
import * as path from 'path';
|
|
35
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
DEFAULT_DASHBOARD_PROJECT_CODES,
|
|
37
|
+
DEFAULT_WEB_PORT,
|
|
38
|
+
startWebServer,
|
|
39
|
+
} from '../console/webServer';
|
|
36
40
|
import { IssueTitleStateCache } from '../console/consoleReadApi';
|
|
37
41
|
import {
|
|
38
42
|
buildPjcodeToProjectUrl,
|
|
@@ -76,6 +80,8 @@ type ServeWebOptions = {
|
|
|
76
80
|
consoleDataOutputDir?: string;
|
|
77
81
|
inTmuxDataDir?: string;
|
|
78
82
|
dashboardDir?: string;
|
|
83
|
+
dashboardDataDir?: string;
|
|
84
|
+
dashboardProjectCodes?: string;
|
|
79
85
|
};
|
|
80
86
|
|
|
81
87
|
const DEFAULT_IN_TMUX_DATA_DIR =
|
|
@@ -83,6 +89,19 @@ const DEFAULT_IN_TMUX_DATA_DIR =
|
|
|
83
89
|
|
|
84
90
|
const DEFAULT_DASHBOARD_DIR = '/home/hiromi/0_workspaces/workspace1/jsonpub';
|
|
85
91
|
|
|
92
|
+
const DEFAULT_DASHBOARD_DATA_DIR: string | null = null;
|
|
93
|
+
|
|
94
|
+
const parseDashboardProjectCodes = (raw: string | undefined): string[] => {
|
|
95
|
+
if (raw === undefined) {
|
|
96
|
+
return DEFAULT_DASHBOARD_PROJECT_CODES;
|
|
97
|
+
}
|
|
98
|
+
const codes = raw
|
|
99
|
+
.split(',')
|
|
100
|
+
.map((code) => code.trim())
|
|
101
|
+
.filter((code) => code.length > 0);
|
|
102
|
+
return codes.length > 0 ? codes : DEFAULT_DASHBOARD_PROJECT_CODES;
|
|
103
|
+
};
|
|
104
|
+
|
|
86
105
|
type SelectOauthTokenOptions = {
|
|
87
106
|
tokenListJsonPath?: string;
|
|
88
107
|
cacheDir?: string;
|
|
@@ -695,6 +714,11 @@ const runServeWeb = async (options: ServeWebOptions): Promise<void> => {
|
|
|
695
714
|
const consoleDataOutputDir = options.consoleDataOutputDir ?? null;
|
|
696
715
|
const inTmuxDataDir = options.inTmuxDataDir ?? DEFAULT_IN_TMUX_DATA_DIR;
|
|
697
716
|
const dashboardDir = options.dashboardDir ?? DEFAULT_DASHBOARD_DIR;
|
|
717
|
+
const dashboardDataDir =
|
|
718
|
+
options.dashboardDataDir ?? DEFAULT_DASHBOARD_DATA_DIR;
|
|
719
|
+
const dashboardProjectCodes = parseDashboardProjectCodes(
|
|
720
|
+
options.dashboardProjectCodes,
|
|
721
|
+
);
|
|
698
722
|
|
|
699
723
|
await startWebServer({
|
|
700
724
|
accessToken,
|
|
@@ -702,6 +726,8 @@ const runServeWeb = async (options: ServeWebOptions): Promise<void> => {
|
|
|
702
726
|
consoleDataOutputDir,
|
|
703
727
|
inTmuxDataDir,
|
|
704
728
|
dashboardDir,
|
|
729
|
+
dashboardDataDir,
|
|
730
|
+
dashboardProjectCodes,
|
|
705
731
|
githubToken: token,
|
|
706
732
|
issueRepository,
|
|
707
733
|
resolveProject,
|
|
@@ -731,7 +757,15 @@ const addServeWebOptions = (command: Command): Command =>
|
|
|
731
757
|
)
|
|
732
758
|
.option(
|
|
733
759
|
'--dashboardDir <path>',
|
|
734
|
-
`Directory containing the dashboard HTML fragment tdpm.txt served
|
|
760
|
+
`Directory containing the static dashboard HTML fragment tdpm.txt served at /tdpm.txt when compose mode is not active (default: ${DEFAULT_DASHBOARD_DIR})`,
|
|
761
|
+
)
|
|
762
|
+
.option(
|
|
763
|
+
'--dashboardDataDir <path>',
|
|
764
|
+
'Directory containing the dashboard data files (projects/<pjcode>.json, machine-status.json, token-status.json); when set and every required file is present the server composes the /tdpm.txt fragment from them at request time, otherwise it falls back to serving the static tdpm.txt from --dashboardDir (unset when not configured)',
|
|
765
|
+
)
|
|
766
|
+
.option(
|
|
767
|
+
'--dashboardProjectCodes <codes>',
|
|
768
|
+
`Comma-separated project codes, in display order, for the dashboard project grid (default: ${DEFAULT_DASHBOARD_PROJECT_CODES.join(',')})`,
|
|
735
769
|
);
|
|
736
770
|
|
|
737
771
|
addServeWebOptions(program.command('serveWeb'))
|