@vibexnpm/talkx 5.4.0 → 6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibexnpm/talkx",
3
- "version": "5.4.0",
3
+ "version": "6.1.0",
4
4
  "type": "module",
5
5
  "description": "TalkFlow SDK - Chat & WebRTC unified client",
6
6
  "main": "dist/talkflow-sdk.umd.js",
@@ -35,15 +35,11 @@ class TalkFlowClient extends EventEmitter {
35
35
  * @param {number} [options.logLevel=LogLevel.WARN] - 로그 레벨
36
36
  *
37
37
  * @example
38
- * // 1단계: 고객사 backend 가 talkflow JWT 를 발급받음 (Server API Key 사용)
39
- * // POST https://chat.apiorbit.net/api/v1/users/auth
40
- * // Headers: X-API-KEY: <SERVER_KEY>, X-PROJECT-ID: <project-id>
41
- * // Body: { userId: 'user-123', nickname: '홍길동' }
42
- * // Response: { accessToken, refreshToken }
38
+ * // 1단계: 로그인 — 클라이언트 생성 «전» 에 부른다. ck/projectId/JWT 를 한 번에 받는다.
39
+ * // const { clientKey, projectId, accessToken } = await TalkFlowClient.loginUser({
40
+ * // serverUrl, email, password });
43
41
  *
44
- * // 2단계: 고객사 backend 가 최종 사용자 브라우저에 JWT 전달 (자사 API 응답 등)
45
- *
46
- * // 3단계: 브라우저에서 SDK 초기화
42
+ * // 2단계: 받은 값으로 SDK 초기화
47
43
  * const client = new TalkFlowClient({
48
44
  * apiKey: 'CLIENT_KEY', // 브라우저에 노출되는 Client Key
49
45
  * projectId: 'your-project-id',
@@ -174,24 +170,13 @@ class TalkFlowClient extends EventEmitter {
174
170
  throw new Error('projectId is required');
175
171
  }
176
172
 
177
- // Server Key (sk-) 브라우저 사용 경고
178
- // - development: 경고만 (registerUser 빠른 테스트 허용)
179
- // - staging/production: 경고 + 강력 안내
173
+ // Server Key (sk-) 는 SDK 가 쓸 일이 없다 — 유일한 용도였던 registerUser(/users/auth) 가
174
+ // 사라졌다. 그래도 들어오면 잘못된 키를 넣은 것이므로 알린다.
180
175
  if (options.apiKey.startsWith('sk-')) {
181
- const env = options.env || 'production';
182
- if (env === 'development') {
183
- console.warn(
184
- '⚠️ [TalkFlow] Server Key (sk-) 를 개발 모드에서 사용 중입니다. ' +
185
- '프로덕션 배포 전에 반드시 Client Key (ck-) 로 교체하세요.'
186
- );
187
- } else {
188
- console.error(
189
- '🚨 [TalkFlow] Server Key (sk-) 가 비개발 환경 (' + env + ') 에서 감지되었습니다. ' +
190
- '보안 위험: Server Key 를 브라우저에 포함하면 공격자가 임의 사용자로 JWT 를 발급받을 수 있습니다. ' +
191
- '반드시 Client Key (ck-) 로 교체하세요. ' +
192
- '서버에서도 프로덕션 모드에서 Server Key 의 브라우저 호출을 차단합니다.'
193
- );
194
- }
176
+ console.error(
177
+ '🚨 [TalkFlow] Server Key (sk-) 로 SDK 를 초기화했습니다. ' +
178
+ 'SDK 는 Client Key (ck-) 만 사용합니다 — loginUser() 응답의 clientKey 를 그대로 쓰세요.'
179
+ );
195
180
  }
196
181
  }
197
182
 
@@ -238,57 +223,123 @@ class TalkFlowClient extends EventEmitter {
238
223
  // ==================== 사용자 API ====================
239
224
 
240
225
  /**
241
- * 사용자 인증 (등록/로그인).
242
- * 인증 성공 시 반환된 JWT 토큰을 자동으로 설정합니다.
226
+ * 회원 로그인 — 이메일/비밀번호로 채팅 접속 일체를 받는다. **클라이언트 생성 전에 호출한다.**
243
227
  *
244
- * <h3>⚠️ Server API Key 전용</h3>
245
- * <p>이 메서드는 JWT 를 발급하는 엔드포인트 ({@code POST /api/v1/users/auth}) 를 호출합니다.
246
- * <strong>프로덕션 환경에서는 Client API Key 로 이 메서드를 호출하면 서버가 403 을 반환합니다.</strong></p>
228
+ * <p>Server Key 도 고객사 BFF 도 필요 없다. 응답의
229
+ * {@code clientKey}/{@code projectId}/{@code accessToken} 이 그대로 SDK 초기화 입력이다.</p>
247
230
  *
248
- * <ul>
249
- * <li><strong>개발/프로토타입</strong>: Server Key 로 SDK 를 초기화한 뒤 이 메서드를 사용하면 빠르게 테스트 가능</li>
250
- * <li><strong>프로덕션</strong>: 고객사 backend 에서 Server Key 로 직접 {@code POST /api/v1/users/auth} 를 호출 →
251
- * JWT 를 받아 SDK 에 {@code setToken(jwt)} 으로 전달. 이 메서드를 브라우저에서 쓰지 마세요.</li>
252
- * </ul>
231
+ * <p><b>회사 선택 단계가 없다</b>: 로그인 키가 전역 유일이라 이메일 하나로 회원과 회사가
232
+ * 함께 정해진다. 퇴사하면 그 키가 풀려 다른 회사에서 같은 이메일을 다시 쓸 수 있다.</p>
253
233
  *
254
- * <p>자세한 내용은 README 의 "프로덕션 인증 플로우" 섹션 참고.</p>
255
- *
256
- * @param {Object} userData
257
- * @param {string} userData.userId - 사용자 ID (필수, 고객사 회원 ID)
258
- * @param {string} userData.nickname - 닉네임 (필수, max 100자)
259
- * @param {string} [userData.profileImageUrl] - 프로필 이미지 URL (max 500자)
260
- * @param {Object} [userData.metadata] - 추가 메타데이터
261
- * @returns {Promise<Object>} 인증 결과 (토큰 포함)
234
+ * @param {Object} options
235
+ * @param {string} options.serverUrl - 채팅 서버 base URL
236
+ * @param {string} options.email
237
+ * @param {string} options.password
238
+ * @returns {Promise<Object>} `{accessToken, tokenType, expiresIn, projectId, clientKey, user}`
262
239
  *
263
240
  * @example
264
- * // 개발 환경 — Server Key 로 빠른 테스트
241
+ * const login = await TalkFlowClient.loginUser({
242
+ * serverUrl: 'https://chat.apiorbit.net',
243
+ * email: 'dan@example.com',
244
+ * password: '********'
245
+ * });
246
+ *
265
247
  * const client = new TalkFlowClient({
266
- * apiKey: 'SERVER_KEY',
267
- * projectId: 'your-project-id',
268
- * env: 'development'
248
+ * apiKey: login.clientKey,
249
+ * projectId: login.projectId,
250
+ * jwtToken: login.accessToken
269
251
  * });
270
- * await client.registerUser({ userId: 'user-123', nickname: '홍길동' });
271
252
  * await client.connect();
272
253
  */
273
- async registerUser(userData) {
274
- // 다국어: navigator.language 자동 시드 — 호출자가 preferredLanguage 를 명시하지 않은 경우에만.
275
- // 서버 /users/auth 는 seed-if-absent (기존 값 비클로버) 라 매 인증마다 덮어쓰지 않아 안전.
276
- const payload = { ...userData };
277
- if (payload.preferredLanguage === undefined) {
278
- const detected = TalkFlowClient.detectBrowserLanguage();
279
- if (detected) {
280
- payload.preferredLanguage = detected;
281
- }
254
+ static async loginUser({ serverUrl, email, password }) {
255
+ if (!serverUrl) throw new Error('serverUrl is required');
256
+ if (!email) throw new Error('email is required');
257
+ if (!password) throw new Error('password is required');
258
+
259
+ // 로그인은 키를 «받기 위해» 부르는 경로라 X-API-KEY/X-PROJECT-ID 가 없다(서버에서 필터 제외).
260
+ const client = new ApiClient({ baseUrl: serverUrl, apiKey: '', projectId: '' });
261
+ const response = await client.post('/api/v1/auth/login', { email, password });
262
+ return (response && response.data) ? response.data : response;
263
+ }
264
+
265
+ /**
266
+ * 회원 로그인 (인스턴스) — 성공 시 JWT 를 자동 설정한다.
267
+ *
268
+ * <p>이미 클라이언트가 있는 상태의 재로그인·계정 전환용이다. 최초 진입은 클라이언트 생성 전에
269
+ * 부르는 정적 {@link TalkFlowClient.loginUser} 를 쓴다 — 그쪽이 {@code clientKey} 까지 준다.</p>
270
+ *
271
+ * @param {Object} credentials
272
+ * @param {string} credentials.email
273
+ * @param {string} credentials.password
274
+ * @returns {Promise<Object>} 로그인 응답
275
+ */
276
+ async loginUser({ email, password }) {
277
+ const response = await this.apiClient.post('/api/v1/auth/login', { email, password });
278
+ const data = (response && response.data) ? response.data : response;
279
+
280
+ if (data && data.accessToken) {
281
+ await this.setToken(data.accessToken);
282
+ this.logger.info('Member logged in, token auto-set');
282
283
  }
284
+ return data;
285
+ }
283
286
 
284
- const response = await this.apiClient.post('/api/v1/users/auth', payload);
287
+ /**
288
+ * 내 정보 조회.
289
+ *
290
+ * @returns {Promise<Object>} `{userId, email, name, phone, profileUrl, companyRole, orgNodeId, orgName, ...}`
291
+ */
292
+ async getMyProfile() {
293
+ this._checkToken();
294
+ const response = await this.apiClient.get('/api/v1/me');
295
+ return (response && response.data) ? response.data : response;
296
+ }
285
297
 
286
- if (response.data && response.data.accessToken) {
287
- await this.setToken(response.data.accessToken);
288
- this.logger.info('User authenticated, token auto-set');
298
+ /**
299
+ * 내 정보 수정 — 이름·전화번호·프로필 이미지.
300
+ *
301
+ * <p>서버가 <b>새 JWT</b> 를 함께 준다: {@code name}/{@code profileUrl} 이 토큰 claim 이라
302
+ * 갱신하지 않으면 옛 값이 토큰 만료(7일)까지 계속 나간다. 여기서 자동 채택한다.</p>
303
+ *
304
+ * <p>이메일·조직·역할은 바꿀 수 없다 — 이메일은 로그인 키, 조직은 파트너가 정하고,
305
+ * 역할은 본인이 승급할 수 없어야 한다.</p>
306
+ *
307
+ * @param {Object} profile
308
+ * @param {string} [profile.name]
309
+ * @param {string} [profile.phone]
310
+ * @param {string} [profile.profileUrl]
311
+ * @returns {Promise<Object>} 갱신된 내 정보 (accessToken 포함)
312
+ */
313
+ async updateMyProfile(profile) {
314
+ this._checkToken();
315
+ const response = await this.apiClient.put('/api/v1/me', profile);
316
+ const data = (response && response.data) ? response.data : response;
317
+
318
+ if (data && data.accessToken) {
319
+ // 토큰 교체 실패로 수정 자체를 실패시키지 않는다 — 서버 반영은 이미 끝났고 옛 토큰도
320
+ // 유효(낡았을 뿐)하므로 여기서 throw 하면 호출자가 이미 적용된 수정을 다시 시도한다.
321
+ try {
322
+ await this.setToken(data.accessToken);
323
+ } catch (error) {
324
+ this.logger.warn('Profile updated but token refresh failed; keeping the old token', error);
325
+ }
289
326
  }
327
+ return data;
328
+ }
290
329
 
291
- return response;
330
+ /**
331
+ * 비밀번호 변경 — 현재 비밀번호를 재확인한다.
332
+ *
333
+ * <p>토큰만 탈취해도 비밀번호를 바꿀 수 있으면 계정을 통째로 빼앗긴다. 비밀번호는 바뀌어도
334
+ * 토큰은 그대로 유효하다 — 재로그인이 필요하지 않다.</p>
335
+ *
336
+ * @param {Object} params
337
+ * @param {string} params.currentPassword
338
+ * @param {string} params.newPassword - 8자 이상
339
+ */
340
+ async changeMyPassword({ currentPassword, newPassword }) {
341
+ this._checkToken();
342
+ return this.apiClient.put('/api/v1/me/password', { currentPassword, newPassword });
292
343
  }
293
344
 
294
345
  /**
@@ -344,7 +395,9 @@ class TalkFlowClient extends EventEmitter {
344
395
  * 지역 변형을 그대로 보내면 명시 요청({@code lang: 'en'})과 키가 분열되어 같은 언어 이중 생성·조회 miss 가
345
396
  * 발생한다. 서버 {@code LanguageCode} 도 동일 규칙으로 축약하므로 어느 쪽으로 들어와도 같은 값이 된다.</p>
346
397
  *
347
- * <p>{@link #registerUser} 자동 시드에 사용되며, 고객사 BFF 의 {@code /users/auth} 시드에도 쓸 수 있다.</p>
398
+ * <p>예전에는 {@code registerUser} 가 이 값을 자동으로 실어 보냈는데 그 경로가 사라졌다.
399
+ * 지금은 호출자가 직접 감지해 {@code PUT /api/v1/users/update} 의 {@code preferredLanguage}
400
+ * 로 넣어야 한다 — 넣지 않으면 사용자 기본 언어가 미설정으로 남아 자동 번역이 서버 기본값을 쓴다.</p>
348
401
  * @returns {string|null}
349
402
  */
350
403
  static detectBrowserLanguage() {
@@ -398,16 +451,32 @@ class TalkFlowClient extends EventEmitter {
398
451
  }
399
452
 
400
453
  /**
401
- * 사용자 존재 여부 확인
402
- * @param {string} projectUserId - 프로젝트 사용자 ID
403
- * @returns {Promise<Object>}
454
+ * 회사 조직도 + 인원 — <b>방 만들 때 «조직으로 초대»</b> 가 쓴다.
455
+ *
456
+ * <p>조직을 고르면 <b>하위 조직 인원까지</b> 초대된다. 그래서 각 노드의 {@code memberCount} 는
457
+ * 자손을 포함한 누적이고, {@code members} 는 그 노드 <b>직속</b>만이다 — 누적 목록이 필요하면
458
+ * {@code children} 을 따라 내려가며 모은다(상위마다 중복해 실으면 응답이 제곱으로 커진다).</p>
459
+ *
460
+ * <p>부서가 없는 회원은 {@code unassignedMembers} 에 있다. 퇴사·차단 회원은 어디에도 없다.</p>
461
+ *
462
+ * @returns {Promise<Object>} `{tree, unassignedMembers, totalMemberCount, membersOmitted}`
463
+ *
464
+ * @example
465
+ * const dir = await client.getCompanyDirectory();
466
+ * // 조직 하나를 고르면 그 자손까지 전부 모은다
467
+ * const collect = (node) => [...node.members, ...node.children.flatMap(collect)];
404
468
  */
405
- async checkUserExists(projectUserId) {
406
- return this.apiClient.get(`/api/v1/users/${projectUserId}/exists`);
469
+ async getCompanyDirectory() {
470
+ this._checkToken();
471
+ return this.apiClient.get('/api/v1/company/directory');
407
472
  }
408
473
 
409
474
  /**
410
475
  * 사용자 목록 조회
476
+ *
477
+ * <p>응답 행에 {@code phone}/{@code orgNodeId}/{@code orgName} 이 포함된다 — 부서명은
478
+ * 저장값이 아니라 조회 시점에 조직 마스터에서 해소한 값이다.</p>
479
+ *
411
480
  * @param {Object} [params]
412
481
  * @param {number} [params.size=50] - 페이지 크기 (1-100)
413
482
  * @param {string} [params.lastId] - 마지막 사용자 ID
@@ -441,9 +510,9 @@ class TalkFlowClient extends EventEmitter {
441
510
  _checkToken() {
442
511
  if (!this.options.jwtToken) {
443
512
  throw new Error(
444
- 'JWT token is required for this operation. Obtain it from your backend ' +
445
- '(POST /api/v1/users/auth with Server API Key) and pass it via setToken(). ' +
446
- 'See README "프로덕션 인증 플로우".'
513
+ 'JWT token is required for this operation. Sign in with ' +
514
+ 'TalkFlowClient.loginUser({ serverUrl, email, password }) and pass the ' +
515
+ 'returned accessToken via setToken(). See README "로그인".'
447
516
  );
448
517
  }
449
518
  }
@@ -376,7 +376,7 @@ class ChatClient extends EventEmitter {
376
376
 
377
377
  /**
378
378
  * 1:1 채팅방 생성/조회
379
- * @param {string} friendId - 상대방 사용자 ID (projectUserId)
379
+ * @param {string} friendId - 상대방 사용자 ID (서버 내부 {@code users._id} — 참가자 목록의 userId)
380
380
  * @returns {Promise<Object>}
381
381
  */
382
382
  async createOneToOneRoom(friendId) {
package/src/index.js CHANGED
@@ -10,35 +10,27 @@
10
10
  * 자세한 내용은 README 의 "프로덕션 인증 플로우" 섹션 참고.
11
11
  *
12
12
  * @example
13
- * // === 빠른 시작 (개발/프로토타입) — Server Key 사용 ===
13
+ * // === 시작하기 — 이메일/비밀번호 로그인 ===
14
14
  * import TalkFlowClient from '@talkflow/sdk';
15
15
  *
16
- * const client = new TalkFlowClient({
17
- * apiKey: 'SERVER_KEY', // 개발용 Server Key (프로덕션에서는 쓰지 마세요)
18
- * projectId: 'your-project-id',
19
- * env: 'development'
16
+ * // 1단계: 로그인. 클라이언트 생성 «전» 에 부른다 — ck/projectId/JWT 를 한 번에 받는다.
17
+ * const { clientKey, projectId, accessToken } = await TalkFlowClient.loginUser({
18
+ * serverUrl: 'https://chat.apiorbit.net',
19
+ * email: 'me@example.com',
20
+ * password: '••••••••'
20
21
  * });
21
22
  *
22
- * await client.registerUser({ userId: 'user-123', nickname: '홍길동' });
23
- * await client.connect();
24
- * await client.chat.sendTextMessage('room-id', '안녕하세요!');
25
- *
26
- * @example
27
- * // === 프로덕션 — Client Key + Backend JWT 발급 ===
28
- * // 1단계: 고객사 backend 에서 Server Key 로 JWT 발급
29
- * // POST /api/v1/users/auth (X-API-KEY: SERVER_KEY)
30
- * // → { accessToken }
31
- *
32
- * // 2단계: 브라우저 SDK 는 Client Key + JWT 로 초기화
23
+ * // 2단계: 받은 값 그대로 초기화
33
24
  * const client = new TalkFlowClient({
34
- * apiKey: 'CLIENT_KEY', // 브라우저 노출 안전 (registerUser 서버 차단)
35
- * projectId: 'your-project-id',
36
- * jwtToken: receivedJwt, // 백엔드로부터 받은 JWT
25
+ * apiKey: clientKey, // ck- (브라우저 노출 안전)
26
+ * projectId,
27
+ * jwtToken: accessToken,
37
28
  * env: 'production'
38
29
  * });
39
30
  *
40
31
  * await client.connect();
41
32
  * await client.chat.sendTextMessage('room-id', '안녕하세요!');
33
+ * await client.chat.sendTextMessage('room-id', '안녕하세요!');
42
34
  */
43
35
 
44
36
  // 메인 클라이언트
@@ -62,9 +62,9 @@ export function applySessionMethods(TalkFlowClient) {
62
62
 
63
63
  if (!this.options.jwtToken) {
64
64
  throw new Error(
65
- 'JWT token is required. Obtain it from your backend (which calls ' +
66
- 'POST /api/v1/users/auth with a Server API Key) and pass it to the SDK ' +
67
- 'via the jwtToken option, setToken(), or connect(jwt). See README "프로덕션 인증 플로우".'
65
+ 'JWT token is required. Sign in with TalkFlowClient.loginUser({ serverUrl, ' +
66
+ 'email, password }) and pass the returned accessToken to the SDK via the ' +
67
+ 'jwtToken option, setToken(), or connect(jwt). See README "로그인".'
68
68
  );
69
69
  }
70
70
 
package/types/index.d.ts CHANGED
@@ -725,8 +725,6 @@ export interface ChatMessage {
725
725
  nickName?: string;
726
726
  /** 발신자 프로필 이미지 URL. */
727
727
  profileUrl?: string | null;
728
- /** 발신자의 프로젝트 스코프 사용자 ID. */
729
- projectUserId?: string;
730
728
  content: string | null;
731
729
  chatMessageType: ChatMessageTypeValue;
732
730
  chatType: string;
@@ -962,12 +960,102 @@ export interface DecisionResponse {
962
960
  optionId: string;
963
961
  }
964
962
 
963
+ /** 로그인한 회원. `userId` 는 메시지 발신자 식별과 같은 값이다. */
964
+ export interface LoginMember {
965
+ userId: string;
966
+ email?: string | null;
967
+ nickname?: string | null;
968
+ profileUrl?: string | null;
969
+ /** 회사 역할 — 클라이언트가 파트너 화면 노출을 결정한다. 인가 근거는 아니다(서버가 재확인). */
970
+ companyRole: 'PARTNER' | 'MEMBER';
971
+ preferredLanguage?: string | null;
972
+ /**
973
+ * 초기 비밀번호를 아직 안 바꿨는가 — `true` 면 **비밀번호 변경 화면으로 보내라**.
974
+ *
975
+ * 회원은 파트너가 정한 임시 비밀번호로 처음 들어오고, 파트너가 초기화하면 다시 이 상태가 된다.
976
+ * 로그인은 **성공** 한 것이다 — 실패로 다루면 사용자가 「비밀번호가 틀렸다」로 오해한다.
977
+ */
978
+ mustChangePassword: boolean;
979
+ }
980
+
981
+ /**
982
+ * `TalkFlowClient.loginUser` 응답.
983
+ *
984
+ * 회사 선택 단계가 없다 — 로그인 키가 전역 유일이라 이메일 하나로 회원과 회사가 함께 정해진다.
985
+ */
986
+ export interface LoginResult {
987
+ accessToken: string;
988
+ tokenType: string;
989
+ expiresIn?: number;
990
+ projectId: string;
991
+ /** 회사의 활성 ck- 키. SDK 초기화의 `apiKey` 로 그대로 넣는다. */
992
+ clientKey: string;
993
+ user: LoginMember;
994
+ }
995
+
996
+ /** 회사 조직도의 회원 — 초대 UI 에 필요한 것만. */
997
+ export interface DirectoryMember {
998
+ userId: string;
999
+ name?: string | null;
1000
+ profileUrl?: string | null;
1001
+ }
1002
+
1003
+ /** 조직도 노드. `parentId` 가 null 이면 최상위. */
1004
+ export interface DirectoryNode {
1005
+ orgNodeId: string;
1006
+ name: string;
1007
+ parentId?: string | null;
1008
+ sortOrder: number;
1009
+ /** 자손 포함 누적 인원 — 화면의 「개발팀 (2)」가 이 값. */
1010
+ memberCount: number;
1011
+ /** 이 노드 직속 인원 수. */
1012
+ directMemberCount: number;
1013
+ /** 이 노드 **직속** 회원. 누적 목록은 `children` 을 따라 모은다. */
1014
+ members: DirectoryMember[];
1015
+ children: DirectoryNode[];
1016
+ }
1017
+
1018
+ /** `getCompanyDirectory()` 응답. */
1019
+ export interface CompanyDirectory {
1020
+ tree: DirectoryNode[];
1021
+ /** 부서가 없는 회원 — 빠지면 초대 화면에서 그 사람을 찾을 수 없다. */
1022
+ unassignedMembers: DirectoryMember[];
1023
+ /** 활성 회원 총수(트리 + 미지정). */
1024
+ totalMemberCount: number;
1025
+ /** true 면 인원 **수** 는 정확하지만 **목록** 이 비어 있다 — 검색 기반 선택으로 넘어가야 한다. */
1026
+ membersOmitted: boolean;
1027
+ }
1028
+
1029
+ /** 내 정보. 수정 응답에는 `accessToken` 이 함께 온다(name/profileUrl 이 토큰 claim). */
1030
+ export interface MyProfile {
1031
+ userId: string;
1032
+ email?: string | null;
1033
+ name?: string | null;
1034
+ phone?: string | null;
1035
+ profileUrl?: string | null;
1036
+ /** 회사 역할 — 방 역할(ParticipantResponse.role)과 다른 축이다. */
1037
+ companyRole: 'PARTNER' | 'MEMBER';
1038
+ orgNodeId?: string | null;
1039
+ orgName?: string | null;
1040
+ preferredLanguage?: string | null;
1041
+ projectId: string;
1042
+ joinedAt?: string | null;
1043
+ /**
1044
+ * 초기 비밀번호를 아직 안 바꿨는가 — `true` 면 비밀번호 변경을 요구해야 한다.
1045
+ *
1046
+ * 로그인 응답에도 같은 값이 있다. 여기에도 있는 이유: 새로고침·딥링크로 로그인 응답을 거치지
1047
+ * 않고 들어오는 화면에서 그 요구가 조용히 사라지지 않게 하려는 것이다.
1048
+ */
1049
+ mustChangePassword: boolean;
1050
+ accessToken?: string;
1051
+ expiresIn?: number;
1052
+ }
1053
+
965
1054
  /** 채팅방 참가자. */
966
1055
  export interface ParticipantResponse {
967
1056
  userId: string;
968
1057
  nickName?: string;
969
1058
  profileUrl?: string | null;
970
- projectUserId?: string;
971
1059
  /**
972
1060
  * 참가자 역할 — 서버 {@code ParticipantRole} enum 과 일치.
973
1061
  * <ul>
@@ -2251,6 +2339,30 @@ export default class TalkFlowClient implements SdkEventEmitter<TalkFlowClientEve
2251
2339
  removeAllListeners(event?: keyof TalkFlowClientEvents): void;
2252
2340
  listenerCount(event: keyof TalkFlowClientEvents): number;
2253
2341
 
2342
+ // 회원 인증 · 내 정보
2343
+ /**
2344
+ * 회원 로그인 — 클라이언트 생성 **전에** 호출한다. Server Key 도 고객사 BFF 도 필요 없다.
2345
+ * 응답의 `clientKey`/`projectId`/`accessToken` 이 그대로 SDK 초기화 입력이다.
2346
+ * 회사 선택 단계가 없다 — 로그인 키가 전역 유일이다.
2347
+ */
2348
+ static loginUser(options: {
2349
+ serverUrl: string;
2350
+ email: string;
2351
+ password: string;
2352
+ }): Promise<LoginResult>;
2353
+
2354
+ /** 회원 로그인 (인스턴스) — 재로그인·계정 전환용. 성공 시 토큰을 자동 설정한다. */
2355
+ loginUser(credentials: { email: string; password: string }): Promise<LoginResult>;
2356
+
2357
+ /** 내 정보 조회. */
2358
+ getMyProfile(): Promise<MyProfile>;
2359
+
2360
+ /** 내 정보 수정 — 응답의 새 토큰을 자동 채택한다(name/profileUrl 이 토큰 claim). */
2361
+ updateMyProfile(profile: { name?: string; phone?: string; profileUrl?: string }): Promise<MyProfile>;
2362
+
2363
+ /** 비밀번호 변경 — 현재 비밀번호 재확인. 토큰은 그대로 유효해 재로그인이 필요 없다. */
2364
+ changeMyPassword(params: { currentPassword: string; newPassword: string }): Promise<unknown>;
2365
+
2254
2366
  // Connection lifecycle
2255
2367
  connect(jwt?: string, options?: ConnectOptions): Promise<void>;
2256
2368
  disconnect(): Promise<void>;