@seasonkoh/webaz 0.1.10 → 0.1.12

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.
@@ -212,8 +212,18 @@ Roles:
212
212
  },
213
213
  {
214
214
  name: 'webaz_search',
215
- description: `Search WebAZ marketplace listings. No auth required.
216
- Returns structured specs + logistics + after-sales + agent_summary (one-line decision hint).
215
+ description: `Search WebAZ marketplace + cross-platform anchor lookup. No auth required.
216
+
217
+ USE THIS when the user:
218
+ - gives KEYWORDS / filters (typical product discovery), OR
219
+ - pastes ANY external product URL/share-text from Taobao / Tmall / JD / PDD / 1688 / Douyin / Xiaohongshu
220
+ → WebAZ exact-matches against its anchor registry (canonical product fingerprints across
221
+ platforms). matched_by='none' means truly not indexed — do NOT silently fall back to keyword,
222
+ and do NOT guess similar products.
223
+
224
+ ⚠️ Do not skip this tool just because user gave you a URL — URL-paste is a first-class mode here,
225
+ NOT a separate browser-fetch capability. Returns structured specs + logistics + after-sales +
226
+ agent_summary (one-line decision hint).
217
227
 
218
228
  【Keyword search】Pass query / category / max_price / min_return_days / max_handling_hours.
219
229
 
@@ -662,6 +672,11 @@ Other actions (create / view / mine / available / eligibility / apply / appeal e
662
672
  name: 'webaz_skill',
663
673
  description: `L4-4 Skill marketplace — sellers publish reusable seller-side behaviour configs; buyer Agents subscribe with one click.
664
674
 
675
+ USE THIS when:
676
+ - seller wants to install reusable selling behavior (auto-accept / catalog-sync / price-haggling / etc.), OR
677
+ - buyer agent wants to subscribe to seller's behaviour to enable priority discovery / auto-deal flows.
678
+ This is NOT a product search — for "find me X" use webaz_search.
679
+
665
680
  ⚠️ Important — Skill is NOT executable code distribution. There are exactly 5 typed Skill kinds (below), each accepts only **structured config parameters** (numbers / enums / amounts). There is NO path for an Agent to download and run arbitrary third-party code via this marketplace. Subscribing = setting a flag + data binding, not installing a plugin. (Common Web2 "plugin marketplace" risk model does NOT apply here.)
666
681
 
667
682
  Skill is WebAZ's cold-start mechanism: existing Amazon/Shopify sellers integrate with zero cost,
@@ -740,6 +755,9 @@ Returns:
740
755
  name: 'webaz_profile',
741
756
  description: `View your own profile / manage roles, AND view any user's public profile + content streams (个人主页内容流).
742
757
 
758
+ USE THIS when user wants info about a SPECIFIC PERSON (by usr_xxx / permanent code / @handle / name),
759
+ OR wants to see someone's listings / notes / activity stream. NOT for product keyword search — use webaz_search.
760
+
743
761
  Self actions (need api_key):
744
762
  - view show your profile & wallet & api_key hint
745
763
  - add_role add a new role
@@ -887,7 +905,11 @@ Returns the action result.`,
887
905
  },
888
906
  {
889
907
  name: 'webaz_nearby',
890
- description: 'Query anonymized nearby (~11km cell) purchase aggregation. k-anonymity ≥ 3 privacy guard. Or set/clear your coarse geo location (0.1° = 11km precision, never stores exact GPS).',
908
+ description: `Query anonymized nearby (~11km cell) purchase aggregation. k-anonymity ≥ 3 privacy guard. Or set/clear your coarse geo location (0.1° = 11km precision, never stores exact GPS).
909
+
910
+ USE THIS when user asks "what's popular/being bought near me / 我附近 / 同城" — geo-aggregated
911
+ view, no specific keyword. NOT for "find product X" — use webaz_search. NOT for "items shippable
912
+ to my address" — use webaz_search ship_to filter.`,
891
913
  inputSchema: {
892
914
  type: 'object',
893
915
  properties: {
@@ -942,6 +964,10 @@ Returns the action result.`,
942
964
  name: 'webaz_rfq',
943
965
  description: `RFQ (Request-for-Quotation) — buyer posts demand, sellers bid within a time window.
944
966
 
967
+ USE THIS when buyer wants to POST a need (and have sellers come to them) — typically because
968
+ webaz_search returned no good match, OR the buyer wants competing quotes (bulk / custom / unusual
969
+ spec / time-sensitive). NOT a search tool. For browsing existing listings use webaz_search.
970
+
945
971
  Actions:
946
972
  - create (buyer): publish RFQ; needs title/qty/max_price/category/urgency/award_mode
947
973
  - mine (buyer): my RFQ list
@@ -1210,6 +1236,10 @@ Kinds:
1210
1236
  {
1211
1237
  name: 'webaz_auction',
1212
1238
  description: `English forward auction — seller posts → buyers raise bids → anti-sniping extension → highest bid wins.
1239
+
1240
+ USE THIS when user wants to BID on auction items OR a seller wants to START an auction (rare goods,
1241
+ collectibles, price-discovery). NOT a regular product search — auctions are time-windowed events.
1242
+ For fixed-price items use webaz_search.
1213
1243
  Actions:
1214
1244
  - create (seller): start auction; needs title/qty/category/starting_price, optional min_increment/reserve_price/window_min/sniper_extend_min
1215
1245
  - browse: public board
@@ -1322,6 +1352,10 @@ Actions:
1322
1352
  name: 'webaz_secondhand',
1323
1353
  description: `Secondhand market (个人闲置二手) — peer-to-peer pre-owned goods, 1% protocol fee, escrow-protected. Supports shipping and in-person handoff.
1324
1354
 
1355
+ USE THIS when user wants USED / pre-owned / 闲置 / 二手 items, OR wants to sell own used items.
1356
+ For NEW manufactured products use webaz_search. Note: secondhand and shop catalog are separate
1357
+ spaces — webaz_search does NOT return secondhand listings.
1358
+
1325
1359
  Actions:
1326
1360
  - browse list available items (filters: category / condition / region / min_price / max_price / query / sort; no auth needed; excludes your own when api_key given)
1327
1361
  - detail one item's detail + seller's other listings (item_id; no auth needed)
@@ -1371,6 +1405,12 @@ fulfillment: shipping / in_person / both
1371
1405
  name: 'webaz_trial',
1372
1406
  description: `Trial-for-review (测评免单) — sellers offer order refunds to buyers who post a qualifying review note; when the review reaches a target view threshold the order is auto-refunded.
1373
1407
 
1408
+ USE THIS when:
1409
+ - buyer asks "is there a trial / free-test / 测评免单 / 0 元试用 for this product?", OR
1410
+ - buyer has already ordered a product with a trial campaign and wants to claim the slot, OR
1411
+ - seller wants to launch a trial campaign to seed reviews.
1412
+ NOT a search tool — for product discovery use webaz_search (can filter by has_trial=true coming soon).
1413
+
1374
1414
  Anti-abuse is enforced server-side (buyer≠seller, must have a confirmed/completed order, account ≥3 days old, IP/UA rate limits, config snapshot at claim time) — MCP just passes through.
1375
1415
 
1376
1416
  Buyer actions:
@@ -4006,7 +4046,7 @@ export async function startMCPServer() {
4006
4046
  type: 'text',
4007
4047
  text: `用户需求:${args.user_intent || '(未提供)'}\n\n` +
4008
4048
  `请使用 webaz_search 工具(可粘贴外链精准匹配,详见工具描述)找到最匹配的商品,` +
4009
- `然后用 webaz_verify_price 锁价(返回 session_token,5 分钟有效),` +
4049
+ `然后用 webaz_verify_price 锁价(返回 session_token,10 分钟有效),` +
4010
4050
  `最后用 webaz_place_order(session_token=...) 下单。` +
4011
4051
  `下单时记得跟用户确认收货地址 + 是否需要使用推荐人 promoter_api_key(可选)。\n\n` +
4012
4052
  `重要:不要跳过 verify_price 这一步 — 它是防价格篡改的关键。`,
@@ -10687,7 +10687,18 @@ window.doRegister = async () => {
10687
10687
  // #1049 Turnstile token(若启用)
10688
10688
  if (window._turnstileToken) body.turnstile_token = window._turnstileToken
10689
10689
  const res = await POST('/register', body)
10690
- if (res.error) return showMsg('error', res.error)
10690
+ if (res.error) {
10691
+ // P0-3: token 单次消耗,任何注册失败(name 重 / invite 缺 / captcha 假)后都要 reset widget 拿新 challenge,
10692
+ // 否则用户 retry 时旧 token 已失效 → 后端 timeout-or-duplicate → 死循环
10693
+ try {
10694
+ window._turnstileToken = ''
10695
+ if (window.turnstile) {
10696
+ const slot = document.getElementById('reg-turnstile-slot')
10697
+ if (slot) window.turnstile.reset(slot)
10698
+ }
10699
+ } catch {}
10700
+ return showMsg('error', res.error)
10701
+ }
10691
10702
  // 注册成功后清掉 hint(已绑定)
10692
10703
  localStorage.removeItem('webaz_share_hint')
10693
10704
  localStorage.removeItem('webaz_ref')
@@ -11631,7 +11642,6 @@ async function renderDiscover(app) {
11631
11642
  position: idx + 1,
11632
11643
  item: {
11633
11644
  '@type': 'Product',
11634
- inLanguage: lang,
11635
11645
  name: nameField,
11636
11646
  url: location.origin + '/#order-product/' + pp.id,
11637
11647
  ...(img ? { image: img } : {}),
@@ -15723,7 +15733,6 @@ async function renderBuyPage(app, productId) {
15723
15733
  setJsonLd({
15724
15734
  '@context': 'https://schema.org',
15725
15735
  '@type': 'Product',
15726
- inLanguage: curLang,
15727
15736
  name: nameField,
15728
15737
  description: descField,
15729
15738
  sku: p.id,
@@ -1,5 +1,5 @@
1
1
  // Service Worker — 网络优先,离线降级缓存;API 请求不缓存
2
- const CACHE = 'webaz-v478'
2
+ const CACHE = 'webaz-v480'
3
3
 
4
4
  self.addEventListener('install', e => {
5
5
  self.skipWaiting()
@@ -113,9 +113,11 @@ export function buildPaymentMandate(input) {
113
113
  forSign.proof.signature = '';
114
114
  return { canonical: canonical(forSign), mandate };
115
115
  }
116
- /** 把 build*Mandate 输出的 canonical 经 signFn 签名后,塞回 mandate.proof.signature */
116
+ /** 把 build*Mandate 输出的 canonical 经 signFn 签名后,返回带 signature 的深 clone(不 mutate 入参) */
117
117
  export async function signMandate(out, signFn) {
118
118
  const sig = await signFn(out.canonical);
119
- out.mandate.proof.signature = sig;
120
- return out.mandate;
119
+ // P2-3:深 clone 后再塞 signature;入参 out.mandate 保持不变,防调用方持引用被旁路修改
120
+ const signed = JSON.parse(JSON.stringify(out.mandate));
121
+ signed.proof.signature = sig;
122
+ return signed;
121
123
  }
@@ -15,6 +15,10 @@ export function registerAuthRegisterRoutes(app, deps) {
15
15
  if (!turnstile_token || typeof turnstile_token !== 'string') {
16
16
  return void errorRes(res, 400, 'CAPTCHA_REQUIRED', '请完成人机校验');
17
17
  }
18
+ // P1-3:Cloudflare Turnstile token 通常 <2KB,卡 4KB 防大体积注入 siteverify 浪费带宽
19
+ if (turnstile_token.length > 4096) {
20
+ return void errorRes(res, 400, 'CAPTCHA_INVALID', '人机校验未通过,请刷新后重试');
21
+ }
18
22
  try {
19
23
  const remoteIp = req.ip || req.socket?.remoteAddress || '';
20
24
  const verifyRes = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
@@ -4995,8 +4995,9 @@ const CROSS_USER_READ_DAILY_CAP = {
4995
4995
  };
4996
4996
  const crossUserReadBuckets = new Map();
4997
4997
  function extractCrossUserTarget(path, currentUserId) {
4998
- // /api/users/:id/anything → :id;同 user 不算跨;sys_* 协议账号不算
4999
- const m = path.match(/^\/api\/users\/([^/]+)\//);
4998
+ // /api/users/:id/api/users/:id/anything → :id;同 user 不算跨;sys_* 协议账号不算
4999
+ // P1-4 修:允许 trailing path 缺失(/api/users/:user_id 是合法 endpoint,返回用户档案)
5000
+ const m = path.match(/^\/api\/users\/([^/?#]+)(?:[/?#]|$)/);
5000
5001
  if (!m)
5001
5002
  return null;
5002
5003
  const target = m[1];
@@ -5193,7 +5194,9 @@ app.use((req, res, next) => {
5193
5194
  });
5194
5195
  }
5195
5196
  // #1043(补 A) 跨用户读日 cap — 真人也罩;只对路径里显式带 other user id 的 GET 计数
5196
- if (req.method === 'GET') {
5197
+ // P0-2 优化:先 regex 快判 path 命中(零开销),命中才查 owner.id + level — 避免每 GET 跑 SQLite
5198
+ // P1-4 修:正则允许 /api/users/:id 无 trailing slash(GET /api/users/:user_id 是返回用户档案的合法端点)
5199
+ if (req.method === 'GET' && /^\/api\/users\/[^/?#]+/.test(req.path)) {
5197
5200
  const owner = db.prepare(`SELECT id FROM users WHERE api_key = ?`).get(apiKey);
5198
5201
  if (owner) {
5199
5202
  const target = extractCrossUserTarget(req.path, owner.id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seasonkoh/webaz",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "Agent-native decentralized commerce protocol. Humans and AI agents trade on the same protocol via MCP tools.",
5
5
  "main": "dist/mcp.js",
6
6
  "bin": {