@agent-relay/wrapper 0.1.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/dist/__fixtures__/claude-outputs.d.ts +49 -0
- package/dist/__fixtures__/claude-outputs.d.ts.map +1 -0
- package/dist/__fixtures__/claude-outputs.js +443 -0
- package/dist/__fixtures__/claude-outputs.js.map +1 -0
- package/dist/__fixtures__/codex-outputs.d.ts +9 -0
- package/dist/__fixtures__/codex-outputs.d.ts.map +1 -0
- package/dist/__fixtures__/codex-outputs.js +94 -0
- package/dist/__fixtures__/codex-outputs.js.map +1 -0
- package/dist/__fixtures__/gemini-outputs.d.ts +19 -0
- package/dist/__fixtures__/gemini-outputs.d.ts.map +1 -0
- package/dist/__fixtures__/gemini-outputs.js +144 -0
- package/dist/__fixtures__/gemini-outputs.js.map +1 -0
- package/dist/__fixtures__/index.d.ts +68 -0
- package/dist/__fixtures__/index.d.ts.map +1 -0
- package/dist/__fixtures__/index.js +44 -0
- package/dist/__fixtures__/index.js.map +1 -0
- package/dist/auth-detection.d.ts +49 -0
- package/dist/auth-detection.d.ts.map +1 -0
- package/dist/auth-detection.js +199 -0
- package/dist/auth-detection.js.map +1 -0
- package/dist/base-wrapper.d.ts +225 -0
- package/dist/base-wrapper.d.ts.map +1 -0
- package/dist/base-wrapper.js +572 -0
- package/dist/base-wrapper.js.map +1 -0
- package/dist/client.d.ts +254 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +801 -0
- package/dist/client.js.map +1 -0
- package/dist/id-generator.d.ts +35 -0
- package/dist/id-generator.d.ts.map +1 -0
- package/dist/id-generator.js +60 -0
- package/dist/id-generator.js.map +1 -0
- package/dist/idle-detector.d.ts +110 -0
- package/dist/idle-detector.d.ts.map +1 -0
- package/dist/idle-detector.js +304 -0
- package/dist/idle-detector.js.map +1 -0
- package/dist/inbox.d.ts +37 -0
- package/dist/inbox.d.ts.map +1 -0
- package/dist/inbox.js +73 -0
- package/dist/inbox.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.d.ts +236 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +1238 -0
- package/dist/parser.js.map +1 -0
- package/dist/prompt-composer.d.ts +67 -0
- package/dist/prompt-composer.d.ts.map +1 -0
- package/dist/prompt-composer.js +168 -0
- package/dist/prompt-composer.js.map +1 -0
- package/dist/relay-pty-orchestrator.d.ts +407 -0
- package/dist/relay-pty-orchestrator.d.ts.map +1 -0
- package/dist/relay-pty-orchestrator.js +1885 -0
- package/dist/relay-pty-orchestrator.js.map +1 -0
- package/dist/shared.d.ts +201 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +341 -0
- package/dist/shared.js.map +1 -0
- package/dist/stuck-detector.d.ts +161 -0
- package/dist/stuck-detector.d.ts.map +1 -0
- package/dist/stuck-detector.js +402 -0
- package/dist/stuck-detector.js.map +1 -0
- package/dist/tmux-resolver.d.ts +55 -0
- package/dist/tmux-resolver.d.ts.map +1 -0
- package/dist/tmux-resolver.js +175 -0
- package/dist/tmux-resolver.js.map +1 -0
- package/dist/tmux-wrapper.d.ts +345 -0
- package/dist/tmux-wrapper.d.ts.map +1 -0
- package/dist/tmux-wrapper.js +1747 -0
- package/dist/tmux-wrapper.js.map +1 -0
- package/dist/trajectory-integration.d.ts +292 -0
- package/dist/trajectory-integration.d.ts.map +1 -0
- package/dist/trajectory-integration.js +979 -0
- package/dist/trajectory-integration.js.map +1 -0
- package/dist/wrapper-types.d.ts +41 -0
- package/dist/wrapper-types.d.ts.map +1 -0
- package/dist/wrapper-types.js +7 -0
- package/dist/wrapper-types.js.map +1 -0
- package/package.json +63 -0
- package/src/__fixtures__/claude-outputs.ts +471 -0
- package/src/__fixtures__/codex-outputs.ts +99 -0
- package/src/__fixtures__/gemini-outputs.ts +151 -0
- package/src/__fixtures__/index.ts +47 -0
- package/src/auth-detection.ts +244 -0
- package/src/base-wrapper.test.ts +540 -0
- package/src/base-wrapper.ts +741 -0
- package/src/client.test.ts +262 -0
- package/src/client.ts +984 -0
- package/src/id-generator.test.ts +71 -0
- package/src/id-generator.ts +69 -0
- package/src/idle-detector.test.ts +390 -0
- package/src/idle-detector.ts +370 -0
- package/src/inbox.test.ts +233 -0
- package/src/inbox.ts +89 -0
- package/src/index.ts +170 -0
- package/src/parser.regression.test.ts +251 -0
- package/src/parser.test.ts +1359 -0
- package/src/parser.ts +1477 -0
- package/src/prompt-composer.test.ts +219 -0
- package/src/prompt-composer.ts +231 -0
- package/src/relay-pty-orchestrator.test.ts +1027 -0
- package/src/relay-pty-orchestrator.ts +2270 -0
- package/src/shared.test.ts +221 -0
- package/src/shared.ts +454 -0
- package/src/stuck-detector.test.ts +303 -0
- package/src/stuck-detector.ts +511 -0
- package/src/tmux-resolver.test.ts +104 -0
- package/src/tmux-resolver.ts +207 -0
- package/src/tmux-wrapper.test.ts +316 -0
- package/src/tmux-wrapper.ts +2010 -0
- package/src/trajectory-detection.test.ts +151 -0
- package/src/trajectory-integration.ts +1261 -0
- package/src/wrapper-types.ts +45 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex CLI output fixtures for parser regression testing.
|
|
3
|
+
*
|
|
4
|
+
* Codex (OpenAI) has its own output formatting patterns.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { OutputFixture } from './claude-outputs.js';
|
|
8
|
+
|
|
9
|
+
export const codexOutputFixtures: OutputFixture[] = [
|
|
10
|
+
// =====================================================================
|
|
11
|
+
// Basic Codex output
|
|
12
|
+
// =====================================================================
|
|
13
|
+
{
|
|
14
|
+
name: 'codex-basic-relay',
|
|
15
|
+
description: 'Basic relay command from Codex',
|
|
16
|
+
input: `I'll help you with that task.
|
|
17
|
+
|
|
18
|
+
->relay:Lead Task analysis complete. Ready for review.
|
|
19
|
+
|
|
20
|
+
Let me know if you need anything else.
|
|
21
|
+
`,
|
|
22
|
+
expectedCommands: [
|
|
23
|
+
{ to: 'Lead', body: 'Task analysis complete. Ready for review.' },
|
|
24
|
+
],
|
|
25
|
+
expectedOutputContains: ['help you with that task', 'Let me know'],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'codex-with-code-output',
|
|
29
|
+
description: 'Codex output with code blocks',
|
|
30
|
+
input: `Here's the fix:
|
|
31
|
+
|
|
32
|
+
\`\`\`python
|
|
33
|
+
def authenticate(user, password):
|
|
34
|
+
# ->relay:Agent This is in code, ignore
|
|
35
|
+
return check_credentials(user, password)
|
|
36
|
+
\`\`\`
|
|
37
|
+
|
|
38
|
+
->relay:Lead Code fix implemented. Please review.
|
|
39
|
+
`,
|
|
40
|
+
expectedCommands: [
|
|
41
|
+
{ to: 'Lead', body: 'Code fix implemented. Please review.' },
|
|
42
|
+
],
|
|
43
|
+
expectedOutputContains: ['->relay:Agent This is in code'],
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// =====================================================================
|
|
47
|
+
// Codex multi-line messages
|
|
48
|
+
// =====================================================================
|
|
49
|
+
{
|
|
50
|
+
name: 'codex-fenced-message',
|
|
51
|
+
description: 'Codex sending fenced multi-line message',
|
|
52
|
+
input: `->relay:Lead <<<
|
|
53
|
+
Code review complete. Issues found:
|
|
54
|
+
|
|
55
|
+
1. Missing error handling in auth.py:45
|
|
56
|
+
2. SQL injection risk in query.py:120
|
|
57
|
+
3. Unused import in utils.py:3
|
|
58
|
+
|
|
59
|
+
Priority: Fix items 1 and 2 immediately.
|
|
60
|
+
>>>
|
|
61
|
+
|
|
62
|
+
I've documented all issues above.
|
|
63
|
+
`,
|
|
64
|
+
expectedCommands: [
|
|
65
|
+
{
|
|
66
|
+
to: 'Lead',
|
|
67
|
+
body: `Code review complete. Issues found:
|
|
68
|
+
|
|
69
|
+
1. Missing error handling in auth.py:45
|
|
70
|
+
2. SQL injection risk in query.py:120
|
|
71
|
+
3. Unused import in utils.py:3
|
|
72
|
+
|
|
73
|
+
Priority: Fix items 1 and 2 immediately.`,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
// =====================================================================
|
|
79
|
+
// Codex with tool use
|
|
80
|
+
// =====================================================================
|
|
81
|
+
{
|
|
82
|
+
name: 'codex-after-tool-use',
|
|
83
|
+
description: 'Relay command after Codex tool execution',
|
|
84
|
+
input: `Running: git status
|
|
85
|
+
|
|
86
|
+
On branch main
|
|
87
|
+
Changes not staged for commit:
|
|
88
|
+
modified: src/auth.ts
|
|
89
|
+
|
|
90
|
+
->relay:Lead Git status shows 1 modified file: src/auth.ts
|
|
91
|
+
`,
|
|
92
|
+
expectedCommands: [
|
|
93
|
+
{ to: 'Lead', body: 'Git status shows 1 modified file: src/auth.ts' },
|
|
94
|
+
],
|
|
95
|
+
expectedOutputContains: ['Running: git status', 'On branch main'],
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
export default codexOutputFixtures;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini CLI output fixtures for parser regression testing.
|
|
3
|
+
*
|
|
4
|
+
* Gemini has unique characteristics:
|
|
5
|
+
* - Uses sparkle character (✦) as output prefix
|
|
6
|
+
* - Can drop into shell mode ($ prompt)
|
|
7
|
+
* - Has specific keyword interpretation issues
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { OutputFixture } from './claude-outputs.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Gemini-specific characters and patterns
|
|
14
|
+
*/
|
|
15
|
+
export const GEMINI = {
|
|
16
|
+
SPARKLE: '✦',
|
|
17
|
+
SHELL_PROMPT: '$',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const geminiOutputFixtures: OutputFixture[] = [
|
|
21
|
+
// =====================================================================
|
|
22
|
+
// Sparkle prefix handling
|
|
23
|
+
// =====================================================================
|
|
24
|
+
{
|
|
25
|
+
name: 'gemini-sparkle-prefix',
|
|
26
|
+
description: 'Relay command with Gemini sparkle prefix',
|
|
27
|
+
input: `${GEMINI.SPARKLE} ->relay:Lead STATUS: Ready for task
|
|
28
|
+
`,
|
|
29
|
+
expectedCommands: [
|
|
30
|
+
{ to: 'Lead', body: 'STATUS: Ready for task' },
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'gemini-sparkle-with-space',
|
|
35
|
+
description: 'Sparkle with extra spacing',
|
|
36
|
+
input: `${GEMINI.SPARKLE} ->relay:Lead Message with extra space
|
|
37
|
+
`,
|
|
38
|
+
expectedCommands: [
|
|
39
|
+
{ to: 'Lead', body: 'Message with extra space' },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'gemini-multiple-sparkle-lines',
|
|
44
|
+
description: 'Multiple lines with sparkle prefix',
|
|
45
|
+
input: `${GEMINI.SPARKLE} Processing your request...
|
|
46
|
+
${GEMINI.SPARKLE} ->relay:Lead Task complete
|
|
47
|
+
${GEMINI.SPARKLE} Ready for next task.
|
|
48
|
+
`,
|
|
49
|
+
expectedCommands: [
|
|
50
|
+
{ to: 'Lead', body: 'Task complete' },
|
|
51
|
+
],
|
|
52
|
+
expectedOutputContains: ['Processing your request', 'Ready for next task'],
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
// =====================================================================
|
|
56
|
+
// Shell mode detection
|
|
57
|
+
// =====================================================================
|
|
58
|
+
{
|
|
59
|
+
name: 'gemini-shell-mode-output',
|
|
60
|
+
description: 'Output that includes shell prompt',
|
|
61
|
+
input: `${GEMINI.SPARKLE} Let me run that command for you.
|
|
62
|
+
$ ls -la
|
|
63
|
+
total 48
|
|
64
|
+
drwxr-xr-x 5 user user 4096 Jan 23 10:00 .
|
|
65
|
+
${GEMINI.SPARKLE} ->relay:Lead Command executed successfully
|
|
66
|
+
`,
|
|
67
|
+
expectedCommands: [
|
|
68
|
+
{ to: 'Lead', body: 'Command executed successfully' },
|
|
69
|
+
],
|
|
70
|
+
expectedOutputContains: ['$ ls -la', 'total 48'],
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
// =====================================================================
|
|
74
|
+
// Gemini-specific edge cases
|
|
75
|
+
// =====================================================================
|
|
76
|
+
{
|
|
77
|
+
name: 'gemini-fenced-with-sparkle',
|
|
78
|
+
description: 'Fenced message with sparkle prefix',
|
|
79
|
+
input: `${GEMINI.SPARKLE} ->relay:Lead <<<
|
|
80
|
+
Here's my detailed analysis:
|
|
81
|
+
|
|
82
|
+
The issue is in the authentication flow.
|
|
83
|
+
Consider these changes:
|
|
84
|
+
1. Update token validation
|
|
85
|
+
2. Add refresh logic
|
|
86
|
+
>>>
|
|
87
|
+
`,
|
|
88
|
+
expectedCommands: [
|
|
89
|
+
{
|
|
90
|
+
to: 'Lead',
|
|
91
|
+
body: `Here's my detailed analysis:
|
|
92
|
+
|
|
93
|
+
The issue is in the authentication flow.
|
|
94
|
+
Consider these changes:
|
|
95
|
+
1. Update token validation
|
|
96
|
+
2. Add refresh logic`,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'gemini-mixed-output',
|
|
102
|
+
description: 'Mix of sparkle and non-sparkle output',
|
|
103
|
+
input: `Processing...
|
|
104
|
+
${GEMINI.SPARKLE} Analyzing the codebase
|
|
105
|
+
${GEMINI.SPARKLE} Found 3 issues
|
|
106
|
+
->relay:Lead Analysis complete with 3 issues found
|
|
107
|
+
${GEMINI.SPARKLE} Done!
|
|
108
|
+
`,
|
|
109
|
+
expectedCommands: [
|
|
110
|
+
{ to: 'Lead', body: 'Analysis complete with 3 issues found' },
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
// =====================================================================
|
|
115
|
+
// Complex Gemini scenarios
|
|
116
|
+
// =====================================================================
|
|
117
|
+
{
|
|
118
|
+
name: 'gemini-code-execution-output',
|
|
119
|
+
description: 'Gemini executing code with relay after',
|
|
120
|
+
input: `${GEMINI.SPARKLE} I'll run the tests for you.
|
|
121
|
+
|
|
122
|
+
$ npm test
|
|
123
|
+
|
|
124
|
+
> project@1.0.0 test
|
|
125
|
+
> vitest run
|
|
126
|
+
|
|
127
|
+
✓ src/auth.test.ts (5 tests)
|
|
128
|
+
✓ src/api.test.ts (12 tests)
|
|
129
|
+
|
|
130
|
+
Test Files 2 passed
|
|
131
|
+
Tests 17 passed
|
|
132
|
+
|
|
133
|
+
${GEMINI.SPARKLE} ->relay:Lead Tests passed: 17/17. Build is green.
|
|
134
|
+
`,
|
|
135
|
+
expectedCommands: [
|
|
136
|
+
{ to: 'Lead', body: 'Tests passed: 17/17. Build is green.' },
|
|
137
|
+
],
|
|
138
|
+
expectedOutputContains: ['npm test', '17 passed'],
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'gemini-broadcast',
|
|
142
|
+
description: 'Gemini sending broadcast message',
|
|
143
|
+
input: `${GEMINI.SPARKLE} ->relay:* All agents: deployment starting in 5 minutes
|
|
144
|
+
`,
|
|
145
|
+
expectedCommands: [
|
|
146
|
+
{ to: '*', body: 'All agents: deployment starting in 5 minutes' },
|
|
147
|
+
],
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
export default geminiOutputFixtures;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser regression test fixtures
|
|
3
|
+
*
|
|
4
|
+
* This module exports CLI output fixtures for testing the parser
|
|
5
|
+
* against real-world terminal output patterns.
|
|
6
|
+
*
|
|
7
|
+
* To add new fixtures:
|
|
8
|
+
* 1. Capture the problematic output from a real CLI session
|
|
9
|
+
* 2. Add it to the appropriate CLI-specific fixture file
|
|
10
|
+
* 3. Run tests to ensure parser handles it correctly
|
|
11
|
+
*
|
|
12
|
+
* When a parser bug is fixed, add a regression test fixture
|
|
13
|
+
* to prevent the bug from recurring.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export { claudeOutputFixtures, type OutputFixture, ANSI } from './claude-outputs.js';
|
|
17
|
+
export { geminiOutputFixtures, GEMINI } from './gemini-outputs.js';
|
|
18
|
+
export { codexOutputFixtures } from './codex-outputs.js';
|
|
19
|
+
|
|
20
|
+
import { claudeOutputFixtures } from './claude-outputs.js';
|
|
21
|
+
import { geminiOutputFixtures } from './gemini-outputs.js';
|
|
22
|
+
import { codexOutputFixtures } from './codex-outputs.js';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* All fixtures combined for comprehensive testing
|
|
26
|
+
*/
|
|
27
|
+
export const allFixtures = [
|
|
28
|
+
...claudeOutputFixtures.map(f => ({ ...f, cli: 'claude' as const })),
|
|
29
|
+
...geminiOutputFixtures.map(f => ({ ...f, cli: 'gemini' as const })),
|
|
30
|
+
...codexOutputFixtures.map(f => ({ ...f, cli: 'codex' as const })),
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get fixtures by CLI type
|
|
35
|
+
*/
|
|
36
|
+
export function getFixturesByCli(cli: 'claude' | 'gemini' | 'codex') {
|
|
37
|
+
switch (cli) {
|
|
38
|
+
case 'claude':
|
|
39
|
+
return claudeOutputFixtures;
|
|
40
|
+
case 'gemini':
|
|
41
|
+
return geminiOutputFixtures;
|
|
42
|
+
case 'codex':
|
|
43
|
+
return codexOutputFixtures;
|
|
44
|
+
default:
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Revocation Detection
|
|
3
|
+
*
|
|
4
|
+
* Detects when an AI CLI's authentication has been revoked.
|
|
5
|
+
* This can happen when:
|
|
6
|
+
* 1. User authenticates the same provider elsewhere (limited sessions)
|
|
7
|
+
* 2. Token expires or is invalidated
|
|
8
|
+
* 3. OAuth refresh fails
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Patterns that indicate authentication has been revoked or expired.
|
|
13
|
+
* These are typically output by Claude CLI, Codex, etc. when auth fails.
|
|
14
|
+
*/
|
|
15
|
+
export const AUTH_REVOCATION_PATTERNS: RegExp[] = [
|
|
16
|
+
// Session/token expiration
|
|
17
|
+
/session\s+(has\s+)?expired/i,
|
|
18
|
+
/token\s+(has\s+)?expired/i,
|
|
19
|
+
/credentials?\s+(have\s+)?expired/i,
|
|
20
|
+
|
|
21
|
+
// Login required
|
|
22
|
+
/please\s+log\s*in\s+again/i,
|
|
23
|
+
/login\s+required/i,
|
|
24
|
+
/authentication\s+required/i,
|
|
25
|
+
/must\s+(be\s+)?log(ged)?\s*in/i,
|
|
26
|
+
/you\s+need\s+to\s+log\s*in/i,
|
|
27
|
+
|
|
28
|
+
// Unauthorized
|
|
29
|
+
/\bunauthorized\b/i,
|
|
30
|
+
/not\s+authorized/i,
|
|
31
|
+
/access\s+denied/i,
|
|
32
|
+
|
|
33
|
+
// Invalid credentials
|
|
34
|
+
/invalid\s+credentials?/i,
|
|
35
|
+
/invalid\s+token/i,
|
|
36
|
+
/invalid\s+session/i,
|
|
37
|
+
/not\s+authenticated/i,
|
|
38
|
+
|
|
39
|
+
// OAuth specific
|
|
40
|
+
/oauth\s+error.*401/i,
|
|
41
|
+
/oauth\s+error.*403/i,
|
|
42
|
+
/oauth\s+token\s+(has\s+)?expired/i,
|
|
43
|
+
/refresh\s+token\s+(is\s+)?invalid/i,
|
|
44
|
+
/failed\s+to\s+refresh/i,
|
|
45
|
+
/please\s+obtain\s+a\s+new\s+token/i,
|
|
46
|
+
|
|
47
|
+
// API errors that indicate auth issues
|
|
48
|
+
/api\s+error.*401/i,
|
|
49
|
+
/api\s+error.*403/i,
|
|
50
|
+
/http\s+401/i,
|
|
51
|
+
/http\s+403/i,
|
|
52
|
+
|
|
53
|
+
// Claude-specific patterns
|
|
54
|
+
/your\s+api\s+key\s+is\s+invalid/i,
|
|
55
|
+
/api\s+key\s+not\s+found/i,
|
|
56
|
+
/signed\s+out/i,
|
|
57
|
+
/session\s+revoked/i,
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Patterns that should NOT trigger auth revocation detection.
|
|
62
|
+
* These are false positives that might match auth patterns but aren't actual auth errors.
|
|
63
|
+
*/
|
|
64
|
+
export const AUTH_FALSE_POSITIVE_PATTERNS: RegExp[] = [
|
|
65
|
+
// Documentation or help text
|
|
66
|
+
/how\s+to\s+log\s*in/i,
|
|
67
|
+
/login\s+instructions/i,
|
|
68
|
+
/authentication\s+guide/i,
|
|
69
|
+
|
|
70
|
+
// Code comments or strings
|
|
71
|
+
/\/\/.*unauthorized/i,
|
|
72
|
+
/\/\*.*unauthorized.*\*\//i,
|
|
73
|
+
/".*unauthorized.*"/i,
|
|
74
|
+
/'.*unauthorized.*'/i,
|
|
75
|
+
|
|
76
|
+
// Error handling code
|
|
77
|
+
/catch.*unauthorized/i,
|
|
78
|
+
/handle.*auth.*error/i,
|
|
79
|
+
/if.*session.*expired/i,
|
|
80
|
+
|
|
81
|
+
// Instructional content
|
|
82
|
+
/you\s+should\s+log\s*in/i,
|
|
83
|
+
/make\s+sure\s+you('re)?\s+logged\s*in/i,
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
export interface AuthRevocationResult {
|
|
87
|
+
detected: boolean;
|
|
88
|
+
pattern?: string;
|
|
89
|
+
confidence: 'high' | 'medium' | 'low';
|
|
90
|
+
message?: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Detect if output indicates authentication has been revoked.
|
|
95
|
+
*
|
|
96
|
+
* @param output - The CLI output to analyze
|
|
97
|
+
* @param recentOutputOnly - If true, only check the last ~500 chars (for real-time detection)
|
|
98
|
+
* @returns Detection result with confidence level
|
|
99
|
+
*/
|
|
100
|
+
export function detectAuthRevocation(
|
|
101
|
+
output: string,
|
|
102
|
+
recentOutputOnly = false
|
|
103
|
+
): AuthRevocationResult {
|
|
104
|
+
// If checking recent output only, truncate to last 500 chars
|
|
105
|
+
const textToCheck = recentOutputOnly ? output.slice(-500) : output;
|
|
106
|
+
|
|
107
|
+
// First check for false positives
|
|
108
|
+
for (const falsePositive of AUTH_FALSE_POSITIVE_PATTERNS) {
|
|
109
|
+
if (falsePositive.test(textToCheck)) {
|
|
110
|
+
return { detected: false, confidence: 'low' };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Check each auth revocation pattern
|
|
115
|
+
for (const pattern of AUTH_REVOCATION_PATTERNS) {
|
|
116
|
+
const match = textToCheck.match(pattern);
|
|
117
|
+
if (match) {
|
|
118
|
+
// Determine confidence based on pattern specificity
|
|
119
|
+
const confidence = getConfidenceLevel(pattern, match[0]);
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
detected: true,
|
|
123
|
+
pattern: pattern.source,
|
|
124
|
+
confidence,
|
|
125
|
+
message: match[0],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return { detected: false, confidence: 'low' };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Determine confidence level based on the matched pattern.
|
|
135
|
+
*/
|
|
136
|
+
function getConfidenceLevel(
|
|
137
|
+
pattern: RegExp,
|
|
138
|
+
_matchedText: string
|
|
139
|
+
): 'high' | 'medium' | 'low' {
|
|
140
|
+
const patternStr = pattern.source.toLowerCase();
|
|
141
|
+
|
|
142
|
+
// High confidence: Explicit auth failure messages
|
|
143
|
+
if (
|
|
144
|
+
patternStr.includes('session') && patternStr.includes('expired') ||
|
|
145
|
+
patternStr.includes('please') && patternStr.includes('log') ||
|
|
146
|
+
patternStr.includes('authentication required') ||
|
|
147
|
+
patternStr.includes('token') && patternStr.includes('expired') ||
|
|
148
|
+
patternStr.includes('oauth') && patternStr.includes('expired') ||
|
|
149
|
+
patternStr.includes('authentication_error') ||
|
|
150
|
+
patternStr.includes('signed out') ||
|
|
151
|
+
patternStr.includes('session revoked') ||
|
|
152
|
+
patternStr.includes('obtain') && patternStr.includes('token')
|
|
153
|
+
) {
|
|
154
|
+
return 'high';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Medium confidence: General auth errors
|
|
158
|
+
if (
|
|
159
|
+
patternStr.includes('unauthorized') ||
|
|
160
|
+
patternStr.includes('401') ||
|
|
161
|
+
patternStr.includes('403') ||
|
|
162
|
+
patternStr.includes('invalid') && patternStr.includes('credentials')
|
|
163
|
+
) {
|
|
164
|
+
return 'medium';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Low confidence: Could be related to other errors
|
|
168
|
+
return 'low';
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Check if the given text looks like an auth-related CLI prompt
|
|
173
|
+
* that's waiting for user action (not an error, but a request to auth).
|
|
174
|
+
*/
|
|
175
|
+
export function isAuthPrompt(text: string): boolean {
|
|
176
|
+
const authPromptPatterns = [
|
|
177
|
+
/open\s+this\s+url/i,
|
|
178
|
+
/visit\s+.*to\s+authorize/i,
|
|
179
|
+
/enter\s+your\s+api\s+key/i,
|
|
180
|
+
/paste\s+your\s+token/i,
|
|
181
|
+
/waiting\s+for\s+authorization/i,
|
|
182
|
+
/complete\s+login\s+in\s+browser/i,
|
|
183
|
+
];
|
|
184
|
+
|
|
185
|
+
return authPromptPatterns.some(pattern => pattern.test(text));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Provider-specific auth detection configuration.
|
|
190
|
+
* Different AI CLIs may have different error messages.
|
|
191
|
+
*/
|
|
192
|
+
export const PROVIDER_AUTH_PATTERNS: Record<string, RegExp[]> = {
|
|
193
|
+
claude: [
|
|
194
|
+
/claude.*session.*expired/i,
|
|
195
|
+
/anthropic.*unauthorized/i,
|
|
196
|
+
/claude.*not\s+authenticated/i,
|
|
197
|
+
/please\s+run\s+claude\s+login/i,
|
|
198
|
+
/please\s+run\s+\/login/i,
|
|
199
|
+
/authentication_error/i,
|
|
200
|
+
],
|
|
201
|
+
codex: [
|
|
202
|
+
/codex.*session.*expired/i,
|
|
203
|
+
/openai.*unauthorized/i,
|
|
204
|
+
/codex.*not\s+authenticated/i,
|
|
205
|
+
],
|
|
206
|
+
gemini: [
|
|
207
|
+
/gemini.*session.*expired/i,
|
|
208
|
+
/google.*unauthorized/i,
|
|
209
|
+
/gemini.*not\s+authenticated/i,
|
|
210
|
+
],
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Detect auth revocation for a specific provider.
|
|
215
|
+
* Uses provider-specific patterns in addition to general patterns.
|
|
216
|
+
*/
|
|
217
|
+
export function detectProviderAuthRevocation(
|
|
218
|
+
output: string,
|
|
219
|
+
provider: string
|
|
220
|
+
): AuthRevocationResult {
|
|
221
|
+
// First check general patterns
|
|
222
|
+
const generalResult = detectAuthRevocation(output, true);
|
|
223
|
+
if (generalResult.detected && generalResult.confidence === 'high') {
|
|
224
|
+
return generalResult;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Check provider-specific patterns
|
|
228
|
+
const providerPatterns = PROVIDER_AUTH_PATTERNS[provider.toLowerCase()];
|
|
229
|
+
if (providerPatterns) {
|
|
230
|
+
for (const pattern of providerPatterns) {
|
|
231
|
+
const match = output.match(pattern);
|
|
232
|
+
if (match) {
|
|
233
|
+
return {
|
|
234
|
+
detected: true,
|
|
235
|
+
pattern: pattern.source,
|
|
236
|
+
confidence: 'high',
|
|
237
|
+
message: match[0],
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return generalResult;
|
|
244
|
+
}
|