github-issue-tower-defence-management 1.97.3 → 1.98.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 +12 -0
- package/bin/adapter/entry-points/cli/index.js +62 -0
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/{index-CDGwyvDF.js → index-BFKRWKvS.js} +1 -1
- package/bin/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/bin/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.js +48 -0
- package/bin/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.js.map +1 -0
- package/bin/adapter/repositories/ProcClaudeInteractiveSessionRepository.js +145 -0
- package/bin/adapter/repositories/ProcClaudeInteractiveSessionRepository.js.map +1 -0
- package/bin/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.js +41 -0
- package/bin/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.js.map +1 -0
- package/bin/domain/usecases/adapter-interfaces/ClaudeInteractiveSessionRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/ClaudeInteractiveSessionRepository.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/cli/index.ts +120 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.tsx +4 -4
- package/src/adapter/entry-points/console/ui-dist/assets/{index-CDGwyvDF.js → index-BFKRWKvS.js} +1 -1
- package/src/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/src/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.test.ts +123 -0
- package/src/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.ts +77 -0
- package/src/adapter/repositories/ProcClaudeInteractiveSessionRepository.test.ts +184 -0
- package/src/adapter/repositories/ProcClaudeInteractiveSessionRepository.ts +138 -0
- package/src/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.test.ts +169 -0
- package/src/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.ts +63 -0
- package/src/domain/usecases/adapter-interfaces/ClaudeInteractiveSessionRepository.ts +9 -0
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.d.ts +18 -0
- package/types/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.d.ts.map +1 -0
- package/types/adapter/repositories/ProcClaudeInteractiveSessionRepository.d.ts +11 -0
- package/types/adapter/repositories/ProcClaudeInteractiveSessionRepository.d.ts.map +1 -0
- package/types/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.d.ts +17 -0
- package/types/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/ClaudeInteractiveSessionRepository.d.ts +9 -0
- package/types/domain/usecases/adapter-interfaces/ClaudeInteractiveSessionRepository.d.ts.map +1 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import {
|
|
4
|
+
ClaudeInteractiveSession,
|
|
5
|
+
ClaudeInteractiveSessionRepository,
|
|
6
|
+
} from '../../domain/usecases/adapter-interfaces/ClaudeInteractiveSessionRepository';
|
|
7
|
+
|
|
8
|
+
const DEFAULT_PROC_DIRECTORY = '/proc';
|
|
9
|
+
const OAUTH_TOKEN_ENVIRON_KEY = 'CLAUDE_CODE_OAUTH_TOKEN';
|
|
10
|
+
const SESSION_ID_ENVIRON_KEY = 'CLAUDE_CODE_SESSION_ID';
|
|
11
|
+
const NAME_ARGUMENT = '--name';
|
|
12
|
+
const TAKE_OWNERSHIP_MARKER = 'Take ownership';
|
|
13
|
+
|
|
14
|
+
const isIssueUrl = (value: string): boolean =>
|
|
15
|
+
value.startsWith('http://') || value.startsWith('https://');
|
|
16
|
+
|
|
17
|
+
const parseCommandLineArguments = (cmdline: string): string[] =>
|
|
18
|
+
cmdline.split('\0').filter((argument) => argument.length > 0);
|
|
19
|
+
|
|
20
|
+
const extractIssueUrl = (commandArguments: string[]): string | null => {
|
|
21
|
+
for (let index = 0; index < commandArguments.length - 1; index += 1) {
|
|
22
|
+
if (commandArguments[index] === NAME_ARGUMENT) {
|
|
23
|
+
const value = commandArguments[index + 1] ?? '';
|
|
24
|
+
if (isIssueUrl(value)) {
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const isTakeOwnershipSpawn = (commandArguments: string[]): boolean =>
|
|
33
|
+
commandArguments.some((argument) => argument.includes(TAKE_OWNERSHIP_MARKER));
|
|
34
|
+
|
|
35
|
+
export class ProcClaudeInteractiveSessionRepository implements ClaudeInteractiveSessionRepository {
|
|
36
|
+
constructor(
|
|
37
|
+
private readonly procDirectory: string = DEFAULT_PROC_DIRECTORY,
|
|
38
|
+
) {}
|
|
39
|
+
|
|
40
|
+
listInteractiveSessions = (): ClaudeInteractiveSession[] => {
|
|
41
|
+
const interactiveSessions: ClaudeInteractiveSession[] = [];
|
|
42
|
+
for (const pidDirectory of this.listProcessIdDirectories()) {
|
|
43
|
+
const interactiveSession = this.readInteractiveSession(pidDirectory);
|
|
44
|
+
if (interactiveSession !== null) {
|
|
45
|
+
interactiveSessions.push(interactiveSession);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return interactiveSessions;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
private listProcessIdDirectories = (): string[] => {
|
|
52
|
+
let entries: string[];
|
|
53
|
+
try {
|
|
54
|
+
entries = fs.readdirSync(this.procDirectory);
|
|
55
|
+
} catch {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
return entries.filter((entry) => /^\d+$/.test(entry));
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
private readInteractiveSession = (
|
|
62
|
+
processIdDirectory: string,
|
|
63
|
+
): ClaudeInteractiveSession | null => {
|
|
64
|
+
const commandArguments = this.readCommandArguments(processIdDirectory);
|
|
65
|
+
if (commandArguments === null) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
if (isTakeOwnershipSpawn(commandArguments)) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const issueUrl = extractIssueUrl(commandArguments);
|
|
72
|
+
if (issueUrl === null) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const environ = this.readEnviron(processIdDirectory);
|
|
76
|
+
if (environ === null) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const token = environ.get(OAUTH_TOKEN_ENVIRON_KEY);
|
|
80
|
+
const sessionId = environ.get(SESSION_ID_ENVIRON_KEY);
|
|
81
|
+
if (
|
|
82
|
+
token === undefined ||
|
|
83
|
+
token.length === 0 ||
|
|
84
|
+
sessionId === undefined ||
|
|
85
|
+
sessionId.length === 0
|
|
86
|
+
) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return { token, sessionId, issueUrl };
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
private readCommandArguments = (
|
|
93
|
+
processIdDirectory: string,
|
|
94
|
+
): string[] | null => {
|
|
95
|
+
const cmdlinePath = path.join(
|
|
96
|
+
this.procDirectory,
|
|
97
|
+
processIdDirectory,
|
|
98
|
+
'cmdline',
|
|
99
|
+
);
|
|
100
|
+
let raw: string;
|
|
101
|
+
try {
|
|
102
|
+
raw = fs.readFileSync(cmdlinePath, 'utf8');
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
return parseCommandLineArguments(raw);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
private readEnviron = (
|
|
110
|
+
processIdDirectory: string,
|
|
111
|
+
): Map<string, string> | null => {
|
|
112
|
+
const environPath = path.join(
|
|
113
|
+
this.procDirectory,
|
|
114
|
+
processIdDirectory,
|
|
115
|
+
'environ',
|
|
116
|
+
);
|
|
117
|
+
let raw: string;
|
|
118
|
+
try {
|
|
119
|
+
raw = fs.readFileSync(environPath, 'utf8');
|
|
120
|
+
} catch {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
const environ = new Map<string, string>();
|
|
124
|
+
for (const entry of raw.split('\0')) {
|
|
125
|
+
if (entry.length === 0) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const separatorIndex = entry.indexOf('=');
|
|
129
|
+
if (separatorIndex <= 0) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const key = entry.slice(0, separatorIndex);
|
|
133
|
+
const value = entry.slice(separatorIndex + 1);
|
|
134
|
+
environ.set(key, value);
|
|
135
|
+
}
|
|
136
|
+
return environ;
|
|
137
|
+
};
|
|
138
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { Issue } from '../entities/Issue';
|
|
2
|
+
import {
|
|
3
|
+
AWAITING_QUALITY_CHECK_STATUS_NAME,
|
|
4
|
+
IN_TMUX_STATUS_NAME,
|
|
5
|
+
} from '../entities/WorkflowStatus';
|
|
6
|
+
import { ClaudeInteractiveSession } from './adapter-interfaces/ClaudeInteractiveSessionRepository';
|
|
7
|
+
import { InTmuxByHumanSessionTokenCountUseCase } from './InTmuxByHumanSessionTokenCountUseCase';
|
|
8
|
+
import { OauthTokenCandidate } from './OauthTokenSelectUseCase';
|
|
9
|
+
|
|
10
|
+
const candidate = (name: string): OauthTokenCandidate => ({
|
|
11
|
+
name,
|
|
12
|
+
token: `fake-token-${name}`,
|
|
13
|
+
snapshot: null,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const session = (
|
|
17
|
+
name: string,
|
|
18
|
+
sessionId: string,
|
|
19
|
+
issueUrl: string,
|
|
20
|
+
): ClaudeInteractiveSession => ({
|
|
21
|
+
token: `fake-token-${name}`,
|
|
22
|
+
sessionId,
|
|
23
|
+
issueUrl,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const issue = (url: string, status: string): Issue => ({
|
|
27
|
+
nameWithOwner: 'HiromiShikata/example',
|
|
28
|
+
number: 1,
|
|
29
|
+
title: 'Example issue',
|
|
30
|
+
state: 'OPEN',
|
|
31
|
+
status,
|
|
32
|
+
story: null,
|
|
33
|
+
nextActionDate: null,
|
|
34
|
+
nextActionHour: null,
|
|
35
|
+
estimationMinutes: null,
|
|
36
|
+
dependedIssueUrls: [],
|
|
37
|
+
completionDate50PercentConfidence: null,
|
|
38
|
+
url,
|
|
39
|
+
assignees: ['hiromi'],
|
|
40
|
+
labels: [],
|
|
41
|
+
org: 'HiromiShikata',
|
|
42
|
+
repo: 'example',
|
|
43
|
+
body: '',
|
|
44
|
+
itemId: 'item-1',
|
|
45
|
+
isPr: false,
|
|
46
|
+
isInProgress: false,
|
|
47
|
+
isClosed: false,
|
|
48
|
+
createdAt: new Date('2026-01-01T00:00:00Z'),
|
|
49
|
+
author: 'hiromi',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('InTmuxByHumanSessionTokenCountUseCase', () => {
|
|
53
|
+
const useCase = new InTmuxByHumanSessionTokenCountUseCase();
|
|
54
|
+
const issueUrlA = 'https://github.com/HiromiShikata/example/issues/1';
|
|
55
|
+
const issueUrlB = 'https://github.com/HiromiShikata/example/issues/2';
|
|
56
|
+
|
|
57
|
+
it('counts a session whose issue is in In-Tmux-by-human status', () => {
|
|
58
|
+
const result = useCase.run(
|
|
59
|
+
[candidate('alpha')],
|
|
60
|
+
[session('alpha', 'session-a', issueUrlA)],
|
|
61
|
+
[issue(issueUrlA, IN_TMUX_STATUS_NAME)],
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const alpha = result.counts.find((entry) => entry.name === 'alpha');
|
|
65
|
+
expect(alpha?.count).toBe(1);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('does not count a session whose issue is in another status', () => {
|
|
69
|
+
const result = useCase.run(
|
|
70
|
+
[candidate('alpha')],
|
|
71
|
+
[session('alpha', 'session-a', issueUrlA)],
|
|
72
|
+
[issue(issueUrlA, AWAITING_QUALITY_CHECK_STATUS_NAME)],
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const alpha = result.counts.find((entry) => entry.name === 'alpha');
|
|
76
|
+
expect(alpha?.count).toBe(0);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('does not count a session whose issue is closed even in In-Tmux-by-human status', () => {
|
|
80
|
+
const closedIssue: Issue = {
|
|
81
|
+
...issue(issueUrlA, IN_TMUX_STATUS_NAME),
|
|
82
|
+
isClosed: true,
|
|
83
|
+
};
|
|
84
|
+
const result = useCase.run(
|
|
85
|
+
[candidate('alpha')],
|
|
86
|
+
[session('alpha', 'session-a', issueUrlA)],
|
|
87
|
+
[closedIssue],
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const alpha = result.counts.find((entry) => entry.name === 'alpha');
|
|
91
|
+
expect(alpha?.count).toBe(0);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('dedupes distinct sessions sharing one session id under the same token', () => {
|
|
95
|
+
const result = useCase.run(
|
|
96
|
+
[candidate('alpha')],
|
|
97
|
+
[
|
|
98
|
+
session('alpha', 'session-a', issueUrlA),
|
|
99
|
+
session('alpha', 'session-a', issueUrlA),
|
|
100
|
+
session('alpha', 'session-a', issueUrlA),
|
|
101
|
+
],
|
|
102
|
+
[issue(issueUrlA, IN_TMUX_STATUS_NAME)],
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const alpha = result.counts.find((entry) => entry.name === 'alpha');
|
|
106
|
+
expect(alpha?.count).toBe(1);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('counts distinct session ids separately under the same token', () => {
|
|
110
|
+
const result = useCase.run(
|
|
111
|
+
[candidate('alpha')],
|
|
112
|
+
[
|
|
113
|
+
session('alpha', 'session-a', issueUrlA),
|
|
114
|
+
session('alpha', 'session-b', issueUrlB),
|
|
115
|
+
],
|
|
116
|
+
[
|
|
117
|
+
issue(issueUrlA, IN_TMUX_STATUS_NAME),
|
|
118
|
+
issue(issueUrlB, IN_TMUX_STATUS_NAME),
|
|
119
|
+
],
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const alpha = result.counts.find((entry) => entry.name === 'alpha');
|
|
123
|
+
expect(alpha?.count).toBe(2);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('reports counts per token across multiple tokens', () => {
|
|
127
|
+
const result = useCase.run(
|
|
128
|
+
[candidate('alpha'), candidate('beta')],
|
|
129
|
+
[
|
|
130
|
+
session('alpha', 'session-a', issueUrlA),
|
|
131
|
+
session('beta', 'session-b', issueUrlB),
|
|
132
|
+
],
|
|
133
|
+
[
|
|
134
|
+
issue(issueUrlA, IN_TMUX_STATUS_NAME),
|
|
135
|
+
issue(issueUrlB, AWAITING_QUALITY_CHECK_STATUS_NAME),
|
|
136
|
+
],
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
expect(result.counts).toEqual([
|
|
140
|
+
{ name: 'alpha', token: 'fake-token-alpha', count: 1 },
|
|
141
|
+
{ name: 'beta', token: 'fake-token-beta', count: 0 },
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
const beta = result.counts.find((entry) => entry.name === 'beta');
|
|
145
|
+
expect(beta?.count).toBe(0);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('reports zero for a token with no matching interactive session', () => {
|
|
149
|
+
const result = useCase.run(
|
|
150
|
+
[candidate('lonely')],
|
|
151
|
+
[session('other', 'session-x', issueUrlA)],
|
|
152
|
+
[issue(issueUrlA, IN_TMUX_STATUS_NAME)],
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const lonely = result.counts.find((entry) => entry.name === 'lonely');
|
|
156
|
+
expect(lonely?.count).toBe(0);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('ignores a session whose issue url is not present among the issues', () => {
|
|
160
|
+
const result = useCase.run(
|
|
161
|
+
[candidate('alpha')],
|
|
162
|
+
[session('alpha', 'session-a', issueUrlB)],
|
|
163
|
+
[issue(issueUrlA, IN_TMUX_STATUS_NAME)],
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const alpha = result.counts.find((entry) => entry.name === 'alpha');
|
|
167
|
+
expect(alpha?.count).toBe(0);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Issue } from '../entities/Issue';
|
|
2
|
+
import { IN_TMUX_STATUS_NAME } from '../entities/WorkflowStatus';
|
|
3
|
+
import { ClaudeInteractiveSession } from './adapter-interfaces/ClaudeInteractiveSessionRepository';
|
|
4
|
+
import { OauthTokenCandidate } from './OauthTokenSelectUseCase';
|
|
5
|
+
|
|
6
|
+
export type InTmuxByHumanSessionTokenCount = {
|
|
7
|
+
name: string;
|
|
8
|
+
token: string;
|
|
9
|
+
count: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type InTmuxByHumanSessionTokenCountResult = {
|
|
13
|
+
counts: InTmuxByHumanSessionTokenCount[];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export class InTmuxByHumanSessionTokenCountUseCase {
|
|
17
|
+
run = (
|
|
18
|
+
candidates: OauthTokenCandidate[],
|
|
19
|
+
interactiveSessions: ClaudeInteractiveSession[],
|
|
20
|
+
issues: Issue[],
|
|
21
|
+
): InTmuxByHumanSessionTokenCountResult => {
|
|
22
|
+
const inTmuxByHumanIssueUrls = this.inTmuxByHumanIssueUrls(issues);
|
|
23
|
+
const distinctSessionIdsByToken = this.distinctSessionIdsByToken(
|
|
24
|
+
interactiveSessions,
|
|
25
|
+
inTmuxByHumanIssueUrls,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const counts = candidates.map((candidate) => ({
|
|
29
|
+
name: candidate.name,
|
|
30
|
+
token: candidate.token,
|
|
31
|
+
count: distinctSessionIdsByToken.get(candidate.token)?.size ?? 0,
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
return { counts };
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
private inTmuxByHumanIssueUrls = (issues: Issue[]): Set<string> => {
|
|
38
|
+
const urls = new Set<string>();
|
|
39
|
+
for (const issue of issues) {
|
|
40
|
+
if (issue.status === IN_TMUX_STATUS_NAME && issue.isClosed === false) {
|
|
41
|
+
urls.add(issue.url);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return urls;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
private distinctSessionIdsByToken = (
|
|
48
|
+
interactiveSessions: ClaudeInteractiveSession[],
|
|
49
|
+
inTmuxByHumanIssueUrls: Set<string>,
|
|
50
|
+
): Map<string, Set<string>> => {
|
|
51
|
+
const sessionIdsByToken = new Map<string, Set<string>>();
|
|
52
|
+
for (const session of interactiveSessions) {
|
|
53
|
+
if (!inTmuxByHumanIssueUrls.has(session.issueUrl)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const sessionIds =
|
|
57
|
+
sessionIdsByToken.get(session.token) ?? new Set<string>();
|
|
58
|
+
sessionIds.add(session.sessionId);
|
|
59
|
+
sessionIdsByToken.set(session.token, sessionIds);
|
|
60
|
+
}
|
|
61
|
+
return sessionIdsByToken;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EACL,UAAU,EACV,cAAc,EACd,wBAAwB,EACxB,YAAY,EACZ,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EACL,UAAU,EACV,cAAc,EACd,wBAAwB,EACxB,YAAY,EACZ,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AA+GzB,eAAO,MAAM,OAAO,SAAgB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Issue } from '../../../domain/entities/Issue';
|
|
2
|
+
import { ClaudeInteractiveSessionRepository } from '../../../domain/usecases/adapter-interfaces/ClaudeInteractiveSessionRepository';
|
|
3
|
+
import { InTmuxByHumanSessionTokenCountUseCase } from '../../../domain/usecases/InTmuxByHumanSessionTokenCountUseCase';
|
|
4
|
+
export type InTmuxByHumanSessionTokenCountHandlerInput = {
|
|
5
|
+
tokenListJsonPath: string | null;
|
|
6
|
+
issues: Issue[];
|
|
7
|
+
};
|
|
8
|
+
export type InTmuxByHumanSessionTokenCountHandlerOutput = {
|
|
9
|
+
lines: string[];
|
|
10
|
+
diagnostics: string[];
|
|
11
|
+
};
|
|
12
|
+
export declare class InTmuxByHumanSessionTokenCountHandler {
|
|
13
|
+
private readonly useCase;
|
|
14
|
+
private readonly interactiveSessionRepository;
|
|
15
|
+
constructor(useCase?: InTmuxByHumanSessionTokenCountUseCase, interactiveSessionRepository?: ClaudeInteractiveSessionRepository);
|
|
16
|
+
handle: (input: InTmuxByHumanSessionTokenCountHandlerInput) => InTmuxByHumanSessionTokenCountHandlerOutput;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=InTmuxByHumanSessionTokenCountHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InTmuxByHumanSessionTokenCountHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,kCAAkC,EAAE,MAAM,gFAAgF,CAAC;AACpI,OAAO,EAAE,qCAAqC,EAAE,MAAM,gEAAgE,CAAC;AAMvH,MAAM,MAAM,0CAA0C,GAAG;IACvD,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,2CAA2C,GAAG;IACxD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAEF,qBAAa,qCAAqC;IAE9C,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,4BAA4B;gBAD5B,OAAO,GAAE,qCAAmF,EAC5F,4BAA4B,GAAE,kCAAiF;IAGlI,MAAM,GACJ,OAAO,0CAA0C,KAChD,2CAA2C,CAiD5C;CACH"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ClaudeInteractiveSession, ClaudeInteractiveSessionRepository } from '../../domain/usecases/adapter-interfaces/ClaudeInteractiveSessionRepository';
|
|
2
|
+
export declare class ProcClaudeInteractiveSessionRepository implements ClaudeInteractiveSessionRepository {
|
|
3
|
+
private readonly procDirectory;
|
|
4
|
+
constructor(procDirectory?: string);
|
|
5
|
+
listInteractiveSessions: () => ClaudeInteractiveSession[];
|
|
6
|
+
private listProcessIdDirectories;
|
|
7
|
+
private readInteractiveSession;
|
|
8
|
+
private readCommandArguments;
|
|
9
|
+
private readEnviron;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=ProcClaudeInteractiveSessionRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProcClaudeInteractiveSessionRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/ProcClaudeInteractiveSessionRepository.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,wBAAwB,EACxB,kCAAkC,EACnC,MAAM,6EAA6E,CAAC;AA6BrF,qBAAa,sCAAuC,YAAW,kCAAkC;IAE7F,OAAO,CAAC,QAAQ,CAAC,aAAa;gBAAb,aAAa,GAAE,MAA+B;IAGjE,uBAAuB,QAAO,wBAAwB,EAAE,CAStD;IAEF,OAAO,CAAC,wBAAwB,CAQ9B;IAEF,OAAO,CAAC,sBAAsB,CA6B5B;IAEF,OAAO,CAAC,oBAAoB,CAe1B;IAEF,OAAO,CAAC,WAAW,CA4BjB;CACH"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Issue } from '../entities/Issue';
|
|
2
|
+
import { ClaudeInteractiveSession } from './adapter-interfaces/ClaudeInteractiveSessionRepository';
|
|
3
|
+
import { OauthTokenCandidate } from './OauthTokenSelectUseCase';
|
|
4
|
+
export type InTmuxByHumanSessionTokenCount = {
|
|
5
|
+
name: string;
|
|
6
|
+
token: string;
|
|
7
|
+
count: number;
|
|
8
|
+
};
|
|
9
|
+
export type InTmuxByHumanSessionTokenCountResult = {
|
|
10
|
+
counts: InTmuxByHumanSessionTokenCount[];
|
|
11
|
+
};
|
|
12
|
+
export declare class InTmuxByHumanSessionTokenCountUseCase {
|
|
13
|
+
run: (candidates: OauthTokenCandidate[], interactiveSessions: ClaudeInteractiveSession[], issues: Issue[]) => InTmuxByHumanSessionTokenCountResult;
|
|
14
|
+
private inTmuxByHumanIssueUrls;
|
|
15
|
+
private distinctSessionIdsByToken;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=InTmuxByHumanSessionTokenCountUseCase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InTmuxByHumanSessionTokenCountUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,wBAAwB,EAAE,MAAM,yDAAyD,CAAC;AACnG,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAEhE,MAAM,MAAM,8BAA8B,GAAG;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,oCAAoC,GAAG;IACjD,MAAM,EAAE,8BAA8B,EAAE,CAAC;CAC1C,CAAC;AAEF,qBAAa,qCAAqC;IAChD,GAAG,GACD,YAAY,mBAAmB,EAAE,EACjC,qBAAqB,wBAAwB,EAAE,EAC/C,QAAQ,KAAK,EAAE,KACd,oCAAoC,CAcrC;IAEF,OAAO,CAAC,sBAAsB,CAQ5B;IAEF,OAAO,CAAC,yBAAyB,CAe/B;CACH"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type ClaudeInteractiveSession = {
|
|
2
|
+
token: string;
|
|
3
|
+
sessionId: string;
|
|
4
|
+
issueUrl: string;
|
|
5
|
+
};
|
|
6
|
+
export interface ClaudeInteractiveSessionRepository {
|
|
7
|
+
listInteractiveSessions: () => ClaudeInteractiveSession[];
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=ClaudeInteractiveSessionRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClaudeInteractiveSessionRepository.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/ClaudeInteractiveSessionRepository.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,WAAW,kCAAkC;IACjD,uBAAuB,EAAE,MAAM,wBAAwB,EAAE,CAAC;CAC3D"}
|