mistagent 0.1.21 → 0.1.22

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.
@@ -1,7 +1,20 @@
1
- import type { AuthStatus, LoginResponse, UserInfo } from '../types/api.js';
1
+ import type { AuthStatus, UserInfo } from '../types/api.js';
2
2
  export declare const authApi: {
3
3
  getStatus(): Promise<AuthStatus>;
4
- login(username: string, password: string): Promise<LoginResponse>;
5
4
  getMe(): Promise<UserInfo>;
6
5
  };
6
+ /** Supabase 直连登录 — POST /auth/v1/token?grant_type=password */
7
+ export interface SupabaseLoginResult {
8
+ access_token: string;
9
+ refresh_token: string;
10
+ user: {
11
+ id: string;
12
+ email: string;
13
+ user_metadata: {
14
+ username?: string;
15
+ display_name?: string;
16
+ };
17
+ };
18
+ }
19
+ export declare function supabaseLogin(supabaseUrl: string, anonKey: string, email: string, password: string): Promise<SupabaseLoginResult>;
7
20
  //# sourceMappingURL=auth.d.ts.map
@@ -3,14 +3,24 @@ export const authApi = {
3
3
  getStatus() {
4
4
  return getClient().get('/api/v1/auth/status');
5
5
  },
6
- login(username, password) {
7
- return getClient().post('/api/v1/auth/login', {
8
- username,
9
- password,
10
- });
11
- },
12
6
  getMe() {
13
7
  return getClient().get('/api/v1/auth/me');
14
8
  },
15
9
  };
10
+ export async function supabaseLogin(supabaseUrl, anonKey, email, password) {
11
+ const res = await fetch(`${supabaseUrl}/auth/v1/token?grant_type=password`, {
12
+ method: 'POST',
13
+ headers: {
14
+ 'Content-Type': 'application/json',
15
+ 'apikey': anonKey,
16
+ },
17
+ body: JSON.stringify({ email, password }),
18
+ });
19
+ if (!res.ok) {
20
+ const body = await res.json().catch(() => null);
21
+ const msg = body?.error_description || body?.msg || 'Login failed';
22
+ throw new Error(msg);
23
+ }
24
+ return res.json();
25
+ }
16
26
  //# sourceMappingURL=auth.js.map
