github-issue-tower-defence-management 1.104.3 → 1.105.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 +20 -0
- package/README.md +8 -7
- package/bin/adapter/entry-points/cli/index.js +21 -13
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/console/{consoleServer.js → webServer.js} +11 -11
- package/bin/adapter/entry-points/console/webServer.js.map +1 -0
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +41 -2
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/dashboardRowWriter.js +34 -0
- package/bin/adapter/entry-points/handlers/dashboardRowWriter.js.map +1 -0
- package/bin/adapter/entry-points/handlers/machineStatusWriter.js +60 -0
- package/bin/adapter/entry-points/handlers/machineStatusWriter.js.map +1 -0
- package/bin/adapter/entry-points/handlers/tokenStatusWriter.js +98 -0
- package/bin/adapter/entry-points/handlers/tokenStatusWriter.js.map +1 -0
- package/bin/adapter/proxy/RateLimitCache.js +3 -0
- package/bin/adapter/proxy/RateLimitCache.js.map +1 -1
- package/bin/adapter/repositories/ProcHostMetricsRepository.js +136 -0
- package/bin/adapter/repositories/ProcHostMetricsRepository.js.map +1 -0
- package/bin/adapter/repositories/ProcTakeOwnershipSpawnRepository.js +131 -0
- package/bin/adapter/repositories/ProcTakeOwnershipSpawnRepository.js.map +1 -0
- package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js +93 -8
- package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js.map +1 -1
- package/bin/domain/usecases/adapter-interfaces/TakeOwnershipSpawnRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/TakeOwnershipSpawnRepository.js.map +1 -0
- package/bin/domain/usecases/dashboard/GenerateDashboardRowUseCase.js +38 -0
- package/bin/domain/usecases/dashboard/GenerateDashboardRowUseCase.js.map +1 -0
- package/bin/domain/usecases/dashboard/GenerateTokenStatusUseCase.js +115 -0
- package/bin/domain/usecases/dashboard/GenerateTokenStatusUseCase.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/cli/index.test.ts +74 -21
- package/src/adapter/entry-points/cli/index.ts +146 -136
- package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +2 -2
- package/src/adapter/entry-points/console/{consoleServer.test.ts → webServer.test.ts} +32 -32
- package/src/adapter/entry-points/console/{consoleServer.ts → webServer.ts} +15 -17
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +52 -0
- package/src/adapter/entry-points/handlers/dashboardRowWriter.test.ts +111 -0
- package/src/adapter/entry-points/handlers/dashboardRowWriter.ts +51 -0
- package/src/adapter/entry-points/handlers/machineStatusWriter.test.ts +100 -0
- package/src/adapter/entry-points/handlers/machineStatusWriter.ts +79 -0
- package/src/adapter/entry-points/handlers/tokenStatusWriter.test.ts +176 -0
- package/src/adapter/entry-points/handlers/tokenStatusWriter.ts +139 -0
- package/src/adapter/proxy/RateLimitCache.test.ts +32 -0
- package/src/adapter/proxy/RateLimitCache.ts +6 -0
- package/src/adapter/repositories/ProcHostMetricsRepository.test.ts +135 -0
- package/src/adapter/repositories/ProcHostMetricsRepository.ts +136 -0
- package/src/adapter/repositories/ProcTakeOwnershipSpawnRepository.test.ts +130 -0
- package/src/adapter/repositories/ProcTakeOwnershipSpawnRepository.ts +118 -0
- package/src/adapter/repositories/issue/GraphqlProjectItemRepository.test.ts +267 -13
- package/src/adapter/repositories/issue/GraphqlProjectItemRepository.ts +140 -47
- package/src/domain/usecases/adapter-interfaces/TakeOwnershipSpawnRepository.ts +8 -0
- package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.test.ts +159 -0
- package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.ts +72 -0
- package/src/domain/usecases/dashboard/GenerateTokenStatusUseCase.test.ts +209 -0
- package/src/domain/usecases/dashboard/GenerateTokenStatusUseCase.ts +195 -0
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
- package/types/adapter/entry-points/console/{consoleServer.d.ts → webServer.d.ts} +7 -7
- package/types/adapter/entry-points/console/webServer.d.ts.map +1 -0
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/dashboardRowWriter.d.ts +15 -0
- package/types/adapter/entry-points/handlers/dashboardRowWriter.d.ts.map +1 -0
- package/types/adapter/entry-points/handlers/machineStatusWriter.d.ts +16 -0
- package/types/adapter/entry-points/handlers/machineStatusWriter.d.ts.map +1 -0
- package/types/adapter/entry-points/handlers/tokenStatusWriter.d.ts +21 -0
- package/types/adapter/entry-points/handlers/tokenStatusWriter.d.ts.map +1 -0
- package/types/adapter/proxy/RateLimitCache.d.ts +2 -0
- package/types/adapter/proxy/RateLimitCache.d.ts.map +1 -1
- package/types/adapter/repositories/ProcHostMetricsRepository.d.ts +23 -0
- package/types/adapter/repositories/ProcHostMetricsRepository.d.ts.map +1 -0
- package/types/adapter/repositories/ProcTakeOwnershipSpawnRepository.d.ts +11 -0
- package/types/adapter/repositories/ProcTakeOwnershipSpawnRepository.d.ts.map +1 -0
- package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts +6 -1
- package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/TakeOwnershipSpawnRepository.d.ts +8 -0
- package/types/domain/usecases/adapter-interfaces/TakeOwnershipSpawnRepository.d.ts.map +1 -0
- package/types/domain/usecases/dashboard/GenerateDashboardRowUseCase.d.ts +19 -0
- package/types/domain/usecases/dashboard/GenerateDashboardRowUseCase.d.ts.map +1 -0
- package/types/domain/usecases/dashboard/GenerateTokenStatusUseCase.d.ts +53 -0
- package/types/domain/usecases/dashboard/GenerateTokenStatusUseCase.d.ts.map +1 -0
- package/bin/adapter/entry-points/console/consoleServer.js.map +0 -1
- package/types/adapter/entry-points/console/consoleServer.d.ts.map +0 -1
|
@@ -32,10 +32,7 @@ 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 {
|
|
36
|
-
DEFAULT_CONSOLE_PORT,
|
|
37
|
-
startConsoleServer,
|
|
38
|
-
} from '../console/consoleServer';
|
|
35
|
+
import { DEFAULT_WEB_PORT, startWebServer } from '../console/webServer';
|
|
39
36
|
import { IssueTitleStateCache } from '../console/consoleReadApi';
|
|
40
37
|
import {
|
|
41
38
|
buildPjcodeToProjectUrl,
|
|
@@ -73,7 +70,7 @@ type CheckIssueReviewReadinessOptions = {
|
|
|
73
70
|
configFilePath: string;
|
|
74
71
|
};
|
|
75
72
|
|
|
76
|
-
type
|
|
73
|
+
type ServeWebOptions = {
|
|
77
74
|
configFilePath: string;
|
|
78
75
|
port?: string;
|
|
79
76
|
consoleDataOutputDir?: string;
|
|
@@ -596,146 +593,159 @@ program
|
|
|
596
593
|
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
597
594
|
});
|
|
598
595
|
|
|
599
|
-
|
|
600
|
-
.
|
|
601
|
-
|
|
602
|
-
.
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
'--dashboardDir <path>',
|
|
620
|
-
`Directory containing the dashboard HTML fragment tdpm.txt served unauthenticated at /tdpm.txt (default: ${DEFAULT_DASHBOARD_DIR})`,
|
|
621
|
-
)
|
|
622
|
-
.action(async (options: ServeConsoleOptions) => {
|
|
623
|
-
const config = loadConfigFile(options.configFilePath);
|
|
624
|
-
|
|
625
|
-
const accessToken = config.consoleAccessToken;
|
|
626
|
-
if (!accessToken) {
|
|
596
|
+
const runServeWeb = async (options: ServeWebOptions): Promise<void> => {
|
|
597
|
+
const config = loadConfigFile(options.configFilePath);
|
|
598
|
+
|
|
599
|
+
const accessToken = config.consoleAccessToken;
|
|
600
|
+
if (!accessToken) {
|
|
601
|
+
console.error(
|
|
602
|
+
'consoleAccessToken is required. Provide it via the config file.',
|
|
603
|
+
);
|
|
604
|
+
process.exit(1);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
let port = DEFAULT_WEB_PORT;
|
|
608
|
+
if (options.port !== undefined) {
|
|
609
|
+
const parsedPort = Number(options.port);
|
|
610
|
+
if (
|
|
611
|
+
!Number.isFinite(parsedPort) ||
|
|
612
|
+
!Number.isInteger(parsedPort) ||
|
|
613
|
+
parsedPort <= 0 ||
|
|
614
|
+
parsedPort > 65535
|
|
615
|
+
) {
|
|
627
616
|
console.error(
|
|
628
|
-
'
|
|
617
|
+
'Invalid value for --port. It must be a positive integer between 1 and 65535.',
|
|
629
618
|
);
|
|
630
619
|
process.exit(1);
|
|
631
620
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
621
|
+
port = parsedPort;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const token = process.env.GH_TOKEN;
|
|
625
|
+
if (!token) {
|
|
626
|
+
console.error('GH_TOKEN environment variable is required');
|
|
627
|
+
process.exit(1);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const projectUrl = config.projectUrl;
|
|
631
|
+
if (!projectUrl) {
|
|
632
|
+
console.error(
|
|
633
|
+
'projectUrl is required. Provide it via the config file or project README.',
|
|
634
|
+
);
|
|
635
|
+
process.exit(1);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const projectName = config.projectName ?? 'default';
|
|
639
|
+
const localStorageRepository = new LocalStorageRepository();
|
|
640
|
+
const cachePath = `./tmp/cache/${projectName}`;
|
|
641
|
+
const localStorageCacheRepository = new LocalStorageCacheRepository(
|
|
642
|
+
localStorageRepository,
|
|
643
|
+
cachePath,
|
|
644
|
+
);
|
|
645
|
+
const githubRepositoryParams = buildGithubRepositoryParams(
|
|
646
|
+
localStorageRepository,
|
|
647
|
+
token,
|
|
648
|
+
);
|
|
649
|
+
const projectRepository = new GraphqlProjectRepository(
|
|
650
|
+
...githubRepositoryParams,
|
|
651
|
+
);
|
|
652
|
+
const apiV3IssueRepository = new ApiV3IssueRepository(
|
|
653
|
+
...githubRepositoryParams,
|
|
654
|
+
);
|
|
655
|
+
const restIssueRepository = new RestIssueRepository(
|
|
656
|
+
...githubRepositoryParams,
|
|
657
|
+
);
|
|
658
|
+
const graphqlProjectItemRepository = new GraphqlProjectItemRepository(
|
|
659
|
+
...githubRepositoryParams,
|
|
660
|
+
);
|
|
661
|
+
const issueRepository = new ApiV3CheerioRestIssueRepository(
|
|
662
|
+
apiV3IssueRepository,
|
|
663
|
+
restIssueRepository,
|
|
664
|
+
graphqlProjectItemRepository,
|
|
665
|
+
localStorageCacheRepository,
|
|
666
|
+
...githubRepositoryParams,
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
const pjcodeToProjectUrl = buildPjcodeToProjectUrl(
|
|
670
|
+
projectName,
|
|
671
|
+
projectUrl,
|
|
672
|
+
config.consoleProjects ?? null,
|
|
673
|
+
);
|
|
674
|
+
const resolveProject = createConsoleProjectResolver(
|
|
675
|
+
pjcodeToProjectUrl,
|
|
676
|
+
async (targetProjectUrl: string) => {
|
|
677
|
+
const targetProjectId =
|
|
678
|
+
await projectRepository.findProjectIdByUrl(targetProjectUrl);
|
|
679
|
+
if (!targetProjectId) {
|
|
680
|
+
console.error(`No project found for projectUrl ${targetProjectUrl}`);
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
const loadedProject = await projectRepository.getProject(targetProjectId);
|
|
684
|
+
if (!loadedProject) {
|
|
642
685
|
console.error(
|
|
643
|
-
|
|
686
|
+
`Failed to load project for projectUrl ${targetProjectUrl}`,
|
|
644
687
|
);
|
|
645
|
-
|
|
688
|
+
return null;
|
|
646
689
|
}
|
|
647
|
-
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
690
|
+
return loadedProject;
|
|
691
|
+
},
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
const uiDistDir = path.join(__dirname, '..', 'console', 'ui-dist');
|
|
695
|
+
const consoleDataOutputDir = options.consoleDataOutputDir ?? null;
|
|
696
|
+
const inTmuxDataDir = options.inTmuxDataDir ?? DEFAULT_IN_TMUX_DATA_DIR;
|
|
697
|
+
const dashboardDir = options.dashboardDir ?? DEFAULT_DASHBOARD_DIR;
|
|
698
|
+
|
|
699
|
+
await startWebServer({
|
|
700
|
+
accessToken,
|
|
701
|
+
uiDistDir,
|
|
702
|
+
consoleDataOutputDir,
|
|
703
|
+
inTmuxDataDir,
|
|
704
|
+
dashboardDir,
|
|
705
|
+
githubToken: token,
|
|
706
|
+
issueRepository,
|
|
707
|
+
resolveProject,
|
|
708
|
+
issueTitleStateCache: new IssueTitleStateCache(),
|
|
709
|
+
port,
|
|
710
|
+
});
|
|
711
|
+
console.log(`TDPM web server listening on port ${port}`);
|
|
712
|
+
};
|
|
663
713
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
)
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
714
|
+
const addServeWebOptions = (command: Command): Command =>
|
|
715
|
+
command
|
|
716
|
+
.requiredOption(
|
|
717
|
+
'--configFilePath <path>',
|
|
718
|
+
'Path to config file for tower defence management',
|
|
719
|
+
)
|
|
720
|
+
.option(
|
|
721
|
+
'--port <number>',
|
|
722
|
+
`Port for the web HTTP server (default: ${DEFAULT_WEB_PORT})`,
|
|
723
|
+
)
|
|
724
|
+
.option(
|
|
725
|
+
'--consoleDataOutputDir <path>',
|
|
726
|
+
'Directory where console data files are written and served from',
|
|
727
|
+
)
|
|
728
|
+
.option(
|
|
729
|
+
'--inTmuxDataDir <path>',
|
|
730
|
+
`Directory containing the flat in-tmux-by-human static JSON files served at /in-tmux-by-human/*.json (default: ${DEFAULT_IN_TMUX_DATA_DIR})`,
|
|
731
|
+
)
|
|
732
|
+
.option(
|
|
733
|
+
'--dashboardDir <path>',
|
|
734
|
+
`Directory containing the dashboard HTML fragment tdpm.txt served unauthenticated at /tdpm.txt (default: ${DEFAULT_DASHBOARD_DIR})`,
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
addServeWebOptions(program.command('serveWeb'))
|
|
738
|
+
.description(
|
|
739
|
+
'Start the local TDPM web server (console tabs, dashboard, and in-tmux session list)',
|
|
740
|
+
)
|
|
741
|
+
.action(async (options: ServeWebOptions) => {
|
|
742
|
+
await runServeWeb(options);
|
|
743
|
+
});
|
|
694
744
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
);
|
|
700
|
-
const resolveProject = createConsoleProjectResolver(
|
|
701
|
-
pjcodeToProjectUrl,
|
|
702
|
-
async (targetProjectUrl: string) => {
|
|
703
|
-
const targetProjectId =
|
|
704
|
-
await projectRepository.findProjectIdByUrl(targetProjectUrl);
|
|
705
|
-
if (!targetProjectId) {
|
|
706
|
-
console.error(`No project found for projectUrl ${targetProjectUrl}`);
|
|
707
|
-
return null;
|
|
708
|
-
}
|
|
709
|
-
const loadedProject =
|
|
710
|
-
await projectRepository.getProject(targetProjectId);
|
|
711
|
-
if (!loadedProject) {
|
|
712
|
-
console.error(
|
|
713
|
-
`Failed to load project for projectUrl ${targetProjectUrl}`,
|
|
714
|
-
);
|
|
715
|
-
return null;
|
|
716
|
-
}
|
|
717
|
-
return loadedProject;
|
|
718
|
-
},
|
|
719
|
-
);
|
|
720
|
-
|
|
721
|
-
const uiDistDir = path.join(__dirname, '..', 'console', 'ui-dist');
|
|
722
|
-
const consoleDataOutputDir = options.consoleDataOutputDir ?? null;
|
|
723
|
-
const inTmuxDataDir = options.inTmuxDataDir ?? DEFAULT_IN_TMUX_DATA_DIR;
|
|
724
|
-
const dashboardDir = options.dashboardDir ?? DEFAULT_DASHBOARD_DIR;
|
|
725
|
-
|
|
726
|
-
await startConsoleServer({
|
|
727
|
-
accessToken,
|
|
728
|
-
uiDistDir,
|
|
729
|
-
consoleDataOutputDir,
|
|
730
|
-
inTmuxDataDir,
|
|
731
|
-
dashboardDir,
|
|
732
|
-
githubToken: token,
|
|
733
|
-
issueRepository,
|
|
734
|
-
resolveProject,
|
|
735
|
-
issueTitleStateCache: new IssueTitleStateCache(),
|
|
736
|
-
port,
|
|
737
|
-
});
|
|
738
|
-
console.log(`TDPM Console server listening on port ${port}`);
|
|
745
|
+
addServeWebOptions(program.command('serveConsole'))
|
|
746
|
+
.description('Deprecated alias for serveWeb. Use serveWeb instead.')
|
|
747
|
+
.action(async (options: ServeWebOptions) => {
|
|
748
|
+
await runServeWeb(options);
|
|
739
749
|
});
|
|
740
750
|
|
|
741
751
|
program
|
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
} from '../../../../../domain/usecases/adapter-interfaces/IssueRepository';
|
|
14
14
|
import type { ConsoleProjectBinding } from '../../consoleOperationApi';
|
|
15
15
|
import { IssueTitleStateCache } from '../../consoleReadApi';
|
|
16
|
-
import {
|
|
16
|
+
import { startWebServer } from '../../webServer';
|
|
17
17
|
|
|
18
18
|
export const CONSOLE_E2E_PJCODE = 'umino';
|
|
19
19
|
export const CONSOLE_E2E_TOKEN = 'console-e2e-fixture-token-3f9c1a';
|
|
@@ -362,7 +362,7 @@ export const startConsoleE2eHarness = async (): Promise<ConsoleE2eHarness> => {
|
|
|
362
362
|
): Promise<ConsoleProjectBinding | null> =>
|
|
363
363
|
pjcode === CONSOLE_E2E_PJCODE ? { pjcode, project } : null;
|
|
364
364
|
|
|
365
|
-
const server = await
|
|
365
|
+
const server = await startWebServer({
|
|
366
366
|
accessToken: CONSOLE_E2E_TOKEN,
|
|
367
367
|
uiDistDir,
|
|
368
368
|
consoleDataOutputDir,
|
|
@@ -4,7 +4,7 @@ import * as os from 'os';
|
|
|
4
4
|
import * as path from 'path';
|
|
5
5
|
import { mock } from 'jest-mock-extended';
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
DEFAULT_WEB_PORT,
|
|
8
8
|
CONSOLE_TOKEN_HEADER,
|
|
9
9
|
hasDotSegment,
|
|
10
10
|
requiresToken,
|
|
@@ -13,8 +13,8 @@ import {
|
|
|
13
13
|
extractProvidedToken,
|
|
14
14
|
resolveFlatInTmuxFilePath,
|
|
15
15
|
resolveDashboardFilePath,
|
|
16
|
-
|
|
17
|
-
} from './
|
|
16
|
+
startWebServer,
|
|
17
|
+
} from './webServer';
|
|
18
18
|
import type { ImageFetcher } from './consoleImageProxy';
|
|
19
19
|
import { IssueTitleStateCache } from './consoleReadApi';
|
|
20
20
|
import { readDoneProjectItemIds } from './consoleDoneStore';
|
|
@@ -22,10 +22,10 @@ import { IssueRepository } from '../../../domain/usecases/adapter-interfaces/Iss
|
|
|
22
22
|
import { Project } from '../../../domain/entities/Project';
|
|
23
23
|
import { Issue } from '../../../domain/entities/Issue';
|
|
24
24
|
|
|
25
|
-
describe('
|
|
26
|
-
describe('
|
|
25
|
+
describe('webServer pure helpers', () => {
|
|
26
|
+
describe('DEFAULT_WEB_PORT', () => {
|
|
27
27
|
it('is 9981', () => {
|
|
28
|
-
expect(
|
|
28
|
+
expect(DEFAULT_WEB_PORT).toBe(9981);
|
|
29
29
|
});
|
|
30
30
|
});
|
|
31
31
|
|
|
@@ -123,7 +123,7 @@ describe('consoleServer pure helpers', () => {
|
|
|
123
123
|
});
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
-
describe('
|
|
126
|
+
describe('webServer integration', () => {
|
|
127
127
|
const testToken = 'integration-test-token-value';
|
|
128
128
|
|
|
129
129
|
const requestServer = (
|
|
@@ -175,7 +175,7 @@ describe('consoleServer integration', () => {
|
|
|
175
175
|
|
|
176
176
|
it('starts on an ephemeral port and closes gracefully', async () => {
|
|
177
177
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
178
|
-
const server = await
|
|
178
|
+
const server = await startWebServer({
|
|
179
179
|
accessToken: testToken,
|
|
180
180
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
181
181
|
consoleDataOutputDir: null,
|
|
@@ -192,7 +192,7 @@ describe('consoleServer integration', () => {
|
|
|
192
192
|
|
|
193
193
|
it('serves the placeholder index without a token when ui-dist is absent', async () => {
|
|
194
194
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
195
|
-
const server = await
|
|
195
|
+
const server = await startWebServer({
|
|
196
196
|
accessToken: testToken,
|
|
197
197
|
uiDistDir: path.join(tmpDir, 'missing-ui-dist'),
|
|
198
198
|
consoleDataOutputDir: null,
|
|
@@ -218,7 +218,7 @@ describe('consoleServer integration', () => {
|
|
|
218
218
|
|
|
219
219
|
it('returns 404 for a missing non-index file when ui-dist is absent', async () => {
|
|
220
220
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
221
|
-
const server = await
|
|
221
|
+
const server = await startWebServer({
|
|
222
222
|
accessToken: testToken,
|
|
223
223
|
uiDistDir: path.join(tmpDir, 'missing-ui-dist'),
|
|
224
224
|
consoleDataOutputDir: null,
|
|
@@ -248,7 +248,7 @@ describe('consoleServer integration', () => {
|
|
|
248
248
|
path.join(uiDistDir, 'assets', 'app.js'),
|
|
249
249
|
'console.log("app");',
|
|
250
250
|
);
|
|
251
|
-
const server = await
|
|
251
|
+
const server = await startWebServer({
|
|
252
252
|
accessToken: testToken,
|
|
253
253
|
uiDistDir,
|
|
254
254
|
consoleDataOutputDir: null,
|
|
@@ -280,7 +280,7 @@ describe('consoleServer integration', () => {
|
|
|
280
280
|
path.join(uiDistDir, 'index.html'),
|
|
281
281
|
'<!DOCTYPE html><title>spa</title><div id="root"></div>',
|
|
282
282
|
);
|
|
283
|
-
const server = await
|
|
283
|
+
const server = await startWebServer({
|
|
284
284
|
accessToken: testToken,
|
|
285
285
|
uiDistDir,
|
|
286
286
|
consoleDataOutputDir: null,
|
|
@@ -309,7 +309,7 @@ describe('consoleServer integration', () => {
|
|
|
309
309
|
|
|
310
310
|
it('serves the placeholder index for per-project routes when ui-dist is absent', async () => {
|
|
311
311
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
312
|
-
const server = await
|
|
312
|
+
const server = await startWebServer({
|
|
313
313
|
accessToken: testToken,
|
|
314
314
|
uiDistDir: path.join(tmpDir, 'missing-ui-dist'),
|
|
315
315
|
consoleDataOutputDir: null,
|
|
@@ -332,7 +332,7 @@ describe('consoleServer integration', () => {
|
|
|
332
332
|
const uiDistDir = path.join(tmpDir, 'ui-dist');
|
|
333
333
|
fs.mkdirSync(uiDistDir, { recursive: true });
|
|
334
334
|
fs.writeFileSync(path.join(uiDistDir, '.env'), 'SECRET=should-not-serve');
|
|
335
|
-
const server = await
|
|
335
|
+
const server = await startWebServer({
|
|
336
336
|
accessToken: testToken,
|
|
337
337
|
uiDistDir,
|
|
338
338
|
consoleDataOutputDir: null,
|
|
@@ -358,7 +358,7 @@ describe('consoleServer integration', () => {
|
|
|
358
358
|
|
|
359
359
|
it('rejects .json and /api/* without a token and passes the gate with a valid token', async () => {
|
|
360
360
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
361
|
-
const server = await
|
|
361
|
+
const server = await startWebServer({
|
|
362
362
|
accessToken: testToken,
|
|
363
363
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
364
364
|
consoleDataOutputDir: null,
|
|
@@ -403,7 +403,7 @@ describe('consoleServer integration', () => {
|
|
|
403
403
|
});
|
|
404
404
|
});
|
|
405
405
|
|
|
406
|
-
describe('
|
|
406
|
+
describe('webServer new routes integration', () => {
|
|
407
407
|
const testToken = 'integration-test-token-value';
|
|
408
408
|
|
|
409
409
|
const closeServer = (server: http.Server): Promise<void> =>
|
|
@@ -496,7 +496,7 @@ describe('consoleServer new routes integration', () => {
|
|
|
496
496
|
path.join(listDir, '.done.json'),
|
|
497
497
|
JSON.stringify({ projectItemIds: ['PVTI_drop'] }),
|
|
498
498
|
);
|
|
499
|
-
const server = await
|
|
499
|
+
const server = await startWebServer({
|
|
500
500
|
accessToken: testToken,
|
|
501
501
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
502
502
|
consoleDataOutputDir: dataDir,
|
|
@@ -533,7 +533,7 @@ describe('consoleServer new routes integration', () => {
|
|
|
533
533
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
534
534
|
const issueRepository = mock<IssueRepository>();
|
|
535
535
|
issueRepository.getIssueOrPullRequestBody.mockResolvedValue('body text');
|
|
536
|
-
const server = await
|
|
536
|
+
const server = await startWebServer({
|
|
537
537
|
accessToken: testToken,
|
|
538
538
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
539
539
|
consoleDataOutputDir: null,
|
|
@@ -567,7 +567,7 @@ describe('consoleServer new routes integration', () => {
|
|
|
567
567
|
createdAt: new Date('2026-06-18T03:21:00.000Z'),
|
|
568
568
|
},
|
|
569
569
|
]);
|
|
570
|
-
const server = await
|
|
570
|
+
const server = await startWebServer({
|
|
571
571
|
accessToken: testToken,
|
|
572
572
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
573
573
|
consoleDataOutputDir: null,
|
|
@@ -616,7 +616,7 @@ describe('consoleServer new routes integration', () => {
|
|
|
616
616
|
...mock<Issue>(),
|
|
617
617
|
itemId: 'PVTI_loaded',
|
|
618
618
|
});
|
|
619
|
-
const server = await
|
|
619
|
+
const server = await startWebServer({
|
|
620
620
|
accessToken: testToken,
|
|
621
621
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
622
622
|
consoleDataOutputDir: dataDir,
|
|
@@ -662,7 +662,7 @@ describe('consoleServer new routes integration', () => {
|
|
|
662
662
|
issueRepository.approvePullRequest.mockRejectedValue(
|
|
663
663
|
new Error('Failed to approve PR https://github.com/o/r/pull/1: HTTP 422'),
|
|
664
664
|
);
|
|
665
|
-
const server = await
|
|
665
|
+
const server = await startWebServer({
|
|
666
666
|
accessToken: testToken,
|
|
667
667
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
668
668
|
consoleDataOutputDir: null,
|
|
@@ -698,7 +698,7 @@ describe('consoleServer new routes integration', () => {
|
|
|
698
698
|
it('rejects an operation api with a malformed json body', async () => {
|
|
699
699
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
700
700
|
const issueRepository = mock<IssueRepository>();
|
|
701
|
-
const server = await
|
|
701
|
+
const server = await startWebServer({
|
|
702
702
|
accessToken: testToken,
|
|
703
703
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
704
704
|
consoleDataOutputDir: null,
|
|
@@ -749,7 +749,7 @@ describe('consoleServer new routes integration', () => {
|
|
|
749
749
|
|
|
750
750
|
it('returns 404 for a read api when no repository is injected', async () => {
|
|
751
751
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
752
|
-
const server = await
|
|
752
|
+
const server = await startWebServer({
|
|
753
753
|
accessToken: testToken,
|
|
754
754
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
755
755
|
consoleDataOutputDir: null,
|
|
@@ -809,7 +809,7 @@ describe('resolveFlatInTmuxFilePath', () => {
|
|
|
809
809
|
});
|
|
810
810
|
});
|
|
811
811
|
|
|
812
|
-
describe('
|
|
812
|
+
describe('webServer flat in-tmux-by-human route integration', () => {
|
|
813
813
|
const testToken = 'integration-test-token-value';
|
|
814
814
|
|
|
815
815
|
const requestServer = (
|
|
@@ -878,7 +878,7 @@ describe('consoleServer flat in-tmux-by-human route integration', () => {
|
|
|
878
878
|
fs.writeFileSync(path.join(inTmuxDataDir, 'index.v4.json'), indexV4Raw);
|
|
879
879
|
fs.writeFileSync(path.join(inTmuxDataDir, 'index.v3.json'), indexV3Raw);
|
|
880
880
|
fs.writeFileSync(path.join(tmpDir, 'secret.json'), '{"secret":true}');
|
|
881
|
-
const server = await
|
|
881
|
+
const server = await startWebServer({
|
|
882
882
|
accessToken: testToken,
|
|
883
883
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
884
884
|
consoleDataOutputDir: null,
|
|
@@ -992,7 +992,7 @@ describe('consoleServer flat in-tmux-by-human route integration', () => {
|
|
|
992
992
|
|
|
993
993
|
it('returns 404 for the flat route when inTmuxDataDir is null', async () => {
|
|
994
994
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
995
|
-
const server = await
|
|
995
|
+
const server = await startWebServer({
|
|
996
996
|
accessToken: testToken,
|
|
997
997
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
998
998
|
consoleDataOutputDir: null,
|
|
@@ -1030,7 +1030,7 @@ describe('resolveDashboardFilePath', () => {
|
|
|
1030
1030
|
});
|
|
1031
1031
|
});
|
|
1032
1032
|
|
|
1033
|
-
describe('
|
|
1033
|
+
describe('webServer dashboard /tdpm.txt route integration', () => {
|
|
1034
1034
|
const testToken = 'integration-test-token-value';
|
|
1035
1035
|
|
|
1036
1036
|
const requestServer = (
|
|
@@ -1094,7 +1094,7 @@ describe('consoleServer dashboard /tdpm.txt route integration', () => {
|
|
|
1094
1094
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
1095
1095
|
fs.writeFileSync(path.join(tmpDir, 'tdpm.txt'), dashboardRaw);
|
|
1096
1096
|
fs.writeFileSync(path.join(tmpDir, 'secret.txt'), 'secret content');
|
|
1097
|
-
const server = await
|
|
1097
|
+
const server = await startWebServer({
|
|
1098
1098
|
accessToken: testToken,
|
|
1099
1099
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
1100
1100
|
consoleDataOutputDir: null,
|
|
@@ -1125,7 +1125,7 @@ describe('consoleServer dashboard /tdpm.txt route integration', () => {
|
|
|
1125
1125
|
|
|
1126
1126
|
it('returns 404 for /tdpm.txt when the file is absent', async () => {
|
|
1127
1127
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
1128
|
-
const server = await
|
|
1128
|
+
const server = await startWebServer({
|
|
1129
1129
|
accessToken: testToken,
|
|
1130
1130
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
1131
1131
|
consoleDataOutputDir: null,
|
|
@@ -1146,7 +1146,7 @@ describe('consoleServer dashboard /tdpm.txt route integration', () => {
|
|
|
1146
1146
|
const { server, tmpDir } = await (async () => {
|
|
1147
1147
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
1148
1148
|
fs.writeFileSync(path.join(dir, 'tdpm.txt'), dashboardRaw);
|
|
1149
|
-
const srv = await
|
|
1149
|
+
const srv = await startWebServer({
|
|
1150
1150
|
accessToken: testToken,
|
|
1151
1151
|
uiDistDir: path.join(dir, 'ui-dist'),
|
|
1152
1152
|
consoleDataOutputDir: null,
|
|
@@ -1177,7 +1177,7 @@ describe('consoleServer dashboard /tdpm.txt route integration', () => {
|
|
|
1177
1177
|
});
|
|
1178
1178
|
});
|
|
1179
1179
|
|
|
1180
|
-
describe('
|
|
1180
|
+
describe('webServer image proxy', () => {
|
|
1181
1181
|
const testToken = 'image-proxy-token-value';
|
|
1182
1182
|
const githubToken = 'gh-token-value';
|
|
1183
1183
|
const allowedUrl = 'https://github.com/user-attachments/assets/abc-123';
|
|
@@ -1244,7 +1244,7 @@ describe('consoleServer image proxy', () => {
|
|
|
1244
1244
|
): Promise<{ server: http.Server; tmpDir: string }> =>
|
|
1245
1245
|
(async () => {
|
|
1246
1246
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'console-server-'));
|
|
1247
|
-
const server = await
|
|
1247
|
+
const server = await startWebServer({
|
|
1248
1248
|
accessToken: testToken,
|
|
1249
1249
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
1250
1250
|
consoleDataOutputDir: null,
|