@lofder/dsers-mcp-product 1.3.8 → 1.5.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 (100) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +107 -26
  3. package/dist/auth/browser-finder.d.ts +5 -5
  4. package/dist/auth/browser-finder.d.ts.map +1 -1
  5. package/dist/auth/browser-finder.js +13 -100
  6. package/dist/auth/browser-finder.js.map +1 -1
  7. package/dist/auth/index.d.ts +3 -5
  8. package/dist/auth/index.d.ts.map +1 -1
  9. package/dist/auth/index.js +3 -5
  10. package/dist/auth/index.js.map +1 -1
  11. package/dist/auth/oauth.d.ts +36 -0
  12. package/dist/auth/oauth.d.ts.map +1 -0
  13. package/dist/auth/oauth.js +173 -0
  14. package/dist/auth/oauth.js.map +1 -0
  15. package/dist/auth/token-store.d.ts +10 -2
  16. package/dist/auth/token-store.d.ts.map +1 -1
  17. package/dist/auth/token-store.js +27 -2
  18. package/dist/auth/token-store.js.map +1 -1
  19. package/dist/cli.js +38 -63
  20. package/dist/cli.js.map +1 -1
  21. package/dist/dsers/auth.d.ts +5 -6
  22. package/dist/dsers/auth.d.ts.map +1 -1
  23. package/dist/dsers/auth.js +72 -88
  24. package/dist/dsers/auth.js.map +1 -1
  25. package/dist/dsers/client.d.ts +9 -2
  26. package/dist/dsers/client.d.ts.map +1 -1
  27. package/dist/dsers/client.js +24 -10
  28. package/dist/dsers/client.js.map +1 -1
  29. package/dist/dsers/config.d.ts +19 -16
  30. package/dist/dsers/config.d.ts.map +1 -1
  31. package/dist/dsers/config.js +22 -31
  32. package/dist/dsers/config.js.map +1 -1
  33. package/dist/dsers/product.d.ts +56 -0
  34. package/dist/dsers/product.d.ts.map +1 -1
  35. package/dist/dsers/product.js +64 -0
  36. package/dist/dsers/product.js.map +1 -1
  37. package/dist/dsers/retry.d.ts +59 -0
  38. package/dist/dsers/retry.d.ts.map +1 -0
  39. package/dist/dsers/retry.js +214 -0
  40. package/dist/dsers/retry.js.map +1 -0
  41. package/dist/error-map.d.ts.map +1 -1
  42. package/dist/error-map.js +15 -3
  43. package/dist/error-map.js.map +1 -1
  44. package/dist/index.d.ts +2 -2
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +17 -10
  47. package/dist/index.js.map +1 -1
  48. package/dist/instructions.d.ts.map +1 -1
  49. package/dist/instructions.js +14 -2
  50. package/dist/instructions.js.map +1 -1
  51. package/dist/logger.d.ts +55 -0
  52. package/dist/logger.d.ts.map +1 -0
  53. package/dist/logger.js +122 -0
  54. package/dist/logger.js.map +1 -0
  55. package/dist/provider/sku-matcher.d.ts +154 -0
  56. package/dist/provider/sku-matcher.d.ts.map +1 -0
  57. package/dist/provider/sku-matcher.js +1175 -0
  58. package/dist/provider/sku-matcher.js.map +1 -0
  59. package/dist/service/index.d.ts +2 -0
  60. package/dist/service/index.d.ts.map +1 -1
  61. package/dist/service/index.js +7 -0
  62. package/dist/service/index.js.map +1 -1
  63. package/dist/service/preview.d.ts +1 -1
  64. package/dist/service/preview.d.ts.map +1 -1
  65. package/dist/service/preview.js +48 -21
  66. package/dist/service/preview.js.map +1 -1
  67. package/dist/service/sku-mapping.d.ts +674 -0
  68. package/dist/service/sku-mapping.d.ts.map +1 -0
  69. package/dist/service/sku-mapping.js +1879 -0
  70. package/dist/service/sku-mapping.js.map +1 -0
  71. package/dist/tools.d.ts +3 -0
  72. package/dist/tools.d.ts.map +1 -1
  73. package/dist/tools.js +322 -2
  74. package/dist/tools.js.map +1 -1
  75. package/package.json +5 -2
  76. package/dist/auth/cdp-session.d.ts +0 -6
  77. package/dist/auth/cdp-session.d.ts.map +0 -1
  78. package/dist/auth/cdp-session.js +0 -369
  79. package/dist/auth/cdp-session.js.map +0 -1
  80. package/dist/auth/safari-fallback.d.ts +0 -7
  81. package/dist/auth/safari-fallback.d.ts.map +0 -1
  82. package/dist/auth/safari-fallback.js +0 -73
  83. package/dist/auth/safari-fallback.js.map +0 -1
  84. package/dist/auth/terminal-prompt.d.ts +0 -6
  85. package/dist/auth/terminal-prompt.d.ts.map +0 -1
  86. package/dist/auth/terminal-prompt.js +0 -118
  87. package/dist/auth/terminal-prompt.js.map +0 -1
  88. package/dist/provider.d.ts +0 -90
  89. package/dist/provider.d.ts.map +0 -1
  90. package/dist/provider.js +0 -1577
  91. package/dist/provider.js.map +0 -1
  92. package/dist/service/browse.d.ts +0 -20
  93. package/dist/service/browse.d.ts.map +0 -1
  94. package/dist/service/browse.js +0 -159
  95. package/dist/service/browse.js.map +0 -1
  96. package/dist/service.d.ts +0 -27
  97. package/dist/service.d.ts.map +0 -1
  98. package/dist/service.js +0 -949
  99. package/dist/service.js.map +0 -1
  100. package/dist/tsconfig.tsbuildinfo +0 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,71 @@
