qaos 0.0.1 → 0.0.2

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.
Files changed (81) hide show
  1. package/dist/app.d.ts +2 -1
  2. package/dist/app.js +253 -3
  3. package/dist/cli.js +96 -24
  4. package/dist/commands/auth.d.ts +41 -0
  5. package/dist/commands/auth.js +137 -0
  6. package/dist/commands/hist.d.ts +21 -0
  7. package/dist/commands/hist.js +41 -0
  8. package/dist/components/auth/auth-ui.d.ts +8 -0
  9. package/dist/components/auth/auth-ui.js +47 -0
  10. package/dist/components/auth/loading-spinner.d.ts +2 -0
  11. package/dist/components/auth/loading-spinner.js +15 -0
  12. package/dist/components/hist/hist-ui.d.ts +7 -0
  13. package/dist/components/hist/hist-ui.js +139 -0
  14. package/dist/components/landing/splash.js +7 -4
  15. package/dist/components/run/run-ui.d.ts +11 -0
  16. package/dist/components/run/run-ui.js +298 -0
  17. package/dist/components/run/spinner.d.ts +6 -0
  18. package/dist/components/run/spinner.js +15 -0
  19. package/dist/components/run/thinking-animation.d.ts +6 -0
  20. package/dist/components/run/thinking-animation.js +16 -0
  21. package/dist/components/settings/settings-ui.d.ts +5 -0
  22. package/dist/components/settings/settings-ui.js +52 -0
  23. package/dist/config.d.ts +2 -0
  24. package/dist/config.js +22 -0
  25. package/dist/constants/colors.d.ts +8 -0
  26. package/dist/constants/colors.js +8 -0
  27. package/dist/constants/commands.d.ts +1 -0
  28. package/dist/constants/commands.js +1 -0
  29. package/dist/constants/help-instructions.d.ts +4 -0
  30. package/dist/constants/help-instructions.js +126 -0
  31. package/dist/constants/ws-enums.d.ts +7 -0
  32. package/dist/constants/ws-enums.js +8 -0
  33. package/dist/contexts/language-context.d.ts +13 -0
  34. package/dist/contexts/language-context.js +39 -0
  35. package/dist/dev-config.d.ts +3 -0
  36. package/dist/dev-config.js +3 -0
  37. package/dist/entry-functions.d.ts +26 -0
  38. package/dist/entry-functions.js +357 -0
  39. package/dist/i18n/language-service.d.ts +18 -0
  40. package/dist/i18n/language-service.js +34 -0
  41. package/dist/i18n/locales/en.json +176 -0
  42. package/dist/i18n/locales/fr.json +176 -0
  43. package/dist/prod-config.d.ts +3 -0
  44. package/dist/prod-config.js +3 -0
  45. package/dist/services/auth-server.d.ts +17 -0
  46. package/dist/services/auth-server.js +109 -0
  47. package/dist/services/auth-service.d.ts +68 -0
  48. package/dist/services/auth-service.js +194 -0
  49. package/dist/services/browser-action-service.d.ts +212 -0
  50. package/dist/services/browser-action-service.js +655 -0
  51. package/dist/services/browser-service.d.ts +116 -0
  52. package/dist/services/browser-service.js +368 -0
  53. package/dist/services/communication-service.d.ts +35 -0
  54. package/dist/services/communication-service.js +197 -0
  55. package/dist/services/dom-extraction-service.d.ts +89 -0
  56. package/dist/services/dom-extraction-service.js +472 -0
  57. package/dist/services/dom-views.d.ts +144 -0
  58. package/dist/services/dom-views.js +899 -0
  59. package/dist/services/socket-service.d.ts +43 -0
  60. package/dist/services/socket-service.js +236 -0
  61. package/dist/services/state-service.d.ts +26 -0
  62. package/dist/services/state-service.js +344 -0
  63. package/dist/types/app-props.d.ts +17 -0
  64. package/dist/types/app-props.js +1 -0
  65. package/dist/types/config-data.d.ts +3 -0
  66. package/dist/types/config-data.js +1 -0
  67. package/dist/types/initialize-connection-response.d.ts +4 -0
  68. package/dist/types/initialize-connection-response.js +1 -0
  69. package/dist/types/next-actions-response.d.ts +22 -0
  70. package/dist/types/next-actions-response.js +1 -0
  71. package/dist/types/run-args.d.ts +6 -0
  72. package/dist/types/run-args.js +1 -0
  73. package/dist/types/run-ui-state.d.ts +54 -0
  74. package/dist/types/run-ui-state.js +1 -0
  75. package/dist/types/send-new-state-request.d.ts +13 -0
  76. package/dist/types/send-new-state-request.js +1 -0
  77. package/dist/utils/config-validator.d.ts +8 -0
  78. package/dist/utils/config-validator.js +88 -0
  79. package/dist/utils/logger.d.ts +1 -0
  80. package/dist/utils/logger.js +6 -0
  81. package/package.json +9 -4
