lansenger-sdk-ts 1.0.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 (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.fr.md +501 -0
  3. package/README.md +504 -0
  4. package/README.zhHans.md +501 -0
  5. package/README.zhHant.md +501 -0
  6. package/README.zhHantHK.md +501 -0
  7. package/dist/accountMessages.d.ts +12 -0
  8. package/dist/accountMessages.js +41 -0
  9. package/dist/auth.d.ts +13 -0
  10. package/dist/auth.js +70 -0
  11. package/dist/calendars.d.ts +84 -0
  12. package/dist/calendars.js +278 -0
  13. package/dist/callbacks.d.ts +384 -0
  14. package/dist/callbacks.js +712 -0
  15. package/dist/chats.d.ts +22 -0
  16. package/dist/chats.js +88 -0
  17. package/dist/client.d.ts +439 -0
  18. package/dist/client.js +712 -0
  19. package/dist/config.d.ts +14 -0
  20. package/dist/config.js +42 -0
  21. package/dist/constants.d.ts +30 -0
  22. package/dist/constants.js +187 -0
  23. package/dist/contacts.d.ts +38 -0
  24. package/dist/contacts.js +161 -0
  25. package/dist/departments.d.ts +18 -0
  26. package/dist/departments.js +69 -0
  27. package/dist/exceptions.d.ts +20 -0
  28. package/dist/exceptions.js +42 -0
  29. package/dist/groupMessages.d.ts +11 -0
  30. package/dist/groupMessages.js +39 -0
  31. package/dist/groups.d.ts +66 -0
  32. package/dist/groups.js +218 -0
  33. package/dist/http.d.ts +7 -0
  34. package/dist/http.js +67 -0
  35. package/dist/index.d.ts +24 -0
  36. package/dist/index.js +189 -0
  37. package/dist/media.d.ts +16 -0
  38. package/dist/media.js +178 -0
  39. package/dist/models.d.ts +925 -0
  40. package/dist/models.js +991 -0
  41. package/dist/oauth.d.ts +17 -0
  42. package/dist/oauth.js +107 -0
  43. package/dist/persistence.d.ts +26 -0
  44. package/dist/persistence.js +210 -0
  45. package/dist/reminders.d.ts +10 -0
  46. package/dist/reminders.js +31 -0
  47. package/dist/streaming.d.ts +9 -0
  48. package/dist/streaming.js +40 -0
  49. package/dist/todos.d.ts +75 -0
  50. package/dist/todos.js +282 -0
  51. package/dist/urlHelpers.d.ts +7 -0
  52. package/dist/urlHelpers.js +22 -0
  53. package/dist/userMessages.d.ts +8 -0
  54. package/dist/userMessages.js +34 -0
  55. package/dist/users.d.ts +6 -0
  56. package/dist/users.js +32 -0
  57. package/package.json +33 -0
package/README.md ADDED
@@ -0,0 +1,504 @@
1
+ [English](README.md) | [简体中文](README.zhHans.md) | [繁体中文](README.zhHant.md) | [繁体中文香港](README.zhHantHK.md) | [Français](README.fr.md)
2
+
3
+ # lansenger-sdk-ts
4
+
5
+ Framework-independent TypeScript SDK for the Lansenger (蓝信) platform — supports Lansenger apps, organization bots, and personal bots.
6
+
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+ [![TypeScript 5+](https://img.shields.io/badge/TypeScript-5%2B-blue)](https://www.typescriptlang.org/)
9
+ [![Node 18+](https://img.shields.io/badge/Node-18%2B-green)](https://nodejs.org/)
10
+
11
+ > Zero framework dependencies — only `node-fetch` (v2, CommonJS compatible). Works with any Node.js project.
12
+
13
+ ## Supported Bot Types
14
+
15
+ | Bot Type | Auth | WebSocket Inbound | All APIs |
16
+ |----------|------|-------------------|----------|
17
+ | **Lansenger App** | appToken + userToken | ✗ (uses webhook) | ✓ |
18
+ | **Organization Bot** | appToken + userToken | ✗ (uses webhook) | ✓ |
19
+ | **Personal Bot** | appToken | ✓ (WebSocket) | ✓ (limited for non-bot APIs) |
20
+
21
+ All three bot types use the same auth mechanism: `appToken` is required for every API call; `userToken` is only needed for specific user-level operations (user info, staff search, calendar, etc.).
22
+
23
+ ## Features
24
+
25
+ - **Async client** — `LansengerClient` with Promise-based API
26
+ - **Credential & token persistence** — `CredentialStore` saves app_id, app_secret, URLs, appToken, userToken to file (survives restarts)
27
+ - **OAuth2 user authentication** — authorize URL, code exchange, token refresh
28
+ - **Organization & departments** — org info, department detail/children/staff
29
+ - **Staff & contacts** — basic/detailed info, ID mapping, department ancestors, search
30
+ - **Messaging** — 3 private chat channels (bot, official account, user impersonate) + group chat, all message types, @mention, human/bot sender identity, urgent reminders
31
+ - **Rich cards** — appCard (with dynamic status updates), oacard, linkCard, appArticles
32
+ - **Streaming messages** — SSE-based real-time delivery for AI agents
33
+ - **Media upload/download** — files, images, videos with auto type detection, media path fetch
34
+ - **Message management** — revoke, dynamic card update
35
+ - **Groups** — create, info, members, list, membership check, update settings & members, dismiss
36
+ - **Calendar & schedule** — primary calendar, schedule CRUD + update, attendee management + attendee metadata
37
+ - **Unified todo** — create, update, delete, query, executor management, status counts
38
+ - **Callback events** — 25 event types, structured parsing, AES decryption (per 4.10.1.4), SHA1 signature verification
39
+ - **Chat reading** — fetch chat list, fetch chat messages (4.24 MCP)
40
+
41
+ ## Quick Install
42
+
43
+ ```bash
44
+ npm install lansenger-sdk-ts
45
+ ```
46
+
47
+ For development:
48
+
49
+ ```bash
50
+ git clone https://github.com/lansenger-pm/lansenger-sdk-ts.git
51
+ cd lansenger-sdk-ts
52
+ npm install
53
+ npm run build
54
+ npm test
55
+ ```
56
+
57
+ ## 1. Authentication
58
+
59
+ ### appToken — Required for all API calls
60
+
61
+ Every SDK method requires `appToken`. The client automatically obtains and refreshes it using your `app_id` + `app_secret`. You never need to manage appToken manually — the `TokenManager` handles the lifecycle:
62
+
63
+ 1. **First call** → `GET /v1/apptoken/create` with app_id + app_secret → returns `appToken` (valid 2 hours)
64
+ 2. **Subsequent calls** → reuse cached appToken until expiry
65
+ 3. **Token expired** → automatically refresh via the same endpoint
66
+
67
+ ```typescript
68
+ import { LansengerClient } from "lansenger-sdk-ts";
69
+
70
+ const client = new LansengerClient("your-appid", "your-secret");
71
+
72
+ // You can also get/invalidate token manually
73
+ const token = await client.getToken();
74
+ client.invalidateToken(); // force refresh on next call
75
+ ```
76
+
77
+ ### userToken — Only needed for specific endpoints
78
+
79
+ `userToken` represents a specific Lansenger user's authorization (obtained via OAuth2). It's only required for:
80
+ - User-level information (fetchUserInfo, fetchStaffDetail, searchStaff)
81
+ - Calendar & schedule operations (fetchPrimaryCalendar, createSchedule, etc.)
82
+ - Group operations as a human sender
83
+
84
+ ### Getting Credentials
85
+
86
+ | Bot Type | How to get app_id + app_secret |
87
+ |----------|--------------------------------|
88
+ | **Personal Bot** | Lansenger desktop → Contacts → Smart Bots → Personal Bots → click ℹ️ icon (mobile client does NOT show credentials) |
89
+ | **Lansenger App** | Create at Lansenger Developer Center — may require organization admin approval |
90
+ | **Organization Bot** | Create at Lansenger Developer Center — may require organization admin approval |
91
+
92
+ ### OAuth2 user-level auth
93
+
94
+ ```typescript
95
+ // Build authorize URL — redirect user to Lansenger passport
96
+ const url = client.buildAuthorizeUrl("https://myapp.com/callback");
97
+
98
+ // After user authorizes, exchange code for userToken + refreshToken
99
+ const tokenResult = await client.exchangeCode("auth_code_from_callback");
100
+
101
+ // Refresh expired userToken
102
+ const newToken = await client.refreshUserToken(tokenResult.refresh_token!);
103
+
104
+ // Fetch user profile
105
+ const userInfo = await client.fetchUserInfo(tokenResult.user_token!);
106
+ ```
107
+
108
+ ### Factory methods
109
+
110
+ ```typescript
111
+ // From environment variables (LANSENGER_APP_ID, LANSENGER_APP_SECRET, etc.)
112
+ const client = LansengerClient.fromEnv();
113
+
114
+ // From a LansengerConfig object
115
+ const config = new LansengerConfig("appid", "secret", "https://open.e.lanxin.cn/open/apigw");
116
+ const client = LansengerClient.fromConfig(config);
117
+
118
+ // From a CredentialStore (reads persisted credentials)
119
+ const client = LansengerClient.fromStore();
120
+ ```
121
+
122
+ ## 2. Organization & Departments
123
+
124
+ ```typescript
125
+ // Organization info
126
+ const org = await client.fetchOrgInfo("orgId");
127
+
128
+ // Department hierarchy
129
+ const detail = await client.fetchDepartmentDetail("deptId");
130
+ const children = await client.fetchDepartmentChildren("deptId");
131
+ const staffs = await client.fetchDepartmentStaffs("deptId");
132
+ ```
133
+
134
+ ## 3. Staff & Contacts
135
+
136
+ ```typescript
137
+ // Basic staff info
138
+ const staff = await client.fetchStaffBasicInfo("staffOpenId");
139
+
140
+ // Detailed profile (userToken recommended)
141
+ const detail = await client.fetchStaffDetail("staffOpenId", { user_token: "ut" });
142
+
143
+ // Map phone → staffId
144
+ const mapping = await client.fetchStaffIdMapping("orgId", "mobile", "13800138000");
145
+
146
+ // Department ancestors for a staff member
147
+ const ancestors = await client.fetchDepartmentAncestors("staffOpenId");
148
+
149
+ // Search staff (requires userToken)
150
+ const results = await client.searchStaff("Zhang San", { user_token: "ut" });
151
+
152
+ // Org extra field IDs
153
+ const fields = await client.fetchOrgExtraFieldIds("orgId");
154
+ ```
155
+
156
+ ## 4. Messaging & Media
157
+
158
+ #### Bot private chat — most common
159
+
160
+ ```typescript
161
+ const result = await client.sendText("staff123", "Hello!");
162
+ const result = await client.sendMarkdown("staff123", "**Bold**");
163
+ const result = await client.sendFile("staff123", "/path/to/report.pdf");
164
+ ```
165
+
166
+ #### Public account channel
167
+
168
+ ```typescript
169
+ const result = await client.sendAccountMessage(
170
+ "text", { text: { content: "System notice" } },
171
+ ["staff1", "staff2"], // chat_ids
172
+ undefined, // department_ids
173
+ { account_id: "524288-xxxx" },
174
+ );
175
+ ```
176
+
177
+ #### User impersonate channel (requires userToken)
178
+
179
+ ```typescript
180
+ const result = await client.sendUserMessage(
181
+ "staff456", "text", { text: { content: "Hello" } },
182
+ { user_token: "ut" }, // required
183
+ );
184
+ ```
185
+
186
+ #### Group chat
187
+
188
+ ```typescript
189
+ // Bot → group
190
+ const result = await client.sendText("group123", "Notice", { is_group: true });
191
+
192
+ // Human → group (with userToken)
193
+ const result = await client.sendGroupMessage(
194
+ "group123", "text", { text: { content: "I'll handle it" } },
195
+ { user_token: "ut" },
196
+ );
197
+
198
+ // @mention in group
199
+ const result = await client.sendText(
200
+ "group123", "Important!", { is_group: true, reminder_all: true },
201
+ );
202
+ ```
203
+
204
+ #### Rich cards
205
+
206
+ ```typescript
207
+ const result = await client.sendAppCard("staff123", "Approval", { is_dynamic: true });
208
+ const result = await client.sendLinkCard("staff123", "Article", "https://...");
209
+ const result = await client.sendAppArticles("staff123", [{ title: "Article 1", link: "..." }]);
210
+
211
+ // Update dynamic card status
212
+ const result = await client.updateDynamicCard("msg123", { is_last_update: true });
213
+ ```
214
+
215
+ #### Streaming messages (for AI agents)
216
+
217
+ ```typescript
218
+ const result = await client.createStreamMessage("staff1", "staff", "stream-id-1");
219
+ const result = await client.fetchStreamMessage("msg123");
220
+ ```
221
+
222
+ #### Media
223
+
224
+ ```typescript
225
+ // Upload
226
+ const upload = await client.uploadMediaFile("/path/to/file.pdf");
227
+
228
+ // Download
229
+ const download = await client.downloadMediaFile("media123");
230
+
231
+ // Save to file
232
+ const filePath = await client.downloadMediaToFile("media123", { target_path: "/tmp/file.pdf" });
233
+
234
+ // Get download URL path (4.5.3)
235
+ const pathResult = await client.fetchMediaPathInfo("media123");
236
+
237
+ // Revoke messages
238
+ const result = await client.revokeMessage(["msg1", "msg2"]);
239
+ ```
240
+
241
+ #### Urgent reminders (4.6.14)
242
+
243
+ ```typescript
244
+ import { REMINDER_TYPE_POPUP, REMINDER_TYPE_SMS } from "lansenger-sdk-ts";
245
+
246
+ const result = await client.sendReminderMsg(
247
+ "msg123",
248
+ [REMINDER_TYPE_POPUP, REMINDER_TYPE_SMS],
249
+ ["staff1", "staff2"],
250
+ );
251
+ ```
252
+
253
+ ## 5. Groups
254
+
255
+ ```typescript
256
+ // Create group
257
+ const group = await client.createGroup("Project Chat", "orgId", { staff_id_list: ["s1", "s2", "s3"] });
258
+
259
+ // Fetch info & members
260
+ const info = await client.fetchGroupInfo("groupOpenId");
261
+ const members = await client.fetchGroupMembers("groupOpenId");
262
+ const groups = await client.fetchGroupList();
263
+
264
+ // Check membership
265
+ const result = await client.checkIsInGroup("groupOpenId", { staff_id: "staff1" });
266
+
267
+ // Update settings
268
+ await client.updateGroupInfo("groupId", { name: "New Name", manage_mode: 1 });
269
+
270
+ // Add/remove members
271
+ await client.updateGroupMembers("groupId", { add_user_list: ["staff4"], del_user_list: ["staff3"] });
272
+
273
+ // Dismiss/delete group (owner only, 4.28.6)
274
+ await client.dismissGroup("groupId");
275
+ ```
276
+
277
+ ## 6. Calendar & Schedule
278
+
279
+ ```typescript
280
+ // Get primary calendar (requires userToken or userId)
281
+ const cal = await client.fetchPrimaryCalendar({ user_token: "ut" });
282
+
283
+ // Create schedule
284
+ const schedule = await client.createSchedule(
285
+ cal.calendar_id!, "Team Meeting",
286
+ { date: "2024-01-15", time: "10:00", timeZone: "Asia/Shanghai" },
287
+ { date: "2024-01-15", time: "11:00", timeZone: "Asia/Shanghai" },
288
+ [{ staffId: "staff1", attendeeFlag: "required" }],
289
+ { user_token: "ut" },
290
+ );
291
+
292
+ // Fetch/delete schedule
293
+ const info = await client.fetchSchedule("cal1", "sch1", { user_token: "ut" });
294
+ await client.deleteSchedule("cal1", "sch1", { user_token: "ut" });
295
+
296
+ // Schedule list in time range (max 42 days)
297
+ const schedules = await client.fetchScheduleList("cal1", 1705276800000, 1707940800000, { user_token: "ut" });
298
+
299
+ // Attendee management
300
+ const attendees = await client.fetchScheduleAttendees("cal1", "sch1", { user_token: "ut" });
301
+ await client.addScheduleAttendees("cal1", "sch1", ["staff2"], { user_token: "ut" });
302
+ await client.deleteScheduleAttendees("cal1", "sch1", ["staff2"], { user_token: "ut" });
303
+
304
+ // Update schedule (4.23.12)
305
+ await client.updateSchedule("cal1", "sch1", { summary: "Updated Meeting", user_token: "ut" });
306
+
307
+ // Update attendee metadata (4.23.17) — RSVP, color, busy/free, reminders
308
+ await client.updateScheduleAttendeeMeta("cal1", "sch1", {
309
+ rsvp_status: "accept", busy_free_state: "busy", remind_times: [5, 15], user_token: "ut",
310
+ });
311
+ ```
312
+
313
+ ## 7. Unified Todo
314
+
315
+ ```typescript
316
+ import { TODO_TYPE_APPROVAL, TODO_TODO_STATUS_DONE } from "lansenger-sdk-ts";
317
+
318
+ // Create todo task
319
+ const todo = await client.createTodoTask(
320
+ "Approval Request", "https://app.com/a/1", "https://pc.app.com/a/1",
321
+ ["staff1"], "org1", TODO_TYPE_APPROVAL,
322
+ );
323
+
324
+ // Update status (11=pending-read, 12=read, 21=pending-do, 22=done)
325
+ await client.updateTodoTaskStatus("taskId", TODO_TODO_STATUS_DONE, "org1");
326
+
327
+ // Update content
328
+ await client.updateTodoTask("taskId", "Updated", "l", "p", "org1");
329
+
330
+ // Delete (sender only)
331
+ await client.deleteTodoTask("taskId", "org1");
332
+
333
+ // Query
334
+ const listResult = await client.fetchTodoTaskList("org1");
335
+ const task = await client.fetchTodoTaskById("taskId", "org1");
336
+ const task = await client.fetchTodoTaskBySourceId("src1", "org1");
337
+ const counts = await client.fetchTodoTaskStatusCounts("staff1", "org1");
338
+
339
+ // Executor management
340
+ await client.addExecutors(["staff2"], "org1", { todotask_id: "taskId" });
341
+ await client.deleteExecutors(["staff2"], "org1", { todotask_id: "taskId" });
342
+ const executors = await client.fetchExecutorList("taskId", "org1");
343
+ await client.updateExecutorStatus(
344
+ [{ executorId: "staff1", todotaskId: "taskId", status: "22" }],
345
+ "org1",
346
+ );
347
+ ```
348
+
349
+ ## 8. Chat Reading (4.24 MCP)
350
+
351
+ ```typescript
352
+ // Fetch chat list (private + group conversations)
353
+ const chatList = await client.fetchChatList({ user_token: "ut" });
354
+
355
+ // Fetch chat messages
356
+ const messages = await client.fetchChatMessages({
357
+ staff_id: "staff1", // or group_id: "group1"
358
+ user_token: "ut",
359
+ });
360
+ ```
361
+
362
+ ## 9. Callback Events
363
+
364
+ The SDK supports both plain JSON and AES-encrypted callback payloads (per Lansenger API spec 4.10.1.4).
365
+
366
+ ### Configuration
367
+
368
+ Set `encoding_key` and `callback_token` (from Lansenger Developer Center callback settings):
369
+
370
+ ```typescript
371
+ const client = new LansengerClient("appid", "secret", undefined, undefined, undefined, undefined, "BASE64_AES_KEY", "CALLBACK_TOKEN");
372
+ ```
373
+
374
+ Or via environment variables: `LANSENGER_ENCODING_KEY`, `LANSENGER_CALLBACK_TOKEN`.
375
+
376
+ ### Parse callback payload (auto-detects encrypted vs plain JSON)
377
+
378
+ ```typescript
379
+ import { parseCallbackPayload } from "lansenger-sdk-ts";
380
+
381
+ // Plain JSON webhook
382
+ const events = parseCallbackPayload('{"events": [...]}');
383
+
384
+ // AES-encrypted payload (auto-decrypts with encodingKey)
385
+ const events = parseCallbackPayload(encryptedData, {
386
+ encoding_key: "BASE64_AES_KEY",
387
+ known_app_id: "your-appid",
388
+ });
389
+ ```
390
+
391
+ ### Verify signature
392
+
393
+ ```typescript
394
+ import { verifyCallbackSignature } from "lansenger-sdk-ts";
395
+
396
+ // sha1(sort(token, timestamp, nonce, dataEncrypt))
397
+ const isValid = verifyCallbackSignature(
398
+ timestamp, nonce, signature, encodingKey,
399
+ { data_encrypt: encryptedData, callback_token: "CALLBACK_TOKEN" },
400
+ );
401
+ ```
402
+
403
+ ### Event types
404
+
405
+ ```typescript
406
+ const types = LansengerClient.getCallbackEventTypes(); // 25 event types across 13 categories
407
+ ```
408
+
409
+ ## Message Type Capability Matrix
410
+
411
+ | msgType | Markdown | @mention | Attachments | Private Channels | Group Chat | Notes |
412
+ |---------|----------|----------|-------------|------------------|------------|-------|
413
+ | `text` | ✗ | ✓ (group) | ✓ | Bot, Official Account, User Impersonate | ✓ | Up to 6000 bytes |
414
+ | `formatText` | ✓ | ✗ | ✗ | User Impersonate only | ✓ | Markdown via formatType=1 |
415
+ | `oacard` | ✗ | ✗ | ✗ | Bot, Official Account, User Impersonate | ✓ | Simple card with fields |
416
+ | `appCard` | ✓ (div tags) | ✗ | ✗ | Bot, Official Account, User Impersonate | ✓ | Rich card, dynamic updates |
417
+ | `linkCard` | ✗ | ✗ | ✗ | Bot, Official Account | ✓ | Link preview card |
418
+ | `appArticles` | ✗ | ✗ | ✗ | Bot private only | ✓ | Article list (1+ articles) |
419
+
420
+ **Group chat** supports all message types. Only group chat supports @mention.
421
+
422
+ ## Configuration
423
+
424
+ ### Environment Variables
425
+
426
+ | Variable | Required | Description | Default |
427
+ |----------|----------|-------------|---------|
428
+ | `LANSENGER_APP_ID` | ✓ | App/Bot ID | — |
429
+ | `LANSENGER_APP_SECRET` | ✓ | App/Bot Secret | — |
430
+ | `LANSENGER_API_GATEWAY_URL` | ✗ | API Gateway URL | `https://open.e.lanxin.cn/open/apigw` |
431
+ | `LANSENGER_PASSPORT_URL` | ✗ | Passport URL (for OAuth2) | — |
432
+ | `LANSENGER_ENCODING_KEY` | ✗ | Callback AES encryption key (Base64) | — |
433
+ | `LANSENGER_CALLBACK_TOKEN` | ✗ | Callback signature token | — |
434
+
435
+ ### Credential & Token Persistence
436
+
437
+ By default, credentials and tokens stay in memory only (lost on process exit). Enable file persistence with `storePath`:
438
+
439
+ ```typescript
440
+ import { LansengerClient, CredentialStore } from "lansenger-sdk-ts";
441
+
442
+ // Auto-persist to ~/.lansenger/sdk_state.json
443
+ const client = new LansengerClient("appid", "secret", undefined, undefined, undefined, "~/.lansenger/sdk_state.json", "BASE64_AES_KEY", "CALLBACK_TOKEN");
444
+
445
+ // Or from env with persistence
446
+ const client = LansengerClient.fromEnv("~/.lansenger/sdk_state.json");
447
+
448
+ // Manual store operations
449
+ const store = new CredentialStore("~/.lansenger/sdk_state.json");
450
+ store.saveCredentials("app_id", "app_secret", "https://open.e.lanxin.cn/open/apigw");
451
+ store.saveUserToken("user_token", "refresh_token");
452
+ const token = store.loadAppToken(); // null if expired
453
+ ```
454
+
455
+ ## Project Structure
456
+
457
+ ```
458
+ lansenger-sdk-ts/
459
+ ├── src/
460
+ │ ├── index.ts # All exports
461
+ │ ├── client.ts # LansengerClient (async)
462
+ │ ├── config.ts # LansengerConfig
463
+ │ ├── auth.ts # TokenManager — appToken lifecycle
464
+ │ ├── oauth.ts # OAuth2 helpers
465
+ │ ├── constants.ts # API endpoints, media types, OAuth scopes
466
+ │ ├── exceptions.ts # LansengerError hierarchy
467
+ │ ├── models.ts # 38+ result class types
468
+ │ ├── http.ts # doGet, doPost, doPostMultipart, parseApiResponse
469
+ │ ├── urlHelpers.ts # buildApiUrl with pathVars support
470
+ │ ├── contacts.ts # Staff & org info APIs
471
+ │ ├── departments.ts # Department APIs
472
+ │ ├── accountMessages.ts # Public account channel
473
+ │ ├── userMessages.ts # User impersonate channel
474
+ │ ├── groupMessages.ts # Group chat channel
475
+ │ ├── media.ts # Upload/download
476
+ │ ├── streaming.ts # SSE streaming
477
+ │ ├── persistence.ts # CredentialStore — file-based persistence
478
+ │ ├── callbacks.ts # Callback events — 25 event types, AES decryption, SHA1 verification
479
+ │ ├── groups.ts # Group APIs (including dismiss 4.28.6)
480
+ │ ├── todos.ts # Unified Todo
481
+ │ ├── calendars.ts # Calendar & Schedule
482
+ │ ├── reminders.ts # Urgent message reminders (4.6.14)
483
+ │ ├── chats.ts # Chat reading (4.24 MCP)
484
+ │ └── users.ts # User info
485
+ ├── tests/ # Unit tests
486
+ ├── package.json
487
+ ├── tsconfig.json
488
+ ├── jest.config.js
489
+ ├── LICENSE
490
+ └── README.md
491
+ ```
492
+
493
+ ## Development
494
+
495
+ ```bash
496
+ npm install
497
+ npm run build # Compile TypeScript → dist/
498
+ npm test # Run Jest tests
499
+ npm run lint # Type-check only (tsc --noEmit)
500
+ ```
501
+
502
+ ## License
503
+
504
+ MIT — see [LICENSE](LICENSE).