@seasonkoh/webaz 0.1.28 → 0.1.30
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 +3 -2
- package/README.zh-CN.md +7 -6
- package/dist/bond-refund-blockers.js +50 -0
- package/dist/bond-slash.js +100 -0
- package/dist/bond-terms.js +31 -0
- package/dist/currency.js +16 -0
- package/dist/deposit-rails.js +52 -0
- package/dist/direct-pay-aml-monitor.js +67 -0
- package/dist/direct-pay-aml-review.js +40 -0
- package/dist/direct-pay-base-bond-entry.js +5 -0
- package/dist/direct-pay-bond-rail-clearance.js +99 -0
- package/dist/direct-pay-cancel-refund.js +160 -0
- package/dist/direct-pay-compliance-ingress.js +145 -0
- package/dist/direct-pay-controls.js +136 -0
- package/dist/direct-pay-create.js +231 -0
- package/dist/direct-pay-deferral-quota.js +43 -0
- package/dist/direct-pay-disclosures.js +50 -0
- package/dist/direct-pay-eligibility.js +46 -0
- package/dist/direct-pay-fee-ar.js +241 -0
- package/dist/direct-pay-fee-prepay-request.js +80 -0
- package/dist/direct-pay-launch-readiness.js +108 -0
- package/dist/direct-pay-launch-summary.js +68 -0
- package/dist/direct-pay-ledger.js +94 -0
- package/dist/direct-pay-returns.js +104 -0
- package/dist/direct-pay-stock.js +9 -0
- package/dist/direct-receive-account-qr.js +77 -0
- package/dist/direct-receive-accounts.js +82 -0
- package/dist/direct-receive-deferral.js +171 -0
- package/dist/direct-receive-deposits.js +365 -0
- package/dist/direct-receive-payment-instruction.js +26 -0
- package/dist/free-shipping.js +37 -0
- package/dist/fx-rates.js +78 -0
- package/dist/layer0-foundation/L0-1-database/schema.js +777 -1
- package/dist/layer0-foundation/L0-2-state-machine/engine.js +1 -0
- package/dist/layer0-foundation/L0-2-state-machine/genuine-sale.js +15 -5
- package/dist/layer0-foundation/L0-2-state-machine/transitions.js +114 -0
- package/dist/layer0-foundation/L0-5-manifest/manifest.js +1 -1
- package/dist/layer1-agent/L1-1-mcp-server/cli.js +64 -0
- package/dist/layer1-agent/L1-1-mcp-server/network-mode.js +11 -0
- package/dist/layer1-agent/L1-1-mcp-server/server.js +99 -32
- package/dist/layer2-business/L2-6-notifications/notification-engine.js +110 -41
- package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +133 -31
- package/dist/layer3-trust/L3-1-dispute-engine/evidence-storage.js +16 -4
- package/dist/layer3-trust/L3-1-dispute-engine/mutual-cancel.js +156 -0
- package/dist/layer4-economics/L4-4-skill-market/skill-engine.js +4 -4
- package/dist/ledger.js +12 -3
- package/dist/mcp.js +23 -4
- package/dist/merchant-bond-domain.js +64 -0
- package/dist/merchant-bond-exposure.js +78 -0
- package/dist/merchant-bond-watcher.js +26 -0
- package/dist/payment-rails.js +29 -0
- package/dist/platform-receive-accounts.js +94 -0
- package/dist/product-verification.js +93 -0
- package/dist/pwa/acp-feed.js +3 -2
- package/dist/pwa/arbitration-read-admin.js +37 -0
- package/dist/pwa/arbitrator-lifecycle.js +100 -0
- package/dist/pwa/contract-fingerprint.js +18 -0
- package/dist/pwa/direct-pay-guards.js +20 -0
- package/dist/pwa/direct-pay-order-redaction.js +42 -0
- package/dist/pwa/endpoint-actions.js +3 -0
- package/dist/pwa/human-presence.js +2 -6
- package/dist/pwa/public/app-account.js +9 -9
- package/dist/pwa/public/app-admin-disputes.js +55 -0
- package/dist/pwa/public/app-agent-appeal.js +90 -0
- package/dist/pwa/public/app-agent-approvals.js +93 -0
- package/dist/pwa/public/app-agent-pair.js +127 -0
- package/dist/pwa/public/app-ai.js +10 -10
- package/dist/pwa/public/app-arbitrator-admin.js +87 -0
- package/dist/pwa/public/app-arbitrator-entry.js +9 -0
- package/dist/pwa/public/app-bond-deferral-ui.js +9 -0
- package/dist/pwa/public/app-bond-refund-ui.js +66 -0
- package/dist/pwa/public/app-bond-slash-ui.js +74 -0
- package/dist/pwa/public/app-bond-terms-ui.js +23 -0
- package/dist/pwa/public/app-bond-ui.js +108 -0
- package/dist/pwa/public/app-chat-poll.js +29 -0
- package/dist/pwa/public/app-contribution-hub.js +23 -0
- package/dist/pwa/public/app-create-kinds.js +17 -0
- package/dist/pwa/public/app-direct-pay-accounts.js +141 -0
- package/dist/pwa/public/app-direct-pay-buyer.js +72 -0
- package/dist/pwa/public/app-direct-pay-cancel-refund.js +72 -0
- package/dist/pwa/public/app-direct-pay-compliance.js +67 -0
- package/dist/pwa/public/app-direct-pay-copy.js +12 -0
- package/dist/pwa/public/app-direct-pay-deferral-admin.js +72 -0
- package/dist/pwa/public/app-direct-pay-deferral.js +61 -0
- package/dist/pwa/public/app-direct-pay-fee-center.js +33 -0
- package/dist/pwa/public/app-direct-pay-fee-history.js +39 -0
- package/dist/pwa/public/app-direct-pay-fee-ops.js +112 -0
- package/dist/pwa/public/app-direct-pay-fee-request.js +81 -0
- package/dist/pwa/public/app-direct-pay-fee-requests-admin.js +70 -0
- package/dist/pwa/public/app-direct-pay-memo.js +14 -0
- package/dist/pwa/public/app-direct-pay-negotiation.js +35 -0
- package/dist/pwa/public/app-direct-pay-pay.js +15 -0
- package/dist/pwa/public/app-direct-pay-paymodal.js +32 -0
- package/dist/pwa/public/app-direct-pay-product-verify.js +103 -0
- package/dist/pwa/public/app-direct-pay-readiness.js +38 -0
- package/dist/pwa/public/app-direct-pay-reconcile.js +19 -0
- package/dist/pwa/public/app-direct-pay-returns.js +56 -0
- package/dist/pwa/public/app-direct-pay-reveal.js +82 -0
- package/dist/pwa/public/app-direct-pay-sales-report.js +70 -0
- package/dist/pwa/public/app-direct-pay-store-verify.js +100 -0
- package/dist/pwa/public/app-direct-pay.js +226 -0
- package/dist/pwa/public/app-discover.js +20 -20
- package/dist/pwa/public/app-dispute-close-ui.js +38 -0
- package/dist/pwa/public/app-external-links.js +32 -0
- package/dist/pwa/public/app-free-shipping-ui.js +29 -0
- package/dist/pwa/public/app-gmv-rail-split.js +12 -0
- package/dist/pwa/public/app-listing-commerce-ui.js +72 -0
- package/dist/pwa/public/app-listings.js +4 -4
- package/dist/pwa/public/app-mutual-cancel.js +54 -0
- package/dist/pwa/public/app-notif-templates-orders.js +43 -0
- package/dist/pwa/public/app-notif-templates.js +22 -0
- package/dist/pwa/public/app-order-accept-ui.js +158 -0
- package/dist/pwa/public/app-order-errors.js +50 -0
- package/dist/pwa/public/app-order-labels.js +19 -0
- package/dist/pwa/public/app-order-rail-filter.js +26 -0
- package/dist/pwa/public/app-platform-receive-accounts.js +140 -0
- package/dist/pwa/public/app-poll-governor.js +22 -0
- package/dist/pwa/public/app-prelaunch-waz.js +39 -0
- package/dist/pwa/public/app-price.js +55 -0
- package/dist/pwa/public/app-product-media.js +15 -0
- package/dist/pwa/public/app-profile.js +10 -10
- package/dist/pwa/public/app-purchase-terms-ui.js +68 -0
- package/dist/pwa/public/app-sale-regions-ui.js +38 -0
- package/dist/pwa/public/app-seller.js +5 -5
- package/dist/pwa/public/app-shop.js +19 -19
- package/dist/pwa/public/app-trade-tax-ui.js +33 -0
- package/dist/pwa/public/app.js +264 -295
- package/dist/pwa/public/i18n.js +1068 -197
- package/dist/pwa/public/index.html +58 -0
- package/dist/pwa/public/openapi.json +1845 -653
- package/dist/pwa/public/style.css +3 -0
- package/dist/pwa/routes/admin-analytics.js +4 -2
- package/dist/pwa/routes/admin-direct-receive-deposits.js +533 -0
- package/dist/pwa/routes/admin-protocol-params.js +16 -0
- package/dist/pwa/routes/admin-reports.js +31 -5
- package/dist/pwa/routes/agent-governance.js +1 -1
- package/dist/pwa/routes/agent-grants.js +253 -32
- package/dist/pwa/routes/analytics.js +2 -0
- package/dist/pwa/routes/arbitrator.js +67 -14
- package/dist/pwa/routes/bond-seller.js +162 -0
- package/dist/pwa/routes/buyer-feeds.js +2 -1
- package/dist/pwa/routes/chat.js +6 -1
- package/dist/pwa/routes/dashboards.js +2 -1
- package/dist/pwa/routes/direct-pay-availability.js +196 -0
- package/dist/pwa/routes/direct-pay-cancel-refund.js +111 -0
- package/dist/pwa/routes/direct-pay-disclosure-acks.js +79 -0
- package/dist/pwa/routes/direct-pay-pending-accept.js +222 -0
- package/dist/pwa/routes/direct-pay-returns.js +74 -0
- package/dist/pwa/routes/direct-pay-timeouts.js +248 -0
- package/dist/pwa/routes/direct-receive-accounts.js +155 -0
- package/dist/pwa/routes/direct-receive-payment-instructions.js +45 -0
- package/dist/pwa/routes/disputes-read.js +20 -6
- package/dist/pwa/routes/disputes-write.js +91 -33
- package/dist/pwa/routes/external-anchors.js +4 -2
- package/dist/pwa/routes/fee-prepay-requests.js +66 -0
- package/dist/pwa/routes/fx.js +12 -0
- package/dist/pwa/routes/governance-onboarding.js +46 -8
- package/dist/pwa/routes/leaderboard.js +47 -9
- package/dist/pwa/routes/listings.js +2 -2
- package/dist/pwa/routes/logistics.js +6 -2
- package/dist/pwa/routes/manifests.js +38 -0
- package/dist/pwa/routes/me-data.js +5 -2
- package/dist/pwa/routes/mutual-cancel.js +62 -0
- package/dist/pwa/routes/orders-action.js +297 -16
- package/dist/pwa/routes/orders-create.js +21 -6
- package/dist/pwa/routes/orders-read.js +106 -8
- package/dist/pwa/routes/p2p-products.js +2 -2
- package/dist/pwa/routes/platform-receive-accounts.js +111 -0
- package/dist/pwa/routes/products-create.js +15 -4
- package/dist/pwa/routes/products-links.js +34 -0
- package/dist/pwa/routes/products-list.js +2 -1
- package/dist/pwa/routes/products-update.js +37 -3
- package/dist/pwa/routes/profile-identity.js +4 -2
- package/dist/pwa/routes/promoter.js +3 -0
- package/dist/pwa/routes/referral.js +4 -0
- package/dist/pwa/routes/returns.js +60 -4
- package/dist/pwa/routes/rewards-apply.js +10 -5
- package/dist/pwa/routes/rewards-clearing-mature.js +96 -0
- package/dist/pwa/routes/rewards-escrow-expire.js +6 -2
- package/dist/pwa/routes/seller-directpay-report.js +110 -0
- package/dist/pwa/routes/seller-quota.js +5 -1
- package/dist/pwa/routes/shipping-templates.js +219 -0
- package/dist/pwa/routes/shops.js +2 -1
- package/dist/pwa/routes/snf.js +4 -1
- package/dist/pwa/routes/url-claim.js +2 -2
- package/dist/pwa/routes/users-public.js +8 -0
- package/dist/pwa/routes/wallet-read.js +3 -0
- package/dist/pwa/routes/webauthn.js +3 -3
- package/dist/pwa/server.js +112 -132
- package/dist/runtime/agent-grant-scopes.js +69 -1
- package/dist/runtime/webaz-schema-helpers.js +161 -3
- package/dist/sale-regions.js +116 -0
- package/dist/shipping-templates.js +119 -0
- package/dist/store-verification.js +77 -0
- package/dist/trade-tax.js +99 -0
- package/dist/trade-terms.js +76 -0
- package/dist/version.js +1 -1
- package/package.json +128 -2
|
@@ -478,6 +478,7 @@ function getActiveDeadline(order, db) {
|
|
|
478
478
|
// 修:picked_up 状态视为"已揽收,等运输/投递",下一个 deadline 是 delivery_deadline
|
|
479
479
|
// QA 轮 7 P1(另一条):disputed 状态下没读 dispute_cases 的 arbitrate_deadline → agent 不知道仲裁还有多久
|
|
480
480
|
const deadlineMapMarket = {
|
|
481
|
+
pending_accept: 'pending_accept_deadline', // 手动接单(v16):等卖家确认,超时无责取消
|
|
481
482
|
created: 'pay_deadline',
|
|
482
483
|
paid: 'accept_deadline',
|
|
483
484
|
accepted: 'ship_deadline',
|
|
@@ -6,16 +6,26 @@
|
|
|
6
6
|
* 这些**退款 / 违约 / 争议**处置终态。所以裸 `status='completed'` ≠ 有效成交 —— 被退款的失败交易也是
|
|
7
7
|
* completed。任何「有效成交」语义的资格门 / 信任信号都不能裸用 status。
|
|
8
8
|
*
|
|
9
|
-
* 真实成交 = 该订单曾进入过 `confirmed`(买家确认收货,或送达 72h 自动确认)
|
|
10
|
-
*
|
|
9
|
+
* 真实成交 = 该订单曾进入过 `confirmed`(买家确认收货,或送达 72h 自动确认)**且未被全额退货**。仅 happy path
|
|
10
|
+
* 经过 confirmed;所有 fault/退款/争议终态都不经过。据 `order_state_history`(transition() 每次状态变更写入)判定。
|
|
11
|
+
*
|
|
12
|
+
* RFC-018 PR4:退货发生在 `confirmed→completed` **之后**(returns 只作用于 completed 单),所以仅靠 confirmed
|
|
13
|
+
* 判据会把【已全额退货】的单仍算成真实成交 —— 退货虚增资格门 / completion_count / sales_count。故再排除
|
|
14
|
+
* 【累计退款达到订单总额】的单:`SUM(return_requests.refund_amount WHERE status='refunded') >= 订单总额`。
|
|
15
|
+
* 用累计 SUM 而非单行 `refund_amount >= total` —— 同一单可先后多次退货(已退款的旧 return 不挡新 return),
|
|
16
|
+
* 多次部分退款累计到全额也应排除。累计 < total 仍算真实成交(确曾成交,只是部分退款)。
|
|
11
17
|
*
|
|
12
18
|
* 用法:把查询里的 `status='completed'` 条件替换为本谓词。
|
|
13
19
|
* 单表:`... WHERE buyer_id = ? AND ${genuineSalePredicate()}` // 默认别名 orders
|
|
14
20
|
* 相关子查询:`... FROM orders o WHERE ... AND ${genuineSalePredicate('o')}`
|
|
15
21
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
22
|
+
* Direct Pay (Rail 1) 排除:`payment_rail='direct_p2p'` 是场外直付,付款不可观测、本档无资金保障,
|
|
23
|
+
* 不计真实成交/佣金/PV(设计稿 §3「Commission/PV/genuine-sale on Rail 1」)。escrow 及将来可观测的
|
|
24
|
+
* Rail 2(链上全额质押)仍计入。NULL → 视为 escrow(老行)。
|
|
25
|
+
*
|
|
26
|
+
* 注:这是纯 SQL 片段(无 DB 依赖),保证所有消费方用同一定义。整体加括号防 OR 上下文优先级。
|
|
27
|
+
* 若将来规模下相关子查询成性能瓶颈,再升级为 settleOrder 写入的 `orders.settled_ok_at` 列(届时只改本文件)。
|
|
18
28
|
*/
|
|
19
29
|
export function genuineSalePredicate(ordersAlias = 'orders') {
|
|
20
|
-
return `EXISTS (SELECT 1 FROM order_state_history osh WHERE osh.order_id = ${ordersAlias}.id AND osh.to_status = 'confirmed')`;
|
|
30
|
+
return `(EXISTS (SELECT 1 FROM order_state_history osh WHERE osh.order_id = ${ordersAlias}.id AND osh.to_status = 'confirmed') AND (SELECT COALESCE(SUM(rr.refund_amount), 0) FROM return_requests rr WHERE rr.order_id = ${ordersAlias}.id AND rr.status = 'refunded') < ${ordersAlias}.total_amount AND COALESCE(${ordersAlias}.payment_rail, 'escrow') != 'direct_p2p')`;
|
|
21
31
|
}
|
|
@@ -20,6 +20,112 @@ export const VALID_TRANSITIONS = {
|
|
|
20
20
|
requiresEvidence: false,
|
|
21
21
|
description: '下单后付款前取消订单'
|
|
22
22
|
},
|
|
23
|
+
// ── Direct Pay (Rail 1) 直付:只"支付"这一步变,之后汇入既有流程 ──────────────
|
|
24
|
+
// 设计稿 docs/modules/DIRECT-PAYMENT-MODULE-DESIGN.INTERNAL.md §4。
|
|
25
|
+
// 本金(货款)不经协议;协议不验证付款;买家声明已付款 → 汇入 accepted(卖家发货)。
|
|
26
|
+
'created→direct_pay_window': {
|
|
27
|
+
allowedRoles: ['system'], // 直付下单路由锁定卖家费用质押后,以 system 显式推进(非超时驱动)
|
|
28
|
+
requiresEvidence: false,
|
|
29
|
+
description: 'Rail1 直付:卖家已质押平台费、展示收款方式,进入付款窗口'
|
|
30
|
+
},
|
|
31
|
+
// ── 手动接单模式(accept_mode='manual',v16):付款前先等卖家确认接单 ──────────
|
|
32
|
+
// 直付轨:created→pending_accept(替代 created→direct_pay_window),卖家接单才进付款窗口 ——
|
|
33
|
+
// 非托管无 hold 可言,时序门(接单前不出示收款信息)是唯一正确的付款风控。
|
|
34
|
+
// escrow 轨不用本状态:escrow 本身就是条件中间态(付款进托管,接单才继续,超时自动全额退回)。
|
|
35
|
+
'created→pending_accept': {
|
|
36
|
+
allowedRoles: ['system'], // 建单路由按 accept_mode 快照以 system 推进(与 created→direct_pay_window 同模式)
|
|
37
|
+
requiresEvidence: false,
|
|
38
|
+
description: '手动接单:订单等待卖家确认接单(付款前,不展示收款信息)'
|
|
39
|
+
},
|
|
40
|
+
'pending_accept→direct_pay_window': {
|
|
41
|
+
allowedRoles: ['seller', 'system'],
|
|
42
|
+
requiresEvidence: false,
|
|
43
|
+
description: '卖家确认接单 → 进入直付付款窗口(此刻起买家方可见收款信息)'
|
|
44
|
+
},
|
|
45
|
+
'pending_accept→cancelled': {
|
|
46
|
+
// deadlineField 供专属 cron(direct-pay-timeouts.ts)读取;故意不设 autoFaultState →
|
|
47
|
+
// 通用 engine.checkTimeouts 绝不触发本转移(会漏库存回补等副作用)。超时=无责取消(没人付过钱)。
|
|
48
|
+
allowedRoles: ['seller', 'buyer', 'system'],
|
|
49
|
+
deadlineField: 'pending_accept_deadline',
|
|
50
|
+
requiresEvidence: false,
|
|
51
|
+
description: '接单前取消:卖家谢绝 / 买家撤单 / 超时未接单(system) → 无责取消 + 回补库存'
|
|
52
|
+
},
|
|
53
|
+
'direct_pay_window→accepted': {
|
|
54
|
+
allowedRoles: ['buyer'],
|
|
55
|
+
requiresEvidence: false,
|
|
56
|
+
description: 'Rail1 直付:买家声明"我已付款" → 汇入既有流程(卖家发货);协议不验证付款,谎报则卖家不发货(零损失自纠正)'
|
|
57
|
+
},
|
|
58
|
+
'direct_pay_window→cancelled': {
|
|
59
|
+
allowedRoles: ['buyer', 'system'],
|
|
60
|
+
requiresEvidence: false,
|
|
61
|
+
description: 'Rail1 直付:付款前买家取消(或卖家请求取消经买家确认)→ 释放费用质押'
|
|
62
|
+
},
|
|
63
|
+
'direct_pay_window→direct_expired_unconfirmed': {
|
|
64
|
+
// deadlineField 供【专属 cron(direct-pay-timeouts.ts)】读取;故意【不设 autoFaultState】→
|
|
65
|
+
// 通用 engine.checkTimeouts(需 deadlineField+autoFaultState 两者)绝不触发本转移,避免漏掉释放质押等副作用。
|
|
66
|
+
allowedRoles: ['system'],
|
|
67
|
+
deadlineField: 'direct_pay_window_deadline',
|
|
68
|
+
requiresEvidence: false,
|
|
69
|
+
description: 'Rail1 直付:付款窗口超时未标记 → 不静默关单,转可争议态(专属 cron 释放费用质押 + 停用收款指令)'
|
|
70
|
+
},
|
|
71
|
+
'direct_expired_unconfirmed→disputed': {
|
|
72
|
+
allowedRoles: ['buyer'],
|
|
73
|
+
requiresEvidence: true,
|
|
74
|
+
evidenceHint: '上传付款凭证(证据分级:链上 tx / 银行回执 / 截图)',
|
|
75
|
+
description: 'Rail1 直付:买家"我确实付了" → 升级争议(证据级信誉裁决,本档无资金赔付)'
|
|
76
|
+
},
|
|
77
|
+
'direct_expired_unconfirmed→cancelled': {
|
|
78
|
+
allowedRoles: ['buyer', 'system'],
|
|
79
|
+
requiresEvidence: false,
|
|
80
|
+
description: 'Rail1 直付:买家确认未付 / 宽限期后系统关单(终态)'
|
|
81
|
+
},
|
|
82
|
+
// ── Direct Pay (Rail 1) 取消退款握手(审计项 C):买家已付款、卖家未发货 → 三步握手无责关单 ──────────
|
|
83
|
+
// request(买家)→ mark_refunded(卖家声明已场外退款)→ confirm(买家确认收到,Passkey)→ 本边(system 执行)。
|
|
84
|
+
// 仅 system:买家不能单方取消已付款订单,必须经握手(域模块 src/direct-pay-cancel-refund.ts 收口,非托管零资金)。
|
|
85
|
+
'accepted→cancelled': {
|
|
86
|
+
allowedRoles: ['system'],
|
|
87
|
+
requiresEvidence: false,
|
|
88
|
+
description: 'Rail1 直付:取消退款握手完成(卖家已场外退款+买家确认收到)→ 系统无责关单,恢复库存'
|
|
89
|
+
},
|
|
90
|
+
// ── Direct Pay (Rail 1) 货款协商(争议≠仲裁):卖家报未收款 → 双方先协商,谈不拢才升举证仲裁 ──────────
|
|
91
|
+
'accepted→payment_query': {
|
|
92
|
+
allowedRoles: ['seller'],
|
|
93
|
+
requiresEvidence: false,
|
|
94
|
+
description: 'Rail1 直付:卖家报告未收到货款 → 进入协商(非仲裁),暂停履约。买家可取消/主张已付,卖家可确认已收/(宽限后)取消'
|
|
95
|
+
},
|
|
96
|
+
'payment_query→accepted': {
|
|
97
|
+
allowedRoles: ['seller'],
|
|
98
|
+
requiresEvidence: false,
|
|
99
|
+
description: 'Rail1 直付:卖家确认已收到货款 → 恢复原订单继续履约'
|
|
100
|
+
},
|
|
101
|
+
'payment_query→cancelled': {
|
|
102
|
+
// buyer=主动取消(承认未付);seller=买家静默宽限后请求取消;system=cron 在 7 天买家申诉窗口后终结。时序/窗口由路由+cron 守。
|
|
103
|
+
allowedRoles: ['buyer', 'seller', 'system'],
|
|
104
|
+
requiresEvidence: false,
|
|
105
|
+
description: 'Rail1 直付:协商关单(买家承认未付 / 卖家宽限后取消 / 系统申诉窗口后终结)'
|
|
106
|
+
},
|
|
107
|
+
'payment_query→disputed': {
|
|
108
|
+
allowedRoles: ['buyer', 'seller'],
|
|
109
|
+
requiresEvidence: true,
|
|
110
|
+
evidenceHint: '上传付款/未收款凭证(链上 tx / 银行回执 / 截图)',
|
|
111
|
+
description: 'Rail1 直付:协商未果(买家主张已付 + 卖家否认)→ 升级举证仲裁(证据制信誉裁决,不涉资金)'
|
|
112
|
+
},
|
|
113
|
+
'disputed→payment_query': {
|
|
114
|
+
// 裁定前回到协商的两条路径:①买卖任一方【撤回仲裁申请】(pq_withdraw);②仲裁员【驳回仲裁,退回协商】——
|
|
115
|
+
// 协议(sys_protocol)在仲裁员现场 Passkey 授权后代执行,故加 'system'。均仅裁定前有效(裁定后 disputed 已终结),时序/授权由路由守。
|
|
116
|
+
allowedRoles: ['buyer', 'seller', 'system'],
|
|
117
|
+
requiresEvidence: false,
|
|
118
|
+
description: 'Rail1 直付:回到协商(裁定前)—— 买卖双方撤回仲裁,或仲裁员驳回仲裁退回协商(system 代执行)'
|
|
119
|
+
},
|
|
120
|
+
'disputed→confirmed': {
|
|
121
|
+
// 争议协商收口(买家侧):买家撤诉并确认收货 —— 包裹晚到/放代收点后来找到等场景,不必只剩仲裁一条收口。
|
|
122
|
+
// 限定【delivered 来源的履约争议 + 争议发起人本人 + 裁定前】,来源/发起人/时序由路由权威门守
|
|
123
|
+
// (orders-action dispute_withdraw_confirm,与 disputed→payment_query 的 pq_withdraw 同模式);
|
|
124
|
+
// 同一事务内争议 dismissed + confirmed→completed + settleOrder(与既有 confirm 结算完全同链)。
|
|
125
|
+
allowedRoles: ['buyer'],
|
|
126
|
+
requiresEvidence: false,
|
|
127
|
+
description: '买家撤诉并确认收货(裁定前,限履约争议):争议无责关闭,订单正常完成结算'
|
|
128
|
+
},
|
|
23
129
|
// ── 卖家接单 ──────────────────────────────────────────────
|
|
24
130
|
'paid→accepted': {
|
|
25
131
|
allowedRoles: ['seller'],
|
|
@@ -243,6 +349,10 @@ export const CURRENT_RESPONSIBLE = {
|
|
|
243
349
|
in_transit: 'logistics', // 等物流投递
|
|
244
350
|
delivered: 'buyer', // 等买家确认
|
|
245
351
|
disputed: 'arbitrator', // 等仲裁处理
|
|
352
|
+
direct_pay_window: 'buyer', // Rail1: 等买家付款并标记
|
|
353
|
+
direct_expired_unconfirmed: 'buyer', // Rail1: 等买家确认未付 或 升级争议
|
|
354
|
+
payment_query: 'buyer', // Rail1: 卖家报未收款,等买家协商响应(取消/主张已付)
|
|
355
|
+
pending_accept: 'seller', // 手动接单:等卖家确认接单(超时无责取消)
|
|
246
356
|
};
|
|
247
357
|
/** Phase 1 self-fulfill 覆盖表:seller 一人承担 shipped/picked_up/in_transit 全程 */
|
|
248
358
|
export const CURRENT_RESPONSIBLE_SELF_FULFILL = {
|
|
@@ -275,6 +385,10 @@ export const ORDER_STATE_MEANINGS = {
|
|
|
275
385
|
refunded_full: { zh: '仲裁裁全额退款,订单作废(终态)', en: 'arbitration full refund, order voided (terminal)' },
|
|
276
386
|
dispute_dismissed: { zh: '争议被驳回(无效,终态)', en: 'dispute dismissed (terminal)' },
|
|
277
387
|
expired: { zh: '订单超时自动失败(通用兜底,终态)', en: 'order expired (generic timeout, terminal)' },
|
|
388
|
+
direct_pay_window: { zh: 'Rail1 直付:已质押平台费,等买家付款(协议不持货款)', en: 'Rail1 direct-pay: fee-staked, awaiting buyer off-protocol payment (protocol holds no funds)' },
|
|
389
|
+
direct_expired_unconfirmed: { zh: 'Rail1 直付:付款窗口超时未标记(不静默关单,留争议/确认窗口)', en: 'Rail1 direct-pay: payment window expired unmarked (not silently closed; dispute/confirm window open)' },
|
|
390
|
+
payment_query: { zh: 'Rail1 直付:卖家报未收货款,双方协商中(非仲裁;暂停履约)', en: 'Rail1 direct-pay: seller reported non-receipt, buyer↔seller negotiating (not arbitration; fulfillment paused)' },
|
|
391
|
+
pending_accept: { zh: '手动接单:等待卖家确认接单(付款前;直付轨此阶段不展示收款信息,超时无责取消)', en: 'manual accept: awaiting seller acceptance (pre-payment; direct-pay hides payment info at this stage; times out to no-fault cancel)' },
|
|
278
392
|
};
|
|
279
393
|
/** 订单/争议生命周期契约 —— 集成方 agent 读它即懂"订单怎么流转 + 每步谁驱动 + 何时 + 含义"。 */
|
|
280
394
|
export function orderLifecycleContract() {
|
|
@@ -303,7 +303,7 @@ async function getLiveStats() {
|
|
|
303
303
|
const disputes = await n('SELECT COUNT(*) as n FROM disputes');
|
|
304
304
|
const skills = await n("SELECT COUNT(*) as n FROM skills WHERE active = 1");
|
|
305
305
|
const totalVolume = ((await dbOne("SELECT COALESCE(SUM(total_amount),0) as v FROM orders WHERE status = 'completed'"))?.v ?? 0);
|
|
306
|
-
return { users, active_products: products, total_orders: orders, completed_orders: completed, total_disputes: disputes, active_skills: skills,
|
|
306
|
+
return { users, active_products: products, total_orders: orders, completed_orders: completed, total_disputes: disputes, active_skills: skills, total_volume_waz: totalVolume };
|
|
307
307
|
}
|
|
308
308
|
catch {
|
|
309
309
|
return null;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP 命令行标志(--version / --help / --mode / --doctor)。
|
|
3
|
+
*
|
|
4
|
+
* 目的:让人 / 陌生 agent 在【不启动 stdio server】的前提下验证安装、看清运行模式与真网络可达性。
|
|
5
|
+
* 无标志 → cliQuickResponse 返回 null,mcp.ts 照常启动 server(既有 MCP 客户端从不传 argv → 零破坏)。
|
|
6
|
+
* 模式判定复用 resolveMode(network-mode.ts)—— 与 server.ts 同一真相源,--mode/--doctor 报的就是 server 实际用的。
|
|
7
|
+
*/
|
|
8
|
+
import { SOFTWARE_VERSION } from '../../version.js';
|
|
9
|
+
import { resolveMode } from './network-mode.js';
|
|
10
|
+
const HELP = `webaz — WebAZ MCP server (@seasonkoh/webaz)
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
npx @seasonkoh/webaz start the MCP stdio server (what MCP clients invoke)
|
|
14
|
+
npx @seasonkoh/webaz --version print version and exit
|
|
15
|
+
npx @seasonkoh/webaz --mode print the resolved run mode and exit
|
|
16
|
+
npx @seasonkoh/webaz --doctor print mode + config + live-network reachability, then exit
|
|
17
|
+
npx @seasonkoh/webaz --help show this help
|
|
18
|
+
|
|
19
|
+
Modes (RFC-003):
|
|
20
|
+
network WEBAZ_API_KEY set — can transact on the live webaz.xyz network
|
|
21
|
+
network_readonly no key (DEFAULT) — public reads hit live webaz.xyz; transactions need a key
|
|
22
|
+
sandbox explicit WEBAZ_MODE=sandbox — local SQLite, isolated from the live network
|
|
23
|
+
|
|
24
|
+
Env:
|
|
25
|
+
WEBAZ_API_KEY your api_key (get one at https://webaz.xyz/#welcome). Set → network mode.
|
|
26
|
+
WEBAZ_MODE force a mode: network | network_readonly | sandbox (sandbox is opt-in only)
|
|
27
|
+
WEBAZ_API_URL network endpoint (default https://webaz.xyz)`;
|
|
28
|
+
/** 同步、纯:处理 --version/-v · --help/-h · --mode。命中 → 返回要打印的字符串;无这些标志 → null(mcp.ts 照常启动 server)。 */
|
|
29
|
+
export function cliQuickResponse(argv, env) {
|
|
30
|
+
const has = (...names) => names.some(n => argv.includes(n));
|
|
31
|
+
if (has('--version', '-v'))
|
|
32
|
+
return SOFTWARE_VERSION;
|
|
33
|
+
if (has('--help', '-h'))
|
|
34
|
+
return HELP;
|
|
35
|
+
if (has('--mode'))
|
|
36
|
+
return resolveMode(env);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
/** --doctor:模式 + 配置 + 真网络可达性。fetch 可注入便于测试;5s 超时保护,绝不 hang。sandbox 不探网络。 */
|
|
40
|
+
export async function runDoctor(env, fetchImpl = fetch) {
|
|
41
|
+
const mode = resolveMode(env);
|
|
42
|
+
const url = (env.WEBAZ_API_URL ?? 'https://webaz.xyz').replace(/\/+$/, '');
|
|
43
|
+
const hasKey = !!(env.WEBAZ_API_KEY ?? '');
|
|
44
|
+
const lines = [
|
|
45
|
+
`webaz doctor — @seasonkoh/webaz v${SOFTWARE_VERSION}`,
|
|
46
|
+
` mode: ${mode}`,
|
|
47
|
+
` api_key: ${hasKey ? 'set (network transactions enabled)' : 'not set (network read-only; set WEBAZ_API_KEY to transact)'}`,
|
|
48
|
+
` api_url: ${url}`,
|
|
49
|
+
];
|
|
50
|
+
if (mode === 'sandbox') {
|
|
51
|
+
lines.push(' reachability: skipped (sandbox = local SQLite, isolated from the live network)');
|
|
52
|
+
return lines.join('\n');
|
|
53
|
+
}
|
|
54
|
+
let reach;
|
|
55
|
+
try {
|
|
56
|
+
const r = await fetchImpl(`${url}/api/protocol-status`, { signal: AbortSignal.timeout(5000) });
|
|
57
|
+
reach = r.ok ? `ok (${url} reachable, HTTP ${r.status})` : `degraded (HTTP ${r.status})`;
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
reach = `unreachable (${e.message})`;
|
|
61
|
+
}
|
|
62
|
+
lines.push(` reachability: ${reach}`);
|
|
63
|
+
return lines.join('\n');
|
|
64
|
+
}
|
|
@@ -7,6 +7,17 @@
|
|
|
7
7
|
* the predicate so the gate decision is unit-testable without booting the stdio
|
|
8
8
|
* server. (Extracted from server.ts; behavior unchanged except adding webaz_pair.)
|
|
9
9
|
*/
|
|
10
|
+
/**
|
|
11
|
+
* 解析运行模式(纯函数,单一真相源 —— server.ts 的 MODE + CLI 的 --mode/--doctor 都用它,杜绝漂移)。
|
|
12
|
+
* 显式 WEBAZ_MODE(network|network_readonly|sandbox)优先;否则有 api_key → network,无 key → network_readonly。
|
|
13
|
+
* sandbox 只能显式设置(无 key 【不】回落 sandbox)。key 的真伪判定与 server.ts 一致(WEBAZ_API_KEY ?? '' 的真值性)。
|
|
14
|
+
*/
|
|
15
|
+
export function resolveMode(env) {
|
|
16
|
+
const m = (env.WEBAZ_MODE ?? '').toLowerCase();
|
|
17
|
+
if (m === 'network' || m === 'sandbox' || m === 'network_readonly')
|
|
18
|
+
return m;
|
|
19
|
+
return (env.WEBAZ_API_KEY ?? '') ? 'network' : 'network_readonly';
|
|
20
|
+
}
|
|
10
21
|
// Tools that talk to the live webaz.xyz network (Bearer api_key where needed).
|
|
11
22
|
// Un-listed tools run sandbox (local). `_mode` annotation = network for these.
|
|
12
23
|
export const NETWORK_TOOLS = new Set([
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* webaz_wallet 查看钱包余额
|
|
14
14
|
* …(39 工具,完整定义见下方 TOOLS 数组;数量以 TOOLS.length 为准)
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
* 关联 / Related: AGENTS.md · RFC-003(
|
|
16
|
+
* 三态(RFC-003):NETWORK(WEBAZ_API_KEY → 调 webaz.xyz)/ NETWORK 只读(无 key,公共读走 webaz.xyz)/ SANDBOX(仅 WEBAZ_MODE=sandbox,本机库);见 NETWORK_TOOLS / apiCall / toolBackend。
|
|
17
|
+
* 关联 / Related: AGENTS.md · RFC-003(三态:network / network_readonly / sandbox) · RFC-004(webaz_feedback) · 元规则 #4 不撒谎(_mode 戳) /
|
|
18
18
|
* #6 不滥用(agent 责任制 + Iron-Rule 真人动作)· 生产端点在 src/pwa(NETWORK 共用)
|
|
19
19
|
*/
|
|
20
20
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
@@ -25,13 +25,14 @@ import { initDatabase, generateId } from '../../layer0-foundation/L0-1-database/
|
|
|
25
25
|
import { setSeamDb } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam(本进程注入)
|
|
26
26
|
import { applyWebazRuntimeSchema } from '../../runtime/apply-webaz-runtime-schema.js'; // 与 PWA 同源的纯 schema 桥(防 MCP fresh DB 漂移)
|
|
27
27
|
import { generateCodeVerifier, pkceChallengeS256 } from '../../runtime/agent-pairing.js'; // RFC-020 PR-C1 — PKCE 配对
|
|
28
|
-
import { NETWORK_TOOLS, NETWORK_SELF_AWARE, toolAllowedInNetworkMode } from './network-mode.js'; // RFC-003 网络门(可单测)
|
|
28
|
+
import { NETWORK_TOOLS, NETWORK_SELF_AWARE, toolAllowedInNetworkMode, resolveMode } from './network-mode.js'; // RFC-003 网络门(可单测)+ 模式解析(单一真相源)
|
|
29
29
|
import { homedir } from 'node:os';
|
|
30
30
|
import { join as pathJoin } from 'node:path';
|
|
31
31
|
import { existsSync as fsExists, mkdirSync, writeFileSync, readFileSync, unlinkSync, chmodSync } from 'node:fs';
|
|
32
32
|
import { execFileSync } from 'node:child_process';
|
|
33
33
|
import { transition, getOrderStatus, initSystemUser, } from '../../layer0-foundation/L0-2-state-machine/engine.js';
|
|
34
|
-
import { initDisputeSchema, createDispute, respondToDispute, getDisputeDetails, getOrderDispute, getOpenDisputes,
|
|
34
|
+
import { initDisputeSchema, createDispute, respondToDispute, getDisputeDetails, getOrderDispute, getOpenDisputes, isActiveWhitelistArbitrator, // 仲裁能力唯一源=active 白名单;legacy user.role==='arbitrator' 旁路已移除
|
|
35
|
+
} from '../../layer3-trust/L3-1-dispute-engine/dispute-engine.js';
|
|
35
36
|
import { initNotificationSchema, notifyTransition, getNotifications, getUnreadCount, markRead, } from '../../layer2-business/L2-6-notifications/notification-engine.js';
|
|
36
37
|
import { initSkillSchema, publishSkill, listSkills, getMySkills, subscribeSkill, unsubscribeSkill, getMySubscriptions, formatSkillForAgent, shouldAutoAccept, SKILL_TYPE_META, } from '../../layer4-economics/L4-4-skill-market/skill-engine.js';
|
|
37
38
|
import { initReputationSchema, recordOrderReputation, getReputation, getSearchBoost, getStakeDiscount, } from '../../layer4-economics/L4-3-reputation/reputation-engine.js';
|
|
@@ -45,7 +46,7 @@ const TELEMETRY_URL = process.env.WEBAZ_TELEMETRY_URL ?? 'https://webaz.xyz/api/
|
|
|
45
46
|
// 2026-06-01: phase A pre-launch 默认 OFF(opt-in)— W8 public launch 时翻回 default ON + 加 README 披露段
|
|
46
47
|
// Phase A pre-launch: telemetry default OFF (opt-in). Flip to default ON at W8 launch + add README disclosure section.
|
|
47
48
|
const TELEMETRY_ENABLED = (process.env.WEBAZ_TELEMETRY ?? 'off').toLowerCase() === 'on';
|
|
48
|
-
// ─── RFC-003 P0:
|
|
49
|
+
// ─── RFC-003 P0: 三态(network_readonly / network / sandbox)骨架 ─────────────────────
|
|
49
50
|
// NETWORK = 带 api_key 调 webaz.xyz/api(加入共享生产网络);SANDBOX = 本地 SQLite(离线试玩,与全网隔离)。
|
|
50
51
|
// P0 不迁移任何工具(NETWORK_TOOLS 为空)→ 一切仍走本地 = 零行为变化;P1/P2 逐个把工具名加入集合切到网络。
|
|
51
52
|
const WEBAZ_API_URL = (process.env.WEBAZ_API_URL ?? 'https://webaz.xyz').replace(/\/+$/, '');
|
|
@@ -59,15 +60,12 @@ export function resolveMcpApiKey(args, envKey = WEBAZ_API_KEY) {
|
|
|
59
60
|
const explicit = typeof args?.api_key === 'string' ? args.api_key.trim() : '';
|
|
60
61
|
return explicit || envKey;
|
|
61
62
|
}
|
|
62
|
-
const WEBAZ_MODE_ENV = (process.env.WEBAZ_MODE ?? '').toLowerCase();
|
|
63
63
|
// 模式:显式 WEBAZ_MODE 优先;否则有 api_key → network,无 key → network_readonly(装完即见真网络)。
|
|
64
64
|
// network_readonly(L1 onboarding,2026-06-08):无 key 默认。公共读匿名打 webaz.xyz(真 catalog/协议),
|
|
65
65
|
// 需身份的写/读返回"设 WEBAZ_API_KEY(到 #welcome 申请邀请)"。离线本地 playground 改为【显式】 WEBAZ_MODE=sandbox。
|
|
66
66
|
// —— 治"装完=空沙盒劝退"的死首体验;route/guard 与 network 同路(见 isNetworkMode),只是无 Bearer + 文案不同。
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
: WEBAZ_MODE_ENV === 'network_readonly' ? 'network_readonly'
|
|
70
|
-
: (WEBAZ_API_KEY ? 'network' : 'network_readonly');
|
|
67
|
+
// 单一真相源:resolveMode(见 network-mode.ts),CLI 的 --mode/--doctor 用同一函数,杜绝漂移。
|
|
68
|
+
const MODE = resolveMode(process.env);
|
|
71
69
|
// network 或 network_readonly 都"走真网络"(后者无 Bearer)。sandbox 才是本地。
|
|
72
70
|
const isNetworkMode = () => MODE === 'network' || MODE === 'network_readonly';
|
|
73
71
|
const recentCalls = [];
|
|
@@ -328,7 +326,51 @@ export async function handlePair(args) {
|
|
|
328
326
|
note: 'Grant verified LIVE by the server (per-call: active/expiry/revoked/subject-suspension/scope + audited). `grant` is authoritative; `local_cache` is advisory. Safe scopes only; no business tool or risk scope consumes this grant.',
|
|
329
327
|
};
|
|
330
328
|
}
|
|
331
|
-
|
|
329
|
+
if (action === 'request') {
|
|
330
|
+
// The agent asks the human to EXPAND this grant with more SAFE scope / a permission bundle. Uses the
|
|
331
|
+
// stored grant bearer; the server binds the request to (human, grant). Approval is Passkey-gated on the
|
|
332
|
+
// human's side. Only SAFE scopes — risk/never-delegable are structurally rejected.
|
|
333
|
+
const cred = resolveGrantCredential();
|
|
334
|
+
if (!cred)
|
|
335
|
+
return { status: 'not_paired', error_code: 'NO_GRANT_CREDENTIAL', hint: 'No stored grant. Pair first: webaz_pair action="start" → human approves → action="complete".' };
|
|
336
|
+
const bundle = typeof args.bundle === 'string' ? args.bundle : undefined;
|
|
337
|
+
const scopes = Array.isArray(args.scopes) ? args.scopes.map(String).filter(Boolean) : undefined;
|
|
338
|
+
if (!bundle && (!scopes || !scopes.length))
|
|
339
|
+
return { error: 'provide a bundle (e.g. "catalog_agent") OR a scopes[] array of SAFE scopes', error_code: 'NO_SCOPES' };
|
|
340
|
+
const resp = await apiCall('/api/agent-grants/permission-requests', {
|
|
341
|
+
method: 'POST', apiKey: cred.token,
|
|
342
|
+
body: { bundle, scopes, reason: typeof args.reason === 'string' ? args.reason : undefined, duration: typeof args.duration === 'string' ? args.duration : undefined },
|
|
343
|
+
});
|
|
344
|
+
if (resp.error)
|
|
345
|
+
return resp;
|
|
346
|
+
return {
|
|
347
|
+
status: 'requested',
|
|
348
|
+
approval_id: resp.approval_id,
|
|
349
|
+
approval_url: `${WEBAZ_API_URL}${String(resp.approval_url || '')}`,
|
|
350
|
+
requested_scopes: resp.requested_scopes,
|
|
351
|
+
permission_bundle: resp.permission_bundle,
|
|
352
|
+
risk_level: resp.risk_level,
|
|
353
|
+
suggested_duration: resp.suggested_duration,
|
|
354
|
+
next: 'Ask the human to open approval_url (logged in at webaz.xyz) and approve with their Passkey. Then simply RETRY your original call — on approval your grant carries the new scope. Use action="requests" to poll status.',
|
|
355
|
+
note: 'Only SAFE (read/draft) scopes can be requested; risk actions are never delegated to a persistent grant.',
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
if (action === 'requests') {
|
|
359
|
+
// Poll THIS grant's own permission requests + their status (pending/approved/rejected) without hitting the
|
|
360
|
+
// target surface. Grant-authed, grant-scoped (you see only your own requests). Server audits every call.
|
|
361
|
+
const cred = resolveGrantCredential();
|
|
362
|
+
if (!cred)
|
|
363
|
+
return { status: 'not_paired', error_code: 'NO_GRANT_CREDENTIAL', hint: 'No stored grant. Pair first: webaz_pair action="start" → human approves → action="complete".' };
|
|
364
|
+
const resp = await apiCall('/api/agent-grants/my-permission-requests', { method: 'GET', apiKey: cred.token });
|
|
365
|
+
if (resp.error)
|
|
366
|
+
return resp;
|
|
367
|
+
return {
|
|
368
|
+
status: 'ok',
|
|
369
|
+
requests: resp.requests,
|
|
370
|
+
note: 'Your grant\'s own permission requests + status. On "approved", just retry your original call — the grant now carries the scope. "pending" → the human has not approved yet.',
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
return { error: `unknown action "${action}" — use "start" | "complete" | "verify" | "request" | "requests"`, error_code: 'BAD_ACTION' };
|
|
332
374
|
}
|
|
333
375
|
// 启动 banner(stderr)+ status 声明用 —— 让用户/agent 一眼知道现在是真网络还是沙盒
|
|
334
376
|
function modeBanner() {
|
|
@@ -401,8 +443,12 @@ db.exec(`
|
|
|
401
443
|
error_msg TEXT
|
|
402
444
|
)
|
|
403
445
|
`);
|
|
404
|
-
|
|
405
|
-
|
|
446
|
+
// 索引命名与 runtime bridge(apply-webaz-runtime-schema)统一 —— 两组合根曾各自命名(idx_mcp_tool_calls_* vs idx_mcp_tc_*)
|
|
447
|
+
// → 同库跑过两根就出现同列重复索引,且 pg 产物逐列/索引 parity 因根不同而漂移。老名幂等清除。
|
|
448
|
+
db.exec(`DROP INDEX IF EXISTS idx_mcp_tool_calls_ts`);
|
|
449
|
+
db.exec(`DROP INDEX IF EXISTS idx_mcp_tool_calls_tool`);
|
|
450
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_mcp_tc_ts ON mcp_tool_calls(ts)`);
|
|
451
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_mcp_tc_tool ON mcp_tool_calls(tool_name, ts)`);
|
|
406
452
|
// ─── 4 层身份模型 helpers(与 PWA server 同源逻辑)───────────────
|
|
407
453
|
const PERMA_ALPHABET_MCP = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
|
408
454
|
function mcpGeneratePermanentCode() {
|
|
@@ -489,14 +535,21 @@ No auth required, no parameters needed.
|
|
|
489
535
|
2. action="complete" → retrieves the credential ONCE (PKCE) and stores it in your OS secret store (macOS Keychain → ~/.webaz/credentials 0600 fallback). Returns only a credential_handle + status — the raw token is never shown.
|
|
490
536
|
3. action="verify" → uses the stored grant against a safe read endpoint to confirm it is still valid. The server re-checks active/expiry/revoked/suspension/scope and audits the call every time. Returns the grant principal/status; never the raw token.
|
|
491
537
|
|
|
492
|
-
|
|
538
|
+
Need MORE scope later? If a call returns error_code="PERMISSION_REQUIRED", do NOT re-pair:
|
|
539
|
+
4. action="request" (bundle="catalog_agent" OR scopes=[...]) → asks the human to expand THIS grant. Returns an approval_id + approval_url; the human approves with a Passkey, then you simply RETRY your original call.
|
|
540
|
+
5. action="requests" → lists your grant's own requests + status (pending/approved/rejected) so you can poll instead of hitting the target surface.
|
|
541
|
+
|
|
542
|
+
Scopes: SAFE only (read_public, profile_read, search, list_product_draft, product_publish_request, draft_order + seller_*_read / seller_product_draft / seller_pricing_suggestion; bundle "catalog_agent"). Risk scopes (place_order/wallet/refund/...) and never-delegable actions (withdraw/key-change/vote/...) are hard-rejected.
|
|
493
543
|
|
|
494
544
|
NOTE: this consumes the grant only on safe read paths; no business tool and no risk scope is wired to grants. No account is created (registration stays human-only at webaz.xyz).`,
|
|
495
545
|
inputSchema: {
|
|
496
546
|
type: 'object',
|
|
497
547
|
properties: {
|
|
498
|
-
action: { type: 'string', enum: ['start', 'complete', 'verify'], description: 'start a pairing, complete it after the human approves,
|
|
499
|
-
capabilities: { type: 'array', items: { type: 'string' }, description: '
|
|
548
|
+
action: { type: 'string', enum: ['start', 'complete', 'verify', 'request', 'requests'], description: 'start a pairing, complete it after the human approves, verify the stored grant, request MORE scope (bundle/scopes), or list your requests (default: start)' },
|
|
549
|
+
capabilities: { type: 'array', items: { type: 'string' }, description: 'action="start": requested SAFE scopes (default: read_public, search). Non-safe scopes are rejected.' },
|
|
550
|
+
bundle: { type: 'string', description: 'action="request": a permission bundle key (e.g. "catalog_agent") — a named all-safe scope set the human approves as one thing.' },
|
|
551
|
+
scopes: { type: 'array', items: { type: 'string' }, description: 'action="request": individual SAFE scopes to request (alternative to bundle).' },
|
|
552
|
+
duration: { type: 'string', enum: ['once', '1h', '24h', '7d', '30d'], description: 'action="request": how long the expanded grant should last (safe scopes may be long-term; the server caps by risk).' },
|
|
500
553
|
agent_label: { type: 'string', description: 'Human-friendly name for this agent (shown in the consent screen)' },
|
|
501
554
|
reason: { type: 'string', description: 'Free-text reason shown to the human (you cannot relabel the scopes)' },
|
|
502
555
|
},
|
|
@@ -813,9 +866,9 @@ Actions:
|
|
|
813
866
|
- list_open pending disputes (arbitrators only)
|
|
814
867
|
- respond respondent rebuttal (before 48h deadline)
|
|
815
868
|
- add_evidence any participant supplements
|
|
816
|
-
- arbitrate ruling
|
|
869
|
+
- arbitrate ruling (assigned arbitrator only). ESCROW: fund disposition. DIRECT PAY (payment_rail=direct_p2p, non-custodial): reputation-only ruling — NO refund/release/arbitration-fee, NO amounts.
|
|
817
870
|
|
|
818
|
-
Ruling options: refund_buyer | release_seller | partial_refund (needs refund_amount; optional liable_party → 3rd-party fault, seller settled full + deducted from liable) | liability_split (needs liability_parties[]={user_id,amount}).
|
|
871
|
+
Ruling options — ESCROW: refund_buyer | release_seller | partial_refund (needs refund_amount; optional liable_party → 3rd-party fault, seller settled full + deducted from liable) | liability_split (needs liability_parties[]={user_id,amount}). DIRECT PAY: refund_buyer=买家胜诉 | release_seller=卖家胜诉 | partial_refund=部分责任 — reputation verdict only, do NOT pass refund_amount/liability_parties (protocol holds no funds). Check \`view\`→payment_rail/ruling_options first.
|
|
819
872
|
|
|
820
873
|
Protocol auto-judges (no human): respondent silent 48h → favor initiator; arbitrator silent 120h → refund_buyer. Executed rulings are instant + irreversible.
|
|
821
874
|
|
|
@@ -1899,10 +1952,10 @@ async function handleInfo() {
|
|
|
1899
1952
|
? '🟢 NETWORK(只读):无 api_key。公共读(搜索/榜单/价格史/浏览)打 webaz.xyz 真网络(见 network_live)。要交易(注册/下单/上架等)请设 WEBAZ_API_KEY —— 到 ' + WEBAZ_API_URL + '/#welcome 申请邀请。'
|
|
1900
1953
|
: '🟡 SANDBOX:所有工具都在本机本地 SQLite 运行,与 webaz.xyz 全网隔离(显式 dev/demo 模式)。任何计数 / 账号 / 订单仅本机有效。不设 WEBAZ_MODE 则默认走真网络只读。',
|
|
1901
1954
|
phase: 'pre_launch',
|
|
1902
|
-
real_users_on_canonical
|
|
1955
|
+
// 真实用户/规模【不在此硬编码】—— 网络模式下 network_live(上方,实时拉 /api/protocol-status)带有 real_users_on_canonical 真值。
|
|
1903
1956
|
canonical_endpoint: 'https://webaz.xyz',
|
|
1904
1957
|
economic_flow: 'simulated WAZ (test currency, 1 WAZ ≈ 1 USDC peg is a模拟基准, not a real exchange rate). No fiat/crypto settles yet.',
|
|
1905
|
-
disclaimer: '
|
|
1958
|
+
disclaimer: '本协议尚未公开上线(pre-launch),无真实经济流转。真实用户/规模请以 network_live(本次从 webaz.xyz 实时拉取的 /api/protocol-status,含 real_users_on_canonical)为准;下方 live_stats / 工具示例的所有计数均来自**本机 MCP 服务器的本地 SQLite**(~/.webaz/webaz.db),仅供 dev / demo,不代表协议全网真实状态。请勿据此评估市场规模、做投资决策、或替终端用户承诺任何经济关系。',
|
|
1906
1959
|
},
|
|
1907
1960
|
description: 'WebAZ is a decentralized commerce protocol. Every transaction flows through a state machine; each state transition needs proof-of-action from the responsible party. Any timeout-without-action: protocol auto-rules default + executes remedy. / WebAZ 是去中心化商业协议;每笔交易通过状态机流转,每次状态转移需对应责任方的操作证明;超时未操作则协议自动判违约并执行处置。',
|
|
1908
1961
|
// 给【终端用户/买家】的价值主张 —— 陌生 agent / 普通买家先要知道"对我有什么用",而非只读抽象 tagline + 技术 description。
|
|
@@ -1996,7 +2049,7 @@ function handleRegister(args) {
|
|
|
1996
2049
|
],
|
|
1997
2050
|
register_url: WEBAZ_API_URL,
|
|
1998
2051
|
why_not_agent_self_register: 'agent 自助注册会绕过邀请 / captcha / 真人 Passkey 责任制,破坏协议的可问责性(#6 不滥用 / CHARTER §4 I-5)。',
|
|
1999
|
-
want_to_try_offline_first: '只想离线试玩、暂不连网络?设环境变量 WEBAZ_MODE=sandbox
|
|
2052
|
+
want_to_try_offline_first: '只想离线试玩、暂不连网络?设环境变量 WEBAZ_MODE=sandbox(本机沙盒需显式开启;无 key 仍是 NETWORK 只读,不会进沙盒),webaz_register 会在本机沙盒建一个测试账号(仅本机有效)。',
|
|
2000
2053
|
};
|
|
2001
2054
|
}
|
|
2002
2055
|
// ─── SANDBOX 模式:本机建测试账号(显著标注,仅本机有效,与 webaz.xyz 全网隔离)──
|
|
@@ -2524,8 +2577,8 @@ async function handleListProduct(args) {
|
|
|
2524
2577
|
id, seller_id, title, description, price, stock, category, stake_amount,
|
|
2525
2578
|
specs, brand, model, source_price,
|
|
2526
2579
|
ship_regions, handling_hours, estimated_days, fragile,
|
|
2527
|
-
return_days, return_condition, warranty_days
|
|
2528
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
2580
|
+
return_days, return_condition, warranty_days, currency
|
|
2581
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'WAZ')
|
|
2529
2582
|
`).run(id, user.id, args.title, args.description, price, args.stock ?? 1, args.category ?? null, stakeAmount, specsJson, args.brand ?? null, args.model ?? null, args.source_price != null ? Number(args.source_price) : null, args.ship_regions ?? '全国', args.handling_hours != null ? Number(args.handling_hours) : 24, estJson, args.fragile ? 1 : 0, args.return_days != null ? Number(args.return_days) : 7, args.return_condition ?? '', args.warranty_days != null ? Number(args.warranty_days) : 0);
|
|
2530
2583
|
// 注:per-order stake 模型不在此 lock — 移到 place_order 时按订单总额锁
|
|
2531
2584
|
// S2/S3/S4 新字段:通过 HTTP PUT 走标准校验路径(i18n 语言白名单 / origin_claims 4KB+sha256 / 库存阈值)
|
|
@@ -2788,7 +2841,7 @@ async function handleUpdateOrder(args) {
|
|
|
2788
2841
|
const isParticipant = order.buyer_id === user.id ||
|
|
2789
2842
|
order.seller_id === user.id ||
|
|
2790
2843
|
order.logistics_id === user.id;
|
|
2791
|
-
if (!isParticipant && user.
|
|
2844
|
+
if (!isParticipant && !isActiveWhitelistArbitrator(db, user.id)) { // 白名单(非 legacy role):role-only/已吊销不可越权;下游 transition 仍按 actor.role 守边
|
|
2792
2845
|
return { error: '你不是这笔订单的参与方,无法操作' };
|
|
2793
2846
|
}
|
|
2794
2847
|
// action → 状态映射
|
|
@@ -3091,10 +3144,13 @@ async function handleDispute(args) {
|
|
|
3091
3144
|
WHERE e.order_id = ? AND u.role = ?
|
|
3092
3145
|
ORDER BY e.created_at ASC
|
|
3093
3146
|
`).all(orderId, uploaderRole);
|
|
3147
|
+
const dpNonCustodial = dispute.payment_rail === 'direct_p2p'; // 直付=非托管:仅信誉裁决,无退款/放款/仲裁费/金额
|
|
3094
3148
|
return {
|
|
3095
3149
|
dispute_id: dispute.id,
|
|
3096
3150
|
order_id: dispute.order_id,
|
|
3097
3151
|
status: dispute.status,
|
|
3152
|
+
payment_rail: dispute.payment_rail || 'escrow',
|
|
3153
|
+
non_custodial: dpNonCustodial,
|
|
3098
3154
|
initiator: `${dispute.initiator_name}(${dispute.initiator_role})`,
|
|
3099
3155
|
defendant: `${dispute.defendant_name}(${dispute.defendant_role})`,
|
|
3100
3156
|
reason: dispute.reason,
|
|
@@ -3104,21 +3160,27 @@ async function handleDispute(args) {
|
|
|
3104
3160
|
defendant_notes: dispute.defendant_notes ?? '(被诉方尚未提交回应)',
|
|
3105
3161
|
defendant_evidence: JSON.parse(dispute.defendant_evidence_ids || '[]'),
|
|
3106
3162
|
ruling: dispute.ruling_type
|
|
3107
|
-
?
|
|
3163
|
+
? (dpNonCustodial
|
|
3164
|
+
? { type: dispute.ruling_type, reason: dispute.verdict_reason, note: '非托管信誉裁决:无退款/放款金额' }
|
|
3165
|
+
: { type: dispute.ruling_type, refund_amount: dispute.refund_amount, reason: dispute.verdict_reason })
|
|
3108
3166
|
: null,
|
|
3109
|
-
//
|
|
3110
|
-
ruling_options: dispute.ruling_type ? undefined : [
|
|
3167
|
+
// 未裁定时暴露可选 ruling 列表。direct_p2p:仅信誉裁决(胜负/责任,无金额);escrow:含金额/责任分配。
|
|
3168
|
+
ruling_options: dispute.ruling_type ? undefined : (dpNonCustodial ? [
|
|
3169
|
+
{ type: 'refund_buyer', desc: '判买家胜诉(信誉裁决;非托管:无退款/放款/仲裁费)' },
|
|
3170
|
+
{ type: 'release_seller', desc: '判卖家胜诉(信誉裁决;无退款/放款)' },
|
|
3171
|
+
{ type: 'partial_refund', desc: '判部分责任(信誉裁决;无金额)' },
|
|
3172
|
+
] : [
|
|
3111
3173
|
{ type: 'refund_buyer', desc: '全额退款给买家(卖家败诉)' },
|
|
3112
3174
|
{ type: 'release_seller', desc: '放款给卖家(买家败诉)' },
|
|
3113
3175
|
{ type: 'partial_refund', desc: '部分退款(需传 refund_amount)' },
|
|
3114
3176
|
{ type: 'liability_split', desc: '按责任分配(需传 liability_parties 数组)' },
|
|
3115
|
-
],
|
|
3177
|
+
]),
|
|
3116
3178
|
resolved_at: dispute.resolved_at,
|
|
3117
3179
|
};
|
|
3118
3180
|
}
|
|
3119
3181
|
// ── 仲裁员查看所有待处理争议 ───────────────────────────────
|
|
3120
3182
|
if (action === 'list_open') {
|
|
3121
|
-
if (user.
|
|
3183
|
+
if (!isActiveWhitelistArbitrator(db, user.id)) { // 白名单(非 legacy role):whitelist-only 真人可用,role-only/已吊销 403
|
|
3122
3184
|
return { error: '只有仲裁员可以查看所有待处理争议' };
|
|
3123
3185
|
}
|
|
3124
3186
|
const disputes = await getOpenDisputes(db);
|
|
@@ -3131,7 +3193,7 @@ async function handleDispute(args) {
|
|
|
3131
3193
|
initiator: `${d.initiator_name}(${d.initiator_role})`,
|
|
3132
3194
|
defendant: `${d.defendant_name}(${d.defendant_role})`,
|
|
3133
3195
|
reason: d.reason,
|
|
3134
|
-
amount: `${d.total_amount} WAZ`,
|
|
3196
|
+
amount: d.payment_rail === 'direct_p2p' ? `${d.total_amount} USDC(非托管·信誉裁决)` : `${d.total_amount} WAZ`,
|
|
3135
3197
|
respond_deadline: d.respond_deadline,
|
|
3136
3198
|
arbitrate_deadline: d.arbitrate_deadline,
|
|
3137
3199
|
created_at: d.created_at,
|
|
@@ -3713,13 +3775,18 @@ async function handleReferral(args) {
|
|
|
3713
3775
|
state = 'never_activated';
|
|
3714
3776
|
note = 'Rewards inactive — attributions recorded; commissions held in pending_commission_escrow (30d window per protocol_params.rewards_opt_in.escrow_days) until you activate via PWA #me.';
|
|
3715
3777
|
}
|
|
3716
|
-
|
|
3717
|
-
const
|
|
3778
|
+
// RFC-018: matures_at IS NULL = opt-out escrow only (clearing rows auto-mature; surfaced separately in PR3).
|
|
3779
|
+
const pending = db.prepare("SELECT COUNT(*) AS n, COALESCE(SUM(amount),0) AS total FROM pending_commission_escrow WHERE recipient_user_id = ? AND status = 'pending' AND matures_at IS NULL").get(userId);
|
|
3780
|
+
const expired = db.prepare("SELECT COUNT(*) AS n, COALESCE(SUM(amount),0) AS total FROM pending_commission_escrow WHERE recipient_user_id = ? AND status = 'expired' AND matures_at IS NULL").get(userId);
|
|
3781
|
+
// RFC-018 clearing window: opted-in commission accrued but not yet matured into the wallet
|
|
3782
|
+
// (matures_at NOT NULL). Distinct from opt-out escrow above — pure read, surfaced for the agent.
|
|
3783
|
+
const clearing = db.prepare("SELECT COUNT(*) AS n, COALESCE(SUM(amount),0) AS total FROM pending_commission_escrow WHERE recipient_user_id = ? AND status = 'pending' AND matures_at IS NOT NULL").get(userId);
|
|
3718
3784
|
return {
|
|
3719
3785
|
state,
|
|
3720
3786
|
opted_in: optIn === 1,
|
|
3721
3787
|
note,
|
|
3722
3788
|
pending_escrow: { count: pending.n, total_amount: pending.total },
|
|
3789
|
+
clearing: { count: clearing.n, total_amount: clearing.total }, // RFC-018: accrued, maturing after the return window (not yet paid)
|
|
3723
3790
|
expired_to_charity: { count: expired.n, total_amount: expired.total },
|
|
3724
3791
|
spec: 'RFC-002 §3.5 — rewards / share-commission opt-in (RFC-002)',
|
|
3725
3792
|
};
|