package/dist/app.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  import React from 'react';
2
- export default function App(): React.JSX.Element;
2
+ import { AppProps } from './types/app-props.js';
3
+ export default function App(args: AppProps): React.JSX.Element;
package/dist/app.js CHANGED
@@ -1,5 +1,255 @@
1
- import React from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
2
  import Splash from './components/landing/splash.js';
3
- export default function App() {
4
- return React.createElement(Splash, null);
3
+ import { Text } from 'ink';
4
+ import { run, runWorkflow, getActions } from './entry-functions.js';
5
+ import RunUi, { RunConnecting, RunError } from './components/run/run-ui.js';
6
+ import SettingsUI from './components/settings/settings-ui.js';
7
+ import { useLanguage } from './contexts/language-context.js';
8
+ import AuthUI from './components/auth/auth-ui.js';
9
+ import HistUI from './components/hist/hist-ui.js';
10
+ import { authenticate, logout, getAuthStatus } from './commands/auth.js';
11
+ // Exit delay constants (in milliseconds)
12
+ const EXIT_DELAY_SUCCESS = 1000;
13
+ const EXIT_DELAY_INFO = 1500;
14
+ const EXIT_DELAY_ERROR = 2000;
15
+ export default function App(args) {
16
+ const { input: command, flags } = args.args;
17
+ const { t } = useLanguage();
18
+ const [runResult, setRunResult] = useState(null);
19
+ const [loading, setLoading] = useState(false);
20
+ const [runUiState, setRunUiState] = useState(null);
21
+ const [authStatus, setAuthStatus] = useState('idle');
22
+ const [authMessage, setAuthMessage] = useState('');
23
+ const [authError, setAuthError] = useState('');
24
+ const createInitialRunUiState = () => ({
25
+ phase: 'idle',
26
+ connection: 'connecting',
27
+ thinking: '',
28
+ tasks: [],
29
+ actions: [],
30
+ });
31
+ const reduceRunUiState = (prevState, event) => {
32
+ const baseState = prevState ?? createInitialRunUiState();
33
+ switch (event.type) {
34
+ case 'config_loaded':
35
+ return {
36
+ ...baseState,
37
+ tasks: event.tasks,
38
+ };
39
+ case 'connection_status':
40
+ return {
41
+ ...baseState,
42
+ connection: event.status,
43
+ phase: event.status === 'connected'
44
+ ? 'connected'
45
+ : baseState.phase === 'idle'
46
+ ? 'connecting'
47
+ : baseState.phase,
48
+ };
49
+ case 'run_started':
50
+ return {
51
+ ...baseState,
52
+ phase: 'running',
53
+ runId: event.runId,
54
+ reportUrl: event.reportUrl,
55
+ errorMessage: undefined,
56
+ };
57
+ case 'run_completed':
58
+ return {
59
+ ...baseState,
60
+ phase: 'completed',
61
+ };
62
+ case 'server_state': {
63
+ const taskStatusMap = event.taskStatus ?? {};
64
+ const updatedTasks = baseState.tasks.length > 0
65
+ ? baseState.tasks.map(task => ({
66
+ ...task,
67
+ status: taskStatusMap[task.id] ?? task.status,
68
+ }))
69
+ : Object.keys(taskStatusMap).map(taskId => ({
70
+ id: taskId,
71
+ description: `Task ${taskId}`,
72
+ status: taskStatusMap[taskId],
73
+ }));
74
+ return {
75
+ ...baseState,
76
+ thinking: event.thinking,
77
+ tasks: updatedTasks,
78
+ actions: event.actions ?? baseState.actions,
79
+ qaosProgress: event.qaosProgress ?? baseState.qaosProgress,
80
+ };
81
+ }
82
+ case 'error':
83
+ return {
84
+ ...baseState,
85
+ phase: 'error',
86
+ errorMessage: event.message,
87
+ };
88
+ default:
89
+ return baseState;
90
+ }
91
+ };
92
+ useEffect(() => {
93
+ if (command === 'run') {
94
+ setRunUiState(createInitialRunUiState());
95
+ run({
96
+ configFilePath: flags.config || './qaos-config.json',
97
+ headed: flags.headed,
98
+ onUiEvent: event => {
99
+ setRunUiState(prevState => reduceRunUiState(prevState, event));
100
+ },
101
+ }, t)
102
+ .then(res => {
103
+ setRunResult(res);
104
+ })
105
+ .catch(err => {
106
+ setRunResult({ error: err.message });
107
+ setRunUiState(prevState => reduceRunUiState(prevState, {
108
+ type: 'error',
109
+ message: err.message,
110
+ }));
111
+ });
112
+ }
113
+ if (command === 'workflow') {
114
+ setLoading(true);
115
+ const actions = getActions();
116
+ runWorkflow(actions, t)
117
+ .then(res => {
118
+ setRunResult(res);
119
+ setLoading(false);
120
+ })
121
+ .catch(err => {
122
+ setRunResult({ error: err.message });
123
+ setLoading(false);
124
+ });
125
+ }
126
+ if (command === 'auth') {
127
+ setAuthStatus('validating');
128
+ setAuthMessage(t('run.processingAuth'));
129
+ (async () => {
130
+ try {
131
+ // Handle logout
132
+ if (flags.logout) {
133
+ const result = await logout(t);
134
+ setAuthMessage(result.message);
135
+ setAuthStatus('success');
136
+ setTimeout(() => process.exit(0), EXIT_DELAY_SUCCESS);
137
+ return;
138
+ }
139
+ // Handle API token authentication
140
+ if (flags.api) {
141
+ const result = await authenticate({
142
+ method: 'api',
143
+ token: flags.api,
144
+ }, t);
145
+ setAuthMessage(result.message);
146
+ if (result.success) {
147
+ setAuthStatus('success');
148
+ setTimeout(() => process.exit(0), EXIT_DELAY_SUCCESS);
149
+ }
150
+ else {
151
+ setAuthStatus('error');
152
+ setAuthError(result.error || t('auth.unknownError'));
153
+ setTimeout(() => process.exit(1), EXIT_DELAY_ERROR);
154
+ }
155
+ return;
156
+ }
157
+ // Handle UI authentication (default when no flags provided and not authenticated)
158
+ const status = await getAuthStatus();
159
+ if (!status.authenticated) {
160
+ // User is not authenticated, initiate UI auth flow
161
+ setAuthStatus('validating');
162
+ setAuthMessage(t('run.openingBrowser'));
163
+ const result = await authenticate({
164
+ method: 'ui',
165
+ onProgress: (authStatus) => {
166
+ if (authStatus === 'opening_browser') {
167
+ setAuthMessage(t('run.openingBrowserShort'));
168
+ }
169
+ else if (authStatus === 'waiting_auth') {
170
+ setAuthMessage(t('run.waitingAuth'));
171
+ setAuthStatus('validating');
172
+ }
173
+ else if (authStatus === 'success') {
174
+ setAuthMessage(t('run.authComplete'));
175
+ setAuthStatus('success');
176
+ }
177
+ },
178
+ }, t);
179
+ setAuthMessage(result.message);
180
+ if (result.success) {
181
+ setAuthStatus('success');
182
+ setTimeout(() => process.exit(0), EXIT_DELAY_SUCCESS);
183
+ }
184
+ else {
185
+ setAuthStatus('error');
186
+ setAuthError(result.error || t('auth.unknownError'));
187
+ setTimeout(() => process.exit(1), EXIT_DELAY_ERROR);
188
+ }
189
+ }
190
+ else {
191
+ // User is already authenticated, show status
192
+ setAuthMessage(t('auth.alreadyLoggedIn'));
193
+ setAuthStatus('success');
194
+ setTimeout(() => process.exit(0), EXIT_DELAY_INFO);
195
+ }
196
+ }
197
+ catch (err) {
198
+ setAuthStatus('error');
199
+ setAuthMessage('Authentication failed');
200
+ setAuthError(err instanceof Error ? err.message : t('auth.unknownError'));
201
+ setTimeout(() => process.exit(1), EXIT_DELAY_ERROR);
202
+ }
203
+ })();
204
+ }
205
+ }, [command]);
206
+ switch (command) {
207
+ case undefined:
208
+ return React.createElement(Splash, null);
209
+ case 'run':
210
+ if (!runUiState)
211
+ return React.createElement(Text, null, t('run.preparing'));
212
+ if (runUiState.phase === 'error') {
213
+ return (React.createElement(RunError, { message: runUiState.errorMessage || t('auth.unknownError') }));
214
+ }
215
+ if (runUiState.phase === 'completed') {
216
+ // TODO : Antoine rendre ca mieux
217
+ setTimeout(() => {
218
+ process.exit(0);
219
+ }, EXIT_DELAY_SUCCESS);
220
+ return (React.createElement(React.Fragment, null,
221
+ React.createElement(Text, { color: "green", bold: true }, t('workflow.allTasksComplete')),
222
+ runUiState.reportUrl && (React.createElement(Text, null,
223
+ t('run.reportReady'),
224
+ ' ',
225
+ React.createElement(Text, { color: "cyan", bold: true }, `\x1b]8;;${runUiState.reportUrl}\x07${runUiState.reportUrl}\x1b]8;;\x07`))),
226
+ React.createElement(Text, { dimColor: true }, t('workflow.exiting'))));
227
+ }
228
+ if (runUiState.phase !== 'running') {
229
+ return React.createElement(RunConnecting, { uiState: runUiState });
230
+ }
231
+ return React.createElement(RunUi, { uiState: runUiState });
232
+ case 'workflow':
233
+ if (loading)
234
+ return React.createElement(Text, null, t('run.executing'));
235
+ if (!runResult)
236
+ return React.createElement(Text, null, t('run.noResult'));
237
+ if (runResult.error) {
238
+ return React.createElement(Text, { color: "red" },
239
+ "Error: ",
240
+ runResult.error);
241
+ }
242
+ // Workflow outputs to console, just show completion message
243
+ return (React.createElement(React.Fragment, null,
244
+ React.createElement(Text, { color: "green", bold: true }, t('run.complete')),
245
+ React.createElement(Text, null, t('run.checkConsole'))));
246
+ case 'hist':
247
+ return (React.createElement(HistUI, { limit: flags.limit, projectId: flags.project }));
248
+ case 'auth':
249
+ return (React.createElement(AuthUI, { status: authStatus, message: authMessage, errorMessage: authError }));
250
+ case 'settings':
251
+ return React.createElement(SettingsUI, { onExit: () => process.exit(0) });
252
+ default:
253
+ return React.createElement(Text, { color: "red" }, t('errors.unknownCommand', { command }));
254
+ }
5
255
  }
package/dist/cli.js CHANGED
@@ -1,25 +1,97 @@
1
1
  #!/usr/bin/env node
2
- import React from 'react';
3
- import { render } from 'ink';
4
- // import meow from 'meow';
5
- import App from './app.js';
6
- // const cli = meow(
7
- // `
8
- // Usage
9
- // $ qaos
10
- // Options
11
- // --name Your name
12
- // Examples
13
- // $ qaos --name=Jane
14
- // Hello, Jane
15
- // `,
16
- // {
17
- // importMeta: import.meta,
18
- // flags: {
19
- // name: {
20
- // type: 'string',
21
- // },
22
- // },
23
- // },
24
- // );
25
- render(React.createElement(App, null));
2
+ const originalEmitWarning = process.emitWarning.bind(process);
3
+ process.emitWarning = ((warning, ...args) => {
4
+ const warningMessage = typeof warning === 'string' ? warning : warning.message;
5
+ const warningTypeOutput = typeof args[0] === 'string' ? args[0] : undefined;
6
+ const warningType = typeof warning === 'string' ? warningTypeOutput : warning.name;
7
+ if (warningType === 'ExperimentalWarning' &&
8
+ warningMessage.includes('Importing JSON modules is an experimental feature')) {
9
+ return;
10
+ }
11
+ return originalEmitWarning(warning, ...args);
12
+ });
13
+ const [{ default: React }, { render, Text }, { default: meow }, { default: App }, { getHelpInstructions }, { validCommands }, { LanguageProvider }, { authService }, { createTranslator },] = await Promise.all([
14
+ import('react'),
15
+ import('ink'),
16
+ import('meow'),
17
+ import('./app.js'),
18
+ import('./constants/help-instructions.js'),
19
+ import('./constants/commands.js'),
20
+ import('./contexts/language-context.js'),
21
+ import('./services/auth-service.js'),
22
+ import('./i18n/language-service.js'),
23
+ ]);
24
+ // Load language preference synchronously for CLI initialization
25
+ const userLanguage = authService.getLanguageSync();
26
+ const t = createTranslator(userLanguage);
27
+ const cli = meow(getHelpInstructions(t), {
28
+ importMeta: import.meta,
29
+ flags: {
30
+ config: {
31
+ type: 'string',
32
+ alias: 'c',
33
+ description: 'Path to config file (default: ./qaos-config.json)',
34
+ },
35
+ headed: {
36
+ type: 'boolean',
37
+ alias: 'h',
38
+ description: 'Run browser in headed mode (default: headless)',
39
+ default: false,
40
+ },
41
+ api: {
42
+ type: 'string',
43
+ alias: 'a',
44
+ description: 'Authenticate with API token',
45
+ },
46
+ ui: {
47
+ type: 'boolean',
48
+ alias: 'u',
49
+ description: 'Authenticate with browser UI (OAuth)',
50
+ default: false,
51
+ },
52
+ logout: {
53
+ type: 'boolean',
54
+ description: 'Logout and remove credentials',
55
+ default: false,
56
+ },
57
+ settings: {
58
+ type: 'boolean',
59
+ alias: 's',
60
+ description: 'Open settings interface',
61
+ default: false,
62
+ },
63
+ limit: {
64
+ type: 'number',
65
+ alias: 'l',
66
+ description: 'Number of recent runs to display (default: 10, max: 50)',
67
+ },
68
+ project: {
69
+ type: 'string',
70
+ alias: 'p',
71
+ description: 'Filter runs by project ID',
72
+ },
73
+ },
74
+ });
75
+ const command = cli.input[0];
76
+ if (cli.input.length > 1 && !validCommands.includes(command)) {
77
+ render(React.createElement(LanguageProvider, null,
78
+ React.createElement(Text, { color: "red" },
79
+ "Error: Unknown command '$",
80
+ command,
81
+ "'. Use 'qaos --help' for details.")));
82
+ process.exit(1);
83
+ }
84
+ render(React.createElement(LanguageProvider, null,
85
+ React.createElement(App, { args: {
86
+ input: command,
87
+ flags: {
88
+ ...cli.flags,
89
+ api: cli.flags['api'],
90
+ ui: cli.flags['ui'],
91
+ logout: cli.flags['logout'],
92
+ settings: cli.flags['settings'],
93
+ limit: cli.flags['limit'],
94
+ project: cli.flags['project'],
95
+ },
96
+ } })));
97
+ export {};
@@ -0,0 +1,41 @@
1
+ type TranslationFunction = (key: string, params?: Record<string, string | number>) => string;
2
+ export interface AuthArgs {
3
+ method?: 'api' | 'ui';
4
+ token?: string;
5
+ onProgress?: (status: 'opening_browser' | 'waiting_auth' | 'success') => void;
6
+ }
7
+ /**
8
+ * Handle CLI authentication via browser UI
9
+ * Opens a browser to the auth page and captures the token from a local server
10
+ */
11
+ export declare function authenticateViaUI(websiteUrl: string, t: TranslationFunction, onProgress?: (status: 'opening_browser' | 'waiting_auth' | 'success') => void): Promise<{
12
+ success: boolean;
13
+ message: string;
14
+ token?: string;
15
+ error?: string;
16
+ }>;
17
+ /**
18
+ * Handle CLI authentication
19
+ * Supports both API token and UI-based authentication
20
+ */
21
+ export declare function authenticate(args: AuthArgs, t: TranslationFunction): Promise<{
22
+ success: boolean;
23
+ message: string;
24
+ error?: string;
25
+ token?: string;
26
+ }>;
27
+ /**
28
+ * Get current authentication status
29
+ */
30
+ export declare function getAuthStatus(): Promise<{
31
+ authenticated: boolean;
32
+ serverUrl?: string;
33
+ }>;
34
+ /**
35
+ * Clear authentication (logout)
36
+ */
37
+ export declare function logout(t: TranslationFunction): Promise<{
38
+ success: boolean;
39
+ message: string;
40
+ }>;
41
+ export {};
@@ -0,0 +1,137 @@
1
+ import { authService } from '../services/auth-service.js';
2
+ import { startAuthServer } from '../services/auth-server.js';
3
+ import open from 'open';
4
+ import { randomBytes } from 'crypto';
5
+ import { configPromise } from '../config.js';
6
+ /**
7
+ * Handle CLI authentication via browser UI
8
+ * Opens a browser to the auth page and captures the token from a local server
9
+ */
10
+ export async function authenticateViaUI(websiteUrl, t, onProgress) {
11
+ const callbackPort = 9999;
12
+ try {
13
+ // Start local server to receive auth callback
14
+ onProgress?.('opening_browser');
15
+ const allowedOrigin = new URL(websiteUrl).origin;
16
+ const callbackState = randomBytes(16).toString('hex');
17
+ const authServer = await startAuthServer(callbackPort, {
18
+ allowedOrigin,
19
+ callbackState,
20
+ });
21
+ const callbackUrl = authServer.getCallbackUrl();
22
+ // Build auth URL with callback
23
+ const authUrl = new URL('/cli-auth', websiteUrl);
24
+ authUrl.searchParams.set('callback_url', callbackUrl);
25
+ // Open browser
26
+ await open(authUrl.toString());
27
+ onProgress?.('waiting_auth');
28
+ // Wait for token from browser
29
+ const tokenData = await Promise.race([
30
+ authServer.waitForToken(),
31
+ new Promise((_, reject) => setTimeout(() => reject(new Error(t('auth.timeout'))), 5 * 60 * 1000)),
32
+ ]);
33
+ await authServer.close();
34
+ // Validate and save token
35
+ const validationResult = await authService.validateToken(tokenData.token, websiteUrl);
36
+ if (!validationResult.valid) {
37
+ return {
38
+ success: false,
39
+ message: t('auth.tokenValidationFailed'),
40
+ error: validationResult.error || t('auth.invalidToken'),
41
+ };
42
+ }
43
+ await authService.saveToken(tokenData.token, websiteUrl, validationResult.userId);
44
+ onProgress?.('success');
45
+ return {
46
+ success: true,
47
+ message: t('auth.successWithPath', {
48
+ path: authService.getAuthFilePath(),
49
+ }),
50
+ token: tokenData.token,
51
+ };
52
+ }
53
+ catch (error) {
54
+ return {
55
+ success: false,
56
+ message: 'Authentication failed',
57
+ error: error instanceof Error ? error.message : t('auth.unknownError'),
58
+ };
59
+ }
60
+ }
61
+ /**
62
+ * Handle CLI authentication
63
+ * Supports both API token and UI-based authentication
64
+ */
65
+ export async function authenticate(args, t) {
66
+ try {
67
+ const method = args.method || 'ui ';
68
+ const config = await configPromise;
69
+ const serverUrl = config.API_SERVER_URL;
70
+ const websiteUrl = config.WEBSITE_URL;
71
+ if (method === 'api') {
72
+ if (!args.token) {
73
+ return {
74
+ success: false,
75
+ message: t('auth.noTokenProvided'),
76
+ error: t('auth.usage'),
77
+ };
78
+ }
79
+ const validationResult = await authService.validateToken(args.token, serverUrl);
80
+ if (!validationResult.valid) {
81
+ return {
82
+ success: false,
83
+ message: t('auth.tokenValidationFailed'),
84
+ error: validationResult.error || t('auth.invalidToken'),
85
+ };
86
+ }
87
+ await authService.saveToken(args.token, serverUrl, validationResult.userId);
88
+ return {
89
+ success: true,
90
+ message: t('auth.successWithPath', {
91
+ path: authService.getAuthFilePath(),
92
+ }),
93
+ token: args.token,
94
+ };
95
+ }
96
+ else {
97
+ // UI method - open browser for authentication
98
+ return await authenticateViaUI(websiteUrl, t, args.onProgress);
99
+ }
100
+ }
101
+ catch (error) {
102
+ return {
103
+ success: false,
104
+ message: 'Authentication failed',
105
+ error: error instanceof Error ? error.message : t('auth.unknownError'),
106
+ };
107
+ }
108
+ }
109
+ /**
110
+ * Get current authentication status
111
+ */
112
+ export async function getAuthStatus() {
113
+ const isAuthenticated = await authService.isAuthenticated();
114
+ const serverUrl = await authService.getServerUrl();
115
+ return {
116
+ authenticated: isAuthenticated,
117
+ serverUrl: serverUrl || undefined,
118
+ };
119
+ }
120
+ /**
121
+ * Clear authentication (logout)
122
+ */
123
+ export async function logout(t) {
124
+ try {
125
+ await authService.clearAuth();
126
+ return {
127
+ success: true,
128
+ message: t('auth.logoutSuccess'),
129
+ };
130
+ }
131
+ catch (error) {
132
+ return {
133
+ success: false,
134
+ message: `Failed to logout: ${error instanceof Error ? error.message : t('auth.unknownError')}`,
135
+ };
136
+ }
137
+ }
@@ -0,0 +1,21 @@
1
+ export interface RunSummary {
2
+ id: string;
3
+ project_id: string;
4
+ title: string;
5
+ site_url: string;
6
+ status: string;
7
+ score: number | null;
8
+ problem_count: number;
9
+ test_count: number;
10
+ length_ms: number;
11
+ created_at: string;
12
+ }
13
+ export interface FetchRunsResult {
14
+ success: boolean;
15
+ runs?: RunSummary[];
16
+ error?: string;
17
+ }
18
+ export declare function fetchRuns(options: {
19
+ limit?: number;
20
+ projectId?: string;
21
+ }): Promise<FetchRunsResult>;
@@ -0,0 +1,41 @@
1
+ import { authService } from '../services/auth-service.js';
2
+ import { configPromise } from '../config.js';
3
+ export async function fetchRuns(options) {
4
+ const token = await authService.getAccessToken();
5
+ if (!token) {
6
+ return { success: false, error: 'not_authenticated' };
7
+ }
8
+ const defaultServerUrl = (await configPromise).API_SERVER_URL;
9
+ const serverUrl = (await authService.getServerUrl()) || defaultServerUrl;
10
+ const url = new URL('/api/cli/runs', serverUrl);
11
+ if (options.limit) {
12
+ url.searchParams.set('limit', String(options.limit));
13
+ }
14
+ if (options.projectId) {
15
+ url.searchParams.set('projectId', options.projectId);
16
+ }
17
+ try {
18
+ const response = await fetch(url.toString(), {
19
+ headers: {
20
+ Authorization: `Bearer ${token}`,
21
+ },
22
+ });
23
+ const data = (await response.json());
24
+ if (!response.ok) {
25
+ if (response.status === 404) {
26
+ return {
27
+ success: false,
28
+ error: `project_not_found:${options.projectId}`,
29
+ };
30
+ }
31
+ return { success: false, error: data.error || `HTTP ${response.status}` };
32
+ }
33
+ return { success: true, runs: data.runs || [] };
34
+ }
35
+ catch (error) {
36
+ return {
37
+ success: false,
38
+ error: `network_error:${error instanceof Error ? error.message : String(error)}`,
39
+ };
40
+ }
41
+ }
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ export type AuthStatus = 'idle' | 'validating' | 'success' | 'error';
3
+ export interface AuthUIProps {
4
+ status: AuthStatus;
5
+ message: string;
6
+ errorMessage?: string;
7
+ }
8
+ export default function AuthUI({ status, message, errorMessage }: AuthUIProps): React.JSX.Element;