@lofder/dsers-mcp-product 1.3.8 → 1.4.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/README.md CHANGED
@@ -30,7 +30,7 @@
30
30
  - **Safety checks** — automatically blocks pushes that would result in below-cost pricing, zero price, or zero stock
31
31
  - **SEO optimization** — let AI rewrite the title and description for better search rankings before pushing
32
32
 
33
- The server is hosted on [Vercel](https://dsers-mcp-product.vercel.app/api/mcp) and published across multiple platforms:
33
+ The server is hosted on [Vercel](https://ai.silentrillmcp.com/dropshipping/mcp) and published across multiple platforms:
34
34
 
35
35
  ### Available On
36
36
 
@@ -105,21 +105,33 @@ A browser window opens to the official DSers login page. You log in on DSers's o
105
105
 
106
106
  That's it. No passwords in config files.
107
107
 
108
+ **Remote server (no install needed):**
109
+
110
+ If you don't want to install anything locally, you can connect directly to the hosted MCP server at `https://ai.silentrillmcp.com/dropshipping/mcp`. This works with any MCP client that supports Streamable HTTP transport. You'll be prompted to authorize with your DSers account on first connect.
111
+
112
+ ```json
113
+ {
114
+ "mcpServers": {
115
+ "dropshipping": {
116
+ "url": "https://ai.silentrillmcp.com/dropshipping/mcp"
117
+ }
118
+ }
119
+ }
120
+ ```
121
+
108
122
  Also listed on the official [MCP Registry](https://registry.modelcontextprotocol.io/servers/io.github.lofder/dsers-mcp-product).
109
123
 
110
- ### Authentication — Zero-Password Login
124
+ ### Authentication — OAuth 2.1
111
125
 
112
126
  Your DSers password **never touches this tool**. Here's how login works:
113
127
 
114
128
  1. You run `npx @lofder/dsers-mcp-product login`
115
- 2. Your browser opens the official DSers login page
116
- 3. You log in on DSers's own website, as usual
117
- 4. The tool picks up your login session and encrypts it locally
118
- 5. Done — from now on, the MCP server just works. No passwords in any config file.
119
-
120
- Works with Chrome, Edge, Brave, and other Chromium browsers. On Mac, Safari works too.
129
+ 2. Your browser opens the DSers OAuth authorization page
130
+ 3. You authorize access on DSers's own website
131
+ 4. The tool receives an OAuth token and encrypts it locally
132
+ 5. Done — the MCP server uses the token automatically. No passwords in config files.
121
133
 
122
- **Sessions last about 6 hours.** When it expires, your AI agent will ask you to run `login` again — takes 10 seconds.
134
+ **Tokens refresh automatically** a long-lived refresh token keeps your session active without manual re-login. You should rarely need to run `login` again.
123
135
 
124
136
  **Switching accounts?**
125
137
 
@@ -128,7 +140,7 @@ npx @lofder/dsers-mcp-product logout
128
140
  npx @lofder/dsers-mcp-product login
129
141
  ```
130
142
 
131
- > **For developers:** The server also accepts a `DSERS_TOKEN` env var for headless/CI environments. For most users, just use `login`.
143
+ > **For developers:** The server also accepts `DSERS_ACCESS_TOKEN` and `DSERS_REFRESH_TOKEN` env vars for headless/CI environments.
132
144
 
133
145
  ### Usage Examples
134
146
 
@@ -329,7 +341,7 @@ MIT
329
341
  - **安全校验** — 推送前自动拦截低于成本价、零售价为零、库存为零的商品
330
342
  - **SEO 优化** — 让 AI 重写标题和描述,提高搜索排名后再推送
331
343
 
332
- 服务已托管在 [Vercel](https://dsers-mcp-product.vercel.app/api/mcp),并发布到多个平台:
344
+ 服务已托管在 [Vercel](https://ai.silentrillmcp.com/dropshipping/mcp),并发布到多个平台:
333
345
 
334
346
  ### 发布平台
335
347
 
@@ -404,21 +416,33 @@ npx @lofder/dsers-mcp-product login
404
416
 
405
417
  搞定。配置文件里不需要任何密码。
406
418
 
419
+ **在线版(免安装):**
420
+
421
+ 如果不想在本地安装,可以直接连接托管的 MCP 服务端 `https://ai.silentrillmcp.com/dropshipping/mcp`。支持 Streamable HTTP transport 的 MCP 客户端都能用,首次连接会引导你授权 DSers 账号。
422
+
423
+ ```json
424
+ {
425
+ "mcpServers": {
426
+ "dropshipping": {
427
+ "url": "https://ai.silentrillmcp.com/dropshipping/mcp"
428
+ }
429
+ }
430
+ }
431
+ ```
432
+
407
433
  同时已收录到官方 [MCP Registry](https://registry.modelcontextprotocol.io/servers/io.github.lofder/dsers-mcp-product)。
408
434
 
409
- ### 授权认证 — 零密码登录
435
+ ### 授权认证 — OAuth 2.1
410
436
 
411
437
  你的 DSers 密码**完全不经过本工具**。登录过程是这样的:
412
438
 
413
439
  1. 运行 `npx @lofder/dsers-mcp-product login`
414
- 2. 浏览器自动打开 DSers 官方登录页
415
- 3. 你在 DSers 网站上正常登录
416
- 4. 工具拿到登录状态,加密存到本地
440
+ 2. 浏览器自动打开 DSers OAuth 授权页
441
+ 3. 你在 DSers 网站上授权
442
+ 4. 工具拿到 OAuth token,加密存到本地
417
443
  5. 搞定 — 之后 MCP 直接能用,配置文件里不需要写任何密码
418
444
 
419
- 支持 Chrome、Edge、Brave 等主流浏览器。Mac Safari 也行。
420
-
421
- **登录大约 6 小时有效。** 过期了 AI 助手会提醒你重新跑一下 `login`,10 秒的事。
445
+ **Token 自动续期** refresh token 会自动保持登录状态,你基本不需要重新登录。
422
446
 
423
447
  **换账号?**
424
448
 
@@ -427,7 +451,7 @@ npx @lofder/dsers-mcp-product logout
427
451
  npx @lofder/dsers-mcp-product login
428
452
  ```
429
453
 
430
- > **开发者注:** headless / CI 环境也支持通过 `DSERS_TOKEN` 环境变量传入凭据。普通用户直接用 `login` 就行。
454
+ > **开发者注:** headless / CI 环境支持通过 `DSERS_ACCESS_TOKEN` `DSERS_REFRESH_TOKEN` 环境变量传入凭据。
431
455
 
432
456
  ### 使用示例
433
457
 
@@ -1,6 +1,6 @@
1
- export interface BrowserMatch {
2
- name: string;
3
- path: string;
4
- }
5
- export declare function findChromiumBrowser(): BrowserMatch | null;
1
+ /**
2
+ * Open a URL in the system's default browser.
3
+ * Uses spawn() with explicit arguments to avoid command injection.
4
+ */
5
+ export declare function openBrowser(url: string): void;
6
6
  //# sourceMappingURL=browser-finder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser-finder.d.ts","sourceRoot":"","sources":["../../src/auth/browser-finder.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AA8FD,wBAAgB,mBAAmB,IAAI,YAAY,GAAG,IAAI,CAMzD"}
1
+ {"version":3,"file":"browser-finder.d.ts","sourceRoot":"","sources":["../../src/auth/browser-finder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAW7C"}
@@ -1,104 +1,17 @@
1
- import { execSync } from "node:child_process";
2
- import { existsSync } from "node:fs";
3
- import { join } from "node:path";
4
- const MACOS_BROWSERS = [
5
- ["Google Chrome", "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"],
6
- ["Microsoft Edge", "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"],
7
- ["Brave Browser", "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"],
8
- ["Arc", "/Applications/Arc.app/Contents/MacOS/Arc"],
9
- ["Chromium", "/Applications/Chromium.app/Contents/MacOS/Chromium"],
10
- ["Opera", "/Applications/Opera.app/Contents/MacOS/Opera"],
11
- ["Vivaldi", "/Applications/Vivaldi.app/Contents/MacOS/Vivaldi"],
12
- ];
13
- const LINUX_COMMANDS = [
14
- ["Google Chrome", "google-chrome-stable"],
15
- ["Google Chrome", "google-chrome"],
16
- ["Chromium", "chromium-browser"],
17
- ["Chromium", "chromium"],
18
- ["Microsoft Edge", "microsoft-edge-stable"],
19
- ["Microsoft Edge", "microsoft-edge"],
20
- ["Brave Browser", "brave-browser"],
21
- ["Brave Browser", "brave-browser-stable"],
22
- ["Opera", "opera"],
23
- ["Vivaldi", "vivaldi-stable"],
24
- ["Vivaldi", "vivaldi"],
25
- ];
26
- const WIN_PATHS = [
27
- ["Google Chrome", "Google\\Chrome\\Application\\chrome.exe"],
28
- ["Microsoft Edge", "Microsoft\\Edge\\Application\\msedge.exe"],
29
- ["Brave Browser", "BraveSoftware\\Brave-Browser\\Application\\brave.exe"],
30
- ["Opera", "Opera\\opera.exe"],
31
- ["Vivaldi", "Vivaldi\\Application\\vivaldi.exe"],
32
- ["Chromium", "Chromium\\Application\\chrome.exe"],
33
- ];
34
- function whichSync(cmd) {
35
- if (!/^[a-z0-9._-]+$/i.test(cmd))
36
- return null;
37
- try {
38
- return execSync(`which ${cmd} 2>/dev/null`, { encoding: "utf-8", timeout: 3000 }).trim() || null;
1
+ /**
2
+ * Open a URL in the system's default browser.
3
+ * Uses spawn() with explicit arguments to avoid command injection.
4
+ */
5
+ import { spawnSync } from "node:child_process";
6
+ export function openBrowser(url) {
7
+ const cmd = process.platform === "darwin" ? "open"
8
+ : process.platform === "win32" ? "cmd"
9
+ : "xdg-open";
10
+ if (process.platform === "win32") {
11
+ spawnSync(cmd, ["/c", "start", "", url], { stdio: "ignore" });
39
12
  }
40
- catch {
41
- return null;
13
+ else {
14
+ spawnSync(cmd, [url], { stdio: "ignore" });
42
15
  }
43
16
  }
44
- function findOnMacOS() {
45
- for (const [name, p] of MACOS_BROWSERS) {
46
- if (existsSync(p))
47
- return { name, path: p };
48
- }
49
- // mdfind fallback for non-standard install locations
50
- try {
51
- const result = execSync('mdfind "kMDItemCFBundleIdentifier == com.google.Chrome" 2>/dev/null', {
52
- encoding: "utf-8",
53
- timeout: 3000,
54
- }).trim();
55
- if (result) {
56
- const appPath = result.split("\n")[0];
57
- const execPath = join(appPath, "Contents/MacOS/Google Chrome");
58
- if (existsSync(execPath))
59
- return { name: "Google Chrome", path: execPath };
60
- }
61
- }
62
- catch {
63
- /* mdfind unavailable or timed out */
64
- }
65
- return null;
66
- }
67
- function findOnLinux() {
68
- for (const [name, cmd] of LINUX_COMMANDS) {
69
- const p = whichSync(cmd);
70
- if (p)
71
- return { name, path: p };
72
- }
73
- return null;
74
- }
75
- function findOnWindows() {
76
- const roots = [
77
- process.env.PROGRAMFILES ?? "C:\\Program Files",
78
- process.env["PROGRAMFILES(X86)"] ?? "C:\\Program Files (x86)",
79
- process.env.LOCALAPPDATA ?? "",
80
- ].filter(Boolean);
81
- for (const [name, rel] of WIN_PATHS) {
82
- for (const root of roots) {
83
- const full = join(root, rel);
84
- if (existsSync(full))
85
- return { name, path: full };
86
- }
87
- }
88
- // Edge is pre-installed on Windows 10+ in a special location
89
- const edgeSys = "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe";
90
- if (existsSync(edgeSys))
91
- return { name: "Microsoft Edge", path: edgeSys };
92
- return null;
93
- }
94
- export function findChromiumBrowser() {
95
- const platform = process.platform;
96
- if (platform === "darwin")
97
- return findOnMacOS();
98
- if (platform === "linux")
99
- return findOnLinux();
100
- if (platform === "win32")
101
- return findOnWindows();
102
- return null;
103
- }
104
17
  //# sourceMappingURL=browser-finder.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser-finder.js","sourceRoot":"","sources":["../../src/auth/browser-finder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browser-finder.js","sourceRoot":"","sources":["../../src/auth/browser-finder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,GAAG,GACP,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM;QACtC,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK;YACtC,CAAC,CAAC,UAAU,CAAC;IAEf,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAChE,CAAC;SAAM,CAAC;QACN,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC"}
@@ -1,6 +1,4 @@
1
- export { findChromiumBrowser, type BrowserMatch } from "./browser-finder.js";
2
- export { loginViaCDP, type CDPLoginResult } from "./cdp-session.js";
3
- export { loginViaSafari, isMacOS, type SafariLoginResult } from "./safari-fallback.js";
4
- export { loginViaTerminal, type TerminalLoginResult } from "./terminal-prompt.js";
5
- export { saveToken, loadToken, clearToken, tokenFilePath, type StoredSession } from "./token-store.js";
1
+ export { openBrowser } from "./browser-finder.js";
2
+ export { authorizeWithPKCE, refreshAccessToken } from "./oauth.js";
3
+ export { saveToken, loadToken, clearToken, tokenFilePath, hasLegacyCredentials, isLegacySession, type StoredSession, } from "./token-store.js";
6
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EACL,SAAS,EACT,SAAS,EACT,UAAU,EACV,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,KAAK,aAAa,GACnB,MAAM,kBAAkB,CAAC"}
@@ -1,6 +1,4 @@
1
- export { findChromiumBrowser } from "./browser-finder.js";
2
- export { loginViaCDP } from "./cdp-session.js";
3
- export { loginViaSafari, isMacOS } from "./safari-fallback.js";
4
- export { loginViaTerminal } from "./terminal-prompt.js";
5
- export { saveToken, loadToken, clearToken, tokenFilePath } from "./token-store.js";
1
+ export { openBrowser } from "./browser-finder.js";
2
+ export { authorizeWithPKCE, refreshAccessToken } from "./oauth.js";
3
+ export { saveToken, loadToken, clearToken, tokenFilePath, hasLegacyCredentials, isLegacySession, } from "./token-store.js";
6
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAqB,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAuB,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,OAAO,EAA0B,MAAM,sBAAsB,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAA4B,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAsB,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EACL,SAAS,EACT,SAAS,EACT,UAAU,EACV,aAAa,EACb,oBAAoB,EACpB,eAAe,GAEhB,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * OAuth 2.1 PKCE authorization flow for DSers MCP.
3
+ *
4
+ * Flow:
5
+ * 1. Fetch AS metadata from /.well-known/oauth-authorization-server
6
+ * 2. Register client dynamically (or reuse saved client_id)
7
+ * 3. Open browser to authorization_endpoint with PKCE challenge
8
+ * 4. Listen on localhost for callback with authorization code
9
+ * 5. Exchange code for access_token + refresh_token
10
+ *
11
+ * Reference: client-simple/src/index.ts (verified working with DSers OAuth AS)
12
+ */
13
+ export interface OAuthTokens {
14
+ access_token: string;
15
+ refresh_token: string;
16
+ expires_at: number;
17
+ client_id: string;
18
+ oauth_base: string;
19
+ base_url: string;
20
+ ts: number;
21
+ }
22
+ /**
23
+ * Full OAuth 2.1 PKCE authorization flow.
24
+ * Opens browser, waits for callback, exchanges code for tokens.
25
+ */
26
+ export declare function authorizeWithPKCE(log: (msg: string) => void, savedClientId?: string, oauthBase?: string): Promise<OAuthTokens>;
27
+ /**
28
+ * Refresh an expired access_token using the refresh_token.
29
+ * Returns updated tokens or throws on failure.
30
+ */
31
+ export declare function refreshAccessToken(refreshToken: string, clientId: string, oauthBase?: string): Promise<{
32
+ access_token: string;
33
+ refresh_token: string;
34
+ expires_at: number;
35
+ }>;
36
+ //# sourceMappingURL=oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAeH,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACZ;AA+GD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EAC1B,aAAa,CAAC,EAAE,MAAM,EACtB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,WAAW,CAAC,CAwCtB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAsB9E"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * OAuth 2.1 PKCE authorization flow for DSers MCP.
3
+ *
4
+ * Flow:
5
+ * 1. Fetch AS metadata from /.well-known/oauth-authorization-server
6
+ * 2. Register client dynamically (or reuse saved client_id)
7
+ * 3. Open browser to authorization_endpoint with PKCE challenge
8
+ * 4. Listen on localhost for callback with authorization code
9
+ * 5. Exchange code for access_token + refresh_token
10
+ *
11
+ * Reference: client-simple/src/index.ts (verified working with DSers OAuth AS)
12
+ */
13
+ import * as http from "node:http";
14
+ import * as crypto from "node:crypto";
15
+ import { spawnSync } from "node:child_process";
16
+ const DEFAULT_OAUTH_BASE = "https://mcp-oauth-api-gw.dsers.com";
17
+ const DEFAULT_BFF_BASE = "https://bff-api-gw.dsers.com";
18
+ const CB_PORT = 3001;
19
+ const REDIRECT_URI = `http://localhost:${CB_PORT}/callback`;
20
+ const OAUTH_SCOPES = "user:read store:read store:write staff:read supplier:read " +
21
+ "product:read product:import product:push " +
22
+ "settings:read billing:read plan:read order:read";
23
+ // ── PKCE helpers ──
24
+ function generateVerifier() {
25
+ return crypto.randomBytes(32).toString("base64url");
26
+ }
27
+ function generateChallenge(verifier) {
28
+ return crypto.createHash("sha256").update(verifier).digest("base64url");
29
+ }
30
+ // ── HTTP helpers ──
31
+ async function fetchJSON(url, init) {
32
+ const res = await fetch(url, init);
33
+ if (!res.ok) {
34
+ const body = await res.text().catch(() => "");
35
+ throw new Error(`${init?.method ?? "GET"} ${url} → ${res.status}: ${body.slice(0, 200)}`);
36
+ }
37
+ return res.json();
38
+ }
39
+ function openBrowser(url, log) {
40
+ const cmd = process.platform === "darwin" ? "open"
41
+ : process.platform === "win32" ? "cmd"
42
+ : "xdg-open";
43
+ try {
44
+ if (process.platform === "win32") {
45
+ spawnSync(cmd, ["/c", "start", "", url], { stdio: "ignore" });
46
+ }
47
+ else {
48
+ spawnSync(cmd, [url], { stdio: "ignore" });
49
+ }
50
+ }
51
+ catch {
52
+ log(`Please open this URL in your browser:\n${url}`);
53
+ }
54
+ }
55
+ // ── OAuth flow ──
56
+ async function getASMetadata(oauthBase) {
57
+ return fetchJSON(`${oauthBase}/.well-known/oauth-authorization-server`);
58
+ }
59
+ async function registerClient(registrationEndpoint, log) {
60
+ log("Registering OAuth client...");
61
+ const data = await fetchJSON(registrationEndpoint, {
62
+ method: "POST",
63
+ headers: { "Content-Type": "application/json" },
64
+ body: JSON.stringify({
65
+ client_name: "DSers MCP Product",
66
+ redirect_uris: [REDIRECT_URI],
67
+ }),
68
+ });
69
+ return data.client_id;
70
+ }
71
+ function waitForCallback(log) {
72
+ return new Promise((resolve, reject) => {
73
+ const server = http.createServer((req, res) => {
74
+ const url = new URL(req.url, `http://localhost:${CB_PORT}`);
75
+ const code = url.searchParams.get("code");
76
+ const error = url.searchParams.get("error");
77
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
78
+ res.end("<html><body><h2>Authorization successful. You can close this tab.</h2></body></html>");
79
+ server.close();
80
+ if (error)
81
+ reject(new Error(`OAuth error: ${error}`));
82
+ else if (code)
83
+ resolve(code);
84
+ else
85
+ reject(new Error("Callback missing authorization code"));
86
+ });
87
+ server.listen(CB_PORT, () => log(`Waiting for OAuth callback on port ${CB_PORT}...`));
88
+ server.on("error", (err) => {
89
+ if (err.code === "EADDRINUSE") {
90
+ reject(new Error(`Port ${CB_PORT} is in use. Close the other process and try again.`));
91
+ }
92
+ else {
93
+ reject(err);
94
+ }
95
+ });
96
+ });
97
+ }
98
+ async function exchangeCode(tokenEndpoint, clientId, code, verifier, oauthBase) {
99
+ return fetchJSON(tokenEndpoint, {
100
+ method: "POST",
101
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
102
+ body: new URLSearchParams({
103
+ grant_type: "authorization_code",
104
+ code,
105
+ code_verifier: verifier,
106
+ client_id: clientId,
107
+ redirect_uri: REDIRECT_URI,
108
+ resource: oauthBase,
109
+ }).toString(),
110
+ });
111
+ }
112
+ /**
113
+ * Full OAuth 2.1 PKCE authorization flow.
114
+ * Opens browser, waits for callback, exchanges code for tokens.
115
+ */
116
+ export async function authorizeWithPKCE(log, savedClientId, oauthBase) {
117
+ const base = oauthBase || process.env.DSERS_OAUTH_BASE || DEFAULT_OAUTH_BASE;
118
+ const meta = await getASMetadata(base);
119
+ // Reuse saved client_id or register new one
120
+ let clientId = savedClientId;
121
+ if (!clientId) {
122
+ clientId = await registerClient(meta.registration_endpoint, log);
123
+ log(`Client registered: ${clientId}`);
124
+ }
125
+ const verifier = generateVerifier();
126
+ const challenge = generateChallenge(verifier);
127
+ const state = crypto.randomBytes(16).toString("hex");
128
+ const authURL = new URL(meta.authorization_endpoint);
129
+ authURL.searchParams.set("response_type", "code");
130
+ authURL.searchParams.set("client_id", clientId);
131
+ authURL.searchParams.set("redirect_uri", REDIRECT_URI);
132
+ authURL.searchParams.set("scope", OAUTH_SCOPES);
133
+ authURL.searchParams.set("code_challenge", challenge);
134
+ authURL.searchParams.set("code_challenge_method", "S256");
135
+ authURL.searchParams.set("state", state);
136
+ authURL.searchParams.set("resource", base);
137
+ log("Opening browser for DSers authorization...");
138
+ openBrowser(authURL.toString(), log);
139
+ const code = await waitForCallback(log);
140
+ const tokenData = await exchangeCode(meta.token_endpoint, clientId, code, verifier, base);
141
+ return {
142
+ access_token: tokenData.access_token,
143
+ refresh_token: tokenData.refresh_token,
144
+ expires_at: Date.now() + tokenData.expires_in * 1000 - 60_000,
145
+ client_id: clientId,
146
+ oauth_base: base,
147
+ base_url: process.env.DSERS_BASE_URL || DEFAULT_BFF_BASE,
148
+ ts: Date.now(),
149
+ };
150
+ }
151
+ /**
152
+ * Refresh an expired access_token using the refresh_token.
153
+ * Returns updated tokens or throws on failure.
154
+ */
155
+ export async function refreshAccessToken(refreshToken, clientId, oauthBase) {
156
+ const base = oauthBase || DEFAULT_OAUTH_BASE;
157
+ const meta = await getASMetadata(base);
158
+ const data = await fetchJSON(meta.token_endpoint, {
159
+ method: "POST",
160
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
161
+ body: new URLSearchParams({
162
+ grant_type: "refresh_token",
163
+ refresh_token: refreshToken,
164
+ client_id: clientId,
165
+ }).toString(),
166
+ });
167
+ return {
168
+ access_token: data.access_token,
169
+ refresh_token: data.refresh_token,
170
+ expires_at: Date.now() + data.expires_in * 1000 - 60_000,
171
+ };
172
+ }
173
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,19 @@
1
+ /** OAuth token storage format (shared with DSClaw) */
1
2
  export interface StoredSession {
2
- session_id: string;
3
- state: string;
3
+ access_token: string;
4
+ refresh_token: string;
5
+ expires_at: number;
6
+ client_id: string;
7
+ oauth_base: string;
4
8
  base_url: string;
5
9
  ts: number;
6
10
  }
11
+ /** Check if a parsed credential is the old CDP format */
12
+ export declare function isLegacySession(obj: Record<string, unknown>): boolean;
7
13
  export declare function saveToken(session: StoredSession): void;
8
14
  export declare function loadToken(): StoredSession | null;
9
15
  export declare function clearToken(): boolean;
10
16
  export declare function tokenFilePath(): string;
17
+ /** Check if a legacy CDP credential file exists (for migration messaging) */
18
+ export declare function hasLegacyCredentials(): boolean;
11
19
  //# sourceMappingURL=token-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACZ;AA8CD,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAYtD;AAED,wBAAgB,SAAS,IAAI,aAAa,GAAG,IAAI,CAahD;AAED,wBAAgB,UAAU,IAAI,OAAO,CASpC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
1
+ {"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AAWA,sDAAsD;AACtD,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACZ;AAsDD,yDAAyD;AACzD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAErE;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAYtD;AAED,wBAAgB,SAAS,IAAI,aAAa,GAAG,IAAI,CAkBhD;AAED,wBAAgB,UAAU,IAAI,OAAO,CASpC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,IAAI,OAAO,CAY9C"}
@@ -49,6 +49,10 @@ function decrypt(encoded) {
49
49
  return null;
50
50
  }
51
51
  }
52
+ /** Check if a parsed credential is the old CDP format */
53
+ export function isLegacySession(obj) {
54
+ return typeof obj.session_id === "string" && !obj.access_token;
55
+ }
52
56
  export function saveToken(session) {
53
57
  const dir = getCredDir();
54
58
  mkdirSync(dir, { recursive: true, mode: 0o700 });
@@ -72,8 +76,12 @@ export function loadToken() {
72
76
  const json = decrypt(encrypted);
73
77
  if (!json)
74
78
  return null;
75
- const session = JSON.parse(json);
76
- if (!session.session_id)
79
+ const parsed = JSON.parse(json);
80
+ // Detect legacy CDP format → caller should prompt re-login
81
+ if (isLegacySession(parsed))
82
+ return null;
83
+ const session = parsed;
84
+ if (!session.access_token)
77
85
  return null;
78
86
  return session;
79
87
  }
@@ -96,4 +104,21 @@ export function clearToken() {
96
104
  export function tokenFilePath() {
97
105
  return getCredFile();
98
106
  }
107
+ /** Check if a legacy CDP credential file exists (for migration messaging) */
108
+ export function hasLegacyCredentials() {
109
+ const file = getCredFile();
110
+ if (!existsSync(file))
111
+ return false;
112
+ try {
113
+ const encrypted = readFileSync(file, "utf-8").trim();
114
+ const json = decrypt(encrypted);
115
+ if (!json)
116
+ return false;
117
+ const parsed = JSON.parse(json);
118
+ return isLegacySession(parsed);
119
+ }
120
+ catch {
121
+ return false;
122
+ }
123
+ }
99
124
  //# sourceMappingURL=token-store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"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"}