k-linkedin-mcp 0.2.0 → 0.2.1
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/CHANGELOG.md +17 -3
- package/README.md +116 -38
- package/package.json +1 -1
- package/src/admin/cli.js +4 -4
- package/src/admin/service.js +20 -4
- package/src/admin/telegram.js +1 -1
- package/src/http_app.js +2 -2
- package/tests/admin_service.test.js +4 -4
- package/tests/telegram.test.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,21 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
+
## [0.2.1] — 2026-03-22
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- [x] `setAccessToken` now async with best-effort `GET /v2/userinfo` — populates `member_id`, `name`, `email` in token.json (required for posting tools that call `_memberUrn()`)
|
|
9
|
+
- [x] Status CLI display: `State dir` instead of `Admin env file` path
|
|
10
|
+
- [x] Redirect URI dynamically computed from port override in `runOAuthFlow` (was using hardcoded `config.oauth.redirect_uri`)
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- [x] linkedin-mcp registered in Codex (`~/.codex/config.toml`), Copilot (`~/.copilot/mcp-config.json`), Vibe (`~/.vibe/config.toml`) — HTTP primary + stdio fallback
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
5
17
|
## [0.2.0] — 2026-03-22
|
|
6
18
|
|
|
7
19
|
### Added
|
|
8
20
|
- [x] Unified credential management — CLI, HTTP, Telegram: `client-id set/unset`, `client-secret set/unset`, `token set/unset`
|
|
9
21
|
- [x] Admin env file pattern (`LINKEDIN_ADMIN_ENV_FILE`, default `/data/linkedin-admin.env`) — persists credentials across restarts
|
|
10
22
|
- [x] `auth --port N` option to avoid `EADDRINUSE` (default port moved to 3001 via `config.json`)
|
|
11
|
-
- [x] OAuth callback port externalized to `config.json` (
|
|
23
|
+
- [x] OAuth callback port externalized to `config.json` (DEFAULTS remain 3000 as last-resort fallback only)
|
|
12
24
|
- [x] New HTTP POST routes: `/admin/{client-id,client-secret,token}/{set,unset}`
|
|
13
25
|
- [x] New Telegram commands: `/token_set`, `/token_unset`, `/client_id_set`, `/client_id_unset`, `/client_secret_set`, `/client_secret_unset`
|
|
14
|
-
- [x] Status table
|
|
26
|
+
- [x] Status table: shows `LINKEDIN_CLIENT_ID`, `LINKEDIN_CLIENT_SECRET`, `OAuth Token` — Telegram token removed
|
|
15
27
|
- [x] 61 tests (18 new) — all pass
|
|
16
28
|
|
|
17
29
|
### Fixed
|
|
18
30
|
- [x] `TELEGRAM_LINKEDIN_TOKEN` → `TELEGRAM_LINKEDIN_HOMELAB_TOKEN` reference in HTTP status handler
|
|
19
31
|
|
|
32
|
+
---
|
|
33
|
+
|
|
20
34
|
## [0.1.0] — 2026-03-22
|
|
21
35
|
|
|
22
36
|
### Added
|
|
@@ -27,5 +41,5 @@
|
|
|
27
41
|
- [x] Token lifecycle management — 60-day expiry tracking, `tokenSummary` with days_left
|
|
28
42
|
- [x] Test suite — 41 tests, 5 files, 0 live API calls (mocked fetch + temp dirs)
|
|
29
43
|
- [x] Docker image (`oven/bun:1-slim`) + `deploy/docker-compose.yml` with Traefik labels
|
|
30
|
-
- [x] GitLab CI — test →
|
|
44
|
+
- [x] GitLab CI — test → deploy pipeline (homelab runner, no SSH)
|
|
31
45
|
- [x] Editable install via `bun link`
|
package/README.md
CHANGED
|
@@ -34,29 +34,30 @@ linkedin-mcp/
|
|
|
34
34
|
│ ├── http_app.js ← Express HTTP app: /health /admin/* /mcp
|
|
35
35
|
│ ├── .env.example ← required secrets template
|
|
36
36
|
│ └── admin/
|
|
37
|
-
│ ├── cli.js ← Commander CLI (auth / status / logout / logs / health / urls
|
|
38
|
-
│
|
|
39
|
-
│ ├──
|
|
40
|
-
│
|
|
37
|
+
│ ├── cli.js ← Commander CLI (auth / status / logout / logs / health / urls
|
|
38
|
+
│ │ / client-id / client-secret / token)
|
|
39
|
+
│ ├── service.js ← unified admin kernel — single source of truth for all surfaces
|
|
40
|
+
│ ├── oauth.js ← LinkedIn OAuth 2.0 + OIDC flow (configurable callback port)
|
|
41
|
+
│ └── telegram.js ← optional Telegram bot admin (15 commands)
|
|
41
42
|
├── tools/
|
|
42
43
|
│ ├── registry.js ← listTools() + callTool()
|
|
43
44
|
│ ├── guide.js ← linkedin_guide
|
|
44
45
|
│ ├── profile.js ← linkedin_get_profile, linkedin_auth_status
|
|
45
46
|
│ ├── posts.js ← linkedin_create_post, linkedin_create_image_post, linkedin_delete_post
|
|
46
47
|
│ └── social.js ← linkedin_like_post, linkedin_create_comment
|
|
47
|
-
├── tests/ ← bun test (
|
|
48
|
+
├── tests/ ← bun test (61 tests, 0 deps on live API)
|
|
48
49
|
├── deploy/
|
|
49
50
|
│ ├── docker-compose.yml
|
|
50
51
|
│ └── docker-compose.override.example.yml
|
|
51
52
|
├── Dockerfile
|
|
52
53
|
├── .gitlab-ci.yml
|
|
53
|
-
└── config.json ← non-secret defaults (port 8095, state dir, scopes)
|
|
54
|
+
└── config.json ← non-secret defaults (port 8095, OAuth port 3001, state dir, scopes)
|
|
54
55
|
```
|
|
55
56
|
|
|
56
57
|
**Transport strategy:**
|
|
57
58
|
|
|
58
59
|
```
|
|
59
|
-
Agent / Claude / Gemini
|
|
60
|
+
Agent / Claude / Gemini / Codex / Copilot / Vibe
|
|
60
61
|
│
|
|
61
62
|
├─── HTTP (homelab) ──→ https://linkedin.kpihx-labs.com/mcp (primary)
|
|
62
63
|
└─── stdio (local) ──→ ~/.bun/bin/linkedin-mcp serve (fallback)
|
|
@@ -84,37 +85,59 @@ Agent / Claude / Gemini
|
|
|
84
85
|
### CLI (`linkedin-admin`)
|
|
85
86
|
|
|
86
87
|
```bash
|
|
87
|
-
|
|
88
|
-
linkedin-admin
|
|
89
|
-
linkedin-admin
|
|
90
|
-
linkedin-admin
|
|
91
|
-
linkedin-admin
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
linkedin-admin
|
|
88
|
+
# Authentication
|
|
89
|
+
linkedin-admin auth [--port N] # OAuth flow — opens browser, saves token (default port 3001)
|
|
90
|
+
linkedin-admin token set <value> # Set access token directly (fetches profile, no browser)
|
|
91
|
+
linkedin-admin token unset # Clear stored token (same as logout)
|
|
92
|
+
linkedin-admin logout # Clear stored token
|
|
93
|
+
|
|
94
|
+
# Credential management (stored in admin env file)
|
|
95
|
+
linkedin-admin client-id set <v> # Set LINKEDIN_CLIENT_ID
|
|
96
|
+
linkedin-admin client-id unset # Clear LINKEDIN_CLIENT_ID
|
|
97
|
+
linkedin-admin client-secret set <v>
|
|
98
|
+
linkedin-admin client-secret unset
|
|
99
|
+
|
|
100
|
+
# Status & observability
|
|
101
|
+
linkedin-admin status # Credentials table + token summary
|
|
102
|
+
linkedin-admin status --json # Machine-readable JSON
|
|
103
|
+
linkedin-admin logs [--lines 50] # Tail admin log
|
|
104
|
+
linkedin-admin health # HTTP server reachability
|
|
105
|
+
linkedin-admin urls # All public/private endpoints
|
|
95
106
|
```
|
|
96
107
|
|
|
97
108
|
### HTTP admin
|
|
98
109
|
|
|
99
|
-
| Endpoint | Description |
|
|
100
|
-
|
|
101
|
-
|
|
|
102
|
-
|
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
-
|
|
|
110
|
+
| Endpoint | Method | Description |
|
|
111
|
+
|----------|--------|-------------|
|
|
112
|
+
| `/health` | GET | Liveness probe |
|
|
113
|
+
| `/admin/status` | GET | Full runtime status (token + Telegram state) |
|
|
114
|
+
| `/admin/help` | GET | All commands reference |
|
|
115
|
+
| `/admin/logs?lines=50` | GET | Recent admin log tail |
|
|
116
|
+
| `/admin/client-id/set` | POST | Body: `{ "value": "..." }` |
|
|
117
|
+
| `/admin/client-id/unset` | POST | Clear LINKEDIN_CLIENT_ID |
|
|
118
|
+
| `/admin/client-secret/set` | POST | Body: `{ "value": "..." }` |
|
|
119
|
+
| `/admin/client-secret/unset` | POST | Clear LINKEDIN_CLIENT_SECRET |
|
|
120
|
+
| `/admin/token/set` | POST | Body: `{ "value": "..." }` — sets token + fetches profile |
|
|
121
|
+
| `/admin/token/unset` | POST | Clear stored token |
|
|
122
|
+
| `/mcp` | POST/GET/DELETE | MCP Streamable HTTP transport |
|
|
106
123
|
|
|
107
124
|
### Telegram bot (optional)
|
|
108
125
|
|
|
109
|
-
Set `
|
|
126
|
+
Set `TELEGRAM_LINKEDIN_HOMELAB_TOKEN` + `TELEGRAM_CHAT_IDS` — server polls every 5 s.
|
|
110
127
|
|
|
111
128
|
| Command | Action |
|
|
112
129
|
|---------|--------|
|
|
113
130
|
| `/start` `/help` | Full command reference |
|
|
114
|
-
| `/status` |
|
|
131
|
+
| `/status` | Credentials + token status |
|
|
115
132
|
| `/health` | HTTP server health |
|
|
116
133
|
| `/urls` | Endpoint map |
|
|
117
134
|
| `/logs [n]` | Last n admin log lines (default 20) |
|
|
135
|
+
| `/token_set <value>` | Set access token (fetches profile) |
|
|
136
|
+
| `/token_unset` | Clear stored token |
|
|
137
|
+
| `/client_id_set <value>` | Set LINKEDIN_CLIENT_ID |
|
|
138
|
+
| `/client_id_unset` | Clear LINKEDIN_CLIENT_ID |
|
|
139
|
+
| `/client_secret_set <value>` | Set LINKEDIN_CLIENT_SECRET |
|
|
140
|
+
| `/client_secret_unset` | Clear LINKEDIN_CLIENT_SECRET |
|
|
118
141
|
|
|
119
142
|
---
|
|
120
143
|
|
|
@@ -124,14 +147,21 @@ Set `TELEGRAM_LINKEDIN_TOKEN` + `TELEGRAM_CHAT_IDS` and the server polls every 5
|
|
|
124
147
|
|
|
125
148
|
1. Go to [LinkedIn Developer Portal](https://developer.linkedin.com)
|
|
126
149
|
2. Create an app → add products: **"Sign In with LinkedIn using OpenID Connect"** + **"Share on LinkedIn"**
|
|
127
|
-
3. Set redirect URI: `http://localhost:
|
|
150
|
+
3. Set redirect URI: `http://localhost:3001/callback`
|
|
128
151
|
4. Note `Client ID` and `Client Secret`
|
|
129
152
|
|
|
130
153
|
### 2. Configure secrets
|
|
131
154
|
|
|
132
155
|
```bash
|
|
133
|
-
|
|
156
|
+
# Option A — via admin CLI (written to admin env file)
|
|
157
|
+
linkedin-admin client-id set <your_client_id>
|
|
158
|
+
linkedin-admin client-secret set <your_client_secret>
|
|
159
|
+
|
|
160
|
+
# Option B — via .env file
|
|
161
|
+
cp src/.env.example src/.env
|
|
134
162
|
# Fill in LINKEDIN_CLIENT_ID and LINKEDIN_CLIENT_SECRET
|
|
163
|
+
|
|
164
|
+
# Option C — via bw-env / kshrc (injected at login shell)
|
|
135
165
|
```
|
|
136
166
|
|
|
137
167
|
### 3. Install & authenticate
|
|
@@ -140,8 +170,14 @@ cp src/.env.example .env
|
|
|
140
170
|
bun install
|
|
141
171
|
bun link # editable install: ~/.bun/bin/linkedin-mcp + linkedin-admin
|
|
142
172
|
|
|
143
|
-
|
|
144
|
-
linkedin-admin
|
|
173
|
+
# Full OAuth flow (browser consent)
|
|
174
|
+
linkedin-admin auth # default port 3001
|
|
175
|
+
linkedin-admin auth --port 3002 # if 3001 is busy
|
|
176
|
+
|
|
177
|
+
# Or set access token directly (server/headless contexts)
|
|
178
|
+
linkedin-admin token set <your_access_token>
|
|
179
|
+
|
|
180
|
+
linkedin-admin status # verify credentials and token
|
|
145
181
|
```
|
|
146
182
|
|
|
147
183
|
### 4. Start MCP server
|
|
@@ -159,9 +195,9 @@ linkedin-mcp serve-http
|
|
|
159
195
|
## Docker / Homelab deployment
|
|
160
196
|
|
|
161
197
|
```bash
|
|
162
|
-
|
|
163
|
-
cp
|
|
164
|
-
# Edit
|
|
198
|
+
cd deploy
|
|
199
|
+
cp docker-compose.override.example.yml docker-compose.override.yml
|
|
200
|
+
# Edit override with your secrets (or rely on CI env injection)
|
|
165
201
|
|
|
166
202
|
docker compose up -d
|
|
167
203
|
```
|
|
@@ -170,6 +206,10 @@ Traefik labels in `deploy/docker-compose.yml` expose:
|
|
|
170
206
|
- `https://linkedin.kpihx-labs.com` (primary private trusted route)
|
|
171
207
|
- `https://linkedin.homelab` (fallback)
|
|
172
208
|
|
|
209
|
+
The container persists all state in `/data` (Docker volume):
|
|
210
|
+
- `/data/state/token.json` — OAuth token
|
|
211
|
+
- `/data/linkedin-admin.env` — credentials written by `token set` / `client-id set`
|
|
212
|
+
|
|
173
213
|
---
|
|
174
214
|
|
|
175
215
|
## Agent registration
|
|
@@ -192,11 +232,9 @@ Traefik labels in `deploy/docker-compose.yml` expose:
|
|
|
192
232
|
```json
|
|
193
233
|
{
|
|
194
234
|
"name": "linkedin-mcp",
|
|
195
|
-
"version": "0.1
|
|
235
|
+
"version": "0.2.1",
|
|
196
236
|
"mcpServers": {
|
|
197
|
-
"linkedin-mcp": {
|
|
198
|
-
"httpUrl": "https://linkedin.kpihx-labs.com/mcp"
|
|
199
|
-
},
|
|
237
|
+
"linkedin-mcp": { "httpUrl": "https://linkedin.kpihx-labs.com/mcp" },
|
|
200
238
|
"linkedin-mcp--fallback": {
|
|
201
239
|
"command": "/home/kpihx/.bun/bin/linkedin-mcp",
|
|
202
240
|
"args": ["serve"]
|
|
@@ -205,12 +243,49 @@ Traefik labels in `deploy/docker-compose.yml` expose:
|
|
|
205
243
|
}
|
|
206
244
|
```
|
|
207
245
|
|
|
246
|
+
### Codex (`~/.codex/config.toml`)
|
|
247
|
+
|
|
248
|
+
```toml
|
|
249
|
+
[mcp_servers.linkedin_mcp]
|
|
250
|
+
url = "https://linkedin.kpihx-labs.com/mcp"
|
|
251
|
+
|
|
252
|
+
[mcp_servers.linkedin_mcp_fallback]
|
|
253
|
+
command = "/home/kpihx/.bun/bin/linkedin-mcp"
|
|
254
|
+
args = ["serve"]
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Copilot (`~/.copilot/mcp-config.json`)
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
"linkedin_mcp": { "type": "http", "url": "https://linkedin.kpihx-labs.com/mcp" },
|
|
261
|
+
"linkedin_mcp_fallback": {
|
|
262
|
+
"type": "stdio",
|
|
263
|
+
"command": "/home/kpihx/.bun/bin/linkedin-mcp",
|
|
264
|
+
"args": ["serve"]
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Vibe (`~/.vibe/config.toml`)
|
|
269
|
+
|
|
270
|
+
```toml
|
|
271
|
+
[[mcp_servers]]
|
|
272
|
+
name = "linkedin"
|
|
273
|
+
transport = "http"
|
|
274
|
+
url = "https://linkedin.kpihx-labs.com/mcp"
|
|
275
|
+
|
|
276
|
+
[[mcp_servers]]
|
|
277
|
+
name = "linkedin_fallback"
|
|
278
|
+
transport = "stdio"
|
|
279
|
+
command = "/home/kpihx/.bun/bin/linkedin-mcp"
|
|
280
|
+
args = ["serve"]
|
|
281
|
+
```
|
|
282
|
+
|
|
208
283
|
---
|
|
209
284
|
|
|
210
285
|
## Tests
|
|
211
286
|
|
|
212
287
|
```bash
|
|
213
|
-
bun test #
|
|
288
|
+
bun test # 61 tests, 5 files, 0 live API calls
|
|
214
289
|
bun test --watch # watch mode
|
|
215
290
|
```
|
|
216
291
|
|
|
@@ -223,11 +298,14 @@ All tests use mocked `fetch` and temp directories — no credentials needed.
|
|
|
223
298
|
LinkedIn personal OAuth tokens expire in **60 days**. There is no refresh token for non-Partner apps.
|
|
224
299
|
|
|
225
300
|
```
|
|
226
|
-
Day 0 → linkedin-admin auth
|
|
227
|
-
|
|
301
|
+
Day 0 → linkedin-admin auth (OAuth flow, browser consent)
|
|
302
|
+
OR linkedin-admin token set (direct token, headless/server)
|
|
303
|
+
Day 55+ → linkedin-admin status (shows days_left)
|
|
228
304
|
Day 60 → token invalid, re-run auth
|
|
229
305
|
```
|
|
230
306
|
|
|
307
|
+
Both flows produce a complete `token.json` with `member_id` populated — required for all posting tools.
|
|
308
|
+
|
|
231
309
|
---
|
|
232
310
|
|
|
233
311
|
## License
|
package/package.json
CHANGED
package/src/admin/cli.js
CHANGED
|
@@ -25,7 +25,6 @@ const { loadConfig, resolveEnv, ENV_VARS } = require("../config");
|
|
|
25
25
|
const { tokenSummary, clearToken } = require("../token");
|
|
26
26
|
const { runOAuthFlow } = require("./oauth");
|
|
27
27
|
const {
|
|
28
|
-
adminEnvFilePath,
|
|
29
28
|
adminHelpText,
|
|
30
29
|
appendAdminLog,
|
|
31
30
|
getLogsText,
|
|
@@ -34,6 +33,7 @@ const {
|
|
|
34
33
|
setAccessToken,
|
|
35
34
|
setClientId,
|
|
36
35
|
setClientSecret,
|
|
36
|
+
stateDir,
|
|
37
37
|
statusSummaryText,
|
|
38
38
|
unsetAccessToken,
|
|
39
39
|
unsetClientId,
|
|
@@ -178,7 +178,7 @@ program
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
console.log();
|
|
181
|
-
console.log(`
|
|
181
|
+
console.log(`State dir : ${stateDir()}`);
|
|
182
182
|
console.log();
|
|
183
183
|
|
|
184
184
|
// Credentials table
|
|
@@ -297,8 +297,8 @@ const tokenCmd = program
|
|
|
297
297
|
tokenCmd
|
|
298
298
|
.command("set <value>")
|
|
299
299
|
.description("Set access token directly (bypasses OAuth flow, 60-day default expiry).")
|
|
300
|
-
.action((value) => {
|
|
301
|
-
setAccessToken(value);
|
|
300
|
+
.action(async (value) => {
|
|
301
|
+
await setAccessToken(value);
|
|
302
302
|
console.log("\nAccess token set successfully. linkedin-mcp is ready.\n");
|
|
303
303
|
});
|
|
304
304
|
|
package/src/admin/service.js
CHANGED
|
@@ -124,11 +124,27 @@ function unsetClientSecret() {
|
|
|
124
124
|
|
|
125
125
|
/**
|
|
126
126
|
* Persist an access token directly (bypasses OAuth flow).
|
|
127
|
-
*
|
|
127
|
+
* Does a best-effort GET /v2/userinfo to populate member_id, name, email
|
|
128
|
+
* in token.json — required for posting tools. If the fetch fails (bad token
|
|
129
|
+
* or no network), the token is still saved without profile info.
|
|
128
130
|
*/
|
|
129
|
-
function setAccessToken(value) {
|
|
130
|
-
const config
|
|
131
|
-
|
|
131
|
+
async function setAccessToken(value) {
|
|
132
|
+
const config = loadConfig();
|
|
133
|
+
const tokenData = { access_token: value, expires_in: 5184000 };
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const res = await fetch("https://api.linkedin.com/v2/userinfo", {
|
|
137
|
+
headers: { Authorization: `Bearer ${value}` },
|
|
138
|
+
});
|
|
139
|
+
if (res.ok) {
|
|
140
|
+
const profile = await res.json();
|
|
141
|
+
if (profile.sub) tokenData.member_id = profile.sub;
|
|
142
|
+
if (profile.name) tokenData.name = profile.name;
|
|
143
|
+
if (profile.email) tokenData.email = profile.email;
|
|
144
|
+
}
|
|
145
|
+
} catch { /* best-effort only — token is saved regardless */ }
|
|
146
|
+
|
|
147
|
+
saveToken(config, tokenData);
|
|
132
148
|
appendAdminLog(`token set (${_maskValue(value)})`);
|
|
133
149
|
}
|
|
134
150
|
|
package/src/admin/telegram.js
CHANGED
|
@@ -105,7 +105,7 @@ async function dispatchTelegramCommand(command, args) {
|
|
|
105
105
|
if (command === "/token_set") {
|
|
106
106
|
const value = args[0];
|
|
107
107
|
if (!value) return "Usage: /token_set <access_token>";
|
|
108
|
-
setAccessToken(value);
|
|
108
|
+
await setAccessToken(value);
|
|
109
109
|
return "Access token set successfully. linkedin-mcp is ready.";
|
|
110
110
|
}
|
|
111
111
|
|
package/src/http_app.js
CHANGED
|
@@ -162,10 +162,10 @@ function adminClientSecretUnsetHandler() {
|
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
function adminTokenSetHandler() {
|
|
165
|
-
return (req, res) => {
|
|
165
|
+
return async (req, res) => {
|
|
166
166
|
const { value } = req.body || {};
|
|
167
167
|
if (!value) return res.status(400).json({ ok: false, error: "Missing 'value' in request body" });
|
|
168
|
-
setAccessToken(String(value));
|
|
168
|
+
await setAccessToken(String(value));
|
|
169
169
|
res.json({ ok: true, action: "token set" });
|
|
170
170
|
};
|
|
171
171
|
}
|
|
@@ -161,16 +161,16 @@ describe("credential management (setClientSecret / unsetClientSecret)", () => {
|
|
|
161
161
|
});
|
|
162
162
|
|
|
163
163
|
describe("credential management (setAccessToken / unsetAccessToken)", () => {
|
|
164
|
-
test("setAccessToken writes token.json", () => {
|
|
165
|
-
setAccessToken("my-test-access-token");
|
|
164
|
+
test("setAccessToken writes token.json", async () => {
|
|
165
|
+
await setAccessToken("my-test-access-token");
|
|
166
166
|
const tokenPath = require("path").join(TEST_STATE_DIR, "token.json");
|
|
167
167
|
expect(fs.existsSync(tokenPath)).toBe(true);
|
|
168
168
|
const data = JSON.parse(fs.readFileSync(tokenPath, "utf-8"));
|
|
169
169
|
expect(data.access_token).toBe("my-test-access-token");
|
|
170
170
|
});
|
|
171
171
|
|
|
172
|
-
test("unsetAccessToken removes token.json", () => {
|
|
173
|
-
setAccessToken("temp-token");
|
|
172
|
+
test("unsetAccessToken removes token.json", async () => {
|
|
173
|
+
await setAccessToken("temp-token");
|
|
174
174
|
unsetAccessToken();
|
|
175
175
|
const tokenPath = require("path").join(TEST_STATE_DIR, "token.json");
|
|
176
176
|
expect(fs.existsSync(tokenPath)).toBe(false);
|
package/tests/telegram.test.js
CHANGED
|
@@ -138,7 +138,7 @@ describe("dispatchTelegramCommand — credential management", () => {
|
|
|
138
138
|
});
|
|
139
139
|
|
|
140
140
|
test("/token_unset clears the access token", async () => {
|
|
141
|
-
await dispatchTelegramCommand("/token_set", ["temp-token"]);
|
|
141
|
+
await dispatchTelegramCommand("/token_set", ["temp-token"]); // network fails gracefully
|
|
142
142
|
const reply = await dispatchTelegramCommand("/token_unset", []);
|
|
143
143
|
expect(reply).toContain("cleared");
|
|
144
144
|
const tokenPath = path.join(TEST_STATE_DIR, "token.json");
|