@seasonkoh/webaz 0.1.24 → 0.1.26
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 +5 -1
- package/dist/layer0-foundation/L0-1-database/db-backends/pg-backend.js +51 -0
- package/dist/layer0-foundation/L0-1-database/db-backends/sql-dialect-datetime.js +437 -0
- package/dist/layer0-foundation/L0-1-database/db-backends/sql-placeholders.js +98 -0
- package/dist/layer0-foundation/L0-1-database/db.js +65 -0
- package/dist/layer0-foundation/L0-2-state-machine/order-chain.js +13 -11
- package/dist/layer0-foundation/L0-2-state-machine/transitions.js +1 -1
- package/dist/layer0-foundation/L0-5-manifest/manifest.js +13 -11
- package/dist/layer1-agent/L1-1-mcp-server/server.js +288 -208
- package/dist/layer1-agent/L1-2-external-anchor/anchor-engine.js +14 -12
- package/dist/layer2-business/L2-6-notifications/notification-engine.js +8 -5
- package/dist/layer2-business/L2-7-snf/snf-engine.js +16 -14
- package/dist/layer2-business/L2-8-feedback/build-feedback-engine.js +18 -10
- package/dist/layer2-business/L2-9-contribution/build-reputation-engine.js +37 -23
- package/dist/layer2-business/L2-9-contribution/build-task-agent-metadata-store.js +182 -0
- package/dist/layer2-business/L2-9-contribution/build-task-participation.js +47 -0
- package/dist/layer2-business/L2-9-contribution/build-task-read.js +222 -0
- package/dist/layer2-business/L2-9-contribution/build-tasks-engine.js +11 -3
- package/dist/layer2-business/L2-9-contribution/canonical-contribution-target.js +16 -0
- package/dist/layer2-business/L2-9-contribution/contribution-display-envelope.js +40 -0
- package/dist/layer2-business/L2-9-contribution/contribution-score-contract.js +36 -0
- package/dist/layer2-business/L2-9-contribution/contribution-score-evidence.js +61 -0
- package/dist/layer2-business/L2-9-contribution/github-credential/canonical.js +60 -0
- package/dist/layer2-business/L2-9-contribution/github-credential/github-credential.schema.js +140 -0
- package/dist/layer2-business/L2-9-contribution/github-credential/github-fetch-adapter.js +437 -0
- package/dist/layer2-business/L2-9-contribution/github-credential/self-consistency.js +38 -0
- package/dist/layer2-business/L2-9-contribution/github-credential/verifier.js +231 -0
- package/dist/layer2-business/L2-9-contribution/github-credential-ingestion-engine.js +145 -0
- package/dist/layer2-business/L2-9-contribution/github-credential-store.js +115 -0
- package/dist/layer2-business/L2-9-contribution/identity-binding-engine.js +134 -0
- package/dist/layer2-business/L2-9-contribution/identity-binding-store.js +101 -0
- package/dist/layer2-business/L2-9-contribution/identity-claim-challenge-engine.js +126 -0
- package/dist/layer2-business/L2-9-contribution/identity-claim-challenge-store.js +30 -0
- package/dist/layer2-business/L2-9-contribution/identity-claim-discovery.js +55 -0
- package/dist/layer2-business/L2-9-contribution/identity-claim-engine.js +109 -0
- package/dist/layer2-business/L2-9-contribution/identity-claim-fact-precondition.js +22 -0
- package/dist/layer2-business/L2-9-contribution/identity-claim-proof-verifier.js +97 -0
- package/dist/layer2-business/L2-9-contribution/identity-claim-read.js +59 -0
- package/dist/layer2-business/L2-9-contribution/task-proposal-ai-store.js +99 -0
- package/dist/layer2-business/L2-9-contribution/task-proposal-draft.js +191 -0
- package/dist/layer2-business/L2-9-contribution/task-proposal-store.js +129 -0
- package/dist/layer2-business/L2-notes/note-photo-storage.js +4 -2
- package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +17 -15
- package/dist/layer3-trust/L3-1-dispute-engine/evidence-storage.js +11 -8
- package/dist/layer4-economics/L4-3-reputation/reputation-engine.js +9 -8
- package/dist/layer4-economics/L4-4-skill-market/skill-engine.js +11 -8
- package/dist/layer4-economics/L4-4-skill-market/skill-listing-engine.js +22 -16
- package/dist/pwa/acp-feed.js +13 -1
- package/dist/pwa/admin-bearer-auth.js +21 -0
- package/dist/pwa/contract-fingerprint.js +2 -0
- package/dist/pwa/email-delivery.js +127 -0
- package/dist/pwa/endpoint-actions.js +5 -1
- package/dist/pwa/goal-index.js +8 -8
- package/dist/pwa/human-presence.js +62 -0
- package/dist/pwa/public/app.js +1485 -283
- package/dist/pwa/public/i18n.js +297 -59
- package/dist/pwa/public/index.html +1 -0
- package/dist/pwa/public/openapi.json +5 -5
- package/dist/pwa/public/whitepaper/en/index.html +153 -0
- package/dist/pwa/public/whitepaper/zh-CN/index.html +153 -0
- package/dist/pwa/rate-limit.js +22 -0
- package/dist/pwa/routes/account-deletion.js +15 -13
- package/dist/pwa/routes/addresses.js +10 -9
- package/dist/pwa/routes/admin-admins.js +13 -14
- package/dist/pwa/routes/admin-analytics.js +109 -69
- package/dist/pwa/routes/admin-atomic.js +10 -4
- package/dist/pwa/routes/admin-catalog.js +13 -11
- package/dist/pwa/routes/admin-editor-picks.js +15 -10
- package/dist/pwa/routes/admin-events.js +5 -3
- package/dist/pwa/routes/admin-health.js +2 -1
- package/dist/pwa/routes/admin-moderation.js +50 -29
- package/dist/pwa/routes/admin-ops.js +35 -23
- package/dist/pwa/routes/admin-protocol-params.js +16 -19
- package/dist/pwa/routes/admin-reports.js +23 -21
- package/dist/pwa/routes/admin-tokenomics.js +26 -25
- package/dist/pwa/routes/admin-users-lifecycle.js +37 -40
- package/dist/pwa/routes/admin-users-query.js +65 -53
- package/dist/pwa/routes/admin-verifier-flow.js +82 -41
- package/dist/pwa/routes/admin-verifier-whitelist.js +55 -27
- package/dist/pwa/routes/admin-wallet-ops.js +32 -7
- package/dist/pwa/routes/agent-buy.js +46 -22
- package/dist/pwa/routes/agent-governance.js +52 -56
- package/dist/pwa/routes/ai.js +7 -5
- package/dist/pwa/routes/analytics.js +43 -41
- package/dist/pwa/routes/anchors.js +19 -20
- package/dist/pwa/routes/announcements.js +13 -13
- package/dist/pwa/routes/arbitrator.js +97 -31
- package/dist/pwa/routes/auction.js +157 -116
- package/dist/pwa/routes/auth-login.js +6 -4
- package/dist/pwa/routes/auth-read.js +21 -10
- package/dist/pwa/routes/auth-register.js +111 -26
- package/dist/pwa/routes/auth-sessions.js +12 -11
- package/dist/pwa/routes/blocklist.js +16 -15
- package/dist/pwa/routes/build-feedback.js +10 -9
- package/dist/pwa/routes/build-reputation.js +6 -2
- package/dist/pwa/routes/build-tasks.js +45 -13
- package/dist/pwa/routes/buyer-feeds.js +27 -25
- package/dist/pwa/routes/cart.js +16 -15
- package/dist/pwa/routes/charity.js +212 -150
- package/dist/pwa/routes/chat.js +42 -43
- package/dist/pwa/routes/checkin-tasks.js +10 -9
- package/dist/pwa/routes/checkout-helpers.js +12 -10
- package/dist/pwa/routes/claim-initiators.js +34 -14
- package/dist/pwa/routes/claim-verify.js +86 -53
- package/dist/pwa/routes/claim-voting.js +43 -18
- package/dist/pwa/routes/contribution-identity.js +164 -0
- package/dist/pwa/routes/contribution-score.js +19 -0
- package/dist/pwa/routes/coupons.js +19 -16
- package/dist/pwa/routes/dashboards.js +18 -16
- package/dist/pwa/routes/dispute-cases.js +25 -24
- package/dist/pwa/routes/disputes-read.js +45 -51
- package/dist/pwa/routes/disputes-write.js +124 -61
- package/dist/pwa/routes/evidence.js +9 -9
- package/dist/pwa/routes/external-anchors.js +13 -12
- package/dist/pwa/routes/feedback.js +29 -33
- package/dist/pwa/routes/flash-sales.js +18 -16
- package/dist/pwa/routes/follows.js +25 -24
- package/dist/pwa/routes/governance-auto-deactivate.js +21 -9
- package/dist/pwa/routes/governance-onboarding.js +70 -59
- package/dist/pwa/routes/group-buys.js +22 -22
- package/dist/pwa/routes/growth.js +34 -31
- package/dist/pwa/routes/import-product.js +12 -10
- package/dist/pwa/routes/kyc.js +9 -8
- package/dist/pwa/routes/leaderboard.js +20 -18
- package/dist/pwa/routes/listings.js +23 -22
- package/dist/pwa/routes/logistics.js +10 -8
- package/dist/pwa/routes/manifests.js +27 -27
- package/dist/pwa/routes/me-data.js +23 -21
- package/dist/pwa/routes/notifications.js +7 -6
- package/dist/pwa/routes/offers.js +30 -12
- package/dist/pwa/routes/orders-action.js +51 -29
- package/dist/pwa/routes/orders-create.js +75 -20
- package/dist/pwa/routes/orders-read.js +21 -20
- package/dist/pwa/routes/p2p-products.js +30 -18
- package/dist/pwa/routes/payments-governance.js +61 -56
- package/dist/pwa/routes/peers.js +9 -8
- package/dist/pwa/routes/pin-receipts.js +13 -13
- package/dist/pwa/routes/products-aliases.js +12 -10
- package/dist/pwa/routes/products-claims.js +36 -17
- package/dist/pwa/routes/products-create.js +53 -38
- package/dist/pwa/routes/products-crud.js +17 -16
- package/dist/pwa/routes/products-links.js +49 -26
- package/dist/pwa/routes/products-list.js +6 -4
- package/dist/pwa/routes/products-meta.js +40 -39
- package/dist/pwa/routes/products-update.js +19 -5
- package/dist/pwa/routes/profile-credentials.js +20 -19
- package/dist/pwa/routes/profile-identity.js +14 -13
- package/dist/pwa/routes/profile-location.js +7 -6
- package/dist/pwa/routes/profile-placement.js +20 -19
- package/dist/pwa/routes/profile-prefs.js +11 -11
- package/dist/pwa/routes/promoter.js +58 -66
- package/dist/pwa/routes/public-build-tasks.js +19 -0
- package/dist/pwa/routes/public-utils.js +108 -46
- package/dist/pwa/routes/push.js +16 -15
- package/dist/pwa/routes/ratings.js +92 -32
- package/dist/pwa/routes/recover-key.js +66 -26
- package/dist/pwa/routes/referral.js +37 -52
- package/dist/pwa/routes/reputation.js +3 -2
- package/dist/pwa/routes/returns.js +76 -73
- package/dist/pwa/routes/reviews.js +41 -18
- package/dist/pwa/routes/rewards-apply.js +16 -15
- package/dist/pwa/routes/rewards-auto-downgrade.js +9 -7
- package/dist/pwa/routes/rewards-escrow-expire.js +7 -5
- package/dist/pwa/routes/rfqs.js +163 -85
- package/dist/pwa/routes/search.js +16 -14
- package/dist/pwa/routes/secondhand.js +25 -22
- package/dist/pwa/routes/seller-quota.js +24 -26
- package/dist/pwa/routes/share-redirects.js +60 -55
- package/dist/pwa/routes/shareables-interactions.js +34 -35
- package/dist/pwa/routes/shareables.js +55 -51
- package/dist/pwa/routes/shop-referral.js +58 -0
- package/dist/pwa/routes/shops.js +25 -20
- package/dist/pwa/routes/signaling.js +10 -9
- package/dist/pwa/routes/skill-market.js +16 -16
- package/dist/pwa/routes/skills.js +15 -14
- package/dist/pwa/routes/snf.js +14 -13
- package/dist/pwa/routes/tags.js +10 -9
- package/dist/pwa/routes/task-proposals.js +121 -0
- package/dist/pwa/routes/trial.js +72 -52
- package/dist/pwa/routes/trusted-kpi.js +20 -18
- package/dist/pwa/routes/url-claim.js +67 -28
- package/dist/pwa/routes/users-public.js +62 -70
- package/dist/pwa/routes/variants.js +12 -13
- package/dist/pwa/routes/verifier-user.js +61 -21
- package/dist/pwa/routes/verify-tasks.js +49 -25
- package/dist/pwa/routes/waitlist.js +16 -15
- package/dist/pwa/routes/wallet-read.js +75 -37
- package/dist/pwa/routes/wallet-write.js +12 -9
- package/dist/pwa/routes/webauthn.js +25 -26
- package/dist/pwa/routes/webhooks.js +26 -26
- package/dist/pwa/routes/welcome.js +45 -50
- package/dist/pwa/routes/wishlist-qa.js +29 -32
- package/dist/pwa/server.js +304 -90
- package/dist/version.js +1 -1
- package/package.json +76 -3
|
@@ -22,6 +22,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
22
22
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, // #B.1 a — MCP 三大原语之 Prompts
|
|
23
23
|
GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
24
24
|
import { initDatabase, generateId } from '../../layer0-foundation/L0-1-database/schema.js';
|
|
25
|
+
import { setSeamDb } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam(本进程注入)
|
|
25
26
|
import { transition, getOrderStatus, initSystemUser, } from '../../layer0-foundation/L0-2-state-machine/engine.js';
|
|
26
27
|
import { initDisputeSchema, createDispute, respondToDispute, getDisputeDetails, getOrderDispute, getOpenDisputes, } from '../../layer3-trust/L3-1-dispute-engine/dispute-engine.js';
|
|
27
28
|
import { initNotificationSchema, notifyTransition, getNotifications, getUnreadCount, markRead, } from '../../layer2-business/L2-6-notifications/notification-engine.js';
|
|
@@ -42,6 +43,15 @@ const TELEMETRY_ENABLED = (process.env.WEBAZ_TELEMETRY ?? 'off').toLowerCase() =
|
|
|
42
43
|
// P0 不迁移任何工具(NETWORK_TOOLS 为空)→ 一切仍走本地 = 零行为变化;P1/P2 逐个把工具名加入集合切到网络。
|
|
43
44
|
const WEBAZ_API_URL = (process.env.WEBAZ_API_URL ?? 'https://webaz.xyz').replace(/\/+$/, '');
|
|
44
45
|
const WEBAZ_API_KEY = process.env.WEBAZ_API_KEY ?? '';
|
|
46
|
+
// F6 (dogfood R2): keyed MCP handlers resolve api_key as explicit args.api_key > env WEBAZ_API_KEY >
|
|
47
|
+
// '' (→ the existing typed API_KEY_REQUIRED guards). Explicit ALWAYS wins; env never overrides an explicit
|
|
48
|
+
// key. Keyless actions (list/discover/detail/suggest/browse/get_campaign…) gate their public branches
|
|
49
|
+
// separately and never call this, so a configured env key does NOT change the public read boundary.
|
|
50
|
+
// The key is never printed/returned/logged.
|
|
51
|
+
export function resolveMcpApiKey(args, envKey = WEBAZ_API_KEY) {
|
|
52
|
+
const explicit = typeof args?.api_key === 'string' ? args.api_key.trim() : '';
|
|
53
|
+
return explicit || envKey;
|
|
54
|
+
}
|
|
45
55
|
const WEBAZ_MODE_ENV = (process.env.WEBAZ_MODE ?? '').toLowerCase();
|
|
46
56
|
// 模式:显式 WEBAZ_MODE 优先;否则有 api_key → network,无 key → network_readonly(装完即见真网络)。
|
|
47
57
|
// network_readonly(L1 onboarding,2026-06-08):无 key 默认。公共读匿名打 webaz.xyz(真 catalog/协议),
|
|
@@ -184,6 +194,7 @@ function modeBanner() {
|
|
|
184
194
|
}
|
|
185
195
|
// ─── 初始化 ──────────────────────────────────────────────────
|
|
186
196
|
const db = initDatabase();
|
|
197
|
+
setSeamDb(db); // RFC-016 Phase 1:注入异步 DB seam(本进程)—— 共享引擎迁 seam 后 MCP 进程也能用,否则 dbOne/dbAll 抛"未初始化"
|
|
187
198
|
initSystemUser(db);
|
|
188
199
|
initDisputeSchema(db);
|
|
189
200
|
initNotificationSchema(db);
|
|
@@ -323,7 +334,7 @@ No auth required, no parameters needed.
|
|
|
323
334
|
|
|
324
335
|
Roles: buyer (browse/order/confirm) | seller (list/accept/ship) | logistics (pickup/transit/deliver) | reviewer (reviews) | arbitrator (disputes/rulings).
|
|
325
336
|
|
|
326
|
-
⚠️ **MCP register limitations (anti-bot, by design)**: does NOT set placement_id/sponsor_id — referral/PV chain NOT built via MCP. To build chain: user must arrive via webaz_share_link \`?ref=<
|
|
337
|
+
⚠️ **MCP register limitations (anti-bot, by design)**: does NOT set placement_id/sponsor_id — referral/PV chain NOT built via MCP. To build chain: user must arrive via webaz_share_link \`/i/<permanent_code>\` (or \`?ref=<permanent_code>\`) URL clicked in browser (PWA flow). region defaults 'global'; valid: singapore/china/usa/malaysia/indonesia/thailand/vietnam/taiwan/hk/global.`,
|
|
327
338
|
inputSchema: {
|
|
328
339
|
type: 'object',
|
|
329
340
|
properties: {
|
|
@@ -401,11 +412,11 @@ Skipping is allowed but agent then carries price/stock-race risk itself.`,
|
|
|
401
412
|
inputSchema: {
|
|
402
413
|
type: 'object',
|
|
403
414
|
properties: {
|
|
404
|
-
api_key: { type: 'string', description: "Buyer's api_key" },
|
|
415
|
+
api_key: { type: 'string', description: "Buyer's api_key (or omit and set the WEBAZ_API_KEY env var)" },
|
|
405
416
|
product_id: { type: 'string', description: 'Product ID (from webaz_search)' },
|
|
406
417
|
quantity: { type: 'number', description: 'Quantity, default 1' },
|
|
407
418
|
},
|
|
408
|
-
required: ['
|
|
419
|
+
required: ['product_id'],
|
|
409
420
|
},
|
|
410
421
|
},
|
|
411
422
|
{
|
|
@@ -421,7 +432,7 @@ Actions: create (title/description/price) | mine | update (product_id + changed
|
|
|
421
432
|
inputSchema: {
|
|
422
433
|
type: 'object',
|
|
423
434
|
properties: {
|
|
424
|
-
api_key: { type: 'string', description: "Seller's api_key" },
|
|
435
|
+
api_key: { type: 'string', description: "Seller's api_key (or omit and set the WEBAZ_API_KEY env var)" },
|
|
425
436
|
action: {
|
|
426
437
|
type: 'string',
|
|
427
438
|
enum: ['create', 'mine', 'update', 'delist', 'relist', 'trash', 'delete'],
|
|
@@ -477,7 +488,7 @@ Actions: create (title/description/price) | mine | update (product_id + changed
|
|
|
477
488
|
description: '[S4] Product-origin claims (challengeable). E.g. {"made_in":"Kyoto JP","material":"100% cotton GOTS-cert","certs":[{"name":"GOTS","sha256":"<64-hex>"}]}. Total JSON ≤4KB; any cert sha256 must be 64-hex. Any buyer can challenge.',
|
|
478
489
|
},
|
|
479
490
|
},
|
|
480
|
-
required: [
|
|
491
|
+
required: [],
|
|
481
492
|
},
|
|
482
493
|
},
|
|
483
494
|
{
|
|
@@ -499,7 +510,7 @@ Options:
|
|
|
499
510
|
inputSchema: {
|
|
500
511
|
type: 'object',
|
|
501
512
|
properties: {
|
|
502
|
-
api_key: { type: 'string', description: "Buyer's api_key" },
|
|
513
|
+
api_key: { type: 'string', description: "Buyer's api_key (or omit and set the WEBAZ_API_KEY env var)" },
|
|
503
514
|
product_id: { type: 'string', description: 'Product ID to buy (from webaz_search)' },
|
|
504
515
|
quantity: { type: 'number', description: 'Quantity, default 1' },
|
|
505
516
|
shipping_address: { type: 'string', description: 'Shipping address' },
|
|
@@ -524,7 +535,7 @@ Options:
|
|
|
524
535
|
description: '[B5] Per-order donation pct (0 / 0.5 / 1 / 2 / 5). Computed separately + into charity_fund, posted on order complete.',
|
|
525
536
|
},
|
|
526
537
|
},
|
|
527
|
-
required: ['
|
|
538
|
+
required: ['product_id', 'shipping_address'],
|
|
528
539
|
},
|
|
529
540
|
},
|
|
530
541
|
{
|
|
@@ -532,15 +543,15 @@ Options:
|
|
|
532
543
|
// was ~927 chars, now ~430 chars
|
|
533
544
|
description: `STATUS TRANSITIONS on an order — NOT for editing order content (price/qty/address immutable after creation). Each role can only perform their own actions.
|
|
534
545
|
|
|
535
|
-
- **Seller**: accept (24h after payment) | ship (needs tracking, within handling time)
|
|
536
|
-
- **Logistics**: pickup (48h after ship) | transit | deliver (needs proof description)
|
|
546
|
+
- **Seller**: accept (24h after payment) | ship (needs tracking/notes, within handling time) | pickup/transit/deliver ONLY when order.logistics_id is empty (Phase-1 self-fulfill; seller carries logistics responsibility) | decline (actively refuse a PAID order instead of silent timeout; requires decline_reason_code — objective codes [stock_consumed_concurrent/stale_price_snapshot/force_majeure] go to a PROVISIONAL fault you must then contest within the window, NOT auto-cleared; subjective codes [price_regret/cherry_pick/other] settle immediately as seller-fault + buyer refund) | contest_decline (open human arbitration on an objective-claimed provisional fault, within the contest window, to be cleared to no-fault; pass evidence_description — window expiry finalizes as fault)
|
|
547
|
+
- **Logistics**: pickup (48h after ship) | transit | deliver (needs proof description) when assigned or claiming an unassigned shipped order
|
|
537
548
|
- **Buyer**: confirm (→ fund settlement) | dispute (needs reason; freezes funds → arbitration)
|
|
538
549
|
|
|
539
550
|
Missing deadline → protocol auto-marks party in default.`,
|
|
540
551
|
inputSchema: {
|
|
541
552
|
type: 'object',
|
|
542
553
|
properties: {
|
|
543
|
-
api_key: { type: 'string', description: "Operator's api_key" },
|
|
554
|
+
api_key: { type: 'string', description: "Operator's api_key (or omit and set the WEBAZ_API_KEY env var)" },
|
|
544
555
|
order_id: { type: 'string', description: 'Order ID' },
|
|
545
556
|
action: {
|
|
546
557
|
type: 'string',
|
|
@@ -558,7 +569,7 @@ Missing deadline → protocol auto-marks party in default.`,
|
|
|
558
569
|
description: 'Evidence description (recommended for ship/pickup/deliver; required for dispute)',
|
|
559
570
|
},
|
|
560
571
|
},
|
|
561
|
-
required: ['
|
|
572
|
+
required: ['order_id', 'action'],
|
|
562
573
|
},
|
|
563
574
|
},
|
|
564
575
|
{
|
|
@@ -568,10 +579,10 @@ Missing deadline → protocol auto-marks party in default.`,
|
|
|
568
579
|
inputSchema: {
|
|
569
580
|
type: 'object',
|
|
570
581
|
properties: {
|
|
571
|
-
api_key: { type: 'string', description: "Querier's api_key" },
|
|
582
|
+
api_key: { type: 'string', description: "Querier's api_key (or omit and set the WEBAZ_API_KEY env var)" },
|
|
572
583
|
order_id: { type: 'string', description: 'Order ID' },
|
|
573
584
|
},
|
|
574
|
-
required: ['
|
|
585
|
+
required: ['order_id'],
|
|
575
586
|
},
|
|
576
587
|
},
|
|
577
588
|
{
|
|
@@ -589,14 +600,14 @@ Actions:
|
|
|
589
600
|
inputSchema: {
|
|
590
601
|
type: 'object',
|
|
591
602
|
properties: {
|
|
592
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
603
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
593
604
|
action: {
|
|
594
605
|
type: 'string',
|
|
595
606
|
enum: ['view', 'deposits', 'withdrawals', 'income'],
|
|
596
607
|
description: 'Action type (default: view)',
|
|
597
608
|
},
|
|
598
609
|
},
|
|
599
|
-
required: [
|
|
610
|
+
required: [],
|
|
600
611
|
},
|
|
601
612
|
},
|
|
602
613
|
{
|
|
@@ -608,11 +619,11 @@ Actions:
|
|
|
608
619
|
inputSchema: {
|
|
609
620
|
type: 'object',
|
|
610
621
|
properties: {
|
|
611
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
622
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
612
623
|
unread: { type: 'boolean', description: 'Return only unread (default false)' },
|
|
613
624
|
mark_read: { type: 'boolean', description: 'Auto-mark read after call (default false)' },
|
|
614
625
|
},
|
|
615
|
-
required: [
|
|
626
|
+
required: [],
|
|
616
627
|
},
|
|
617
628
|
},
|
|
618
629
|
{
|
|
@@ -635,7 +646,7 @@ Protocol auto-judges (no human): respondent silent 48h → favor initiator; arbi
|
|
|
635
646
|
inputSchema: {
|
|
636
647
|
type: 'object',
|
|
637
648
|
properties: {
|
|
638
|
-
api_key: { type: 'string', description: "Operator's api_key" },
|
|
649
|
+
api_key: { type: 'string', description: "Operator's api_key (or omit and set the WEBAZ_API_KEY env var)" },
|
|
639
650
|
action: {
|
|
640
651
|
type: 'string',
|
|
641
652
|
enum: ['view', 'list_open', 'respond', 'add_evidence', 'arbitrate'],
|
|
@@ -666,7 +677,7 @@ Protocol auto-judges (no human): respondent silent 48h → favor initiator; arbi
|
|
|
666
677
|
},
|
|
667
678
|
ruling_reason: { type: 'string', description: 'Ruling reason (required for arbitrate; permanently recorded on-chain)' },
|
|
668
679
|
},
|
|
669
|
-
required: ['
|
|
680
|
+
required: ['action'],
|
|
670
681
|
},
|
|
671
682
|
},
|
|
672
683
|
{
|
|
@@ -693,7 +704,7 @@ Actions:
|
|
|
693
704
|
inputSchema: {
|
|
694
705
|
type: 'object',
|
|
695
706
|
properties: {
|
|
696
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
707
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
697
708
|
action: {
|
|
698
709
|
type: 'string',
|
|
699
710
|
enum: ['create', 'view', 'mine', 'submit_seller_evidence', 'available', 'vote', 'eligibility', 'verifier_status', 'apply', 'withdraw_application', 'appeal'],
|
|
@@ -716,7 +727,7 @@ Actions:
|
|
|
716
727
|
// appeal
|
|
717
728
|
reason: { type: 'string', description: 'Appeal reason (required for appeal, ≤500 chars)' },
|
|
718
729
|
},
|
|
719
|
-
required: ['
|
|
730
|
+
required: ['action'],
|
|
720
731
|
},
|
|
721
732
|
},
|
|
722
733
|
{
|
|
@@ -741,7 +752,7 @@ Actions: list (no auth) | publish (seller) | subscribe / unsubscribe (buyer) | m
|
|
|
741
752
|
inputSchema: {
|
|
742
753
|
type: 'object',
|
|
743
754
|
properties: {
|
|
744
|
-
api_key: { type: 'string', description: 'Your api_key (omit for list)' },
|
|
755
|
+
api_key: { type: 'string', description: 'Your api_key (omit for list) (or set the WEBAZ_API_KEY env var)' },
|
|
745
756
|
action: {
|
|
746
757
|
type: 'string',
|
|
747
758
|
enum: ['list', 'publish', 'subscribe', 'unsubscribe', 'my_skills', 'my_subs'],
|
|
@@ -805,7 +816,7 @@ Public-profile actions:
|
|
|
805
816
|
inputSchema: {
|
|
806
817
|
type: 'object',
|
|
807
818
|
properties: {
|
|
808
|
-
api_key: { type: 'string', description: 'Your api_key (required for view/add_role/switch_role/view_user; optional for public feed)' },
|
|
819
|
+
api_key: { type: 'string', description: 'Your api_key (required for view/add_role/switch_role/view_user; optional for public feed) (or set the WEBAZ_API_KEY env var)' },
|
|
809
820
|
action: {
|
|
810
821
|
type: 'string',
|
|
811
822
|
enum: ['view', 'add_role', 'switch_role', 'view_user', 'feed'],
|
|
@@ -839,10 +850,10 @@ Use revoke when: PERMANENT decommission of agent/device, OR want access death NO
|
|
|
839
850
|
inputSchema: {
|
|
840
851
|
type: 'object',
|
|
841
852
|
properties: {
|
|
842
|
-
api_key: { type: 'string', description: 'Your current api_key (the one to revoke)' },
|
|
853
|
+
api_key: { type: 'string', description: 'Your current api_key (the one to revoke) (or set the WEBAZ_API_KEY env var)' },
|
|
843
854
|
reason: { type: 'string', description: 'Optional: leaked / lost_device / rotation / unspecified' },
|
|
844
855
|
},
|
|
845
|
-
required: [
|
|
856
|
+
required: [],
|
|
846
857
|
},
|
|
847
858
|
},
|
|
848
859
|
{
|
|
@@ -854,46 +865,46 @@ Safer than \`webaz_revoke_key\` — atomic swap, no access gap.`,
|
|
|
854
865
|
inputSchema: {
|
|
855
866
|
type: 'object',
|
|
856
867
|
properties: {
|
|
857
|
-
api_key: { type: 'string', description: 'Your current api_key (will be invalidated after PWA confirm)' },
|
|
868
|
+
api_key: { type: 'string', description: 'Your current api_key (will be invalidated after PWA confirm) (or set the WEBAZ_API_KEY env var)' },
|
|
858
869
|
reason: { type: 'string', description: 'Optional: rotation / leaked / scheduled' },
|
|
859
870
|
},
|
|
860
|
-
required: [
|
|
871
|
+
required: [],
|
|
861
872
|
},
|
|
862
873
|
},
|
|
863
874
|
{
|
|
864
875
|
name: 'webaz_referral',
|
|
865
876
|
description: `View your referral status: 3-tier commission team + earnings + invite links + points-matching tier progress + L1 share permission gate + **rewards_status (RFC-002 §3.5 opt-in state + pending escrow)**.
|
|
866
877
|
|
|
867
|
-
⚠️ **Opt-in required (RFC-002)**: rewards default = off. \`rewards_status\` field returns 4-state {opted_in | never_activated | auto_downgraded | deactivated} + pending_escrow tally. Opted-out users still see attribution + tree
|
|
878
|
+
⚠️ **Opt-in required (RFC-002)**: rewards default = off. \`rewards_status\` field returns 4-state {opted_in | never_activated | auto_downgraded | deactivated} + pending_escrow tally. Opted-out users still see attribution + tree. Commission destination differs by state: **never_activated / auto_downgraded** → held in pending_commission_escrow, recoverable by (re-)activating within the window via PWA #me; **deactivated** (active opt-out) → future L1/L2/L3 commission goes to commission_reserve / protocol reserve, NOT escrow and NOT recoverable (re-applying only affects future commission).
|
|
868
879
|
|
|
869
880
|
⚠️ **Consent required**: generating referral links / promoting on a human user's behalf needs the user's explicit authorization. Agent **MUST get explicit consent** before generating referral links / promoting. Do NOT auto-recruit.`,
|
|
870
881
|
inputSchema: {
|
|
871
882
|
type: 'object',
|
|
872
883
|
properties: {
|
|
873
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
884
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
874
885
|
},
|
|
875
|
-
required: [
|
|
886
|
+
required: [],
|
|
876
887
|
},
|
|
877
888
|
},
|
|
878
889
|
{
|
|
879
890
|
name: 'webaz_share_link',
|
|
880
891
|
description: `Generate product share link with your referral attached. Open in any social platform (TikTok/WeChat/Telegram). Clicker registers/buys → counts toward your 3-tier commission (if verified buyer) + points-matching.
|
|
881
892
|
|
|
882
|
-
⚠️ **Opt-in required (RFC-002 §3.5)**: this is a valuation-layer action. Caller must have \`rewards_opted_in=1\` (
|
|
893
|
+
⚠️ **Opt-in required (RFC-002 §3.5)**: this is a valuation-layer (rewards) action, not a contribution gate. Caller must have \`rewards_opted_in=1\` (rewards / share-commission opt-in). Opted-out users get \`{error: 'rewards_opt_in_required', missing_requirements, next_steps}\` — direct user to PWA #me to apply.
|
|
883
894
|
|
|
884
895
|
⚠️ **Consent required**: this builds a referral chain on the user's behalf. Agent acting for a human user **MUST get explicit consent**. Do NOT auto-generate. See webaz_info.commission_model.`,
|
|
885
896
|
inputSchema: {
|
|
886
897
|
type: 'object',
|
|
887
898
|
properties: {
|
|
888
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
899
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
889
900
|
product_id: { type: 'string', description: 'Product to promote (from webaz_search)' },
|
|
890
901
|
side: {
|
|
891
902
|
type: 'string',
|
|
892
|
-
enum: ['
|
|
893
|
-
description: '
|
|
903
|
+
enum: ['auto'],
|
|
904
|
+
description: 'Deprecated / no-op — placement is always automatic (system-decided). Left/right选择已下线。',
|
|
894
905
|
},
|
|
895
906
|
},
|
|
896
|
-
required: ['
|
|
907
|
+
required: ['product_id'],
|
|
897
908
|
},
|
|
898
909
|
},
|
|
899
910
|
{
|
|
@@ -907,12 +918,12 @@ Actions: list | block | unblock.`,
|
|
|
907
918
|
inputSchema: {
|
|
908
919
|
type: 'object',
|
|
909
920
|
properties: {
|
|
910
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
921
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
911
922
|
action: { type: 'string', enum: ['list', 'block', 'unblock'], description: 'list: my blocked users | block: add | unblock: remove' },
|
|
912
923
|
user_id: { type: 'string', description: 'Target user id (required for block/unblock)' },
|
|
913
924
|
reason: { type: 'string', description: 'Optional reason for block (e.g. "fake product", "abuse")' },
|
|
914
925
|
},
|
|
915
|
-
required: ['
|
|
926
|
+
required: ['action'],
|
|
916
927
|
},
|
|
917
928
|
},
|
|
918
929
|
{
|
|
@@ -921,11 +932,11 @@ Actions: list | block | unblock.`,
|
|
|
921
932
|
inputSchema: {
|
|
922
933
|
type: 'object',
|
|
923
934
|
properties: {
|
|
924
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
935
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
925
936
|
action: { type: 'string', enum: ['list', 'follow', 'unfollow', 'status'], description: 'list: my follows + followers | follow/unfollow: change relation | status: check if I follow a user' },
|
|
926
937
|
user_id: { type: 'string', description: 'Target user (required for follow/unfollow/status)' },
|
|
927
938
|
},
|
|
928
|
-
required: ['
|
|
939
|
+
required: ['action'],
|
|
929
940
|
},
|
|
930
941
|
},
|
|
931
942
|
{
|
|
@@ -939,12 +950,12 @@ USE THIS for "what's popular near me / 我附近 / 同城" — geo-aggregated, n
|
|
|
939
950
|
inputSchema: {
|
|
940
951
|
type: 'object',
|
|
941
952
|
properties: {
|
|
942
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
953
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
943
954
|
action: { type: 'string', enum: ['query', 'set_location', 'clear_location'], description: 'query: get aggregated nearby activity | set_location: set your geo cell | clear_location: remove' },
|
|
944
955
|
lat: { type: 'number', description: 'Latitude -90..90 (for set_location, auto-truncated to 0.1°)' },
|
|
945
956
|
lng: { type: 'number', description: 'Longitude -180..180 (for set_location, auto-truncated to 0.1°)' },
|
|
946
957
|
},
|
|
947
|
-
required: ['
|
|
958
|
+
required: ['action'],
|
|
948
959
|
},
|
|
949
960
|
},
|
|
950
961
|
{
|
|
@@ -956,12 +967,12 @@ USE THIS for "what's popular near me / 我附近 / 同城" — geo-aggregated, n
|
|
|
956
967
|
inputSchema: {
|
|
957
968
|
type: 'object',
|
|
958
969
|
properties: {
|
|
959
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
970
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
960
971
|
action: { type: 'string', enum: ['read', 'set'], description: 'read: get current default | set: update' },
|
|
961
972
|
text: { type: 'string', description: 'Full address as free-text string (e.g. "John Doe / 1 Test St / Singapore SG / +65 12345678"). Required for set. ≤ 200 chars.' },
|
|
962
973
|
region: { type: 'string', description: 'Region tag for shipping match (e.g. "global", "china", "SG"). Optional for set. ≤ 40 chars.' },
|
|
963
974
|
},
|
|
964
|
-
required: ['
|
|
975
|
+
required: ['action'],
|
|
965
976
|
},
|
|
966
977
|
},
|
|
967
978
|
{
|
|
@@ -977,7 +988,7 @@ Actions: list_mine | add (external_url + product/anchor) | delete | by_product |
|
|
|
977
988
|
inputSchema: {
|
|
978
989
|
type: 'object',
|
|
979
990
|
properties: {
|
|
980
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
991
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
981
992
|
action: { type: 'string', enum: ['list_mine', 'add', 'delete', 'by_product', 'by_anchor'], description: 'list_mine | add (need external_url + product/anchor) | delete | by_product | by_anchor' },
|
|
982
993
|
external_url: { type: 'string', description: 'For action=add' },
|
|
983
994
|
title: { type: 'string', description: 'For action=add (optional)' },
|
|
@@ -986,7 +997,7 @@ Actions: list_mine | add (external_url + product/anchor) | delete | by_product |
|
|
|
986
997
|
related_anchor: { type: 'string', description: 'For action=add or by_anchor' },
|
|
987
998
|
shareable_id: { type: 'string', description: 'For action=delete' },
|
|
988
999
|
},
|
|
989
|
-
required: ['
|
|
1000
|
+
required: ['action'],
|
|
990
1001
|
},
|
|
991
1002
|
},
|
|
992
1003
|
// ── P3 RFQ / bid / chat / auto_bid(MCP 通过 HTTP 调 PWA,复用所有校验+状态机)────
|
|
@@ -1015,7 +1026,7 @@ Shipping address falls back to webaz_default_address if omitted.`,
|
|
|
1015
1026
|
inputSchema: {
|
|
1016
1027
|
type: 'object',
|
|
1017
1028
|
properties: {
|
|
1018
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
1029
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1019
1030
|
action: { type: 'string', enum: ['create', 'mine', 'browse', 'detail', 'award', 'cancel'] },
|
|
1020
1031
|
// create
|
|
1021
1032
|
title: { type: 'string' },
|
|
@@ -1034,7 +1045,7 @@ Shipping address falls back to webaz_default_address if omitted.`,
|
|
|
1034
1045
|
rfq_id: { type: 'string' },
|
|
1035
1046
|
bid_id: { type: 'string', description: 'Optional for award — if omitted, auto-pick current lowest bid' },
|
|
1036
1047
|
},
|
|
1037
|
-
required: ['
|
|
1048
|
+
required: ['action'],
|
|
1038
1049
|
},
|
|
1039
1050
|
},
|
|
1040
1051
|
{
|
|
@@ -1048,7 +1059,7 @@ Actions: submit (rfq_id + price + qty_offered + fulfillment_type; optional eta/n
|
|
|
1048
1059
|
inputSchema: {
|
|
1049
1060
|
type: 'object',
|
|
1050
1061
|
properties: {
|
|
1051
|
-
api_key: { type: 'string', description: 'Seller api_key' },
|
|
1062
|
+
api_key: { type: 'string', description: 'Seller api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1052
1063
|
action: { type: 'string', enum: ['submit', 'patch', 'cancel', 'list_mine'] },
|
|
1053
1064
|
rfq_id: { type: 'string' },
|
|
1054
1065
|
bid_id: { type: 'string' },
|
|
@@ -1059,7 +1070,7 @@ Actions: submit (rfq_id + price + qty_offered + fulfillment_type; optional eta/n
|
|
|
1059
1070
|
note: { type: 'string' },
|
|
1060
1071
|
offer_id: { type: 'string', description: 'Optional; reference existing offer' },
|
|
1061
1072
|
},
|
|
1062
|
-
required: ['
|
|
1073
|
+
required: ['action'],
|
|
1063
1074
|
},
|
|
1064
1075
|
},
|
|
1065
1076
|
{
|
|
@@ -1079,7 +1090,7 @@ webaz_blocklist hides from search but does NOT auto-silence existing convs (busi
|
|
|
1079
1090
|
inputSchema: {
|
|
1080
1091
|
type: 'object',
|
|
1081
1092
|
properties: {
|
|
1082
|
-
api_key: { type: 'string' },
|
|
1093
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1083
1094
|
action: { type: 'string', enum: ['start', 'list', 'read', 'send', 'mark_read', 'block'] },
|
|
1084
1095
|
kind: { type: 'string', enum: ['order', 'rfq', 'listing_qa'] },
|
|
1085
1096
|
context_id: { type: 'string' },
|
|
@@ -1087,7 +1098,7 @@ webaz_blocklist hides from search but does NOT auto-silence existing convs (busi
|
|
|
1087
1098
|
conversation_id: { type: 'string' },
|
|
1088
1099
|
body: { type: 'string', description: 'Message body for send action (≤2000 chars)' },
|
|
1089
1100
|
},
|
|
1090
|
-
required: ['
|
|
1101
|
+
required: ['action'],
|
|
1091
1102
|
},
|
|
1092
1103
|
},
|
|
1093
1104
|
{
|
|
@@ -1127,7 +1138,7 @@ Actions (15):
|
|
|
1127
1138
|
inputSchema: {
|
|
1128
1139
|
type: 'object',
|
|
1129
1140
|
properties: {
|
|
1130
|
-
api_key: { type: 'string' },
|
|
1141
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1131
1142
|
action: { type: 'string', enum: ['list', 'detail', 'create', 'claim', 'proof', 'confirm', 'disclose', 'cancel', 'me', 'stories', 'leaderboard', 'repay', 'repay_respond', 'donate', 'fund'] },
|
|
1132
1143
|
wish_id: { type: 'string' },
|
|
1133
1144
|
fulfillment_id: { type: 'string' },
|
|
@@ -1163,7 +1174,7 @@ Actions: create (seller, needs title/price/stock + content_hash sha256 + content
|
|
|
1163
1174
|
inputSchema: {
|
|
1164
1175
|
type: 'object',
|
|
1165
1176
|
properties: {
|
|
1166
|
-
api_key: { type: 'string' },
|
|
1177
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1167
1178
|
action: { type: 'string', enum: ['create', 'list', 'detail', 'patch'] },
|
|
1168
1179
|
product_id: { type: 'string' },
|
|
1169
1180
|
title: { type: 'string' }, price: { type: 'number' }, stock: { type: 'number' },
|
|
@@ -1188,11 +1199,11 @@ Actions: toggle (same endpoint; 2nd call auto-unlikes) | status (my like status
|
|
|
1188
1199
|
inputSchema: {
|
|
1189
1200
|
type: 'object',
|
|
1190
1201
|
properties: {
|
|
1191
|
-
api_key: { type: 'string' },
|
|
1202
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1192
1203
|
action: { type: 'string', enum: ['toggle', 'status'] },
|
|
1193
1204
|
shareable_id: { type: 'string' },
|
|
1194
1205
|
},
|
|
1195
|
-
required: ['
|
|
1206
|
+
required: ['action', 'shareable_id'],
|
|
1196
1207
|
},
|
|
1197
1208
|
},
|
|
1198
1209
|
{
|
|
@@ -1237,7 +1248,7 @@ Actions:
|
|
|
1237
1248
|
inputSchema: {
|
|
1238
1249
|
type: 'object',
|
|
1239
1250
|
properties: {
|
|
1240
|
-
api_key: { type: 'string' },
|
|
1251
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1241
1252
|
action: { type: 'string', enum: ['create', 'browse', 'mine', 'detail', 'bid', 'cancel'] },
|
|
1242
1253
|
title: { type: 'string' },
|
|
1243
1254
|
qty: { type: 'number' },
|
|
@@ -1251,7 +1262,7 @@ Actions:
|
|
|
1251
1262
|
auction_id: { type: 'string' },
|
|
1252
1263
|
price: { type: 'number' },
|
|
1253
1264
|
},
|
|
1254
|
-
required: ['
|
|
1265
|
+
required: ['action'],
|
|
1255
1266
|
},
|
|
1256
1267
|
},
|
|
1257
1268
|
{
|
|
@@ -1265,7 +1276,7 @@ Actions: get | set (categories[] / regions[] / max_eta_h / bid_strategy) | disab
|
|
|
1265
1276
|
inputSchema: {
|
|
1266
1277
|
type: 'object',
|
|
1267
1278
|
properties: {
|
|
1268
|
-
api_key: { type: 'string' },
|
|
1279
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1269
1280
|
action: { type: 'string', enum: ['get', 'set', 'disable'] },
|
|
1270
1281
|
categories: { type: 'array', items: { type: 'string' } },
|
|
1271
1282
|
regions: { type: 'array', items: { type: 'string' } },
|
|
@@ -1278,7 +1289,7 @@ Actions: get | set (categories[] / regions[] / max_eta_h / bid_strategy) | disab
|
|
|
1278
1289
|
cooldown_min: { type: 'number' },
|
|
1279
1290
|
enabled: { type: 'boolean' },
|
|
1280
1291
|
},
|
|
1281
|
-
required: ['
|
|
1292
|
+
required: ['action'],
|
|
1282
1293
|
},
|
|
1283
1294
|
},
|
|
1284
1295
|
{
|
|
@@ -1298,7 +1309,7 @@ Actions: list (no auth, filters: kind/billing/query) | detail (public, no conten
|
|
|
1298
1309
|
inputSchema: {
|
|
1299
1310
|
type: 'object',
|
|
1300
1311
|
properties: {
|
|
1301
|
-
api_key: { type: 'string', description: 'Your api_key (omit for list/detail)' },
|
|
1312
|
+
api_key: { type: 'string', description: 'Your api_key (omit for list/detail) (or set the WEBAZ_API_KEY env var)' },
|
|
1302
1313
|
action: {
|
|
1303
1314
|
type: 'string',
|
|
1304
1315
|
enum: ['list', 'detail', 'publish', 'update', 'delist', 'resubmit', 'purchase', 'read', 'my_skills', 'library'],
|
|
@@ -1335,7 +1346,7 @@ Enums: **category** phone/computer/appliance/furniture/clothing/book/toy/sports/
|
|
|
1335
1346
|
inputSchema: {
|
|
1336
1347
|
type: 'object',
|
|
1337
1348
|
properties: {
|
|
1338
|
-
api_key: { type: 'string', description: 'Your api_key (omit for browse/detail)' },
|
|
1349
|
+
api_key: { type: 'string', description: 'Your api_key (omit for browse/detail) (or set the WEBAZ_API_KEY env var)' },
|
|
1339
1350
|
action: { type: 'string', enum: ['browse', 'detail', 'publish', 'update', 'mine', 'buy'], description: 'Action to execute' },
|
|
1340
1351
|
item_id: { type: 'string', description: 'Item ID (required for detail/update/buy)' },
|
|
1341
1352
|
// publish / update
|
|
@@ -1384,7 +1395,7 @@ Seller actions:
|
|
|
1384
1395
|
inputSchema: {
|
|
1385
1396
|
type: 'object',
|
|
1386
1397
|
properties: {
|
|
1387
|
-
api_key: { type: 'string', description: 'Your api_key (omit for get_campaign)' },
|
|
1398
|
+
api_key: { type: 'string', description: 'Your api_key (omit for get_campaign) (or set the WEBAZ_API_KEY env var)' },
|
|
1388
1399
|
action: { type: 'string', enum: ['get_campaign', 'apply', 'link_note', 'my_claims', 'create_campaign', 'cancel_campaign', 'my_campaigns', 'campaign_claims'], description: 'Action to execute' },
|
|
1389
1400
|
product_id: { type: 'string', description: 'Product ID (required for get_campaign/apply/create_campaign/cancel_campaign)' },
|
|
1390
1401
|
claim_id: { type: 'string', description: 'Claim ID (required for link_note)' },
|
|
@@ -1413,7 +1424,7 @@ Gate by type: ux_issue/bug (reporting = using) → login only, NO Passkey, anyon
|
|
|
1413
1424
|
type: 'object',
|
|
1414
1425
|
properties: {
|
|
1415
1426
|
action: { type: 'string', enum: ['submit', 'my', 'get'], description: 'submit (default) | my | get' },
|
|
1416
|
-
api_key: { type: 'string', description: "User's api_key (real person required)" },
|
|
1427
|
+
api_key: { type: 'string', description: "User's api_key (real person required; or set the WEBAZ_API_KEY env var)" },
|
|
1417
1428
|
type: { type: 'string', enum: ['ux_issue', 'bug', 'proposal'], description: 'submit: kind of feedback' },
|
|
1418
1429
|
area: { type: 'string', description: 'submit: which feature, e.g. search / order / dispute' },
|
|
1419
1430
|
severity: { type: 'string', enum: ['low', 'annoying', 'blocking'], description: 'submit: for ux_issue/bug' },
|
|
@@ -1421,33 +1432,49 @@ Gate by type: ux_issue/bug (reporting = using) → login only, NO Passkey, anyon
|
|
|
1421
1432
|
text: { type: 'string', description: 'submit: the feedback / idea (≥5 chars)' },
|
|
1422
1433
|
feedback_id: { type: 'string', description: 'get: the feedback id' },
|
|
1423
1434
|
},
|
|
1424
|
-
required: [
|
|
1435
|
+
required: [],
|
|
1425
1436
|
},
|
|
1426
1437
|
},
|
|
1427
1438
|
{
|
|
1428
1439
|
name: 'webaz_contribute',
|
|
1429
|
-
description: `Coordinate building WebAZ itself (RFC-006) — a
|
|
1440
|
+
description: `Coordinate building WebAZ itself (RFC-006 / RFC-017) — a public task board so contributors don't collide. Check BEFORE starting work on an area. 协调"谁在做什么"防撞车.
|
|
1441
|
+
|
|
1442
|
+
Discovery + suggesting need NO api_key (anyone / any agent can browse and propose). Claiming + submitting need an api_key (a real, accountable identity).
|
|
1430
1443
|
|
|
1431
1444
|
Actions:
|
|
1432
|
-
- list_open (default): open tasks (opt. area
|
|
1433
|
-
-
|
|
1434
|
-
-
|
|
1435
|
-
-
|
|
1436
|
-
-
|
|
1445
|
+
- list_open (default): open public tasks (opt. filters: area / risk_level / auto_claimable / required_capabilities / agent_capabilities / max_duration_minutes / estimated_context_size / estimated_agent_budget — estimated_agent_budget is a resource/effort estimate, NOT a payment). Each task carries its execution boundary + the trusted canonical contribution target. NO api_key needed.
|
|
1446
|
+
- detail: one task's full execution boundary (allowed/forbidden paths, prohibited actions, acceptance criteria, verification commands, deliverables, definition_of_done) + the canonical repo to PR to + a copy-ready agent_handoff. NO api_key needed.
|
|
1447
|
+
- suggest: propose a NEW task (title + summary/reason; opt. area/expected_outcome/source_ref/github_login). It enters the maintainer inbox — it is a suggestion, NOT a contribution fact / reward / participation, and never auto-becomes a task. NO api_key needed.
|
|
1448
|
+
- claim: take an open task (api_key); provenance=human|ai_assisted|ai_authored (self-declared, not detected); auto-expires ~7d if not submitted. Returns a handoff — point a coding agent at it; the human needn't know git but stays accountable (Passkey).
|
|
1449
|
+
- submit: mark in_review with pr_ref + verification_summary (api_key). The PR's base repo MUST be the canonical WebAZ repo, and a verification_summary (what you ran/verified) is REQUIRED — both server-enforced. A human maintainer reviews next; done ≠ merge.
|
|
1450
|
+
- status: tasks you hold (api_key).
|
|
1451
|
+
- profile: your build dashboard — KPI/tier/restrictions+appeal, private self-view (api_key).
|
|
1437
1452
|
|
|
1438
|
-
Coordinates + records only — NO merge/reward; acceptance (done) = human maintainer. build_reputation is a SEPARATE pool, never gates verifier/arbitrator.
|
|
1453
|
+
Coordinates + records only — NO merge/reward; acceptance (done) = human maintainer. Contribution value is uncommitted (RFC-017 I-12). build_reputation is a SEPARATE pool, never gates verifier/arbitrator. NETWORK only (contribution is a real-network act; sandbox has nothing to coordinate with).`,
|
|
1439
1454
|
inputSchema: {
|
|
1440
1455
|
type: 'object',
|
|
1441
1456
|
properties: {
|
|
1442
|
-
action: { type: 'string', enum: ['list_open', 'claim', 'submit', 'status', 'profile'], description: 'list_open (default) | claim | submit | status | profile' },
|
|
1443
|
-
api_key: { type: 'string', description:
|
|
1444
|
-
task_id: { type: 'string', description: 'claim / submit: the task id' },
|
|
1445
|
-
area: { type: 'string', description: 'list_open:
|
|
1457
|
+
action: { type: 'string', enum: ['list_open', 'detail', 'suggest', 'claim', 'submit', 'status', 'profile'], description: 'list_open (default) | detail | suggest | claim | submit | status | profile' },
|
|
1458
|
+
api_key: { type: 'string', description: 'claim/submit/status/profile: your api_key (accountable identity). NOT needed for list_open/detail/suggest. (or set the WEBAZ_API_KEY env var)' },
|
|
1459
|
+
task_id: { type: 'string', description: 'detail / claim / submit: the task id' },
|
|
1460
|
+
area: { type: 'string', description: 'list_open: area filter / suggest: suggested area (e.g. search / docs / mcp)' },
|
|
1461
|
+
risk_level: { type: 'string', enum: ['low', 'medium', 'high', 'critical'], description: 'list_open: optional risk filter' },
|
|
1462
|
+
auto_claimable: { type: 'boolean', description: 'list_open: optional filter — only auto-claimable (true) or manual-claim (false) tasks' },
|
|
1463
|
+
required_capabilities: { type: 'string', description: 'list_open: optional filter — comma-separated; matches tasks that REQUIRE ALL of the listed capabilities (superset/AND match on the task requirement). For "tasks my agent can do", use agent_capabilities instead.' },
|
|
1464
|
+
agent_capabilities: { type: 'string', description: 'list_open: optional filter — capabilities your agent HAS (comma-separated); matches tasks whose required_capabilities are a SUBSET of these, i.e. tasks your agent can actually do' },
|
|
1465
|
+
max_duration_minutes: { type: 'number', description: 'list_open: optional filter — only tasks whose estimated max duration fits within this many minutes (your idle time)' },
|
|
1466
|
+
estimated_context_size: { type: 'string', enum: ['small', 'medium', 'large'], description: 'list_open: optional filter — task estimated context size' },
|
|
1467
|
+
estimated_agent_budget: { type: 'string', enum: ['minimal', 'small', 'moderate', 'large', 'xlarge'], description: 'list_open: optional filter — task estimated agent budget (resource/effort estimate, not a payment)' },
|
|
1446
1468
|
provenance: { type: 'string', enum: ['human', 'ai_assisted', 'ai_authored'], description: 'claim: self-declared authorship (default human)' },
|
|
1447
|
-
pr_ref: { type: 'string', description: 'submit: your PR link or number' },
|
|
1469
|
+
pr_ref: { type: 'string', description: 'submit: your PR link or number (must target the canonical repo)' },
|
|
1470
|
+
verification_summary: { type: 'string', description: 'submit (REQUIRED): summarize what you ran/verified — the task verification_commands you ran and their results' },
|
|
1448
1471
|
note: { type: 'string', description: 'submit: optional note' },
|
|
1472
|
+
title: { type: 'string', description: 'suggest: task title (≥3 chars)' },
|
|
1473
|
+
summary: { type: 'string', description: 'suggest: why it is worth doing / what it solves (the reason)' },
|
|
1474
|
+
expected_outcome: { type: 'string', description: 'suggest: optional — what should be true when done' },
|
|
1475
|
+
source_ref: { type: 'string', description: 'suggest: optional reference link (reference only; does NOT set the target repo)' },
|
|
1476
|
+
proposer_github_login: { type: 'string', description: 'suggest: optional — your GitHub login' },
|
|
1449
1477
|
},
|
|
1450
|
-
required: ['api_key'],
|
|
1451
1478
|
},
|
|
1452
1479
|
},
|
|
1453
1480
|
];
|
|
@@ -1455,7 +1482,7 @@ Coordinates + records only — NO merge/reward; acceptance (done) = human mainta
|
|
|
1455
1482
|
// RFC-004: webaz_feedback — agent-native "use → build" 反馈(双模;仅 NETWORK 能送达)
|
|
1456
1483
|
async function handleFeedback(args) {
|
|
1457
1484
|
const action = args.action || 'submit';
|
|
1458
|
-
const apiKey = args
|
|
1485
|
+
const apiKey = resolveMcpApiKey(args);
|
|
1459
1486
|
if (!apiKey)
|
|
1460
1487
|
return { error: 'api_key required' };
|
|
1461
1488
|
if (toolBackend('webaz_feedback') !== 'network') {
|
|
@@ -1489,19 +1516,100 @@ async function handleFeedback(args) {
|
|
|
1489
1516
|
},
|
|
1490
1517
|
});
|
|
1491
1518
|
}
|
|
1492
|
-
// RFC-006
|
|
1493
|
-
|
|
1519
|
+
// RFC-006 断点1(b)交接:从【可信】canonical 目标(API 响应里,绝不硬编码/不取自 task metadata)构造"怎么真正
|
|
1520
|
+
// 动手"。人的编码 agent 做 git/PR;Passkey 真人担责。sandbox 运行 / 本地草稿不算正式参与。
|
|
1521
|
+
function buildContributeHandoff(cct, taskId) {
|
|
1522
|
+
const c = (cct ?? {});
|
|
1523
|
+
const repoUrl = c.canonical_github_url || 'https://github.com/seasonsagents-art/webaz';
|
|
1524
|
+
const baseRepo = c.expected_pr_base_repo || c.canonical_repository_full_name || 'seasonsagents-art/webaz';
|
|
1525
|
+
const baseBranch = c.base_branch || 'main';
|
|
1526
|
+
return {
|
|
1527
|
+
canonical_repo: baseRepo,
|
|
1528
|
+
repo: repoUrl,
|
|
1529
|
+
base_branch: baseBranch,
|
|
1530
|
+
start_here: 'Read AGENTS.md (project map + before-you-code + PR flow), then CONTRIBUTING.md.',
|
|
1531
|
+
do_the_work: 'Point a coding agent (e.g. Claude Code) at the repo on a single-topic branch. The buyer/shopping agent is not the coding agent — hand off to one.',
|
|
1532
|
+
submit_pr: `Open a PR whose BASE repo is ${baseRepo} (${repoUrl}), base branch ${baseBranch}. If any target repo differs from this canonical repo, STOP and ask the human — never contribute to a non-canonical repository.`,
|
|
1533
|
+
pr_flow: 'Commit with DCO sign-off (git commit -s). If AI-authored, mark the PR per the meta-rule. Humans merge — no auto-merge.',
|
|
1534
|
+
then: `When the PR is open, report it back: webaz_contribute action=submit task_id=${taskId} pr_ref=#<N> verification_summary="<the verification_commands you ran + their results>". Both pr_ref and verification_summary are required.`,
|
|
1535
|
+
not_participation: 'A sandbox run or a local-only draft is NOT participation and is NOT a contribution; only a merged PR (or recognized issue/task/RFC) on the canonical repo enters the contribution record.',
|
|
1536
|
+
human_note: "You don't need to know git — your coding agent does it; you (the Passkey-bound human) stay accountable.",
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
// RFC-006/RFC-017: webaz_contribute — 协调"谁在做什么"。NETWORK only. Discovery + suggest 无需 key(打公开
|
|
1540
|
+
// 端口 #329/#331);claim/submit/status/profile 需 key(真实可问责身份,走受 #330 守卫的 member 端口)。
|
|
1541
|
+
export async function handleContribute(args) {
|
|
1494
1542
|
const action = args.action || 'list_open';
|
|
1495
|
-
const apiKey = args
|
|
1496
|
-
if (!apiKey)
|
|
1497
|
-
return { error: 'api_key required' };
|
|
1543
|
+
const apiKey = resolveMcpApiKey(args);
|
|
1498
1544
|
if (toolBackend('webaz_contribute') !== 'network') {
|
|
1499
1545
|
return {
|
|
1500
1546
|
_mode: 'sandbox',
|
|
1501
|
-
error: 'SANDBOX 模式无协调对象 —— 协调要在真实项目上才有意义。请设 WEBAZ_API_KEY 切到 NETWORK
|
|
1547
|
+
error: 'SANDBOX 模式无协调对象 —— 协调要在真实项目上才有意义。请设 WEBAZ_API_KEY 切到 NETWORK 模式(或不设 key 默认 network_readonly 也可浏览/建议)。 / Coordination needs the live network; sandbox has nothing to coordinate with.',
|
|
1502
1548
|
error_code: 'CONTRIBUTE_NEEDS_NETWORK',
|
|
1503
1549
|
};
|
|
1504
1550
|
}
|
|
1551
|
+
// ── keyless discovery + suggest (public surface; same trusted canonical target as the PWA) ──
|
|
1552
|
+
if (action === 'list_open') {
|
|
1553
|
+
// public endpoint already restricts to audience=public + status=open; only pass the optional filters.
|
|
1554
|
+
const qs = new URLSearchParams();
|
|
1555
|
+
if (args.area)
|
|
1556
|
+
qs.set('area', String(args.area));
|
|
1557
|
+
if (args.risk_level)
|
|
1558
|
+
qs.set('risk_level', String(args.risk_level));
|
|
1559
|
+
if (args.auto_claimable !== undefined)
|
|
1560
|
+
qs.set('auto_claimable', String(Boolean(args.auto_claimable)));
|
|
1561
|
+
if (args.required_capabilities)
|
|
1562
|
+
qs.set('required_capabilities', String(args.required_capabilities));
|
|
1563
|
+
if (args.agent_capabilities !== undefined)
|
|
1564
|
+
qs.set('agent_capabilities', String(args.agent_capabilities)); // forward even '' so the route fail-closes (typed 400), never silently returns the full list
|
|
1565
|
+
if (args.max_duration_minutes !== undefined)
|
|
1566
|
+
qs.set('max_duration_minutes', String(args.max_duration_minutes));
|
|
1567
|
+
if (args.estimated_context_size)
|
|
1568
|
+
qs.set('estimated_context_size', String(args.estimated_context_size));
|
|
1569
|
+
if (args.estimated_agent_budget)
|
|
1570
|
+
qs.set('estimated_agent_budget', String(args.estimated_agent_budget));
|
|
1571
|
+
const q = qs.toString();
|
|
1572
|
+
const r = await apiCall('/api/public/build-tasks' + (q ? '?' + q : ''));
|
|
1573
|
+
if (!r.error)
|
|
1574
|
+
r._next = 'Pick a task, then: webaz_contribute action=detail task_id=<id> for its full execution boundary + the canonical repo to PR to; then action=claim task_id=<id> api_key=<key> to take it (claiming needs an account).';
|
|
1575
|
+
return r;
|
|
1576
|
+
}
|
|
1577
|
+
if (action === 'detail') {
|
|
1578
|
+
const tid = args.task_id;
|
|
1579
|
+
if (!tid)
|
|
1580
|
+
return { error: 'task_id required for action=detail' };
|
|
1581
|
+
const r = await apiCall('/api/public/build-tasks/' + encodeURIComponent(tid));
|
|
1582
|
+
if (!r.error && r.task)
|
|
1583
|
+
r.agent_handoff = buildContributeHandoff(r.canonical_contribution_target, tid);
|
|
1584
|
+
return r;
|
|
1585
|
+
}
|
|
1586
|
+
if (action === 'suggest') {
|
|
1587
|
+
const title = (args.title ?? '').trim();
|
|
1588
|
+
const summary = (args.summary ?? args.note ?? '').trim();
|
|
1589
|
+
if (title.length < 3)
|
|
1590
|
+
return { error: 'title required (≥3 chars) for action=suggest' };
|
|
1591
|
+
if (summary.length < 1)
|
|
1592
|
+
return { error: 'summary (the reason) required for action=suggest' };
|
|
1593
|
+
const r = await apiCall('/api/public/task-proposals', {
|
|
1594
|
+
method: 'POST',
|
|
1595
|
+
body: {
|
|
1596
|
+
title, summary,
|
|
1597
|
+
suggested_area: args.area ?? args.suggested_area,
|
|
1598
|
+
expected_outcome: args.expected_outcome,
|
|
1599
|
+
source_ref: args.source_ref,
|
|
1600
|
+
proposer_github_login: args.proposer_github_login,
|
|
1601
|
+
},
|
|
1602
|
+
});
|
|
1603
|
+
// typed errors (RATE_LIMITED / DUPLICATE_PROPOSAL / validation) are already mapped by apiCall; the
|
|
1604
|
+
// success response already carries the route-level `proposal_notice` (suggestion ≠ contribution/reward).
|
|
1605
|
+
return r;
|
|
1606
|
+
}
|
|
1607
|
+
// ── participation: a real, accountable identity is required ──
|
|
1608
|
+
if (!apiKey)
|
|
1609
|
+
return {
|
|
1610
|
+
error: `api_key required for action=${action} — set WEBAZ_API_KEY (request an invite at ${WEBAZ_API_URL}/#welcome). Discovery (list_open / detail) and suggest work WITHOUT a key.`,
|
|
1611
|
+
error_code: 'API_KEY_REQUIRED',
|
|
1612
|
+
};
|
|
1505
1613
|
if (action === 'status')
|
|
1506
1614
|
return apiCall('/api/build-tasks?mine=1', { apiKey });
|
|
1507
1615
|
if (action === 'profile')
|
|
@@ -1513,31 +1621,25 @@ async function handleContribute(args) {
|
|
|
1513
1621
|
const r = await apiCall('/api/build-tasks/' + encodeURIComponent(tid) + '/claim', {
|
|
1514
1622
|
method: 'POST', apiKey, body: { provenance: args.provenance },
|
|
1515
1623
|
});
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
if (!r.error) {
|
|
1519
|
-
r.handoff = {
|
|
1520
|
-
repo: 'https://github.com/seasonsagents-art/webaz',
|
|
1521
|
-
start_here: 'Read AGENTS.md (project map + before-you-code + PR flow), then CONTRIBUTING.md.',
|
|
1522
|
-
do_the_work: 'Point a coding agent (e.g. Claude Code) at the repo; work on a single-topic branch. The buyer/shopping agent is not the coding agent — hand off to one.',
|
|
1523
|
-
pr_flow: 'Commit with DCO sign-off (git commit -s). If AI-authored, add 🤖🤖🤖 to the PR title + a meta-rule trace. Humans merge — no auto-merge.',
|
|
1524
|
-
then: `When the PR is open, report it back: webaz_contribute action=submit task_id=${tid} pr_ref=#<N>.`,
|
|
1525
|
-
human_note: "You don't need to know git — your coding agent does it; you (the Passkey-bound human) stay accountable.",
|
|
1526
|
-
};
|
|
1527
|
-
}
|
|
1624
|
+
if (!r.error)
|
|
1625
|
+
r.handoff = buildContributeHandoff(r.canonical_contribution_target, tid);
|
|
1528
1626
|
return r;
|
|
1529
1627
|
}
|
|
1530
1628
|
if (action === 'submit') {
|
|
1531
1629
|
const tid = args.task_id;
|
|
1532
1630
|
if (!tid)
|
|
1533
1631
|
return { error: 'task_id required for action=submit' };
|
|
1534
|
-
|
|
1535
|
-
|
|
1632
|
+
const vs = (args.verification_summary ?? '').trim();
|
|
1633
|
+
if (vs.length < 1)
|
|
1634
|
+
return { error: 'verification_summary required for action=submit — summarize what you ran/verified (the task verification_commands and their results)', error_code: 'VERIFICATION_SUMMARY_REQUIRED' };
|
|
1635
|
+
const r = await apiCall('/api/build-tasks/' + encodeURIComponent(tid) + '/submit', {
|
|
1636
|
+
method: 'POST', apiKey, body: { pr_ref: args.pr_ref, note: args.note, verification_summary: vs },
|
|
1536
1637
|
});
|
|
1638
|
+
if (!r.error)
|
|
1639
|
+
r._next = 'A human maintainer reviews next — acceptance (done) is manual and done ≠ merge. Track it with webaz_contribute action=status.';
|
|
1640
|
+
return r;
|
|
1537
1641
|
}
|
|
1538
|
-
|
|
1539
|
-
const q = args.area ? '?status=open&area=' + encodeURIComponent(String(args.area)) : '?status=open';
|
|
1540
|
-
return apiCall('/api/build-tasks' + q, { apiKey });
|
|
1642
|
+
return { error: 'unknown action: ' + action };
|
|
1541
1643
|
}
|
|
1542
1644
|
async function handleInfo() {
|
|
1543
1645
|
const summary = getManifestSummary();
|
|
@@ -1641,10 +1743,10 @@ async function handleInfo() {
|
|
|
1641
1743
|
split: '7:2:1 — L1 70% / L2 20% / L3 10% of an order\'s commission_pool',
|
|
1642
1744
|
jurisdiction_tiers: 'Tiers are graded by the order region\'s max_levels — NOT a uniform 3 tiers everywhere. e.g. global region max_levels=1 → L1 only; singapore (etc.) max_levels=3 → up to L3. A region may also be 0 (no commission tiers; pool → community fund).',
|
|
1643
1745
|
attribution: 'EXPLICIT per-order — commission goes to the promoter attributed at purchase time, not derived from the buyer\'s sponsor chain.',
|
|
1644
|
-
how_to_attribute: 'L1: webaz_place_order(promoter_api_key) records the direct promoter. Full L2/L3 chain requires the buyer to arrive via a webaz_share_link ?ref
|
|
1746
|
+
how_to_attribute: 'L1: webaz_place_order(promoter_api_key) records the direct promoter. Full L2/L3 chain requires the buyer to arrive via a webaz_share_link /i/<permanent_code> (?ref=<permanent_code>) URL clicked in a browser (builds product_share_attribution).',
|
|
1645
1747
|
redirect_rules: 'chain_gap (no L / invalid sponsor) → charity_fund; level beyond the region cap → global_fund.',
|
|
1646
1748
|
l1_gate: 'the promoter must be a verified buyer (≥1 completed order) to receive commission, otherwise that share redirects.',
|
|
1647
|
-
opt_in: 'Participation is opt-in (RFC-002): default = off. A user applies (Passkey + ≥1 completed order); attribution is always recorded
|
|
1749
|
+
opt_in: 'Participation is opt-in (RFC-002): default = off. A user applies (Passkey + ≥1 completed order); attribution is always recorded. Commission destination is state-dependent: never_activated / auto_downgraded → held in pending_commission_escrow (30d grace), recoverable by (re-)activating within the window, else swept to commission_reserve; deactivated (active opt-out) → future commission goes directly to commission_reserve, NOT escrow and NOT recoverable. Never to charity_fund. See docs/rfcs/RFC-002-rewards-opt-in.md.',
|
|
1648
1750
|
},
|
|
1649
1751
|
// QA 轮 3 FAIL:roles 漏 reviewer。register 工具支持 5 个角色,info 必须列全。
|
|
1650
1752
|
roles: {
|
|
@@ -1957,8 +2059,8 @@ async function handleSearch(args) {
|
|
|
1957
2059
|
: '没有找到匹配的商品';
|
|
1958
2060
|
return { found: 0, message: hint, products: [], matched_by: 'strict_no_match' };
|
|
1959
2061
|
}
|
|
1960
|
-
const enriched = products.map((p) => {
|
|
1961
|
-
const boost = getSearchBoost(db, p.seller_id);
|
|
2062
|
+
const enriched = await Promise.all(products.map(async (p) => {
|
|
2063
|
+
const boost = await getSearchBoost(db, p.seller_id);
|
|
1962
2064
|
const rep_level = p.rep_level || 'new';
|
|
1963
2065
|
const rep_points = Number(p.rep_points) || 0;
|
|
1964
2066
|
const completion = Number(p.completion_count) || 0;
|
|
@@ -1983,7 +2085,7 @@ async function handleSearch(args) {
|
|
|
1983
2085
|
const firstSaleBoost = firstSold && (Date.now() - new Date(firstSold.replace(' ', 'T') + 'Z').getTime()) < 14 * 86400_000 ? 5 : 0;
|
|
1984
2086
|
const score = completion * 0.5 + rep_points * 0.1 + sharer * 2.0 + freshness + firstSaleBoost - dispute * 5.0;
|
|
1985
2087
|
return { ...p, _boost: boost, _rep_level: rep_level, _rep_points: rep_points, _score: score, _freshness: freshness, _first_sale_boost: firstSaleBoost };
|
|
1986
|
-
});
|
|
2088
|
+
}));
|
|
1987
2089
|
const sorted = sortMode === 'trending'
|
|
1988
2090
|
? enriched.sort((a, b) => b._score - a._score || b._boost - a._boost).slice(0, limit)
|
|
1989
2091
|
: enriched.slice(0, limit);
|
|
@@ -2045,11 +2147,11 @@ async function handleVerifyPrice(args) {
|
|
|
2045
2147
|
if (toolBackend('webaz_verify_price') === 'network') {
|
|
2046
2148
|
return apiCall('/api/verify-price', {
|
|
2047
2149
|
method: 'POST',
|
|
2048
|
-
apiKey: args
|
|
2150
|
+
apiKey: resolveMcpApiKey(args),
|
|
2049
2151
|
body: { product_id: args.product_id, quantity: Number(args.quantity ?? 1) },
|
|
2050
2152
|
});
|
|
2051
2153
|
}
|
|
2052
|
-
const auth = requireAuth(db, args
|
|
2154
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
2053
2155
|
if ('error' in auth)
|
|
2054
2156
|
return auth;
|
|
2055
2157
|
const { user } = auth;
|
|
@@ -2095,7 +2197,7 @@ async function handleVerifyPrice(args) {
|
|
|
2095
2197
|
async function handleListProduct(args) {
|
|
2096
2198
|
// Wave 3 audit P0: 加 action 分发 — agent 卖家能完整管理目录(不止 create)
|
|
2097
2199
|
const action = args.action || 'create';
|
|
2098
|
-
const apiKey = args
|
|
2200
|
+
const apiKey = resolveMcpApiKey(args);
|
|
2099
2201
|
if (!apiKey)
|
|
2100
2202
|
return { error: 'api_key required' };
|
|
2101
2203
|
// RFC-003 P2b: NETWORK 模式 — 卖家目录管理全部转发生产端点(单一真相源)
|
|
@@ -2212,7 +2314,7 @@ async function handleListProduct(args) {
|
|
|
2212
2314
|
// stake 在 place_order 那一刻按"该订单总额 × stake_rate"现锁,订单结算时退该笔,违约时扣该笔。
|
|
2213
2315
|
// 这样每个 active 订单都有独立 stake 担保,多 stock 商品也不会被空头薅。
|
|
2214
2316
|
// product.stake_amount 字段保留为"indicative rate × price"(前端展示用),不强制 lock。
|
|
2215
|
-
const stakeDiscount = getStakeDiscount(db, user.id);
|
|
2317
|
+
const stakeDiscount = await getStakeDiscount(db, user.id);
|
|
2216
2318
|
const stakeRate = Math.max(0.05, 0.15 - stakeDiscount); // 最低 5%,声誉越高折扣越大
|
|
2217
2319
|
const stakeAmount = Math.round(price * stakeRate * 100) / 100; // indicative only; actual lock per-order
|
|
2218
2320
|
const id = generateId('prd');
|
|
@@ -2273,9 +2375,9 @@ async function handlePlaceOrder(args) {
|
|
|
2273
2375
|
body.shipping_address = args.shipping_address;
|
|
2274
2376
|
if (args.donation_pct != null)
|
|
2275
2377
|
body.donation_pct = args.donation_pct;
|
|
2276
|
-
return apiCall('/api/orders', { method: 'POST', apiKey: args
|
|
2378
|
+
return apiCall('/api/orders', { method: 'POST', apiKey: resolveMcpApiKey(args), body });
|
|
2277
2379
|
}
|
|
2278
|
-
const auth = requireAuth(db, args
|
|
2380
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
2279
2381
|
if ('error' in auth)
|
|
2280
2382
|
return auth;
|
|
2281
2383
|
const { user } = auth;
|
|
@@ -2442,7 +2544,7 @@ async function handleUpdateOrder(args) {
|
|
|
2442
2544
|
return { error: 'order_id and action required' };
|
|
2443
2545
|
return apiCall(`/api/orders/${encodeURIComponent(orderId)}/action`, {
|
|
2444
2546
|
method: 'POST',
|
|
2445
|
-
apiKey: args
|
|
2547
|
+
apiKey: resolveMcpApiKey(args),
|
|
2446
2548
|
body: {
|
|
2447
2549
|
action,
|
|
2448
2550
|
notes: args.notes ?? '',
|
|
@@ -2452,7 +2554,7 @@ async function handleUpdateOrder(args) {
|
|
|
2452
2554
|
},
|
|
2453
2555
|
});
|
|
2454
2556
|
}
|
|
2455
|
-
const auth = requireAuth(db, args
|
|
2557
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
2456
2558
|
if ('error' in auth)
|
|
2457
2559
|
return auth;
|
|
2458
2560
|
const { user } = auth;
|
|
@@ -2466,7 +2568,7 @@ async function handleUpdateOrder(args) {
|
|
|
2466
2568
|
// agent-native 协议要求"哪个接口进结果一致"。MCP confirm 不再自己结算,
|
|
2467
2569
|
// 走 PWA /api/orders/:id/action 的 settleOrder + settleCommission(authoritative)。
|
|
2468
2570
|
if (action === 'confirm') {
|
|
2469
|
-
const apiKey = args
|
|
2571
|
+
const apiKey = resolveMcpApiKey(args);
|
|
2470
2572
|
const result = await pwaApi('POST', `/orders/${encodeURIComponent(orderId)}/action`, apiKey, {
|
|
2471
2573
|
action: 'confirm',
|
|
2472
2574
|
notes,
|
|
@@ -2575,9 +2677,9 @@ async function handleGetStatus(args) {
|
|
|
2575
2677
|
const orderId = args.order_id;
|
|
2576
2678
|
if (!orderId)
|
|
2577
2679
|
return { error: 'order_id required' };
|
|
2578
|
-
return apiCall(`/api/orders/${encodeURIComponent(orderId)}`, { apiKey: args
|
|
2680
|
+
return apiCall(`/api/orders/${encodeURIComponent(orderId)}`, { apiKey: resolveMcpApiKey(args) });
|
|
2579
2681
|
}
|
|
2580
|
-
const auth = requireAuth(db, args
|
|
2682
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
2581
2683
|
if ('error' in auth)
|
|
2582
2684
|
return auth;
|
|
2583
2685
|
const statusInfo = getOrderStatus(db, args.order_id);
|
|
@@ -2614,7 +2716,7 @@ async function handleGetStatus(args) {
|
|
|
2614
2716
|
async function handleWallet(args) {
|
|
2615
2717
|
// Wave 3 audit P0: 加 action 分发 — agent 能查充值/提现/收入历史(写操作仍 UI-only 走 2FA)
|
|
2616
2718
|
const action = args.action || 'view';
|
|
2617
|
-
const apiKey = args
|
|
2719
|
+
const apiKey = resolveMcpApiKey(args);
|
|
2618
2720
|
if (!apiKey)
|
|
2619
2721
|
return { error: 'api_key required' };
|
|
2620
2722
|
// RFC-003 Batch 4:NETWORK 模式 → webaz.xyz 真网络【只读】(Bearer api_key)。
|
|
@@ -2682,20 +2784,20 @@ async function handleWallet(args) {
|
|
|
2682
2784
|
async function handleNotifications(args) {
|
|
2683
2785
|
// RFC-003 Batch 1:NETWORK 模式 → 调 webaz.xyz 真网络通知端点(Bearer api_key);SANDBOX 走本地。
|
|
2684
2786
|
if (toolBackend('webaz_notifications') === 'network') {
|
|
2685
|
-
const apiKey =
|
|
2787
|
+
const apiKey = resolveMcpApiKey(args);
|
|
2686
2788
|
if (!apiKey)
|
|
2687
2789
|
return { error: 'api_key required' };
|
|
2688
2790
|
if (args.mark_read)
|
|
2689
2791
|
await apiCall('/api/notifications/read', { method: 'POST', apiKey });
|
|
2690
2792
|
return await apiCall('/api/notifications' + (args.unread === true ? '?unread=1' : ''), { apiKey });
|
|
2691
2793
|
}
|
|
2692
|
-
const auth = requireAuth(db, args
|
|
2794
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
2693
2795
|
if ('error' in auth)
|
|
2694
2796
|
return auth;
|
|
2695
2797
|
const { user } = auth;
|
|
2696
2798
|
const onlyUnread = args.unread === true;
|
|
2697
|
-
const notifs = getNotifications(db, user.id, onlyUnread, 30);
|
|
2698
|
-
const unread = getUnreadCount(db, user.id);
|
|
2799
|
+
const notifs = await getNotifications(db, user.id, onlyUnread, 30);
|
|
2800
|
+
const unread = await getUnreadCount(db, user.id);
|
|
2699
2801
|
if (args.mark_read) {
|
|
2700
2802
|
markRead(db, user.id);
|
|
2701
2803
|
}
|
|
@@ -2713,7 +2815,7 @@ async function handleNotifications(args) {
|
|
|
2713
2815
|
}
|
|
2714
2816
|
// ─── 争议处理 ─────────────────────────────────────────────────
|
|
2715
2817
|
async function handleDispute(args) {
|
|
2716
|
-
const apiKey = args
|
|
2818
|
+
const apiKey = resolveMcpApiKey(args);
|
|
2717
2819
|
if (!apiKey)
|
|
2718
2820
|
return { error: 'api_key required' };
|
|
2719
2821
|
const action = args.action;
|
|
@@ -2782,9 +2884,9 @@ async function handleDispute(args) {
|
|
|
2782
2884
|
// ── 查看争议详情 ────────────────────────────────────────────
|
|
2783
2885
|
if (action === 'view') {
|
|
2784
2886
|
let dispute = args.dispute_id
|
|
2785
|
-
? getDisputeDetails(db, args.dispute_id)
|
|
2887
|
+
? await getDisputeDetails(db, args.dispute_id)
|
|
2786
2888
|
: args.order_id
|
|
2787
|
-
? getOrderDispute(db, args.order_id)
|
|
2889
|
+
? await getOrderDispute(db, args.order_id)
|
|
2788
2890
|
: null;
|
|
2789
2891
|
if (!dispute)
|
|
2790
2892
|
return { error: '找不到争议记录,请提供 dispute_id 或 order_id' };
|
|
@@ -2824,7 +2926,7 @@ async function handleDispute(args) {
|
|
|
2824
2926
|
if (user.role !== 'arbitrator') {
|
|
2825
2927
|
return { error: '只有仲裁员可以查看所有待处理争议' };
|
|
2826
2928
|
}
|
|
2827
|
-
const disputes = getOpenDisputes(db);
|
|
2929
|
+
const disputes = await getOpenDisputes(db);
|
|
2828
2930
|
return {
|
|
2829
2931
|
open_count: disputes.length,
|
|
2830
2932
|
disputes: disputes.map(d => ({
|
|
@@ -2848,7 +2950,7 @@ async function handleDispute(args) {
|
|
|
2848
2950
|
// 如有证据描述,先创建证据记录
|
|
2849
2951
|
const evidenceIds = [];
|
|
2850
2952
|
if (args.evidence_description) {
|
|
2851
|
-
const dispute = getDisputeDetails(db, args.dispute_id);
|
|
2953
|
+
const dispute = await getDisputeDetails(db, args.dispute_id);
|
|
2852
2954
|
if (dispute) {
|
|
2853
2955
|
const eid = generateId('evt');
|
|
2854
2956
|
db.prepare(`
|
|
@@ -2876,7 +2978,7 @@ async function handleDispute(args) {
|
|
|
2876
2978
|
}
|
|
2877
2979
|
// ─── 索赔验证(claim-verification)处理 — Wave 6 新增 ────────────
|
|
2878
2980
|
async function handleClaimVerify(args) {
|
|
2879
|
-
const apiKey = args
|
|
2981
|
+
const apiKey = resolveMcpApiKey(args);
|
|
2880
2982
|
if (!apiKey)
|
|
2881
2983
|
return { error: 'api_key required' };
|
|
2882
2984
|
const action = String(args.action || '');
|
|
@@ -2964,7 +3066,7 @@ async function handleSkill(args) {
|
|
|
2964
3066
|
const action = args.action;
|
|
2965
3067
|
// RFC-003 Batch 3:NETWORK 模式 → webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地引擎。
|
|
2966
3068
|
if (toolBackend('webaz_skill') === 'network') {
|
|
2967
|
-
const apiKey =
|
|
3069
|
+
const apiKey = resolveMcpApiKey(args);
|
|
2968
3070
|
if (action === 'list') {
|
|
2969
3071
|
const qs = new URLSearchParams();
|
|
2970
3072
|
if (args.skill_type)
|
|
@@ -3001,12 +3103,12 @@ async function handleSkill(args) {
|
|
|
3001
3103
|
// ── 浏览 Skill 市场 ────────────────────────────────────────
|
|
3002
3104
|
if (action === 'list') {
|
|
3003
3105
|
let userId;
|
|
3004
|
-
if (args
|
|
3005
|
-
const a = requireAuth(db, args
|
|
3106
|
+
if (resolveMcpApiKey(args)) {
|
|
3107
|
+
const a = requireAuth(db, resolveMcpApiKey(args));
|
|
3006
3108
|
if (!('error' in a))
|
|
3007
3109
|
userId = a.user.id;
|
|
3008
3110
|
}
|
|
3009
|
-
const skills = listSkills(db, {
|
|
3111
|
+
const skills = await listSkills(db, {
|
|
3010
3112
|
skillType: args.skill_type,
|
|
3011
3113
|
query: args.query,
|
|
3012
3114
|
subscriberId: userId,
|
|
@@ -3019,7 +3121,7 @@ async function handleSkill(args) {
|
|
|
3019
3121
|
};
|
|
3020
3122
|
}
|
|
3021
3123
|
// 以下操作需要身份验证
|
|
3022
|
-
const auth = requireAuth(db, args
|
|
3124
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3023
3125
|
if ('error' in auth)
|
|
3024
3126
|
return auth;
|
|
3025
3127
|
const { user } = auth;
|
|
@@ -3064,7 +3166,7 @@ async function handleSkill(args) {
|
|
|
3064
3166
|
}
|
|
3065
3167
|
// ── 我发布的 Skill ────────────────────────────────────────
|
|
3066
3168
|
if (action === 'my_skills') {
|
|
3067
|
-
const skills = getMySkills(db, user.id);
|
|
3169
|
+
const skills = await getMySkills(db, user.id);
|
|
3068
3170
|
return {
|
|
3069
3171
|
total: skills.length,
|
|
3070
3172
|
skills: skills.map(formatSkillForAgent),
|
|
@@ -3073,7 +3175,7 @@ async function handleSkill(args) {
|
|
|
3073
3175
|
}
|
|
3074
3176
|
// ── 我订阅的 Skill ────────────────────────────────────────
|
|
3075
3177
|
if (action === 'my_subs') {
|
|
3076
|
-
const skills = getMySubscriptions(db, user.id);
|
|
3178
|
+
const skills = await getMySubscriptions(db, user.id);
|
|
3077
3179
|
return {
|
|
3078
3180
|
total: skills.length,
|
|
3079
3181
|
subscriptions: skills.map(formatSkillForAgent),
|
|
@@ -3160,7 +3262,7 @@ function handleMyKey(args) {
|
|
|
3160
3262
|
}
|
|
3161
3263
|
async function handleProfile(args) {
|
|
3162
3264
|
const action = args.action;
|
|
3163
|
-
const apiKey =
|
|
3265
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3164
3266
|
// RFC-003 Batch 1:NETWORK 模式 → 全部 action 调 webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3165
3267
|
if (toolBackend('webaz_profile') === 'network') {
|
|
3166
3268
|
if (action === 'view_user') {
|
|
@@ -3257,7 +3359,7 @@ async function handleProfile(args) {
|
|
|
3257
3359
|
function handleRevokeKey(args) {
|
|
3258
3360
|
// RFC-003 Batch 5:NETWORK 模式 → 不本地校验 key(PWA 会鉴权),直接返回 Passkey 撤销指引。
|
|
3259
3361
|
if (toolBackend('webaz_revoke_key') === 'network') {
|
|
3260
|
-
const apiKey =
|
|
3362
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3261
3363
|
const reason = (args.reason || 'unspecified').trim().slice(0, 100);
|
|
3262
3364
|
return {
|
|
3263
3365
|
_mode: 'network',
|
|
@@ -3275,7 +3377,7 @@ function handleRevokeKey(args) {
|
|
|
3275
3377
|
},
|
|
3276
3378
|
};
|
|
3277
3379
|
}
|
|
3278
|
-
const auth = requireAuth(db, args
|
|
3380
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3279
3381
|
if ('error' in auth)
|
|
3280
3382
|
return auth;
|
|
3281
3383
|
const { user } = auth;
|
|
@@ -3299,7 +3401,7 @@ function handleRevokeKey(args) {
|
|
|
3299
3401
|
function handleRotateKey(args) {
|
|
3300
3402
|
// RFC-003 Batch 5:NETWORK 模式 → 不本地校验 key(PWA 会鉴权),直接返回 Passkey 轮换指引。
|
|
3301
3403
|
if (toolBackend('webaz_rotate_key') === 'network') {
|
|
3302
|
-
const apiKey =
|
|
3404
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3303
3405
|
const reason = (args.reason || 'rotation').trim().slice(0, 100);
|
|
3304
3406
|
return {
|
|
3305
3407
|
_mode: 'network',
|
|
@@ -3316,7 +3418,7 @@ function handleRotateKey(args) {
|
|
|
3316
3418
|
},
|
|
3317
3419
|
};
|
|
3318
3420
|
}
|
|
3319
|
-
const auth = requireAuth(db, args
|
|
3421
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3320
3422
|
if ('error' in auth)
|
|
3321
3423
|
return auth;
|
|
3322
3424
|
const { user } = auth;
|
|
@@ -3340,12 +3442,12 @@ function handleRotateKey(args) {
|
|
|
3340
3442
|
async function handleReferral(args) {
|
|
3341
3443
|
// RFC-003 Batch 2:NETWORK 模式 → webaz.xyz 真网络聚合(Bearer api_key);SANDBOX 走本地。
|
|
3342
3444
|
if (toolBackend('webaz_referral') === 'network') {
|
|
3343
|
-
const apiKey =
|
|
3445
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3344
3446
|
if (!apiKey)
|
|
3345
3447
|
return { error: 'api_key required' };
|
|
3346
3448
|
return await apiCall('/api/referral/me', { apiKey });
|
|
3347
3449
|
}
|
|
3348
|
-
const auth = requireAuth(db, args
|
|
3450
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3349
3451
|
if ('error' in auth)
|
|
3350
3452
|
return auth;
|
|
3351
3453
|
const { user } = auth;
|
|
@@ -3373,10 +3475,14 @@ async function handleReferral(args) {
|
|
|
3373
3475
|
const tiers = db.prepare("SELECT tier, pv_threshold, score_per_hit FROM binary_tier_config WHERE active=1 ORDER BY tier ASC").all();
|
|
3374
3476
|
const pair = Math.min(Number(me?.total_left_pv ?? 0), Number(me?.total_right_pv ?? 0));
|
|
3375
3477
|
const nextTier = tiers.find(t => t.pv_threshold > pair);
|
|
3478
|
+
// invite / share links use permanent_code ONLY — never usr_xxx. (sandbox users have one from register.)
|
|
3479
|
+
const permaCode = db.prepare("SELECT permanent_code FROM users WHERE id = ?").get(userId)?.permanent_code || null;
|
|
3376
3480
|
return {
|
|
3377
3481
|
user_id: userId,
|
|
3378
3482
|
name: user.name,
|
|
3379
|
-
|
|
3483
|
+
invite_code: permaCode,
|
|
3484
|
+
invite_unavailable_reason: permaCode ? null : 'permanent_code_missing — re-register or contact support',
|
|
3485
|
+
base_referral_link: permaCode ? `/i/${permaCode}` : null, // 仅推土机
|
|
3380
3486
|
region: user.region ?? 'global',
|
|
3381
3487
|
permissions: {
|
|
3382
3488
|
can_earn_l1_commission: canL1,
|
|
@@ -3391,10 +3497,8 @@ async function handleReferral(args) {
|
|
|
3391
3497
|
grand_total: byLevel[1].total + byLevel[2].total + byLevel[3].total,
|
|
3392
3498
|
},
|
|
3393
3499
|
binary: {
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
platform_left: `/?placement=${userId}&side=left`, // 仅 PV 条线
|
|
3397
|
-
platform_right: `/?placement=${userId}&side=right`,
|
|
3500
|
+
// pre-public 去左右码:只暴露唯一的推荐码;放置侧别由系统自动决定(无 left/right 选择)
|
|
3501
|
+
referral_link: permaCode ? `/i/${permaCode}` : null,
|
|
3398
3502
|
total_left_pv: Number(me?.total_left_pv ?? 0),
|
|
3399
3503
|
total_right_pv: Number(me?.total_right_pv ?? 0),
|
|
3400
3504
|
pair_volume: pair,
|
|
@@ -3414,7 +3518,7 @@ async function handleReferral(args) {
|
|
|
3414
3518
|
}
|
|
3415
3519
|
else if (lastAction === 'deactivate') {
|
|
3416
3520
|
state = 'deactivated';
|
|
3417
|
-
note = 'You actively deactivated rewards. Future commissions
|
|
3521
|
+
note = 'You actively deactivated rewards. Future L1/L2/L3 commissions go to commission_reserve / protocol reserve, not charity_fund and not pending escrow. Re-applying only affects future commissions.';
|
|
3418
3522
|
}
|
|
3419
3523
|
else if (lastAction === 'auto_downgrade') {
|
|
3420
3524
|
state = 'auto_downgraded';
|
|
@@ -3432,7 +3536,7 @@ async function handleReferral(args) {
|
|
|
3432
3536
|
note,
|
|
3433
3537
|
pending_escrow: { count: pending.n, total_amount: pending.total },
|
|
3434
3538
|
expired_to_charity: { count: expired.n, total_amount: expired.total },
|
|
3435
|
-
spec: 'RFC-002 §3.5 — rewards opt-in
|
|
3539
|
+
spec: 'RFC-002 §3.5 — rewards / share-commission opt-in (RFC-002)',
|
|
3436
3540
|
};
|
|
3437
3541
|
})(),
|
|
3438
3542
|
tip: canL1
|
|
@@ -3443,23 +3547,21 @@ async function handleReferral(args) {
|
|
|
3443
3547
|
async function handleShareLink(args) {
|
|
3444
3548
|
// RFC-003 #1122:NETWORK 模式 → 调 webaz.xyz 的 /api/share-link(服务端同款计算);SANDBOX 走本地。
|
|
3445
3549
|
if (toolBackend('webaz_share_link') === 'network') {
|
|
3446
|
-
const apiKey =
|
|
3550
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3447
3551
|
if (!apiKey)
|
|
3448
3552
|
return { error: 'api_key required' };
|
|
3449
3553
|
if (!args.product_id)
|
|
3450
3554
|
return { error: 'product_id required' };
|
|
3555
|
+
// pre-public 去左右码:不再向 /api/share-link 转发 side(放置永远自动)
|
|
3451
3556
|
const qs = new URLSearchParams({ product_id: String(args.product_id) });
|
|
3452
|
-
if (args.side)
|
|
3453
|
-
qs.set('side', String(args.side));
|
|
3454
3557
|
return await apiCall('/api/share-link?' + qs.toString(), { apiKey });
|
|
3455
3558
|
}
|
|
3456
|
-
const auth = requireAuth(db, args
|
|
3559
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3457
3560
|
if ('error' in auth)
|
|
3458
3561
|
return auth;
|
|
3459
3562
|
const { user } = auth;
|
|
3460
3563
|
const userId = user.id;
|
|
3461
3564
|
const productId = args.product_id;
|
|
3462
|
-
const sideArg = args.side || 'auto';
|
|
3463
3565
|
// RFC-002 §3.5 valuation-layer gate — share_link generation requires opt-in
|
|
3464
3566
|
const optIn = db.prepare("SELECT rewards_opted_in FROM users WHERE id = ?").get(userId)?.rewards_opted_in ?? 0;
|
|
3465
3567
|
if (optIn !== 1) {
|
|
@@ -3480,10 +3582,10 @@ async function handleShareLink(args) {
|
|
|
3480
3582
|
missing.push('application_not_submitted');
|
|
3481
3583
|
return {
|
|
3482
3584
|
error: 'rewards_opt_in_required',
|
|
3483
|
-
message: 'Share-link generation is a valuation-layer action — requires
|
|
3585
|
+
message: 'Share-link generation is a valuation-layer (rewards / share-link) action, NOT a contribution gate — requires rewards / share-commission opt-in (RFC-002 §3.5)',
|
|
3484
3586
|
missing_requirements: missing,
|
|
3485
3587
|
next_steps: [
|
|
3486
|
-
'Open PWA #me → tap "
|
|
3588
|
+
'Open PWA #me → tap "申请分享分润 / Enable share-commission opt-in"',
|
|
3487
3589
|
'Read the 8-second disclosure (cannot skip)',
|
|
3488
3590
|
'Submit application — pre-checks run server-side',
|
|
3489
3591
|
],
|
|
@@ -3492,43 +3594,21 @@ async function handleShareLink(args) {
|
|
|
3492
3594
|
const product = db.prepare("SELECT id, title, price, commission_rate FROM products WHERE id = ? AND status='active'").get(productId);
|
|
3493
3595
|
if (!product)
|
|
3494
3596
|
return { error: '商品不存在或已下架' };
|
|
3495
|
-
|
|
3496
|
-
if (sideArg === 'left' || sideArg === 'right') {
|
|
3497
|
-
side = sideArg;
|
|
3498
|
-
}
|
|
3499
|
-
else {
|
|
3500
|
-
// auto = 与 PWA pickPreferredSide 对齐:尊重 placement_pref(team_count | pv_count)
|
|
3501
|
-
// 老版只看 total_left_pv vs total_right_pv,team_count 用户被错算
|
|
3502
|
-
const u = db.prepare("SELECT placement_pref, total_left_pv, total_right_pv, left_count, right_count FROM users WHERE id = ?")
|
|
3503
|
-
.get(userId);
|
|
3504
|
-
const pref = u?.placement_pref || 'team_count';
|
|
3505
|
-
if (pref === 'pv_count') {
|
|
3506
|
-
const since = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000).toISOString().slice(0, 19).replace('T', ' ');
|
|
3507
|
-
const w = db.prepare(`SELECT COALESCE(SUM(consumed_left_pv),0) AS l, COALESCE(SUM(consumed_right_pv),0) AS r
|
|
3508
|
-
FROM binary_score_records WHERE user_id = ? AND created_at >= ?`)
|
|
3509
|
-
.get(userId, since);
|
|
3510
|
-
const leftPv = Number(u?.total_left_pv ?? 0) + Number(w.l);
|
|
3511
|
-
const rightPv = Number(u?.total_right_pv ?? 0) + Number(w.r);
|
|
3512
|
-
side = leftPv <= rightPv ? 'left' : 'right';
|
|
3513
|
-
}
|
|
3514
|
-
else {
|
|
3515
|
-
// team_count: 整棵子树人数(增量维护的 left_count/right_count,与 PWA pickPreferredSide 对齐)。
|
|
3516
|
-
// 2026-06-04 修:旧版沿单条脊链数(countLeg)名实不符 → 选边失真。分享链接的 side 会被注册时
|
|
3517
|
-
// 当 placement_side 显式采用,必须与 PWA joinPowerLeg 用同一指标,否则两路径不一致。
|
|
3518
|
-
side = (Number(u?.left_count ?? 0) <= Number(u?.right_count ?? 0)) ? 'left' : 'right';
|
|
3519
|
-
}
|
|
3520
|
-
}
|
|
3597
|
+
// pre-public 去左右码:分享链接不再携带 side(放置侧别由注册时系统自动决定)
|
|
3521
3598
|
const completed = db.prepare("SELECT COUNT(*) as n FROM orders WHERE buyer_id = ? AND status = 'completed'").get(userId).n;
|
|
3522
3599
|
const override = db.prepare("SELECT l1_share_override FROM users WHERE id = ?").get(userId)?.l1_share_override ?? 0;
|
|
3523
3600
|
const canL1 = override === 1 || (override === 0 && completed > 0);
|
|
3524
3601
|
const rate = Number(product.commission_rate ?? 0);
|
|
3525
|
-
|
|
3602
|
+
// share ref uses permanent_code ONLY — never usr_xxx
|
|
3603
|
+
const permaCode = db.prepare("SELECT permanent_code FROM users WHERE id = ?").get(userId)?.permanent_code || null;
|
|
3604
|
+
if (!permaCode)
|
|
3605
|
+
return { error: 'permanent_code_missing — cannot build a share link; re-register or contact support', error_code: 'PERMANENT_CODE_MISSING' };
|
|
3606
|
+
const link = `/?ref=${permaCode}#order-product/${productId}`;
|
|
3526
3607
|
return {
|
|
3527
3608
|
product: { id: product.id, title: product.title, price: product.price, commission_rate: rate },
|
|
3528
3609
|
share_link: link,
|
|
3529
3610
|
full_url_hint: 'Prepend webaz.xyz (production) or http://localhost:3000 (local) to get the absolute URL',
|
|
3530
|
-
|
|
3531
|
-
binary_explanation: `New user via this link → placed in your ${side === 'left' ? '🔵 left' : '🟢 right'} subtree (tail anchor)`,
|
|
3611
|
+
placement_note: 'New user via this link → placement is recorded automatically by the system (no left/right choice).',
|
|
3532
3612
|
commission_eligibility: canL1
|
|
3533
3613
|
? `You will earn 3-tier commission: L1=${(rate * 0.70 * 100).toFixed(1)}% L2=${(rate * 0.20 * 100).toFixed(1)}% L3=${(rate * 0.10 * 100).toFixed(1)}% of sale price`
|
|
3534
3614
|
: 'You are NOT verified yet (need 1 completed purchase). 3-tier commission will be skipped, but points-matching still builds.',
|
|
@@ -3539,7 +3619,7 @@ async function handleShareLink(args) {
|
|
|
3539
3619
|
async function handleBlocklist(args) {
|
|
3540
3620
|
// RFC-003 Batch 2:NETWORK 模式 → webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3541
3621
|
if (toolBackend('webaz_blocklist') === 'network') {
|
|
3542
|
-
const apiKey =
|
|
3622
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3543
3623
|
if (!apiKey)
|
|
3544
3624
|
return { error: 'api_key required' };
|
|
3545
3625
|
const act = String(args.action || '');
|
|
@@ -3554,7 +3634,7 @@ async function handleBlocklist(args) {
|
|
|
3554
3634
|
return await apiCall('/api/blocklist/' + uid, { method: 'DELETE', apiKey });
|
|
3555
3635
|
return { error: `unknown action: ${act}` };
|
|
3556
3636
|
}
|
|
3557
|
-
const auth = requireAuth(db, args
|
|
3637
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3558
3638
|
if ('error' in auth)
|
|
3559
3639
|
return auth;
|
|
3560
3640
|
const { user } = auth;
|
|
@@ -3593,7 +3673,7 @@ async function handleBlocklist(args) {
|
|
|
3593
3673
|
async function handleFollows(args) {
|
|
3594
3674
|
// RFC-003 Batch 2:NETWORK 模式 → webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3595
3675
|
if (toolBackend('webaz_follows') === 'network') {
|
|
3596
|
-
const apiKey =
|
|
3676
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3597
3677
|
if (!apiKey)
|
|
3598
3678
|
return { error: 'api_key required' };
|
|
3599
3679
|
const act = String(args.action || '');
|
|
@@ -3610,7 +3690,7 @@ async function handleFollows(args) {
|
|
|
3610
3690
|
return await apiCall('/api/follows/' + uid + '/status', { apiKey });
|
|
3611
3691
|
return { error: `unknown action: ${act}` };
|
|
3612
3692
|
}
|
|
3613
|
-
const auth = requireAuth(db, args
|
|
3693
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3614
3694
|
if ('error' in auth)
|
|
3615
3695
|
return auth;
|
|
3616
3696
|
const { user } = auth;
|
|
@@ -3659,7 +3739,7 @@ async function handleNearby(args) {
|
|
|
3659
3739
|
const action = String(args.action || '');
|
|
3660
3740
|
// RFC-003 Batch 1:NETWORK 模式 → 调 webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3661
3741
|
if (toolBackend('webaz_nearby') === 'network') {
|
|
3662
|
-
const apiKey =
|
|
3742
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3663
3743
|
if (!apiKey)
|
|
3664
3744
|
return { error: 'api_key required' };
|
|
3665
3745
|
if (action === 'set_location')
|
|
@@ -3677,7 +3757,7 @@ async function handleNearby(args) {
|
|
|
3677
3757
|
}
|
|
3678
3758
|
return { error: `unknown action: ${action}` };
|
|
3679
3759
|
}
|
|
3680
|
-
const auth = requireAuth(db, args
|
|
3760
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3681
3761
|
if ('error' in auth)
|
|
3682
3762
|
return auth;
|
|
3683
3763
|
const { user } = auth;
|
|
@@ -3728,7 +3808,7 @@ async function handleNearby(args) {
|
|
|
3728
3808
|
async function handleDefaultAddress(args) {
|
|
3729
3809
|
// RFC-003 Batch 2:NETWORK 模式 → webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3730
3810
|
if (toolBackend('webaz_default_address') === 'network') {
|
|
3731
|
-
const apiKey =
|
|
3811
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3732
3812
|
if (!apiKey)
|
|
3733
3813
|
return { error: 'api_key required' };
|
|
3734
3814
|
const act = String(args.action || '');
|
|
@@ -3747,7 +3827,7 @@ async function handleDefaultAddress(args) {
|
|
|
3747
3827
|
}
|
|
3748
3828
|
return { error: `unknown action: ${act}` };
|
|
3749
3829
|
}
|
|
3750
|
-
const auth = requireAuth(db, args
|
|
3830
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3751
3831
|
if ('error' in auth)
|
|
3752
3832
|
return auth;
|
|
3753
3833
|
const { user } = auth;
|
|
@@ -3788,7 +3868,7 @@ async function handleShareables(args) {
|
|
|
3788
3868
|
const action = String(args.action || '');
|
|
3789
3869
|
// RFC-003 Batch 1:NETWORK 模式 → 调 webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3790
3870
|
if (toolBackend('webaz_shareables') === 'network') {
|
|
3791
|
-
const apiKey =
|
|
3871
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3792
3872
|
if (!apiKey)
|
|
3793
3873
|
return { error: 'api_key required' };
|
|
3794
3874
|
if (action === 'list_mine')
|
|
@@ -3816,7 +3896,7 @@ async function handleShareables(args) {
|
|
|
3816
3896
|
}
|
|
3817
3897
|
return { error: `unknown action: ${action}` };
|
|
3818
3898
|
}
|
|
3819
|
-
const auth = requireAuth(db, args
|
|
3899
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3820
3900
|
if ('error' in auth)
|
|
3821
3901
|
return auth;
|
|
3822
3902
|
const { user } = auth;
|
|
@@ -3972,7 +4052,7 @@ async function pwaApi(method, path, apiKey, body) {
|
|
|
3972
4052
|
}
|
|
3973
4053
|
}
|
|
3974
4054
|
async function handleSecondhand(args) {
|
|
3975
|
-
const apiKey =
|
|
4055
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3976
4056
|
const action = String(args.action || '');
|
|
3977
4057
|
const isPublic = action === 'browse' || action === 'detail';
|
|
3978
4058
|
if (!isPublic) {
|
|
@@ -4043,7 +4123,7 @@ async function handleSecondhand(args) {
|
|
|
4043
4123
|
}
|
|
4044
4124
|
}
|
|
4045
4125
|
async function handleTrial(args) {
|
|
4046
|
-
const apiKey =
|
|
4126
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4047
4127
|
const action = String(args.action || '');
|
|
4048
4128
|
const isPublic = action === 'get_campaign';
|
|
4049
4129
|
if (!isPublic) {
|
|
@@ -4096,7 +4176,7 @@ async function handleTrial(args) {
|
|
|
4096
4176
|
}
|
|
4097
4177
|
}
|
|
4098
4178
|
async function handleSkillMarket(args) {
|
|
4099
|
-
const apiKey =
|
|
4179
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4100
4180
|
const action = String(args.action || '');
|
|
4101
4181
|
const isPublic = action === 'list' || action === 'detail';
|
|
4102
4182
|
if (!isPublic) {
|
|
@@ -4169,7 +4249,7 @@ async function handleSkillMarket(args) {
|
|
|
4169
4249
|
}
|
|
4170
4250
|
}
|
|
4171
4251
|
async function handleRfq(args) {
|
|
4172
|
-
const apiKey =
|
|
4252
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4173
4253
|
const action = String(args.action || '');
|
|
4174
4254
|
if (!apiKey)
|
|
4175
4255
|
return { error: 'api_key required' };
|
|
@@ -4218,7 +4298,7 @@ async function handleRfq(args) {
|
|
|
4218
4298
|
}
|
|
4219
4299
|
}
|
|
4220
4300
|
async function handleBid(args) {
|
|
4221
|
-
const apiKey =
|
|
4301
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4222
4302
|
const action = String(args.action || '');
|
|
4223
4303
|
if (!apiKey)
|
|
4224
4304
|
return { error: 'api_key required' };
|
|
@@ -4292,7 +4372,7 @@ async function handleBid(args) {
|
|
|
4292
4372
|
}
|
|
4293
4373
|
}
|
|
4294
4374
|
async function handleChat(args) {
|
|
4295
|
-
const apiKey =
|
|
4375
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4296
4376
|
const action = String(args.action || '');
|
|
4297
4377
|
if (!apiKey)
|
|
4298
4378
|
return { error: 'api_key required' };
|
|
@@ -4334,7 +4414,7 @@ async function handleChat(args) {
|
|
|
4334
4414
|
}
|
|
4335
4415
|
}
|
|
4336
4416
|
async function handleAutoBidSkill(args) {
|
|
4337
|
-
const apiKey =
|
|
4417
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4338
4418
|
const action = String(args.action || '');
|
|
4339
4419
|
if (!apiKey)
|
|
4340
4420
|
return { error: 'api_key required' };
|
|
@@ -4457,7 +4537,7 @@ async function handleCharity(args) {
|
|
|
4457
4537
|
return readEndpoint('webaz_charity', '/charity/leaderboard');
|
|
4458
4538
|
if (action === 'fund')
|
|
4459
4539
|
return readEndpoint('webaz_charity', '/charity/fund');
|
|
4460
|
-
const apiKey =
|
|
4540
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4461
4541
|
if (!apiKey)
|
|
4462
4542
|
return { error: 'api_key required for this action' };
|
|
4463
4543
|
if (toolBackend('webaz_charity') !== 'network') {
|
|
@@ -4520,7 +4600,7 @@ async function handleP2pProduct(args) {
|
|
|
4520
4600
|
return { error: String(e.message) };
|
|
4521
4601
|
}
|
|
4522
4602
|
}
|
|
4523
|
-
const apiKey =
|
|
4603
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4524
4604
|
if (!apiKey)
|
|
4525
4605
|
return { error: 'api_key required for create/patch' };
|
|
4526
4606
|
const auth = requireAuth(db, apiKey);
|
|
@@ -4547,7 +4627,7 @@ async function handleP2pProduct(args) {
|
|
|
4547
4627
|
return { error: `unknown action: ${action}` };
|
|
4548
4628
|
}
|
|
4549
4629
|
async function handleLike(args) {
|
|
4550
|
-
const apiKey =
|
|
4630
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4551
4631
|
const action = String(args.action || '');
|
|
4552
4632
|
const sid = String(args.shareable_id || '');
|
|
4553
4633
|
if (!apiKey || !sid)
|
|
@@ -4573,7 +4653,7 @@ async function handleLeaderboard(args) {
|
|
|
4573
4653
|
return readEndpoint('webaz_leaderboard', '/leaderboard?kind=' + kind + '&limit=' + limit);
|
|
4574
4654
|
}
|
|
4575
4655
|
async function handleAuction(args) {
|
|
4576
|
-
const apiKey =
|
|
4656
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4577
4657
|
const action = String(args.action || '');
|
|
4578
4658
|
if (!apiKey)
|
|
4579
4659
|
return { error: 'api_key required' };
|
|
@@ -4706,7 +4786,7 @@ export async function startMCPServer() {
|
|
|
4706
4786
|
if (request.params.uri !== MANIFEST_URI) {
|
|
4707
4787
|
throw new Error(`未知资源:${request.params.uri}`);
|
|
4708
4788
|
}
|
|
4709
|
-
const manifest = generateManifest(db);
|
|
4789
|
+
const manifest = await generateManifest(db);
|
|
4710
4790
|
return {
|
|
4711
4791
|
contents: [
|
|
4712
4792
|
{
|
|
@@ -5031,7 +5111,7 @@ function addHours(date, hours) {
|
|
|
5031
5111
|
function recordToolCall(tool, args, result, latencyMs) {
|
|
5032
5112
|
let userId = null;
|
|
5033
5113
|
try {
|
|
5034
|
-
const apiKey = args
|
|
5114
|
+
const apiKey = resolveMcpApiKey(args);
|
|
5035
5115
|
if (apiKey) {
|
|
5036
5116
|
const row = db.prepare('SELECT id FROM users WHERE api_key = ?').get(apiKey);
|
|
5037
5117
|
if (row)
|