agent-messenger 2.34.0 → 2.35.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.
Files changed (67) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/package.json +1 -1
  3. package/dist/src/platforms/kakaotalk/auth/kakao-login.d.ts +2 -0
  4. package/dist/src/platforms/kakaotalk/auth/kakao-login.d.ts.map +1 -1
  5. package/dist/src/platforms/kakaotalk/auth/kakao-login.js +2 -2
  6. package/dist/src/platforms/kakaotalk/auth/kakao-login.js.map +1 -1
  7. package/dist/src/platforms/kakaotalk/auth/oauth-refresh.d.ts +28 -0
  8. package/dist/src/platforms/kakaotalk/auth/oauth-refresh.d.ts.map +1 -0
  9. package/dist/src/platforms/kakaotalk/auth/oauth-refresh.js +100 -0
  10. package/dist/src/platforms/kakaotalk/auth/oauth-refresh.js.map +1 -0
  11. package/dist/src/platforms/kakaotalk/client.d.ts +2 -0
  12. package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
  13. package/dist/src/platforms/kakaotalk/client.js +14 -1
  14. package/dist/src/platforms/kakaotalk/client.js.map +1 -1
  15. package/dist/src/platforms/kakaotalk/index.d.ts +3 -1
  16. package/dist/src/platforms/kakaotalk/index.d.ts.map +1 -1
  17. package/dist/src/platforms/kakaotalk/index.js +1 -0
  18. package/dist/src/platforms/kakaotalk/index.js.map +1 -1
  19. package/dist/src/platforms/kakaotalk/protocol/login-response.d.ts +9 -0
  20. package/dist/src/platforms/kakaotalk/protocol/login-response.d.ts.map +1 -0
  21. package/dist/src/platforms/kakaotalk/protocol/login-response.js +32 -0
  22. package/dist/src/platforms/kakaotalk/protocol/login-response.js.map +1 -0
  23. package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
  24. package/dist/src/platforms/kakaotalk/protocol/session.js +29 -20
  25. package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
  26. package/dist/src/platforms/kakaotalk/protocol/types.d.ts +1 -0
  27. package/dist/src/platforms/kakaotalk/protocol/types.d.ts.map +1 -1
  28. package/dist/src/platforms/kakaotalk/protocol/types.js.map +1 -1
  29. package/dist/src/platforms/kakaotalk/types.d.ts +1 -0
  30. package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
  31. package/dist/src/platforms/kakaotalk/types.js.map +1 -1
  32. package/docs/content/docs/cli/kakaotalk.mdx +10 -0
  33. package/docs/content/docs/sdk/kakaotalk.mdx +54 -0
  34. package/package.json +1 -1
  35. package/skills/agent-channeltalk/SKILL.md +1 -1
  36. package/skills/agent-channeltalkbot/SKILL.md +1 -1
  37. package/skills/agent-discord/SKILL.md +1 -1
  38. package/skills/agent-discordbot/SKILL.md +1 -1
  39. package/skills/agent-imessage/SKILL.md +1 -1
  40. package/skills/agent-instagram/SKILL.md +1 -1
  41. package/skills/agent-kakaotalk/SKILL.md +27 -1
  42. package/skills/agent-kakaotalk/references/authentication.md +59 -0
  43. package/skills/agent-kakaotalk/references/common-patterns.md +8 -0
  44. package/skills/agent-line/SKILL.md +1 -1
  45. package/skills/agent-slack/SKILL.md +1 -1
  46. package/skills/agent-slackbot/SKILL.md +1 -1
  47. package/skills/agent-teams/SKILL.md +1 -1
  48. package/skills/agent-telegram/SKILL.md +1 -1
  49. package/skills/agent-telegrambot/SKILL.md +1 -1
  50. package/skills/agent-webex/SKILL.md +1 -1
  51. package/skills/agent-webexbot/SKILL.md +1 -1
  52. package/skills/agent-wechatbot/SKILL.md +1 -1
  53. package/skills/agent-whatsapp/SKILL.md +1 -1
  54. package/skills/agent-whatsappbot/SKILL.md +1 -1
  55. package/src/platforms/kakaotalk/auth/kakao-login.ts +2 -2
  56. package/src/platforms/kakaotalk/auth/oauth-refresh.test.ts +225 -0
  57. package/src/platforms/kakaotalk/auth/oauth-refresh.ts +144 -0
  58. package/src/platforms/kakaotalk/client.test.ts +55 -0
  59. package/src/platforms/kakaotalk/client.ts +19 -2
  60. package/src/platforms/kakaotalk/index.test.ts +11 -0
  61. package/src/platforms/kakaotalk/index.ts +8 -0
  62. package/src/platforms/kakaotalk/protocol/NOTICE.md +19 -4
  63. package/src/platforms/kakaotalk/protocol/login-response.test.ts +64 -0
  64. package/src/platforms/kakaotalk/protocol/login-response.ts +40 -0
  65. package/src/platforms/kakaotalk/protocol/session.ts +29 -21
  66. package/src/platforms/kakaotalk/protocol/types.ts +1 -0
  67. package/src/platforms/kakaotalk/types.ts +1 -0
