@seasonkoh/webaz 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +156 -20
  3. package/dist/layer0-foundation/L0-1-database/schema.js +5 -4
  4. package/dist/layer0-foundation/L0-2-state-machine/engine.js +228 -7
  5. package/dist/layer0-foundation/L0-2-state-machine/order-chain.js +156 -0
  6. package/dist/layer0-foundation/L0-2-state-machine/transitions.js +53 -12
  7. package/dist/layer0-foundation/L0-5-manifest/manifest.js +14 -1
  8. package/dist/layer1-agent/L1-1-mcp-server/auth.js +1 -1
  9. package/dist/layer1-agent/L1-1-mcp-server/server.js +3543 -852
  10. package/dist/layer1-agent/L1-2-external-anchor/anchor-engine.js +324 -0
  11. package/dist/layer1-agent/L1-2-identity/agent-passport.js +100 -0
  12. package/dist/layer2-business/L2-6-notifications/notification-engine.js +72 -5
  13. package/dist/layer2-business/L2-7-snf/snf-engine.js +287 -0
  14. package/dist/layer2-business/L2-anchor-registry/anchor-registry.js +396 -0
  15. package/dist/layer2-business/L2-notes/note-photo-storage.js +133 -0
  16. package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +6 -6
  17. package/dist/layer3-trust/L3-1-dispute-engine/evidence-storage.js +246 -0
  18. package/dist/layer4-economics/L4-3-reputation/reputation-engine.js +95 -1
  19. package/dist/layer4-economics/L4-4-skill-market/skill-engine.js +31 -2
  20. package/dist/layer4-economics/L4-4-skill-market/skill-listing-engine.js +358 -0
  21. package/dist/pwa/public/app.js +31230 -2345
  22. package/dist/pwa/public/i18n.js +5282 -111
  23. package/dist/pwa/public/icon.svg +11 -0
  24. package/dist/pwa/public/index.html +4 -1
  25. package/dist/pwa/public/manifest.json +39 -4
  26. package/dist/pwa/public/openapi.json +5946 -0
  27. package/dist/pwa/public/style.css +278 -5
  28. package/dist/pwa/public/sw.js +41 -2
  29. package/dist/pwa/public/vendor/jsQR.js +10102 -0
  30. package/dist/pwa/public/webaz-logo.png +0 -0
  31. package/dist/pwa/routes/account-deletion.js +53 -0
  32. package/dist/pwa/routes/addresses.js +105 -0
  33. package/dist/pwa/routes/admin-admins.js +151 -0
  34. package/dist/pwa/routes/admin-analytics.js +253 -0
  35. package/dist/pwa/routes/admin-atomic.js +21 -0
  36. package/dist/pwa/routes/admin-catalog.js +64 -0
  37. package/dist/pwa/routes/admin-editor-picks.js +45 -0
  38. package/dist/pwa/routes/admin-events.js +60 -0
  39. package/dist/pwa/routes/admin-health.js +66 -0
  40. package/dist/pwa/routes/admin-moderation.js +120 -0
  41. package/dist/pwa/routes/admin-ops.js +179 -0
  42. package/dist/pwa/routes/admin-protocol-params.js +79 -0
  43. package/dist/pwa/routes/admin-reports.js +154 -0
  44. package/dist/pwa/routes/admin-tokenomics.js +113 -0
  45. package/dist/pwa/routes/admin-users-lifecycle.js +237 -0
  46. package/dist/pwa/routes/admin-users-query.js +390 -0
  47. package/dist/pwa/routes/admin-verifier-flow.js +126 -0
  48. package/dist/pwa/routes/admin-verifier-whitelist.js +111 -0
  49. package/dist/pwa/routes/admin-wallet-ops.js +66 -0
  50. package/dist/pwa/routes/agent-buy.js +215 -0
  51. package/dist/pwa/routes/agent-governance.js +341 -0
  52. package/dist/pwa/routes/agent-reputation.js +34 -0
  53. package/dist/pwa/routes/ai.js +101 -0
  54. package/dist/pwa/routes/analytics.js +272 -0
  55. package/dist/pwa/routes/anchors.js +169 -0
  56. package/dist/pwa/routes/announcements.js +110 -0
  57. package/dist/pwa/routes/arbitrator.js +117 -0
  58. package/dist/pwa/routes/auction.js +436 -0
  59. package/dist/pwa/routes/auth-login.js +40 -0
  60. package/dist/pwa/routes/auth-read.js +66 -0
  61. package/dist/pwa/routes/auth-register.js +138 -0
  62. package/dist/pwa/routes/auth-sessions.js +62 -0
  63. package/dist/pwa/routes/blocklist.js +60 -0
  64. package/dist/pwa/routes/buyer-feeds.js +224 -0
  65. package/dist/pwa/routes/cart.js +155 -0
  66. package/dist/pwa/routes/charity.js +816 -0
  67. package/dist/pwa/routes/chat.js +318 -0
  68. package/dist/pwa/routes/checkin-tasks.js +122 -0
  69. package/dist/pwa/routes/checkout-helpers.js +85 -0
  70. package/dist/pwa/routes/claim-initiators.js +88 -0
  71. package/dist/pwa/routes/claim-verify.js +615 -0
  72. package/dist/pwa/routes/claim-voting.js +114 -0
  73. package/dist/pwa/routes/claim-withdrawals.js +20 -0
  74. package/dist/pwa/routes/coupons.js +165 -0
  75. package/dist/pwa/routes/dashboards.js +99 -0
  76. package/dist/pwa/routes/dispute-cases.js +267 -0
  77. package/dist/pwa/routes/disputes-read.js +358 -0
  78. package/dist/pwa/routes/disputes-write.js +475 -0
  79. package/dist/pwa/routes/evidence.js +86 -0
  80. package/dist/pwa/routes/external-anchors.js +107 -0
  81. package/dist/pwa/routes/feedback.js +270 -0
  82. package/dist/pwa/routes/flash-sales.js +130 -0
  83. package/dist/pwa/routes/follows.js +103 -0
  84. package/dist/pwa/routes/group-buys.js +208 -0
  85. package/dist/pwa/routes/growth.js +199 -0
  86. package/dist/pwa/routes/import-product.js +153 -0
  87. package/dist/pwa/routes/kyc.js +40 -0
  88. package/dist/pwa/routes/leaderboard.js +149 -0
  89. package/dist/pwa/routes/listings.js +281 -0
  90. package/dist/pwa/routes/logistics.js +35 -0
  91. package/dist/pwa/routes/manifests.js +126 -0
  92. package/dist/pwa/routes/me-data.js +101 -0
  93. package/dist/pwa/routes/notifications.js +48 -0
  94. package/dist/pwa/routes/offers.js +96 -0
  95. package/dist/pwa/routes/orders-action.js +285 -0
  96. package/dist/pwa/routes/orders-create.js +339 -0
  97. package/dist/pwa/routes/orders-read.js +180 -0
  98. package/dist/pwa/routes/p2p-products.js +178 -0
  99. package/dist/pwa/routes/payments-governance.js +311 -0
  100. package/dist/pwa/routes/peers.js +34 -0
  101. package/dist/pwa/routes/pin-receipts.js +39 -0
  102. package/dist/pwa/routes/products-aliases.js +119 -0
  103. package/dist/pwa/routes/products-claims.js +60 -0
  104. package/dist/pwa/routes/products-create.js +206 -0
  105. package/dist/pwa/routes/products-crud.js +73 -0
  106. package/dist/pwa/routes/products-links.js +129 -0
  107. package/dist/pwa/routes/products-list.js +424 -0
  108. package/dist/pwa/routes/products-meta.js +155 -0
  109. package/dist/pwa/routes/products-update.js +125 -0
  110. package/dist/pwa/routes/profile-credentials.js +105 -0
  111. package/dist/pwa/routes/profile-identity.js +174 -0
  112. package/dist/pwa/routes/profile-location.js +35 -0
  113. package/dist/pwa/routes/profile-placement.js +70 -0
  114. package/dist/pwa/routes/profile-prefs.js +93 -0
  115. package/dist/pwa/routes/promoter.js +208 -0
  116. package/dist/pwa/routes/public-utils.js +170 -0
  117. package/dist/pwa/routes/push.js +54 -0
  118. package/dist/pwa/routes/ratings.js +220 -0
  119. package/dist/pwa/routes/recover-key.js +100 -0
  120. package/dist/pwa/routes/referral.js +58 -0
  121. package/dist/pwa/routes/reputation.js +34 -0
  122. package/dist/pwa/routes/returns.js +493 -0
  123. package/dist/pwa/routes/reviews.js +81 -0
  124. package/dist/pwa/routes/rfqs.js +443 -0
  125. package/dist/pwa/routes/search.js +172 -0
  126. package/dist/pwa/routes/secondhand.js +278 -0
  127. package/dist/pwa/routes/seller-quota.js +225 -0
  128. package/dist/pwa/routes/share-redirects.js +164 -0
  129. package/dist/pwa/routes/shareables-interactions.js +212 -0
  130. package/dist/pwa/routes/shareables.js +470 -0
  131. package/dist/pwa/routes/shops.js +98 -0
  132. package/dist/pwa/routes/signaling.js +43 -0
  133. package/dist/pwa/routes/skill-market.js +173 -0
  134. package/dist/pwa/routes/skills.js +174 -0
  135. package/dist/pwa/routes/snf.js +126 -0
  136. package/dist/pwa/routes/tags.js +47 -0
  137. package/dist/pwa/routes/trial.js +333 -0
  138. package/dist/pwa/routes/trusted-kpi.js +87 -0
  139. package/dist/pwa/routes/url-claim.js +113 -0
  140. package/dist/pwa/routes/users-public.js +317 -0
  141. package/dist/pwa/routes/variants.js +156 -0
  142. package/dist/pwa/routes/verifier-user.js +107 -0
  143. package/dist/pwa/routes/verify-tasks.js +120 -0
  144. package/dist/pwa/routes/waitlist.js +65 -0
  145. package/dist/pwa/routes/wallet-read.js +218 -0
  146. package/dist/pwa/routes/wallet-write.js +273 -0
  147. package/dist/pwa/routes/webauthn.js +188 -0
  148. package/dist/pwa/routes/webhooks.js +162 -0
  149. package/dist/pwa/routes/welcome.js +226 -0
  150. package/dist/pwa/routes/wishlist-qa.js +135 -0
  151. package/dist/pwa/security/ssrf.js +110 -0
  152. package/dist/pwa/server.js +9247 -2097
  153. package/package.json +8 -3
