@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
|
|
216
|
-
|
|
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:
|
|
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,
|
|
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 这一步 — 它是防价格篡改的关键。`,
|
package/dist/pwa/public/app.js
CHANGED
|
@@ -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)
|
|
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,
|
package/dist/pwa/public/sw.js
CHANGED
|
@@ -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
|
|
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
|
|
120
|
-
|
|
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', {
|
package/dist/pwa/server.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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