@@ -0,0 +1,64 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+
3
+ import { validateLoginListResponse } from './login-response'
4
+ import type { LocoPacket } from './types'
5
+
6
+ function packet(statusCode: number, body: Record<string, unknown>): LocoPacket {
7
+ return { packetId: 1, statusCode, method: 'LOGINLIST', bodyType: 0, body }
8
+ }
9
+
10
+ function captureError(fn: () => unknown): unknown {
11
+ try {
12
+ fn()
13
+ return undefined
14
+ } catch (error) {
15
+ return error
16
+ }
17
+ }
18
+
19
+ describe('LOGINLIST response classification', () => {
20
+ it('accepts status=0 and returns the login snapshot', () => {
21
+ const body = { status: 0, chatDatas: [], eof: true }
22
+
23
+ expect(validateLoginListResponse(packet(0, body))).toBe(body)
24
+ })
25
+
26
+ it('accepts a successful response when body.status is omitted', () => {
27
+ const body = { chatDatas: [], eof: true }
28
+
29
+ expect(validateLoginListResponse(packet(0, body))).toBe(body)
30
+ })
31
+
32
+ it('classifies body.status=-950 as invalid_access_token', () => {
33
+ expect(() => validateLoginListResponse(packet(0, { status: -950 }))).toThrow(
34
+ expect.objectContaining({ code: 'invalid_access_token', serverStatus: -950 }),
35
+ )
36
+ })
37
+
38
+ it('classifies packet statusCode=-950 as invalid_access_token', () => {
39
+ expect(() => validateLoginListResponse(packet(-950, {}))).toThrow(
40
+ expect.objectContaining({ code: 'invalid_access_token', serverStatus: -950 }),
41
+ )
42
+ })
43
+
44
+ it('classifies an unknown non-zero body status as login_rejected', () => {
45
+ expect(() => validateLoginListResponse(packet(0, { status: -777 }))).toThrow(
46
+ expect.objectContaining({ code: 'login_rejected', serverStatus: -777 }),
47
+ )
48
+ })
49
+
50
+ it('classifies an unknown non-zero packet status as login_rejected', () => {
51
+ expect(() => validateLoginListResponse(packet(-777, {}))).toThrow(
52
+ expect.objectContaining({ code: 'login_rejected', serverStatus: -777 }),
53
+ )
54
+ })
55
+
56
+ it('keeps a synthetic socket close on the transport-error path', () => {
57
+ const error = captureError(() => validateLoginListResponse(packet(-1, { error: 'connection closed' })))
58
+
59
+ expect(error).toBeInstanceOf(Error)
60
+ expect(error).toMatchObject({ message: 'KakaoTalk LOGINLIST transport failed: connection closed' })
61
+ expect(error).not.toHaveProperty('code')
62
+ expect(error).not.toHaveProperty('serverStatus')
63
+ })
64
+ })
@@ -0,0 +1,40 @@
1
+ import type { KakaoAuthErrorCode } from '../types'
2
+ import type { LoginListResponse, LocoPacket } from './types'
3
+
4
+ const CONNECTION_CLOSED_STATUS = -1
5
+ const INVALID_ACCESS_TOKEN_STATUS = -950
6
+
7
+ export class KakaoLoginResponseError extends Error {
8
+ readonly code: KakaoAuthErrorCode
9
+ readonly serverStatus: number
10
+
11
+ constructor(code: KakaoAuthErrorCode, serverStatus: number) {
12
+ const reason = code === 'invalid_access_token' ? 'invalid access token' : 'rejected'
13
+ super(`KakaoTalk LOGINLIST ${reason} (status ${serverStatus})`)
14
+ this.name = 'KakaoLoginResponseError'
15
+ this.code = code
16
+ this.serverStatus = serverStatus
17
+ }
18
+ }
19
+
20
+ function isSyntheticConnectionClose(response: LocoPacket): boolean {
21
+ return response.statusCode === CONNECTION_CLOSED_STATUS && response.body.error === 'connection closed'
22
+ }
23
+
24
+ function throwAuthenticationError(serverStatus: number): never {
25
+ const code: KakaoAuthErrorCode =
26
+ serverStatus === INVALID_ACCESS_TOKEN_STATUS ? 'invalid_access_token' : 'login_rejected'
27
+ throw new KakaoLoginResponseError(code, serverStatus)
28
+ }
29
+
30
+ export function validateLoginListResponse(response: LocoPacket): LoginListResponse {
31
+ if (isSyntheticConnectionClose(response)) {
32
+ throw new Error('KakaoTalk LOGINLIST transport failed: connection closed')
33
+ }
34
+
35
+ const bodyStatus = typeof response.body.status === 'number' ? response.body.status : 0
36
+ if (bodyStatus !== 0) throwAuthenticationError(bodyStatus)
37
+ if (response.statusCode !== 0) throwAuthenticationError(response.statusCode)
38
+
39
+ return response.body as unknown as LoginListResponse
40
+ }
@@ -15,6 +15,7 @@ import {
15
15
  getLocoDeviceConfig,
16
16
  } from './config'