@@ -0,0 +1,156 @@
1
+ /**
2
+ * L0-2b · 订单签名链
3
+ *
4
+ * 每个订单的状态转换 append 到一条 hash-chained 签名事件里。
5
+ * 关键性质:
6
+ * 1. 防篡改 — 每事件含 prev_event_hash,改任一历史事件会让后续 hash 不匹配
7
+ * 2. 防伪造 — 每事件含 HMAC-SHA256 签名(用 actor 的 api_key 签),server 也无法替 actor 凭空写新事件
8
+ * 3. 可独立验证 — 任何人拿到完整链 + 各 actor api_key(仲裁场景)可以离线 replay
9
+ * 4. 不替代而是补强 order_state_history — 旧表保留作为人类可读的状态历史
10
+ *
11
+ * 与 Mobazha 的 serialized_*_signature 字段同思路;区别:
12
+ * - Mobazha 用 protobuf + 多方签名(buyer + vendor + moderator)
13
+ * - 我们 MVP 用 JSON + HMAC(API key),实现简单。可日后升级到 Ed25519 非对称签名
14
+ */
15
+ import crypto from 'crypto';
16
+ import { generateId } from '../L0-1-database/schema.js';
17
+ export function initOrderChainSchema(db) {
18
+ db.exec(`
19
+ CREATE TABLE IF NOT EXISTS order_events (
20
+ id TEXT PRIMARY KEY,
21
+ order_id TEXT NOT NULL,
22
+ seq INTEGER NOT NULL, -- 0,1,2... 序号
23
+ prev_event_hash TEXT, -- 上一个事件的 event_hash(genesis = null)
24
+ event_hash TEXT NOT NULL, -- sha256(canonical_payload) 本事件的指纹
25
+ event_type TEXT NOT NULL, -- 'open' | 'transition' | 'cancel' ...
26
+ from_status TEXT,
27
+ to_status TEXT NOT NULL,
28
+ actor_id TEXT NOT NULL,
29
+ actor_role TEXT NOT NULL,
30
+ payload_json TEXT NOT NULL, -- 完整事件载荷(canonical 序列化)
31
+ signature TEXT NOT NULL, -- HMAC-SHA256(actor_api_key, canonical_payload)
32
+ signed_at TEXT NOT NULL,
33
+ created_at TEXT DEFAULT (datetime('now'))
34
+ )
35
+ `);
36
+ try {
37
+ db.exec('CREATE INDEX IF NOT EXISTS idx_oevt_order ON order_events(order_id, seq)');
38
+ }
39
+ catch { }
40
+ try {
41
+ db.exec('CREATE INDEX IF NOT EXISTS idx_oevt_actor ON order_events(actor_id, created_at DESC)');
42
+ }
43
+ catch { }
44
+ }
45
+ // canonical_payload — 递归 stringify,每层 key 字母序
46
+ // 修复 ultrareview bug_012:浅排序让嵌套对象用插入序,破坏"独立验证"承诺
47
+ // (已知问题:arbitration_ruling 时 liability_parties 是嵌套数组)
48
+ // 与 anchor-engine.ts:canonicalSerialize 算法一致 — 两个协议层产同样 hash
49
+ export function canonicalSerialize(obj) {
50
+ if (obj === null || obj === undefined)
51
+ return JSON.stringify(obj);
52
+ if (Array.isArray(obj))
53
+ return '[' + obj.map(canonicalSerialize).join(',') + ']';
54
+ if (typeof obj === 'object') {
55
+ const keys = Object.keys(obj).sort();
56
+ return '{' + keys.map(k => JSON.stringify(k) + ':' + canonicalSerialize(obj[k])).join(',') + '}';
57
+ }
58
+ return JSON.stringify(obj);
59
+ }
60
+ export function computeEventHash(canonicalPayload) {
61
+ return crypto.createHash('sha256').update(canonicalPayload).digest('hex');
62
+ }
63
+ export function computeEventSignature(canonicalPayload, actorApiKey) {
64
+ return crypto.createHmac('sha256', actorApiKey).update(canonicalPayload).digest('hex');
65
+ }
66
+ // 主入口 — 在 transaction 内调用,写一条事件到链尾
67
+ export function appendOrderEvent(db, args) {
68
+ const actor = db.prepare('SELECT api_key FROM users WHERE id = ?').get(args.actorId);
69
+ if (!actor)
70
+ throw new Error(`actor_not_found:${args.actorId}`);
71
+ const last = db.prepare(`SELECT seq, event_hash FROM order_events WHERE order_id = ? ORDER BY seq DESC LIMIT 1`).get(args.orderId);
72
+ const seq = last ? last.seq + 1 : 0;
73
+ const prevHash = last ? last.event_hash : null;
74
+ const signedAt = new Date().toISOString();
75
+ const payload = {
76
+ order_id: args.orderId,
77
+ seq,
78
+ prev_event_hash: prevHash,
79
+ event_type: args.eventType,
80
+ from_status: args.fromStatus,
81
+ to_status: args.toStatus,
82
+ actor_id: args.actorId,
83
+ actor_role: args.actorRole,
84
+ signed_at: signedAt,
85
+ ...(args.extra || {}),
86
+ };
87
+ const canon = canonicalSerialize(payload);
88
+ const eventHash = computeEventHash(canon);
89
+ const signature = computeEventSignature(canon, actor.api_key);
90
+ const id = generateId('oevt');
91
+ db.prepare(`INSERT INTO order_events
92
+ (id, order_id, seq, prev_event_hash, event_hash, event_type, from_status, to_status,
93
+ actor_id, actor_role, payload_json, signature, signed_at)
94
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(id, args.orderId, seq, prevHash, eventHash, args.eventType, args.fromStatus, args.toStatus, args.actorId, args.actorRole, canon, signature, signedAt);
95
+ return { id, seq, event_hash: eventHash };
96
+ }
97
+ // 验证整条链 — 仲裁时或任何审计场景可调
98
+ export function verifyOrderChain(db, orderId) {
99
+ const rows = db.prepare(`SELECT seq, prev_event_hash, event_hash, payload_json, signature, actor_id
100
+ FROM order_events WHERE order_id = ? ORDER BY seq ASC`).all(orderId);
101
+ if (rows.length === 0)
102
+ return { ok: false, total: 0, verified: 0, reason: 'empty_chain' };
103
+ // 修复 ultrareview bug_007:transition() 的 appendOrderEvent 是 try-catch 软失败
104
+ // (legacy actor 缺 api_key 等场景),但 order_state_history 仍会 commit。
105
+ // 检测:chain 行数应该至少等于 history 行数(chain ≥ history,因为可能有 open genesis 事件无 history)
106
+ // 不等就说明有 silent drop,链不完整 — UI 不应再显示"验证通过"
107
+ const histCount = db.prepare(`SELECT COUNT(*) as n FROM order_state_history WHERE order_id = ?`).get(orderId);
108
+ if (histCount && rows.length < histCount.n) {
109
+ return { ok: false, total: rows.length, verified: 0, reason: 'chain_incomplete', history_count: histCount.n };
110
+ }
111
+ let prevHash = null;
112
+ for (const r of rows) {
113
+ // 1. prev hash 链续
114
+ if (r.prev_event_hash !== prevHash) {
115
+ return { ok: false, total: rows.length, verified: r.seq, firstBrokenSeq: r.seq, reason: 'prev_hash_mismatch' };
116
+ }
117
+ // 2. event_hash = sha256(payload)
118
+ const reHash = computeEventHash(r.payload_json);
119
+ if (reHash !== r.event_hash) {
120
+ return { ok: false, total: rows.length, verified: r.seq, firstBrokenSeq: r.seq, reason: 'event_hash_mismatch' };
121
+ }
122
+ // 3. signature 用 actor api_key 验
123
+ const actor = db.prepare('SELECT api_key FROM users WHERE id = ?').get(r.actor_id);
124
+ if (!actor)
125
+ return { ok: false, total: rows.length, verified: r.seq, firstBrokenSeq: r.seq, reason: 'actor_not_found' };
126
+ const reSig = computeEventSignature(r.payload_json, actor.api_key);
127
+ if (reSig !== r.signature) {
128
+ return { ok: false, total: rows.length, verified: r.seq, firstBrokenSeq: r.seq, reason: 'signature_mismatch' };
129
+ }
130
+ prevHash = r.event_hash;
131
+ }
132
+ return { ok: true, total: rows.length, verified: rows.length };
133
+ }
134
+ export function getOrderChain(db, orderId) {
135
+ const rows = db.prepare(`SELECT seq, event_type, from_status, to_status, actor_id, actor_role, signed_at,
136
+ event_hash, prev_event_hash, signature, payload_json
137
+ FROM order_events WHERE order_id = ? ORDER BY seq ASC`).all(orderId);
138
+ return rows.map(r => ({
139
+ seq: r.seq,
140
+ event_type: r.event_type,
141
+ from_status: r.from_status || null,
142
+ to_status: r.to_status,
143
+ actor_id: r.actor_id,
144
+ actor_role: r.actor_role,
145
+ signed_at: r.signed_at,
146
+ event_hash: r.event_hash,
147
+ prev_event_hash: r.prev_event_hash || null,
148
+ signature: r.signature,
149
+ payload: (() => { try {
150
+ return JSON.parse(r.payload_json);
151
+ }
152
+ catch {
153
+ return {};
154
+ } })(),
155
+ }));
156
+ }
@@ -45,30 +45,32 @@ export const VALID_TRANSITIONS = {
45
45
  description: '卖家将包裹交给物流,提交发货证明'
46
46
  },
47
47
  // ── 物流揽收 ──────────────────────────────────────────────
48
+ // Phase 1(2026-05-27):seller 自选 self-fulfill 也可驱动这 3 个 transition(详见 docs/LOGISTICS-PHASING.md)
49
+ // Phase 2 logistics 市场启用后会按 order.fulfillment_mode 限定真实 logistics 角色
48
50
  'shipped→picked_up': {
49
- allowedRoles: ['logistics'],
51
+ allowedRoles: ['seller', 'logistics'],
50
52
  deadlineField: 'pickup_deadline',
51
53
  requiresEvidence: true,
52
54
  evidenceHint: '上传:揽收扫描记录 + 当前GPS位置',
53
55
  autoFaultState: 'fault_logistics',
54
56
  faultParty: 'logistics',
55
- description: '物流方确认已揽收,包裹完整'
57
+ description: '物流方(或 self-fulfill 卖家)确认已揽收,包裹完整'
56
58
  },
57
59
  // ── 运输中更新 ─────────────────────────────────────────────
58
60
  'picked_up→in_transit': {
59
- allowedRoles: ['logistics', 'system'],
61
+ allowedRoles: ['seller', 'logistics', 'system'],
60
62
  requiresEvidence: false,
61
- description: '包裹开始运输(可自动触发)'
63
+ description: '包裹开始运输(可自动触发;self-fulfill 时由 seller 主动)'
62
64
  },
63
65
  // ── 物流投递 ──────────────────────────────────────────────
64
66
  'in_transit→delivered': {
65
- allowedRoles: ['logistics'],
67
+ allowedRoles: ['seller', 'logistics'],
66
68
  deadlineField: 'delivery_deadline',
67
69
  requiresEvidence: true,
68
70
  evidenceHint: '上传:投递照片(含门牌号)+ 收件人签收/GPS坐标',
69
71
  autoFaultState: 'fault_logistics',
70
72
  faultParty: 'logistics',
71
- description: '物流确认投递完成,提交投递证明'
73
+ description: '物流方(或 self-fulfill 卖家)确认投递完成,提交投递证明'
72
74
  },
73
75
  // ── 买家确认 ──────────────────────────────────────────────
74
76
  'delivered→confirmed': {
@@ -116,18 +118,50 @@ export const VALID_TRANSITIONS = {
116
118
  evidenceHint: '上传:收到货物的照片 + 问题描述',
117
119
  description: '买家收货后发现货不对版或货损'
118
120
  },
119
- // ── 仲裁结束 ──────────────────────────────────────────────
121
+ // ── 仲裁结束(旧粗粒度,保留兼容)─────────────────────────
120
122
  'disputed→completed': {
121
- allowedRoles: ['arbitrator', 'system'], // system = 超时自动裁定
123
+ allowedRoles: ['arbitrator', 'system'],
122
124
  requiresEvidence: false,
123
125
  evidenceHint: '上传仲裁裁定书',
124
- description: '仲裁员完成裁定,释放资金给卖家'
126
+ description: '仲裁员完成裁定,释放资金给卖家(旧粗粒度)'
125
127
  },
126
128
  'disputed→cancelled': {
127
- allowedRoles: ['arbitrator', 'system'], // system = 超时自动裁定
129
+ allowedRoles: ['arbitrator', 'system'],
128
130
  requiresEvidence: false,
129
131
  evidenceHint: '上传仲裁裁定书',
130
- description: '仲裁裁定取消交易,全额退款给买家'
132
+ description: '仲裁裁定取消交易,全额退款给买家(旧粗粒度)'
133
+ },
134
+ // ── 仲裁结案细化(模块 B)─────────────────────────────────
135
+ 'disputed→resolved_for_seller': {
136
+ allowedRoles: ['arbitrator', 'system'],
137
+ requiresEvidence: false,
138
+ description: '仲裁裁定卖家胜诉,资金释放给卖家'
139
+ },
140
+ 'disputed→refunded_partial': {
141
+ allowedRoles: ['arbitrator', 'system'],
142
+ requiresEvidence: false,
143
+ description: '仲裁裁定部分退款给买家'
144
+ },
145
+ 'disputed→refunded_full': {
146
+ allowedRoles: ['arbitrator', 'system'],
147
+ requiresEvidence: false,
148
+ description: '仲裁裁定全额退款给买家,订单作废'
149
+ },
150
+ 'disputed→dispute_dismissed': {
151
+ allowedRoles: ['arbitrator', 'system'],
152
+ requiresEvidence: false,
153
+ description: '争议被驳回(无效)'
154
+ },
155
+ // ── 超时通用兜底(system 触发)─────────────────────────────
156
+ 'paid→expired': {
157
+ allowedRoles: ['system'],
158
+ requiresEvidence: false,
159
+ description: '订单超时自动失败(通用兜底)'
160
+ },
161
+ 'accepted→expired': {
162
+ allowedRoles: ['system'],
163
+ requiresEvidence: false,
164
+ description: '订单超时自动失败(通用兜底)'
131
165
  },
132
166
  // ── 正常完成 ──────────────────────────────────────────────
133
167
  'confirmed→completed': {
@@ -184,7 +218,7 @@ export const VALID_TRANSITIONS = {
184
218
  description: '买家违约:资金转给卖家,扣除买家质押'
185
219
  },
186
220
  };
187
- /** 给定当前状态,返回「当前应该由谁来操作」 */
221
+ /** 给定当前状态,返回「当前应该由谁来操作」(market-fulfill 默认表) */
188
222
  export const CURRENT_RESPONSIBLE = {
189
223
  created: 'buyer', // 等买家付款
190
224
  paid: 'seller', // 等卖家接单
@@ -195,3 +229,10 @@ export const CURRENT_RESPONSIBLE = {
195
229
  delivered: 'buyer', // 等买家确认
196
230
  disputed: 'arbitrator', // 等仲裁处理
197
231
  };
232
+ /** Phase 1 self-fulfill 覆盖表:seller 一人承担 shipped/picked_up/in_transit 全程 */
233
+ export const CURRENT_RESPONSIBLE_SELF_FULFILL = {
234
+ ...CURRENT_RESPONSIBLE,
235
+ shipped: 'seller',
236
+ picked_up: 'seller',
237
+ in_transit: 'seller',
238
+ };
@@ -131,6 +131,19 @@ const ROLES = {
131
131
  key_rights: ['每笔成交自动获得 5% 物流费', '违约赔付有责任上限'],
132
132
  key_duties: ['48h 内揽收', '7天内完成投递', '必须上传揽收和投递证明'],
133
133
  },
134
+ reviewer: {
135
+ label: '测评员',
136
+ description: '结构化商品测评。通过 trial_campaign 申请试用免单,收货后写真实评价。',
137
+ stake_required: false,
138
+ entry_action: 'webaz_register(role=reviewer)',
139
+ workflow: [
140
+ 'webaz_claim_verify(action=apply, campaign_id) → 申请测评免单',
141
+ 'webaz_place_order(...) → 中签后正常下单(卖家承担费用)',
142
+ 'webaz_update_order(action=confirm, evidence="评价文+照片") → 收货后提交真实评价',
143
+ ],
144
+ key_rights: ['通过试用获得免费样品(中签后)', '真实评价获得声誉加成', '与其他测评员协议级竞争公平'],
145
+ key_duties: ['评价必须基于真实使用体验', '不得评价自己关联的卖家', '提交评价的 deadline 跟普通买家一致'],
146
+ },
134
147
  arbitrator: {
135
148
  label: '仲裁员',
136
149
  description: '处理争议案件,做出裁定。需要公正客观,裁定结果永久上链。',
@@ -139,7 +152,7 @@ const ROLES = {
139
152
  workflow: [
140
153
  'webaz_dispute(action=list_open) → 查看所有待处理争议',
141
154
  'webaz_dispute(action=view, dispute_id) → 查看争议详情和双方证据',
142
- 'webaz_dispute(action=arbitrate, ruling=refund_buyer|release_seller|partial_refund, ruling_reason) → 做出裁定',
155
+ 'webaz_dispute(action=arbitrate, ruling=refund_buyer|release_seller|partial_refund, ruling_reason) → 做出裁定 ⚠️ 需 PWA + Passkey(Iron-Rule)',
143
156
  ],
144
157
  key_rights: ['查看所有争议详情', '做出三种裁定选择', '仲裁超时后系统自动裁定(保护所有人)'],
145
158
  key_duties: ['120h 内完成裁定(否则系统自动退款买家)', '裁定必须提供理由(永久记录)', '保持公正客观'],
@@ -7,7 +7,7 @@ export function authenticate(db, apiKey) {
7
7
  if (!apiKey || apiKey === '')
8
8
  return null;
9
9
  return db
10
- .prepare('SELECT id, name, role, roles, api_key FROM users WHERE api_key = ?')
10
+ .prepare('SELECT id, name, handle, role, roles, api_key FROM users WHERE api_key = ?')
11
11
  .get(apiKey);
12
12
  }
13
13
  export function requireAuth(db, apiKey) {