@@ -89,11 +89,9 @@ export class ApiClient {
89
89
  for (const [k, v] of Object.entries(params)) {
90
90
  url.searchParams.set(k, v);
91
91
  }
92
- // SSE auth via query param
93
- if (this.token) {
94
- url.searchParams.set('token', this.token);
95
- }
96
- const res = await fetch(url.toString());
92
+ const res = await fetch(url.toString(), {
93
+ headers: this.authHeaders(),
94
+ });
97
95
  if (!res.ok) {
98
96
  throw new ApiError(res.status, await res.text());
99
97
  }
@@ -105,9 +103,6 @@ export class ApiClient {
105
103
  // SSE streaming via POST - for large payloads (e.g. messages with file contents)
106
104
  async streamPost(path, body) {
107
105
  const url = new URL(path, this.baseUrl);
108
- if (this.token) {
109
- url.searchParams.set('token', this.token);
110
- }
111
106
  const res = await fetch(url.toString(), {
112
107
  method: 'POST',
113
108
  headers: {
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  interface LoginPromptProps {
3
- onLogin: (username: string, password: string) => Promise<void>;
3
+ onLogin: (email: string, password: string) => Promise<void>;
4
4
  error?: string | null;
5
5
  }
6
6
  export declare const LoginPrompt: React.FC<LoginPromptProps>;
@@ -46,8 +46,8 @@ const SimpleInput = ({ value, onChange, onSubmit, mask }) => {
46
46
  // ── LoginPrompt ──────────────────────────────────────────
47
47
  export const LoginPrompt = ({ onLogin, error, }) => {
48
48
  const { subscribe, unsubscribe } = useKeypressContext();
49
- const [step, setStep] = useState('username');
50
- const [username, setUsername] = useState('');
49
+ const [step, setStep] = useState('email');
50
+ const [email, setEmail] = useState('');
51
51
  const [password, setPassword] = useState('');
52
52
  const [isLoading, setIsLoading] = useState(false);
53
53
  const [ctrlCPressedOnce, setCtrlCPressedOnce] = useState(false);
@@ -72,9 +72,9 @@ export const LoginPrompt = ({ onLogin, error, }) => {
72
72
  subscribe(ctrlCHandler, KeypressPriority.Normal);
73
73
  return () => unsubscribe(ctrlCHandler);
74
74
  }, [ctrlCHandler, subscribe, unsubscribe]);
75
- const handleUsernameSubmit = useCallback((val) => {
75
+ const handleEmailSubmit = useCallback((val) => {
76
76
  if (val.trim()) {
77
- setUsername(val.trim());
77
+ setEmail(val.trim());
78
78
  setStep('password');
79
79
  }
80
80
  }, []);
@@ -83,19 +83,19 @@ export const LoginPrompt = ({ onLogin, error, }) => {
83
83
  setPassword(val.trim());
84
84
  setIsLoading(true);
85
85
  try {
86
- await onLogin(username, val.trim());
86
+ await onLogin(email, val.trim());
87
87
  }
88
- catch {
88
+ finally {
89
89
  setIsLoading(false);
90
- setStep('username');
91
- setUsername('');
90
+ setStep('email');
91
+ setEmail('');
92
92
  setPassword('');
93
93
  }
94
94
  }
95
- }, [onLogin, username]);
95
+ }, [onLogin, email]);
96
96
  return (_jsxs(Box, { flexDirection: "column", alignItems: "center", padding: 1, children: [_jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [BANNER_LINES.map((line, i) => {
97
97
  const gradient = getBannerGradient();
98
98
  return (_jsx(Text, { color: gradient[i] ?? gradient[0], children: line }, i));
99
- }), _jsx(Text, { color: palette.textMuted, children: BANNER_SUBTITLE }), _jsx(Text, { color: palette.textDim, children: ' v' + VERSION })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: error ? palette.error : palette.border, paddingX: 3, paddingY: 1, width: 48, children: [error && (_jsx(Box, { marginBottom: 1, justifyContent: "center", children: _jsxs(Text, { color: palette.error, children: ['✗ ', error] }) })), isLoading ? (_jsx(Box, { justifyContent: "center", paddingY: 1, children: _jsx(Spinner, { label: "Authenticating..." }) })) : step === 'username' ? (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: palette.primary, bold: true, children: ' ❯ Username ' }), _jsx(SimpleInput, { value: username, onChange: setUsername, onSubmit: handleUsernameSubmit })] }), _jsx(Box, { children: _jsx(Text, { color: palette.textDim, children: ' ○ Password' }) })] })) : (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: palette.success, children: ' ✓ ' }), _jsx(Text, { color: palette.textMuted, children: 'Username ' }), _jsx(Text, { children: username })] }), _jsxs(Box, { children: [_jsx(Text, { color: palette.primary, bold: true, children: ' ❯ Password ' }), _jsx(SimpleInput, { value: password, onChange: setPassword, onSubmit: handlePasswordSubmit, mask: "\u2022" })] })] })), _jsx(Box, { justifyContent: "center", marginTop: 1, children: ctrlCPressedOnce ? (_jsx(Text, { color: palette.warning, bold: true, children: "Press Ctrl-C again to exit" })) : (_jsxs(Text, { color: palette.textDim, dimColor: true, children: ["Press ", _jsx(Text, { color: palette.textMuted, children: "Enter" }), " to continue"] })) })] })] }));
99
+ }), _jsx(Text, { color: palette.textMuted, children: BANNER_SUBTITLE }), _jsx(Text, { color: palette.textDim, children: ' v' + VERSION })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: error ? palette.error : palette.border, paddingX: 3, paddingY: 1, width: 48, children: [error && (_jsx(Box, { marginBottom: 1, justifyContent: "center", children: _jsxs(Text, { color: palette.error, children: ['✗ ', error] }) })), isLoading ? (_jsx(Box, { justifyContent: "center", paddingY: 1, children: _jsx(Spinner, { label: "Authenticating..." }) })) : step === 'email' ? (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: palette.primary, bold: true, children: ' ❯ Email ' }), _jsx(SimpleInput, { value: email, onChange: setEmail, onSubmit: handleEmailSubmit })] }), _jsx(Box, { children: _jsx(Text, { color: palette.textDim, children: ' ○ Password' }) })] })) : (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: palette.success, children: ' ✓ ' }), _jsx(Text, { color: palette.textMuted, children: 'Email ' }), _jsx(Text, { children: email })] }), _jsxs(Box, { children: [_jsx(Text, { color: palette.primary, bold: true, children: ' ❯ Password ' }), _jsx(SimpleInput, { value: password, onChange: setPassword, onSubmit: handlePasswordSubmit, mask: "\u2022" })] })] })), _jsx(Box, { justifyContent: "center", marginTop: 1, children: ctrlCPressedOnce ? (_jsx(Text, { color: palette.warning, bold: true, children: "Press Ctrl-C again to exit" })) : (_jsxs(Text, { color: palette.textDim, dimColor: true, children: ["Press ", _jsx(Text, { color: palette.textMuted, children: "Enter" }), " to continue"] })) })] })] }));
100
100
  };
101
101
  //# sourceMappingURL=LoginPrompt.js.map
package/dist/src/main.js CHANGED
@@ -3,7 +3,7 @@ import { render } from 'ink';
3
3
  import yargs from 'yargs';
4
4
  import { hideBin } from 'yargs/helpers';