17
17
  import { LocoConnection } from './connection'
18
+ import { validateLoginListResponse } from './login-response'
18
19
  import type { BookingResponse, CheckinResponse, LoginListResponse, LocoPacket, SyncState } from './types'
19
20
 
20
21
  // LOCO opcode string emitted on the wire for typing indicator pulses.
@@ -81,27 +82,34 @@ export class LocoSession {
81
82
  const lastTokenId = syncState ? new Long(syncState.lastTokenId.low, syncState.lastTokenId.high) : Long.fromNumber(0)
82
83
  const lbk = syncState?.lbk ?? 0
83
84
 
84
- const response = await this.connection.sendPacket('LOGINLIST', {
85
- appVer: deviceConfig.appVersion,
86
- prtVer: PROTOCOL_VERSION,
87
- os: deviceConfig.os,
88
- lang: LANG,
89
- dtype: DTYPE,
90
- duuid: deviceUuid,
91
- oauthToken,
92
- ntype: 0,
93
- MCCMNC: MCCMNC,
94
- revision: syncState?.revision ?? 0,
95
- chatIds,
96
- maxIds,
97
- lastTokenId,
98
- lbk,
99
- rp: new Binary(Buffer.from([0x00, 0x00, 0xff, 0xff, 0x00, 0x00])),
100
- bg: false,
101
- })
102
-
103
- this.startPing()
104
- return response.body as unknown as LoginListResponse
85
+ try {
86
+ const response = await this.connection.sendPacket('LOGINLIST', {
87
+ appVer: deviceConfig.appVersion,
88
+ prtVer: PROTOCOL_VERSION,
89
+ os: deviceConfig.os,
90
+ lang: LANG,
91
+ dtype: DTYPE,
92
+ duuid: deviceUuid,
93
+ oauthToken,
94
+ ntype: 0,
95
+ MCCMNC: MCCMNC,
96
+ revision: syncState?.revision ?? 0,
97
+ chatIds,
98
+ maxIds,
99
+ lastTokenId,
100
+ lbk,
101
+ rp: new Binary(Buffer.from([0x00, 0x00, 0xff, 0xff, 0x00, 0x00])),
102
+ bg: false,
103
+ })
104
+
105
+ const loginResult = validateLoginListResponse(response)
106
+ this.startPing()
107
+ return loginResult
108
+ } catch (error) {
109
+ this.connection.close()
110
+ this.connection = null
111
+ throw error
112
+ }
105
113
  }
106
114
 
107
115
  private async bookAndCheckin(
@@ -36,6 +36,7 @@ export interface SyncState {
36
36
 
37
37
  // LOGINLIST uses short BSON field names: c=chatId, t=type, a=activeMembers, etc.
38
38
  export interface LoginListResponse extends ChatListResponse {
39
+ status?: number
39
40
  userId: number
40
41
  revision: number
41
42
  }
@@ -12,6 +12,7 @@ export interface ExtractedKakaoToken {
12
12
  }
13
13
 
14
14
  export type KakaoAuthMethod = 'login' | 'extract'
15
+ export type KakaoAuthErrorCode = 'invalid_access_token' | 'login_rejected'
15
16
 
16
17
  export interface KakaoAccountCredentials {
17
18
  account_id: string