1
+ # Changelog
2
+
3
+ ## 1.5.0 — 2026-04-10
4
+
5
+ 大版本。这版终于把换供应商这件事搞定了。
6
+
7
+ ### 新工具:`dsers_sku_remap`
8
+
9
+ 背景是之前一直有一个绕不过去的场景 —— 老供应商涨价 / 断货 / 被封了,你想换一个新的。DSers 后台手动点也能换,但是每个变体每个变体点下去非常反人类,更不用说你要把 seller 的 variant 精确对应到新 supplier 的哪个 SKU。我之前做了一个单独的 sku-matcher 引擎(用 option 对齐 + 单位规范化 + 同义词表 + dHash 图像相似度做打分),原本是内部工具,这一版整个搬进 MCP 接口,让 AI agent 可以一句话完成整个替换。
10
+
11
+ 用法:
12
+
13
+ - **STRICT 模式**: 你已经有了新供应商的 URL,直接传 `new_supplier_url`,工具用 sku-matcher 对齐两边的 variant,按 `auto_confidence` 阈值决定哪些自动换、哪些保留老的、哪些标记 unmatched 让你手动决定
14
+ - **DISCOVER 模式**: 你只知道"这个供应商不能用了,帮我找一个替代",不传 URL,工具反向图搜 DSers 池,按 `sku 分 + 图搜频次 + 商品评分 + 店铺评分 + 价格接近度 + 库存 + 订单数` 的多因子公式打分排序,自动挑最好的候选
15
+
16
+ 两种模式都是两步走:永远先 `mode='preview'`(只读)看 `diffs` 和 `pool_additions`,确认没问题再 `mode='apply'` 真正写入。
17
+
18
+ 几个关键设计决定:
19
+
20
+ - **老供应商会归档到 mapping.pool[] 历史池**,不是直接丢掉。万一新供应商有问题,`dsers_sku_remap` 下一次跑可以重新考虑老 supplier 作为候选
21
+ - **pool 只增不减**,有一个 validator 专门守着这条不变式
22
+ - **写入前做 6 条结构校验**,包括 supplyProductId 数字格式、supplyVariantId 格式、optionId/valueId 对应关系,防止把 DSers 写脏
23
+ - **自动识别 Basic vs Standard mapping type**,根据 variant 选项对齐情况决定用哪种 mapping schema。对齐不全时强制 degrade 到 Standard 并在 warnings 里说明原因
24
+
25
+ ### 其它工具完善
26
+
27
+ 顺手把之前零散的 3 个工具也正式加进 inputSchema / 描述里(之前 1.4.x 已经写过代码但 manifest / server.json 这些元数据文件没同步,这次补上):
28
+
29
+ - `dsers_import_list` — 浏览导入待推送列表,带成本 / 售价 / 库存 / 加价状态
30
+ - `dsers_my_products` — 查看已经推到店铺的商品(附供应商链接方便重新导入)
31
+ - `dsers_find_product` — DSers 商品池搜索,支持关键词和以图搜图,结果能直接导入
32
+
33
+ 加上 `dsers_sku_remap`,这版从 9 个工具扩到 13 个。manifest.json 的 tools 数组之前漏了这 4 个,这版一起补齐。
34
+
35
+ ### 稳定性修复
36
+
37
+ `dsers_sku_remap` 在 round-1 到 round-5.7.5 中一共发现并修了 15 个问题。按照发现轮次排列的关键几条:
38
+
39
+ - **F1 Path A 候选抓取加 pool-detail fallback** — 当用户传的 supplier URL 已经被账号里别的 product 占用时,DSers import-list 会直接报 "already exists" 但没给 importListId,导致原来的 Path A 走 5-26 秒的 timeout。现在会自动 fallback 到 `product-pool/product/detail` 直接查,几百 ms 返回
40
+ - **F2 入口加 store 归属校验** — shape 合法但不属于账号的 store_id 之前会一路走到 Path B rank_candidates 之后才报"no viable match",看不懂什么意思。现在先跑 `getMyProducts(page:1,size:100)` 做归属检查,几百 ms 内出干净的 `store_id_mismatch` envelope
41
+ - **F3 MCP 边界输入 shape 守卫** — 空字符串 / 字母 / 200 位长数字 / 特殊符号的 `dsers_product_id` 现在都在 MCP 层 0-1ms 被 refuse,不进 handler
42
+ - **F4 CODEC 文案收敛** — DSers 后端的 CODEC reason 码之前会透出到用户看到的错误里(内容是 Go map 的文字表示,不可读),现在统一包装成 `store_id_mismatch` envelope 带 `dsers_store_discover` 和 `dsers_my_products` 的明确修复指引
43
+ - **M1 int64 范围生产级收敛** — store_id / dsers_product_id 的 19 位数字现在不光用 regex 校验,MCP 层加了 BigInt `refine()` 检查 signed int64 上界(9223372036854775807)。超界值直接在 MCP 边界被拒,避免踩 DSers 后端的 int64 解析 bug
44
+ - **O1 URL scheme 大小写规范化** — `HTTPS://` / `HttP://` 这种混合大小写的 scheme 之前虽然运行时通过但回显给用户的 source_url 保留原样。现在在 SUPPLIER_URL_SCHEMA 的 `.transform()` 里统一 lowercase scheme 前缀,path / host / query 完全不动(RFC 3986 说 scheme case-insensitive 但 path case-sensitive,Alibaba 的 `Widget_ABC_1600123456789.html` 必须保真)
45
+ - **O3 hard error 文本清理** — Path A fetch failure 抛出的错误之前会带 DSers 原始 JSON blob / Go nil / map[] 这些 raw 文本。现在有一个 `sanitizePrimaryErrorForWarning` helper 剥掉 JSON 只保留人话,尾部统一附 `(DSers API HTTP NNN)` 标记上游状态码
46
+ - **O-R4-3 checkStoreOwnership 加直连快路径** — 之前 wrong dsers_product_id 要走 list-scan 扫一整页 100 个产品才能说"不存在",耗时 1.3s。新版先用 `getMyProductDetail` 直连查询,wrong-product 失败时间降到和 wrong-store 同数量级(< 500ms)
47
+ - **O-R5-3 validator 接受 DSers 单 SKU `<none>` 哨兵** — DSers 对"Default Title"单 SKU 产品的真实存储是 `supplyVariantId:"<none>"` 字面字符串,这不是 bug 是生产数据。validator 的 check 4 的 pattern `\d+:\d+[#...]` 之前会误拒这种数据,导致账号里**所有单 SKU 产品**(测试账号里有 9 个)的 apply 都会失败。现在 validator 接受 `<none>` 哨兵,前提是 candidate 也是单 SKU(防御性 guard:防止 matcher 把多 SKU candidate 硬塞到哨兵槽)
48
+ - **O-R55-2 / B-R57-1 sku-matcher 单 SKU seller 快路径** — matcher 的维度对齐路径对"seller 是单 SKU Default Title + candidate 是 1v 真实维度名(比如 `{Color, black}`)"这种退化场景会判 0 分 unmatched。新增一个 early-return 快路径: 当 seller 是 trivial 单 SKU(lean 或 Default Title 哨兵),candidate 不管什么形态,都按 `single_sku_seller_trivial` 策略配对,confidence 75。图像路径保留,candidate 多 SKU 时按 dHash 挑最接近的 variant,其它作为 alternatives 返回。**既有算法主体一行未动**(这点经过一周独立验证,不想动),shortcut 只是 early-return。fat-fat 场景(两边都是 Default Title 哨兵)还是走原算法的 exact 80 分,不被 shortcut 抢占
49
+
50
+ ### 文件更新
51
+
52
+ - 修了 `app/dropshipping/[transport]/route.ts` 里硬编码的 `version: "1.3.8"`(遗留 bug,历次发版没同步到)
53
+ - `manifest.json` 补齐 4 个漏掉的 tool 描述
54
+ - README 里 `test count (343)` 同步到实际的 585
55
+ - 新增 `CHANGELOG.md`
56
+
57
+ ### 没做的事(留给后续版本)
58
+
59
+ - `dsers_my_products` 的 `page` 分页参数现在实际不生效(DSers 后端行为,不是本工具的 bug),workaround 是用 `page_size:100` 一次拉完。账号产品超过 100 的用户会撞到这个,另开 issue 跟进
60
+ - DSers GET mapping 对多 supplier(MapAgc)产品返回的 `mappings[]` 数组顺序非确定(服务器行为,10 次里 9 次是顺序 A,1 次是顺序 B,值完全一样)。对做 byte-level diff 的工具链有影响,我们的 validator 不依赖 byte-level 所以不受影响,但使用者要注意
61
+ - `dsers_sku_remap` 的 discover 模式 Path B 对 vp* 虚拟产品(DSers 内部合成的)无效,因为 virtual product 没有 seed 图可以反搜。这是 DSers 侧的限制,不是我们能修的
62
+
63
+ ### 回归验证
64
+
65
+ 这一版从 round-1 做到 round-5.7.5,每轮都是独立 QA 会话(不读前面任何报告、HANDOFF、commit body、测试代码),跑完整 e2e 回归 + 真实 DSers 写入 + 字节级 rollback。585 个单元测试全过,最后一轮 0 Blocker / 0 Major / 0 Minor / 0 Observation。
66
+
67
+ ---
68
+
69
+ ## 1.4.x
70
+
71
+ 之前的版本在 git history 里,主要是 OAuth 2.1 + 远程 MCP + Vercel 部署相关。没有集中的 changelog,看 `git log --oneline v1.4.0..v1.5.0` 或者各 commit message。
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
 