5
5
  import { initClient } from './api/client.js';
6
- import { authApi } from './api/auth.js';
6
+ import { authApi, supabaseLogin } from './api/auth.js';
7
7
  import { modelsApi } from './api/models.js';
8
8
  import { toolsApi } from './api/tools.js';
9
9
  import { skillsApi } from './api/skills.js';
@@ -211,16 +211,24 @@ export async function main() {
211
211
  terminalHeight: process.stdout.rows || 24,
212
212
  });
213
213
  const renderApp = (overrides = {}) => (_jsx(App, { serverUrl: serverUrl, token: overrides.token ?? token, username: overrides.username ?? username, authEnabled: authStatus.auth_enabled, version: healthData?.version ?? VERSION, healthData: healthData, availableCommands: commands, availableTools: tools, initialModels: models, initialCurrentModel: currentModel, initialSessions: initialSessions, initialTheme: initialTheme, onLogin: handleLogin, onLogout: handleLogout, loginError: overrides.loginError ?? loginError, isAuthenticated: overrides.isAuthenticated ?? isAuthenticated, ...getTerminalSize() }));
214
- const handleLogin = async (user, pass) => {
214
+ const handleLogin = async (email, pass) => {
215
+ const sbUrl = authStatus.supabase_url;
216
+ const sbKey = authStatus.supabase_anon_key;
217
+ if (!sbUrl || !sbKey) {
218
+ loginError = 'Supabase not configured on server';
219
+ rerender(renderApp({ token: null, username: null, isAuthenticated: false, loginError }));
220
+ return;
221
+ }
215
222
  try {
216
- const res = await authApi.login(user, pass);
223
+ const res = await supabaseLogin(sbUrl, sbKey, email, pass);
217
224
  client.setToken(res.access_token);
218
225
  saveToken(res.access_token);
219
226
  if (res.refresh_token) {
220
227
  saveRefreshToken(res.refresh_token);
221
228
  }
222
229
  token = res.access_token;
223
- username = res.user.username;
230
+ const meta = res.user?.user_metadata ?? {};
231
+ username = meta.display_name || meta.username || email;
224
232
  isAuthenticated = true;
225
233
  loginError = null;
226
234
  startTokenRefresh();
@@ -1,14 +1,3 @@
1
- export interface LoginResponse {
2
- success: boolean;
3
- access_token: string;
4
- refresh_token?: string;
5
- token_type: string;
6
- user: {
7
- user_id: string;
8
- username: string;
9
- display_name: string;
10
- };
11
- }
12
1
  export interface UserInfo {
13
2
  success: boolean;
14
3
  user_id: string;
@@ -1,4 +1,4 @@
1
- export declare const DEFAULT_SERVER_URL = "https://solidity.slowmist.ai";
1
+ export declare const DEFAULT_SERVER_URL = "https://agent.slowmist.ai";
2
2
  export declare function getServerUrl(cliArg?: string): string;
3
3
  export declare function saveToken(token: string): void;
4
4
  export declare function loadToken(): string | null;
@@ -9,8 +9,8 @@ const TOKEN_FILE = join(CONFIG_DIR, 'token');
9
9
  // Supabase refresh token(用于自动续期 access_token)
10
10
  const REFRESH_TOKEN_FILE = join(CONFIG_DIR, 'refresh_token');
11
11
  // 后端服务器默认地址,可通过 CLI 参数或环境变量 MISTAGENT_SERVER 覆盖
12
- // 发布时由 publish.sh 自动切换为 'https://solidity.slowmist.ai',发布后自动改回
13
- export const DEFAULT_SERVER_URL = 'https://solidity.slowmist.ai';
12
+ // 发布时由 publish.sh 自动切换为 'https://agent.slowmist.ai',发布后自动改回
13
+ export const DEFAULT_SERVER_URL = 'https://agent.slowmist.ai';
14
14
  // 获取后端服务器地址,优先级: CLI 参数 > 环境变量 > 默认值
15
15
  // 非 localhost 地址强制要求 HTTPS,防止中间人攻击导致 token 泄露
16
16
  export function getServerUrl(cliArg) {
@@ -1,4 +1,4 @@
1
- export declare const VERSION = "0.1.21";
1
+ export declare const VERSION = "0.1.22";
2
2
  export declare const BANNER_LINES: string[];
3
3
  export declare const BANNER_SUBTITLE = " A G E N T";
4
4
  export declare function getBannerGradient(): string[];
@@ -1,4 +1,4 @@
1
- export const VERSION = '0.1.21';
1
+ export const VERSION = '0.1.22';
2
2
  // Banner as line array for per-line gradient coloring
3
3
  export const BANNER_LINES = [
4
4
  ' ███╗ ███╗██╗███████╗████████╗',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mistagent",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "description": "MistAgent - Terminal AI Assistant",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",