github-issue-tower-defence-management 1.134.0 → 1.135.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 +8 -0
- package/README.md +5 -4
- package/bin/adapter/entry-points/cli/index.js +11 -1
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/cli/projectConfig.js +21 -0
- package/bin/adapter/entry-points/cli/projectConfig.js.map +1 -1
- package/bin/adapter/entry-points/console/dashboardComposeService.js +18 -0
- package/bin/adapter/entry-points/console/dashboardComposeService.js.map +1 -1
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +81 -54
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/machineStatusWriter.js +6 -0
- package/bin/adapter/entry-points/handlers/machineStatusWriter.js.map +1 -1
- package/bin/adapter/repositories/ProcHostMetricsRepository.js +4 -0
- package/bin/adapter/repositories/ProcHostMetricsRepository.js.map +1 -1
- package/bin/domain/usecases/dashboard/ComposeDashboardUseCase.js +26 -4
- package/bin/domain/usecases/dashboard/ComposeDashboardUseCase.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/cli/index.test.ts +42 -1
- package/src/adapter/entry-points/cli/index.ts +20 -1
- package/src/adapter/entry-points/cli/projectConfig.test.ts +75 -0
- package/src/adapter/entry-points/cli/projectConfig.ts +31 -0
- package/src/adapter/entry-points/console/dashboardComposeService.test.ts +32 -0
- package/src/adapter/entry-points/console/dashboardComposeService.ts +20 -0
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.test.ts +66 -0
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +7 -1
- package/src/adapter/entry-points/handlers/machineStatusWriter.test.ts +61 -0
- package/src/adapter/entry-points/handlers/machineStatusWriter.ts +17 -0
- package/src/adapter/repositories/ProcHostMetricsRepository.test.ts +25 -0
- package/src/adapter/repositories/ProcHostMetricsRepository.ts +5 -0
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +26 -3
- package/src/domain/usecases/dashboard/ComposeDashboardUseCase.test.ts +55 -0
- package/src/domain/usecases/dashboard/ComposeDashboardUseCase.ts +34 -5
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
- package/types/adapter/entry-points/cli/projectConfig.d.ts +5 -0
- package/types/adapter/entry-points/cli/projectConfig.d.ts.map +1 -1
- package/types/adapter/entry-points/console/dashboardComposeService.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts +1 -1
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/machineStatusWriter.d.ts +7 -0
- package/types/adapter/entry-points/handlers/machineStatusWriter.d.ts.map +1 -1
- package/types/adapter/repositories/ProcHostMetricsRepository.d.ts +1 -0
- package/types/adapter/repositories/ProcHostMetricsRepository.d.ts.map +1 -1
- package/types/domain/usecases/dashboard/ComposeDashboardUseCase.d.ts +6 -1
- package/types/domain/usecases/dashboard/ComposeDashboardUseCase.d.ts.map +1 -1
- package/types/index.d.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComposeDashboardUseCase.js","sourceRoot":"","sources":["../../../../src/domain/usecases/dashboard/ComposeDashboardUseCase.ts"],"names":[],"mappings":";;;AAGa,QAAA,wBAAwB,GAAG,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"ComposeDashboardUseCase.js","sourceRoot":"","sources":["../../../../src/domain/usecases/dashboard/ComposeDashboardUseCase.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -70,9 +70,10 @@ jest.mock('../../repositories/FetchWebhookRepository', () => ({
|
|
|
70
70
|
sendGetRequest: jest.fn(),
|
|
71
71
|
})),
|
|
72
72
|
}));
|
|
73
|
+
const mockScheduleHandle = jest.fn().mockResolvedValue(null);
|
|
73
74
|
jest.mock('../handlers/HandleScheduledEventUseCaseHandler', () => ({
|
|
74
75
|
HandleScheduledEventUseCaseHandler: jest.fn().mockImplementation(() => ({
|
|
75
|
-
handle:
|
|
76
|
+
handle: mockScheduleHandle,
|
|
76
77
|
})),
|
|
77
78
|
}));
|
|
78
79
|
jest.mock('../console/ensureConsoleRunning', () => ({
|
|
@@ -162,6 +163,46 @@ describe('CLI', () => {
|
|
|
162
163
|
expect(program).toBeDefined();
|
|
163
164
|
});
|
|
164
165
|
|
|
166
|
+
describe('schedule --inTmuxProjectOrder', () => {
|
|
167
|
+
it('should pass the parsed, trimmed project order to the handler', async () => {
|
|
168
|
+
await program.parseAsync([
|
|
169
|
+
'node',
|
|
170
|
+
'test',
|
|
171
|
+
'schedule',
|
|
172
|
+
'-t',
|
|
173
|
+
'schedule',
|
|
174
|
+
'-c',
|
|
175
|
+
configFilePath,
|
|
176
|
+
'--inTmuxProjectOrder',
|
|
177
|
+
'alpha, beta ,gamma',
|
|
178
|
+
]);
|
|
179
|
+
|
|
180
|
+
expect(mockScheduleHandle).toHaveBeenCalledWith(
|
|
181
|
+
configFilePath,
|
|
182
|
+
undefined,
|
|
183
|
+
['alpha', 'beta', 'gamma'],
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should pass null when --inTmuxProjectOrder is omitted', async () => {
|
|
188
|
+
await program.parseAsync([
|
|
189
|
+
'node',
|
|
190
|
+
'test',
|
|
191
|
+
'schedule',
|
|
192
|
+
'-t',
|
|
193
|
+
'schedule',
|
|
194
|
+
'-c',
|
|
195
|
+
configFilePath,
|
|
196
|
+
]);
|
|
197
|
+
|
|
198
|
+
expect(mockScheduleHandle).toHaveBeenCalledWith(
|
|
199
|
+
configFilePath,
|
|
200
|
+
undefined,
|
|
201
|
+
null,
|
|
202
|
+
);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
165
206
|
describe('loadConfigFile', () => {
|
|
166
207
|
it('should load config from YAML file', () => {
|
|
167
208
|
const config = {
|
|
@@ -132,11 +132,22 @@ const buildGithubRepositoryParams = (
|
|
|
132
132
|
token,
|
|
133
133
|
];
|
|
134
134
|
|
|
135
|
+
const parseInTmuxProjectOrder = (raw: string | undefined): string[] | null => {
|
|
136
|
+
if (raw === undefined) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
return raw
|
|
140
|
+
.split(',')
|
|
141
|
+
.map((name) => name.trim())
|
|
142
|
+
.filter((name) => name.length > 0);
|
|
143
|
+
};
|
|
144
|
+
|
|
135
145
|
interface ScheduleOptions {
|
|
136
146
|
trigger: 'issue' | 'schedule';
|
|
137
147
|
config: string;
|
|
138
148
|
issue?: string;
|
|
139
149
|
verbose: boolean;
|
|
150
|
+
inTmuxProjectOrder?: string;
|
|
140
151
|
}
|
|
141
152
|
|
|
142
153
|
export const program = new Command();
|
|
@@ -156,6 +167,10 @@ program
|
|
|
156
167
|
.requiredOption('-c, --config <path>', 'Path to config YAML file')
|
|
157
168
|
.option('-v, --verbose', 'Verbose output')
|
|
158
169
|
.option('-i, --issue <url>', 'GitHub Issue URL')
|
|
170
|
+
.option(
|
|
171
|
+
'--inTmuxProjectOrder <names>',
|
|
172
|
+
'Comma-separated project names, in display order, for the in-tmux-by-human session list. When omitted, falls back to the inTmuxProjectOrder value in the config file.',
|
|
173
|
+
)
|
|
159
174
|
.action(async (options: ScheduleOptions) => {
|
|
160
175
|
if (options.trigger === 'issue' && !options.issue) {
|
|
161
176
|
console.error('Issue URL is required when trigger type is "issue"');
|
|
@@ -165,7 +180,11 @@ program
|
|
|
165
180
|
const { HandleScheduledEventUseCaseHandler } =
|
|
166
181
|
await import('../handlers/HandleScheduledEventUseCaseHandler');
|
|
167
182
|
const handler = new HandleScheduledEventUseCaseHandler();
|
|
168
|
-
await handler.handle(
|
|
183
|
+
await handler.handle(
|
|
184
|
+
options.config,
|
|
185
|
+
options.verbose,
|
|
186
|
+
parseInTmuxProjectOrder(options.inTmuxProjectOrder),
|
|
187
|
+
);
|
|
169
188
|
}
|
|
170
189
|
});
|
|
171
190
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as os from 'os';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { loadConfigFile, mergeConfigs } from './projectConfig';
|
|
5
|
+
|
|
6
|
+
describe('loadConfigFile disks', () => {
|
|
7
|
+
let dir: string;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'project-config-disks-'));
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const writeConfig = (content: string): string => {
|
|
18
|
+
const filePath = path.join(dir, 'config.yml');
|
|
19
|
+
fs.writeFileSync(filePath, content);
|
|
20
|
+
return filePath;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
it('parses a disks list of title and mountpoint pairs', () => {
|
|
24
|
+
const filePath = writeConfig(
|
|
25
|
+
[
|
|
26
|
+
"projectName: 'demo'",
|
|
27
|
+
'disks:',
|
|
28
|
+
" - title: 'D'",
|
|
29
|
+
" mountpoint: '/'",
|
|
30
|
+
" - title: 'S'",
|
|
31
|
+
" mountpoint: '/mountpoint-secondary'",
|
|
32
|
+
].join('\n'),
|
|
33
|
+
);
|
|
34
|
+
expect(loadConfigFile(filePath).disks).toEqual([
|
|
35
|
+
{ title: 'D', mountpoint: '/' },
|
|
36
|
+
{ title: 'S', mountpoint: '/mountpoint-secondary' },
|
|
37
|
+
]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('yields undefined disks when the key is absent', () => {
|
|
41
|
+
const filePath = writeConfig("projectName: 'demo'\n");
|
|
42
|
+
expect(loadConfigFile(filePath).disks).toBeUndefined();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('yields undefined disks when an entry is missing required fields', () => {
|
|
46
|
+
const filePath = writeConfig(
|
|
47
|
+
['disks:', " - title: 'D'"].join('\n') + '\n',
|
|
48
|
+
);
|
|
49
|
+
expect(loadConfigFile(filePath).disks).toBeUndefined();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('mergeConfigs disks', () => {
|
|
54
|
+
it('prefers cli override disks over the config file disks', () => {
|
|
55
|
+
const merged = mergeConfigs(
|
|
56
|
+
{ disks: [{ title: 'D', mountpoint: '/' }] },
|
|
57
|
+
{ disks: [{ title: 'C', mountpoint: '/cli' }] },
|
|
58
|
+
{},
|
|
59
|
+
);
|
|
60
|
+
expect(merged.disks).toEqual([{ title: 'C', mountpoint: '/cli' }]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('falls back to the config file disks when no cli override is present', () => {
|
|
64
|
+
const merged = mergeConfigs(
|
|
65
|
+
{ disks: [{ title: 'D', mountpoint: '/' }] },
|
|
66
|
+
{},
|
|
67
|
+
{},
|
|
68
|
+
);
|
|
69
|
+
expect(merged.disks).toEqual([{ title: 'D', mountpoint: '/' }]);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('yields undefined disks when neither source provides them', () => {
|
|
73
|
+
expect(mergeConfigs({}, {}, {}).disks).toBeUndefined();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -25,6 +25,12 @@ export type ConfigFile = {
|
|
|
25
25
|
changeTargetPathAliases?: Record<string, string>;
|
|
26
26
|
consoleAccessToken?: string;
|
|
27
27
|
consoleProjects?: Record<string, string>;
|
|
28
|
+
disks?: DiskConfig[];
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type DiskConfig = {
|
|
32
|
+
title: string;
|
|
33
|
+
mountpoint: string;
|
|
28
34
|
};
|
|
29
35
|
|
|
30
36
|
const getStringValue = (
|
|
@@ -82,6 +88,29 @@ const getStringArrayValue = (
|
|
|
82
88
|
export const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
83
89
|
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
84
90
|
|
|
91
|
+
const getDisksValue = (
|
|
92
|
+
obj: Record<string, unknown>,
|
|
93
|
+
key: string,
|
|
94
|
+
): DiskConfig[] | undefined => {
|
|
95
|
+
const value = obj[key];
|
|
96
|
+
if (!Array.isArray(value)) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
const disks: DiskConfig[] = [];
|
|
100
|
+
for (const item of value) {
|
|
101
|
+
if (!isRecord(item)) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
const title = item['title'];
|
|
105
|
+
const mountpoint = item['mountpoint'];
|
|
106
|
+
if (typeof title !== 'string' || typeof mountpoint !== 'string') {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
disks.push({ title, mountpoint });
|
|
110
|
+
}
|
|
111
|
+
return disks;
|
|
112
|
+
};
|
|
113
|
+
|
|
85
114
|
const knownProjectReadmeConfigKeys = [
|
|
86
115
|
'defaultAgentName',
|
|
87
116
|
'defaultLlmModelName',
|
|
@@ -155,6 +184,7 @@ export const loadConfigFile = (configFilePath: string): ConfigFile => {
|
|
|
155
184
|
),
|
|
156
185
|
consoleAccessToken: getStringValue(parsed, 'consoleAccessToken'),
|
|
157
186
|
consoleProjects: getStringRecordValue(parsed, 'consoleProjects'),
|
|
187
|
+
disks: getDisksValue(parsed, 'disks'),
|
|
158
188
|
};
|
|
159
189
|
} catch (error) {
|
|
160
190
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -320,6 +350,7 @@ export const mergeConfigs = (
|
|
|
320
350
|
consoleAccessToken:
|
|
321
351
|
cliOverrides.consoleAccessToken ?? configFile.consoleAccessToken,
|
|
322
352
|
consoleProjects: cliOverrides.consoleProjects ?? configFile.consoleProjects,
|
|
353
|
+
disks: cliOverrides.disks ?? configFile.disks,
|
|
323
354
|
});
|
|
324
355
|
|
|
325
356
|
type GraphqlProjectV2ReadmeResponse = {
|
|
@@ -113,6 +113,7 @@ describe('buildComposeDashboardInput', () => {
|
|
|
113
113
|
memPct: 55,
|
|
114
114
|
cpuPct: 62,
|
|
115
115
|
diskPct: 89,
|
|
116
|
+
disks: null,
|
|
116
117
|
load: [16, 23, 40],
|
|
117
118
|
cycleMinutes: 14,
|
|
118
119
|
});
|
|
@@ -121,6 +122,37 @@ describe('buildComposeDashboardInput', () => {
|
|
|
121
122
|
}
|
|
122
123
|
});
|
|
123
124
|
|
|
125
|
+
it('reads a disks array from machine-status.json', () => {
|
|
126
|
+
const dataDir = makeDataDir();
|
|
127
|
+
try {
|
|
128
|
+
fs.writeFileSync(
|
|
129
|
+
path.join(dataDir, 'machine-status.json'),
|
|
130
|
+
JSON.stringify({
|
|
131
|
+
memPct: 55,
|
|
132
|
+
cpuPct: 62,
|
|
133
|
+
diskPct: 89,
|
|
134
|
+
disks: [
|
|
135
|
+
{ title: 'D', pct: 89 },
|
|
136
|
+
{ title: 'S', pct: 41 },
|
|
137
|
+
],
|
|
138
|
+
load: [16, 23, 40],
|
|
139
|
+
cycleMinutes: 14,
|
|
140
|
+
capturedAt: 'x',
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
143
|
+
const input = buildComposeDashboardInput({
|
|
144
|
+
dashboardDataDir: dataDir,
|
|
145
|
+
projectNames: [],
|
|
146
|
+
});
|
|
147
|
+
expect(input.machineStatus?.disks).toEqual([
|
|
148
|
+
{ title: 'D', pct: 89 },
|
|
149
|
+
{ title: 'S', pct: 41 },
|
|
150
|
+
]);
|
|
151
|
+
} finally {
|
|
152
|
+
fs.rmSync(dataDir, { recursive: true, force: true });
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
124
156
|
it('preserves a null cycleMinutes from machine-status.json', () => {
|
|
125
157
|
const dataDir = makeDataDir();
|
|
126
158
|
try {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import {
|
|
4
|
+
ComposeDashboardDisk,
|
|
4
5
|
ComposeDashboardInput,
|
|
5
6
|
ComposeDashboardMachineStatus,
|
|
6
7
|
ComposeDashboardProject,
|
|
@@ -88,6 +89,24 @@ const parseLoad = (value: unknown): [number, number, number] | null => {
|
|
|
88
89
|
return [oneMinute, fiveMinute, fifteenMinute];
|
|
89
90
|
};
|
|
90
91
|
|
|
92
|
+
const parseDisks = (value: unknown): ComposeDashboardDisk[] | null => {
|
|
93
|
+
if (!Array.isArray(value)) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const disks: ComposeDashboardDisk[] = [];
|
|
97
|
+
for (const entry of value) {
|
|
98
|
+
if (!isRecord(entry) || typeof entry.title !== 'string') {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const pct = asFiniteNumber(entry.pct);
|
|
102
|
+
if (pct === null) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
disks.push({ title: entry.title, pct });
|
|
106
|
+
}
|
|
107
|
+
return disks;
|
|
108
|
+
};
|
|
109
|
+
|
|
91
110
|
const readMachineStatus = (
|
|
92
111
|
dashboardDataDir: string,
|
|
93
112
|
): ComposeDashboardMachineStatus | null => {
|
|
@@ -104,6 +123,7 @@ const readMachineStatus = (
|
|
|
104
123
|
memPct: asFiniteNumber(value.memPct),
|
|
105
124
|
cpuPct: asFiniteNumber(value.cpuPct),
|
|
106
125
|
diskPct: asFiniteNumber(value.diskPct),
|
|
126
|
+
disks: parseDisks(value.disks),
|
|
107
127
|
load: parseLoad(value.load),
|
|
108
128
|
cycleMinutes,
|
|
109
129
|
};
|
|
@@ -126,9 +126,13 @@ jest.mock('./situationFileWriter', () => ({
|
|
|
126
126
|
jest.mock('./rotationOrderFileWriter', () => ({
|
|
127
127
|
writeRotationOrderFile: jest.fn(),
|
|
128
128
|
}));
|
|
129
|
+
jest.mock('./inTmuxByHumanDataWriter', () => ({
|
|
130
|
+
writeInTmuxByHumanData: jest.fn(),
|
|
131
|
+
}));
|
|
129
132
|
|
|
130
133
|
import { HandleScheduledEventUseCaseHandler } from './HandleScheduledEventUseCaseHandler';
|
|
131
134
|
import { writeSituationFile } from './situationFileWriter';
|
|
135
|
+
import { writeInTmuxByHumanData } from './inTmuxByHumanDataWriter';
|
|
132
136
|
import { GraphqlProjectRepository } from '../../repositories/GraphqlProjectRepository';
|
|
133
137
|
import { ApiV3IssueRepository } from '../../repositories/issue/ApiV3IssueRepository';
|
|
134
138
|
import { RestIssueRepository } from '../../repositories/issue/RestIssueRepository';
|
|
@@ -148,6 +152,7 @@ const MockedApiV3CheerioRestIssueRepository = jest.mocked(
|
|
|
148
152
|
const MockedProxyClaudeTokenUsageRepository = jest.mocked(
|
|
149
153
|
ProxyClaudeTokenUsageRepository,
|
|
150
154
|
);
|
|
155
|
+
const MockedWriteInTmuxByHumanData = jest.mocked(writeInTmuxByHumanData);
|
|
151
156
|
|
|
152
157
|
const validConfig = {
|
|
153
158
|
projectName: 'test-project',
|
|
@@ -643,4 +648,65 @@ defaultAgentName: readme-agent
|
|
|
643
648
|
);
|
|
644
649
|
});
|
|
645
650
|
});
|
|
651
|
+
|
|
652
|
+
describe('inTmuxProjectOrder override', () => {
|
|
653
|
+
const getInTmuxProjectOrderArg = (): string[] | null | undefined => {
|
|
654
|
+
const call = MockedWriteInTmuxByHumanData.mock.calls[0];
|
|
655
|
+
if (call === undefined) {
|
|
656
|
+
throw new Error('writeInTmuxByHumanData was not called');
|
|
657
|
+
}
|
|
658
|
+
return call[0].inTmuxProjectOrder;
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
it('should use the CLI override when provided, ignoring the config value', async () => {
|
|
662
|
+
jest.mocked(fs.readFileSync).mockReturnValue(
|
|
663
|
+
YAML.stringify({
|
|
664
|
+
...validConfig,
|
|
665
|
+
inTmuxProjectOrder: ['alpha', 'beta'],
|
|
666
|
+
}),
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
const handler = new HandleScheduledEventUseCaseHandler();
|
|
670
|
+
await handler.handle('config.yml', false, ['gamma', 'delta']);
|
|
671
|
+
|
|
672
|
+
expect(getInTmuxProjectOrderArg()).toEqual(['gamma', 'delta']);
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
it('should fall back to the config value when the CLI override is null', async () => {
|
|
676
|
+
jest.mocked(fs.readFileSync).mockReturnValue(
|
|
677
|
+
YAML.stringify({
|
|
678
|
+
...validConfig,
|
|
679
|
+
inTmuxProjectOrder: ['alpha', 'beta'],
|
|
680
|
+
}),
|
|
681
|
+
);
|
|
682
|
+
|
|
683
|
+
const handler = new HandleScheduledEventUseCaseHandler();
|
|
684
|
+
await handler.handle('config.yml', false, null);
|
|
685
|
+
|
|
686
|
+
expect(getInTmuxProjectOrderArg()).toEqual(['alpha', 'beta']);
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
it('should fall back to the config value when the override argument is omitted', async () => {
|
|
690
|
+
jest.mocked(fs.readFileSync).mockReturnValue(
|
|
691
|
+
YAML.stringify({
|
|
692
|
+
...validConfig,
|
|
693
|
+
inTmuxProjectOrder: ['alpha', 'beta'],
|
|
694
|
+
}),
|
|
695
|
+
);
|
|
696
|
+
|
|
697
|
+
const handler = new HandleScheduledEventUseCaseHandler();
|
|
698
|
+
await handler.handle('config.yml', false);
|
|
699
|
+
|
|
700
|
+
expect(getInTmuxProjectOrderArg()).toEqual(['alpha', 'beta']);
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it('should pass null when neither the override nor the config provides a value', async () => {
|
|
704
|
+
jest.mocked(fs.readFileSync).mockReturnValue(YAML.stringify(validConfig));
|
|
705
|
+
|
|
706
|
+
const handler = new HandleScheduledEventUseCaseHandler();
|
|
707
|
+
await handler.handle('config.yml', false, null);
|
|
708
|
+
|
|
709
|
+
expect(getInTmuxProjectOrderArg()).toBeNull();
|
|
710
|
+
});
|
|
711
|
+
});
|
|
646
712
|
});
|
|
@@ -86,6 +86,7 @@ export class HandleScheduledEventUseCaseHandler {
|
|
|
86
86
|
handle = async (
|
|
87
87
|
configFilePath: string,
|
|
88
88
|
_verbose: boolean,
|
|
89
|
+
inTmuxProjectOrderOverride: string[] | null = null,
|
|
89
90
|
): Promise<{
|
|
90
91
|
project: Project;
|
|
91
92
|
issues: Issue[];
|
|
@@ -103,6 +104,7 @@ export class HandleScheduledEventUseCaseHandler {
|
|
|
103
104
|
claudeCodeOauthTokenListJsonPath?: string;
|
|
104
105
|
consoleDataOutputDir?: string;
|
|
105
106
|
dashboardDataDir?: string;
|
|
107
|
+
disks?: { title: string; mountpoint: string }[];
|
|
106
108
|
workflowBlockerStoryName?: string;
|
|
107
109
|
inTmuxDataOutputDir?: string;
|
|
108
110
|
newIssueRepo?: string;
|
|
@@ -492,6 +494,7 @@ export class HandleScheduledEventUseCaseHandler {
|
|
|
492
494
|
await writeMachineStatus({
|
|
493
495
|
dashboardDataDir,
|
|
494
496
|
allIssuesCacheDir: `${cachePath}/allIssues-${result.project.id}`,
|
|
497
|
+
disks: mergedInput.disks ?? null,
|
|
495
498
|
});
|
|
496
499
|
} catch (error) {
|
|
497
500
|
console.error(
|
|
@@ -524,7 +527,10 @@ export class HandleScheduledEventUseCaseHandler {
|
|
|
524
527
|
inTmuxDataOutputDir: mergedInput.inTmuxDataOutputDir ?? null,
|
|
525
528
|
inTmuxConsoleBaseUrl: mergedInput.inTmuxConsoleBaseUrl ?? null,
|
|
526
529
|
inTmuxConsoleToken: mergedInput.inTmuxConsoleToken ?? null,
|
|
527
|
-
inTmuxProjectOrder:
|
|
530
|
+
inTmuxProjectOrder:
|
|
531
|
+
inTmuxProjectOrderOverride ??
|
|
532
|
+
mergedInput.inTmuxProjectOrder ??
|
|
533
|
+
null,
|
|
528
534
|
pjcode: input.projectName,
|
|
529
535
|
assigneeLogin: input.manager,
|
|
530
536
|
org: input.org,
|
|
@@ -91,6 +91,67 @@ describe('writeMachineStatus', () => {
|
|
|
91
91
|
expect(written).toEqual(expected);
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
+
it('writes a per-mountpoint disks array when a disks list is configured', async () => {
|
|
95
|
+
fs.writeFileSync(
|
|
96
|
+
path.join(procDirectory, 'meminfo'),
|
|
97
|
+
'MemTotal: 1000 kB\nMemAvailable: 450 kB\n',
|
|
98
|
+
);
|
|
99
|
+
fs.writeFileSync(
|
|
100
|
+
path.join(procDirectory, 'loadavg'),
|
|
101
|
+
'1.50 0.80 0.40 1/100 200\n',
|
|
102
|
+
);
|
|
103
|
+
const statPath = path.join(procDirectory, 'stat');
|
|
104
|
+
fs.writeFileSync(statPath, 'cpu 600 0 0 400 0 0 0 0 0 0\n');
|
|
105
|
+
const sleep = async (): Promise<void> => {
|
|
106
|
+
fs.writeFileSync(statPath, 'cpu 660 0 0 440 0 0 0 0 0 0\n');
|
|
107
|
+
};
|
|
108
|
+
const blocksByMountpoint: Record<
|
|
109
|
+
string,
|
|
110
|
+
{ blocks: number; bfree: number; bavail: number }
|
|
111
|
+
> = {
|
|
112
|
+
'/': { blocks: 1100, bfree: 200, bavail: 100 },
|
|
113
|
+
'/mountpoint-secondary': { blocks: 1000, bfree: 700, bavail: 700 },
|
|
114
|
+
};
|
|
115
|
+
const repository = new ProcHostMetricsRepository(
|
|
116
|
+
procDirectory,
|
|
117
|
+
sleep,
|
|
118
|
+
(mountpoint) => blocksByMountpoint[mountpoint],
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
await writeMachineStatus({
|
|
122
|
+
dashboardDataDir: dir,
|
|
123
|
+
allIssuesCacheDir: null,
|
|
124
|
+
disks: [
|
|
125
|
+
{ title: 'D', mountpoint: '/' },
|
|
126
|
+
{ title: 'S', mountpoint: '/mountpoint-secondary' },
|
|
127
|
+
],
|
|
128
|
+
hostMetricsRepository: repository,
|
|
129
|
+
now: new Date('2026-06-26T12:00:00.000Z'),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const written = readJson(path.join(dir, 'machine-status.json'));
|
|
133
|
+
expect(written).toMatchObject({
|
|
134
|
+
diskPct: 90,
|
|
135
|
+
disks: [
|
|
136
|
+
{ title: 'D', pct: 90 },
|
|
137
|
+
{ title: 'S', pct: 30 },
|
|
138
|
+
],
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('omits the disks array when no disks list is configured', async () => {
|
|
143
|
+
const { repository } = buildHostMetricsRepository(procDirectory);
|
|
144
|
+
await writeMachineStatus({
|
|
145
|
+
dashboardDataDir: dir,
|
|
146
|
+
allIssuesCacheDir: null,
|
|
147
|
+
hostMetricsRepository: repository,
|
|
148
|
+
now: new Date('2026-06-26T12:00:00.000Z'),
|
|
149
|
+
});
|
|
150
|
+
const written = readJson(path.join(dir, 'machine-status.json'));
|
|
151
|
+
expect(written).toMatchObject({ diskPct: 90 });
|
|
152
|
+
expect(written).not.toHaveProperty('disks');
|
|
153
|
+
});
|
|
154
|
+
|
|
94
155
|
it('writes cycleMinutes null when there is no previous machine-status', async () => {
|
|
95
156
|
const { repository } = buildHostMetricsRepository(procDirectory);
|
|
96
157
|
const cacheDir = path.join(dir, 'allIssues-PVT');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { DiskConfig } from '../cli/projectConfig';
|
|
3
4
|
import {
|
|
4
5
|
ProcHostMetricsRepository,
|
|
5
6
|
cycleMinutesFromFetchTimestamps,
|
|
@@ -8,14 +9,21 @@ import {
|
|
|
8
9
|
export type MachineStatusWriterParams = {
|
|
9
10
|
dashboardDataDir: string | null | undefined;
|
|
10
11
|
allIssuesCacheDir: string | null | undefined;
|
|
12
|
+
disks?: DiskConfig[] | null;
|
|
11
13
|
hostMetricsRepository?: ProcHostMetricsRepository;
|
|
12
14
|
now?: Date;
|
|
13
15
|
};
|
|
14
16
|
|
|
17
|
+
export type MachineStatusDisk = {
|
|
18
|
+
title: string;
|
|
19
|
+
pct: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
15
22
|
export type MachineStatusFile = {
|
|
16
23
|
memPct: number;
|
|
17
24
|
cpuPct: number;
|
|
18
25
|
diskPct: number;
|
|
26
|
+
disks?: MachineStatusDisk[];
|
|
19
27
|
load: [number, number, number];
|
|
20
28
|
cycleMinutes: number | null;
|
|
21
29
|
lastFetchedAt: string | null;
|
|
@@ -68,6 +76,14 @@ export const writeMachineStatus = async (
|
|
|
68
76
|
const diskPct = hostMetricsRepository.readDiskUsedPercent();
|
|
69
77
|
const load = hostMetricsRepository.readLoadAverages();
|
|
70
78
|
|
|
79
|
+
const configuredDisks = params.disks ?? [];
|
|
80
|
+
const disks: MachineStatusDisk[] = configuredDisks.map((disk) => ({
|
|
81
|
+
title: disk.title,
|
|
82
|
+
pct: hostMetricsRepository.readDiskUsedPercentForMountpoint(
|
|
83
|
+
disk.mountpoint,
|
|
84
|
+
),
|
|
85
|
+
}));
|
|
86
|
+
|
|
71
87
|
const machineStatusPath = path.join(dashboardDataDir, 'machine-status.json');
|
|
72
88
|
const previousLastFetchedAt =
|
|
73
89
|
readLastFetchedAtFromJsonFile(machineStatusPath);
|
|
@@ -83,6 +99,7 @@ export const writeMachineStatus = async (
|
|
|
83
99
|
memPct,
|
|
84
100
|
cpuPct,
|
|
85
101
|
diskPct,
|
|
102
|
+
...(disks.length > 0 ? { disks } : {}),
|
|
86
103
|
load: [load.oneMinute, load.fiveMinute, load.fifteenMinute],
|
|
87
104
|
cycleMinutes,
|
|
88
105
|
lastFetchedAt: currentLastFetchedAt,
|
|
@@ -167,6 +167,31 @@ describe('ProcHostMetricsRepository', () => {
|
|
|
167
167
|
expect(repository.readDiskUsedPercent()).toBe(90);
|
|
168
168
|
});
|
|
169
169
|
|
|
170
|
+
it('reads the df-style disk used percent for a given mountpoint', () => {
|
|
171
|
+
const blocksByMountpoint: Record<string, DiskBlocks> = {
|
|
172
|
+
'/': { blocks: 1100, bfree: 200, bavail: 100 },
|
|
173
|
+
'/mountpoint-a': { blocks: 1000, bfree: 700, bavail: 700 },
|
|
174
|
+
};
|
|
175
|
+
const requestedMountpoints: string[] = [];
|
|
176
|
+
const repository = new ProcHostMetricsRepository(
|
|
177
|
+
procDirectory,
|
|
178
|
+
async () => {},
|
|
179
|
+
(mountpoint) => {
|
|
180
|
+
requestedMountpoints.push(mountpoint);
|
|
181
|
+
const blocks = blocksByMountpoint[mountpoint];
|
|
182
|
+
if (!blocks) {
|
|
183
|
+
throw new Error(`unexpected mountpoint ${mountpoint}`);
|
|
184
|
+
}
|
|
185
|
+
return blocks;
|
|
186
|
+
},
|
|
187
|
+
);
|
|
188
|
+
expect(repository.readDiskUsedPercentForMountpoint('/mountpoint-a')).toBe(
|
|
189
|
+
30,
|
|
190
|
+
);
|
|
191
|
+
expect(repository.readDiskUsedPercentForMountpoint('/')).toBe(90);
|
|
192
|
+
expect(requestedMountpoints).toEqual(['/mountpoint-a', '/']);
|
|
193
|
+
});
|
|
194
|
+
|
|
170
195
|
it('samples /proc/stat twice and computes the busy percent', async () => {
|
|
171
196
|
const statPath = path.join(procDirectory, 'stat');
|
|
172
197
|
let sampleIndex = 0;
|
|
@@ -173,4 +173,9 @@ export class ProcHostMetricsRepository {
|
|
|
173
173
|
const { blocks, bfree, bavail } = this.readDiskBlocks(this.rootPath);
|
|
174
174
|
return parseDiskUsedPercent(blocks, bfree, bavail);
|
|
175
175
|
};
|
|
176
|
+
|
|
177
|
+
readDiskUsedPercentForMountpoint = (mountpoint: string): number => {
|
|
178
|
+
const { blocks, bfree, bavail } = this.readDiskBlocks(mountpoint);
|
|
179
|
+
return parseDiskUsedPercent(blocks, bfree, bavail);
|
|
180
|
+
};
|
|
176
181
|
}
|