@@ -78,7 +78,7 @@ This works for both AliExpress and Alibaba products found on Accio.
78
78
 
79
79
  - A [DSers](https://www.dsers.com/) account (free plan works)
80
80
  - A Shopify or Wix store already connected in DSers
81
- - An MCP-compatible AI client — [Cursor](https://cursor.sh/), [Claude Desktop](https://claude.ai/desktop), [Windsurf](https://codeium.com/windsurf), or any client that supports MCP
81
+ - An MCP-compatible AI client — [Cursor](https://cursor.sh/), [Claude Desktop](https://claude.ai/desktop), [Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview), [Windsurf](https://codeium.com/windsurf), or any client that supports MCP
82
82
 
83
83
  ### Quick Start
84
84
 
@@ -105,21 +105,66 @@ 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
+ **Claude Managed Agents ([docs](https://platform.claude.com/docs/en/managed-agents/overview)):**
125
+
126
+ Build autonomous dropshipping agents that run 24/7 in Anthropic's managed infrastructure. Connect this MCP server via the [Claude Agent SDK](https://platform.claude.com/docs/en/agent-sdk/mcp):
127
+
128
+ ```python
129
+ from claude_agent_sdk import query, ClaudeAgentOptions
130
+
131
+ async for message in query(
132
+ prompt="Find a cheaper supplier for product dp-123 in store st-456 and update the mapping",
133
+ options=ClaudeAgentOptions(
134
+ mcp_servers={
135
+ "dsers": {"command": "npx", "args": ["-y", "@lofder/dsers-mcp-product"]}
136
+ }
137
+ ),
138
+ ):
139
+ print(message)
140
+ ```
141
+
142
+ ```typescript
143
+ import { query } from "@anthropic-ai/claude-agent-sdk";
144
+
145
+ for await (const message of query({
146
+ prompt: "Find a cheaper supplier for product dp-123 in store st-456 and update the mapping",
147
+ options: {
148
+ mcpServers: {
149
+ dsers: { command: "npx", args: ["-y", "@lofder/dsers-mcp-product"] }
150
+ }
151
+ }
152
+ })) {
153
+ console.log(message);
154
+ }
155
+ ```
156
+
157
+ ### Authentication — OAuth 2.1
111
158
 
112
159
  Your DSers password **never touches this tool**. Here's how login works:
113
160
 
114
161
  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.
162
+ 2. Your browser opens the DSers OAuth authorization page
163
+ 3. You authorize access on DSers's own website
164
+ 4. The tool receives an OAuth token and encrypts it locally
165
+ 5. Done — the MCP server uses the token automatically. No passwords in config files.
121
166
 
122
- **Sessions last about 6 hours.** When it expires, your AI agent will ask you to run `login` again — takes 10 seconds.
167
+ **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
168
 
124
169
  **Switching accounts?**
125
170
 
@@ -128,7 +173,9 @@ npx @lofder/dsers-mcp-product logout
128
173
  npx @lofder/dsers-mcp-product login
129
174
  ```
130
175
 
131
- > **For developers:** The server also accepts a `DSERS_TOKEN` env var for headless/CI environments. For most users, just use `login`.
176
+ > **For developers:** The server also accepts `DSERS_ACCESS_TOKEN` and `DSERS_REFRESH_TOKEN` env vars for headless/CI environments.
177
+
178
+ > **Working from source (not the published npm package)?** Use `node dist/cli.js login` instead of `npx @lofder/dsers-mcp-product login`. The `npx` form requires the `dsers-mcp-product` binary to resolve through your `PATH`, which only happens after `npm link` (or after a real npm install). In a fresh local clone the `npx` command exits with `command not found` (exit code 127). Run `npm run build` first so `dist/cli.js` exists.
132
179
 
133
180
  ### Usage Examples
134
181
 
@@ -178,7 +225,7 @@ dsers-mcp-product/
178
225
  │ ├── cli.ts # npx entry — stdio transport, login/logout
179
226
  │ ├── index.ts # MCP server init, tool registration
180
227
  │ ├── instructions.ts # Server-level prompts (agent instructions)
181
- │ ├── tools.ts # 12 MCP tools — schema + handler
228
+ │ ├── tools.ts # 13 MCP tools — schema + handler
182
229
  │ ├── rules.ts # Rule validation & application engine
183
230
  │ ├── push-guard.ts # Pre-push safety checks
184
231
  │ ├── push-options.ts # Push option normalization
@@ -207,12 +254,12 @@ dsers-mcp-product/
207
254
  │ │ ├── product.ts # Product & import APIs
208
255
  │ │ └── settings.ts # Shipping, pricing, billing APIs
209
256
  │ └── auth/ # Browser login (CDP)
210
- ├── test/ # Vitest unit tests (298 tests)
257
+ ├── test/ # Vitest unit tests (585 tests)
211
258
  ├── package.json
212
259
  └── tsconfig.json
213
260
  ```
214
261
 
215
- ### Twelve Tools
262
+ ### Thirteen Tools
216
263
 
217
264
  | # | Tool | What it does |
218
265
  |---|------|-------------|
@@ -228,6 +275,7 @@ dsers-mcp-product/
228
275
  | 10 | `dsers_import_list` | Browse your import staging list with cost & sell price, stock, markup status |
229
276
  | 11 | `dsers_my_products` | See products already pushed to a store, with supplier links for re-import |
230
277
  | 12 | `dsers_find_product` | Search the DSers product pool by keyword or image — results link directly to import |
278
+ | 13 | `dsers_sku_remap` | Replace the supplier on an existing store product at the SKU level. Two modes: provide `new_supplier_url` for a strict swap, or omit it to reverse-image-search the DSers pool and auto-pick the best replacement via multi-factor ranking. Always run `mode='preview'` first (read-only) and inspect `diffs` + `pool_additions` before `mode='apply'`. Requires the `product:mapping` OAuth scope. |
231
279
 
232
280
  All tools return clear error messages so your AI agent knows what went wrong and what to do next.
233
281
 
@@ -290,7 +338,7 @@ Yes. The tool is open-source (MIT license) and completely free to use. You only
290
338
  No passwords are stored or transmitted. Authentication uses a zero-password browser login — you log in on DSers's own website, and the tool picks up the session token. Your credentials never touch the MCP server. The project scored 92/100 on [SafeSkill](https://safeskill.dev/scan/@lofder/dsers-mcp-product) security scanning.
291
339
 
292
340
  **What AI clients does it support?**
293
- Cursor, Claude Desktop, Claude Code, Windsurf, and any MCP-compatible client that supports stdio transport.
341
+ Cursor, Claude Desktop, Claude Code, Claude Managed Agents, Windsurf, and any MCP-compatible client that supports stdio transport.
294
342
 
295
343
  **How is this different from AliDropify, AutoDS, or other dropshipping tools?**
296
344
  Most dropshipping tools have their own UI and require you to click through web interfaces. DSers MCP Product takes a fundamentally different approach — it connects directly to your AI agent, so you automate workflows through conversation instead of clicking buttons. It's also open-source and free, with no subscription tiers.
@@ -329,7 +377,7 @@ MIT
329
377
  - **安全校验** — 推送前自动拦截低于成本价、零售价为零、库存为零的商品
330
378
  - **SEO 优化** — 让 AI 重写标题和描述,提高搜索排名后再推送
331
379
 
332
- 服务已托管在 [Vercel](https://dsers-mcp-product.vercel.app/api/mcp),并发布到多个平台:
380
+ 服务已托管在 [Vercel](https://ai.silentrillmcp.com/dropshipping/mcp),并发布到多个平台:
333
381
 
334
382
  ### 发布平台
335
383
 
@@ -377,7 +425,7 @@ Accio 上搜出来的速卖通和阿里巴巴商品都能用。
377
425
 
378
426
  - 一个 [DSers](https://www.dsers.com/) 账号(免费版就行)
379
427
  - Shopify 或 Wix 店铺已经在 DSers 里绑定好了
380
- - 一个支持 MCP 的 AI 客户端 — [Cursor](https://cursor.sh/)、[Claude Desktop](https://claude.ai/desktop)、[Windsurf](https://codeium.com/windsurf) 或其他支持 MCP 的工具
428
+ - 一个支持 MCP 的 AI 客户端 — [Cursor](https://cursor.sh/)、[Claude Desktop](https://claude.ai/desktop)、[Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview)、[Windsurf](https://codeium.com/windsurf) 或其他支持 MCP 的工具
381
429
 
382
430
  ### 快速开始
383
431
 
@@ -404,21 +452,51 @@ npx @lofder/dsers-mcp-product login
404
452
 
405
453
  搞定。配置文件里不需要任何密码。
406
454
 
455
+ **在线版(免安装):**
456
+
457
+ 如果不想在本地安装,可以直接连接托管的 MCP 服务端 `https://ai.silentrillmcp.com/dropshipping/mcp`。支持 Streamable HTTP transport 的 MCP 客户端都能用,首次连接会引导你授权 DSers 账号。
458
+
459
+ ```json
460
+ {
461
+ "mcpServers": {
462
+ "dropshipping": {
463
+ "url": "https://ai.silentrillmcp.com/dropshipping/mcp"
464
+ }
465
+ }
466
+ }
467
+ ```
468
+
407
469
  同时已收录到官方 [MCP Registry](https://registry.modelcontextprotocol.io/servers/io.github.lofder/dsers-mcp-product)。
408
470
 
409
- ### 授权认证 零密码登录
471
+ **Claude Managed Agents ([文档](https://platform.claude.com/docs/en/managed-agents/overview)):**
472
+
473
+ 通过 [Claude Agent SDK](https://platform.claude.com/docs/en/agent-sdk/mcp) 构建 7×24 自主运行的 dropshipping agent:
474
+
475
+ ```python
476
+ from claude_agent_sdk import query, ClaudeAgentOptions
477
+
478
+ async for message in query(
479
+ prompt="帮商品 dp-123 在店铺 st-456 找个更便宜的供应商并更新映射",
480
+ options=ClaudeAgentOptions(
481
+ mcp_servers={
482
+ "dsers": {"command": "npx", "args": ["-y", "@lofder/dsers-mcp-product"]}
483
+ }
484
+ ),
485
+ ):
486
+ print(message)
487
+ ```
488
+
489
+ ### 授权认证 — OAuth 2.1
410
490
 
411
491
  你的 DSers 密码**完全不经过本工具**。登录过程是这样的:
412
492
 
413
493
  1. 运行 `npx @lofder/dsers-mcp-product login`
414
- 2. 浏览器自动打开 DSers 官方登录页
415
- 3. 你在 DSers 网站上正常登录
416
- 4. 工具拿到登录状态,加密存到本地
494
+ 2. 浏览器自动打开 DSers OAuth 授权页
495
+ 3. 你在 DSers 网站上授权
496
+ 4. 工具拿到 OAuth token,加密存到本地
417
497
  5. 搞定 — 之后 MCP 直接能用,配置文件里不需要写任何密码
418
498
 
419
- 支持 Chrome、Edge、Brave 等主流浏览器。Mac Safari 也行。
420
-
421
- **登录大约 6 小时有效。** 过期了 AI 助手会提醒你重新跑一下 `login`,10 秒的事。
499
+ **Token 自动续期** refresh token 会自动保持登录状态,你基本不需要重新登录。
422
500
 
423
501
  **换账号?**
424
502
 
@@ -427,7 +505,9 @@ npx @lofder/dsers-mcp-product logout
427
505
  npx @lofder/dsers-mcp-product login
428
506
  ```
429
507
 
430
- > **开发者注:** headless / CI 环境也支持通过 `DSERS_TOKEN` 环境变量传入凭据。普通用户直接用 `login` 就行。
508
+ > **开发者注:** headless / CI 环境支持通过 `DSERS_ACCESS_TOKEN` `DSERS_REFRESH_TOKEN` 环境变量传入凭据。
509
+
510
+ > **从源码工作(不是从 npm 包跑)?** 登录用 `node dist/cli.js login`,**不是** `npx @lofder/dsers-mcp-product login`。后者要求 `dsers-mcp-product` binary 解析到 PATH,本地 workspace 没 `npm link`(或者没真正 npm install)的话会 `command not found` 直接退出码 127。先 `npm run build` 让 `dist/cli.js` 存在再跑。
431
511
 
432
512
  ### 使用示例
433
513
 
@@ -469,7 +549,7 @@ npm run build
469
549
  npm test
470
550
  ```
471
551
 
472
- ### 十二个工具
552
+ ### 十三个工具
473
553
 
474
554
  | # | 工具 | 干什么的 |
475
555
  |---|------|---------|
@@ -485,6 +565,7 @@ npm test
485
565
  | 10 | `dsers_import_list` | 浏览导入待推送列表,含成本价、售价、库存、加价状态 |
486
566
  | 11 | `dsers_my_products` | 查看已推到店铺的商品,带供应商链接方便重新导入 |
487
567
  | 12 | `dsers_find_product` | 在 DSers 商品池搜索,支持关键词和以图搜图,结果可直接导入 |
568
+ | 13 | `dsers_sku_remap` | SKU 级别替换已上架商品的供应商。两种模式:传 `new_supplier_url` 走精确替换,不传则反向图搜 DSers 池 + 多因子打分自动挑最佳替代。务必先用 `mode='preview'`(只读)看 `diffs` 和 `pool_additions`,确认无误后再 `mode='apply'`。需要 `product:mapping` OAuth scope。 |
488
569
 
489
570
  报错时会返回清晰的消息,AI 助手能看懂出了什么问题、该怎么办。
490
571
 
@@ -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":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAOjC,MAAM,cAAc,GAAuB;IACzC,CAAC,eAAe,EAAE,8DAA8D,CAAC;IACjF,CAAC,gBAAgB,EAAE,gEAAgE,CAAC;IACpF,CAAC,eAAe,EAAE,8DAA8D,CAAC;IACjF,CAAC,KAAK,EAAE,0CAA0C,CAAC;IACnD,CAAC,UAAU,EAAE,oDAAoD,CAAC;IAClE,CAAC,OAAO,EAAE,8CAA8C,CAAC;IACzD,CAAC,SAAS,EAAE,kDAAkD,CAAC;CAChE,CAAC;AAEF,MAAM,cAAc,GAAuB;IACzC,CAAC,eAAe,EAAE,sBAAsB,CAAC;IACzC,CAAC,eAAe,EAAE,eAAe,CAAC;IAClC,CAAC,UAAU,EAAE,kBAAkB,CAAC;IAChC,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,gBAAgB,EAAE,uBAAuB,CAAC;IAC3C,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IACpC,CAAC,eAAe,EAAE,eAAe,CAAC;IAClC,CAAC,eAAe,EAAE,sBAAsB,CAAC;IACzC,CAAC,OAAO,EAAE,OAAO,CAAC;IAClB,CAAC,SAAS,EAAE,gBAAgB,CAAC;IAC7B,CAAC,SAAS,EAAE,SAAS,CAAC;CACvB,CAAC;AAEF,MAAM,SAAS,GAAuB;IACpC,CAAC,eAAe,EAAE,yCAAyC,CAAC;IAC5D,CAAC,gBAAgB,EAAE,0CAA0C,CAAC;IAC9D,CAAC,eAAe,EAAE,sDAAsD,CAAC;IACzE,CAAC,OAAO,EAAE,kBAAkB,CAAC;IAC7B,CAAC,SAAS,EAAE,mCAAmC,CAAC;IAChD,CAAC,UAAU,EAAE,mCAAmC,CAAC;CAClD,CAAC;AAEF,SAAS,SAAS,CAAC,GAAW;IAC5B,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,SAAS,GAAG,cAAc,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC;IACnG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,WAAW;IAClB,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,cAAc,EAAE,CAAC;QACvC,IAAI,UAAU,CAAC,CAAC,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAC9C,CAAC;IACD,qDAAqD;IACrD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,qEAAqE,EAAE;YAC7F,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,IAAI;SACd,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,8BAA8B,CAAC,CAAC;YAC/D,IAAI,UAAU,CAAC,QAAQ,CAAC;gBAAE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC7E,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,qCAAqC;IACvC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,WAAW;IAClB,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,cAAc,EAAE,CAAC;QACzC,MAAM,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAClC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,aAAa;IACpB,MAAM,KAAK,GAAG;QACZ,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,mBAAmB;QAC/C,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,yBAAyB;QAC7D,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE;KAC/B,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAElB,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,SAAS,EAAE,CAAC;QACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAC7B,IAAI,UAAU,CAAC,IAAI,CAAC;gBAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACpD,CAAC;IACH,CAAC;IAED,6DAA6D;IAC7D,MAAM,OAAO,GAAG,mEAAmE,CAAC;IACpF,IAAI,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAE1E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,IAAI,QAAQ,KAAK,QAAQ;QAAE,OAAO,WAAW,EAAE,CAAC;IAChD,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,WAAW,EAAE,CAAC;IAC/C,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,aAAa,EAAE,CAAC;IACjD,OAAO,IAAI,CAAC;AACd,CAAC"}
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"}