@seasonkoh/webaz 0.1.0

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.
@@ -0,0 +1,1062 @@
1
+ /**
2
+ * L1-1 · MCP Server 核心
3
+ * 把 WebAZ暴露给所有支持 MCP 的 AI Agent(Claude、GPT 等)
4
+ *
5
+ * 包含工具:
6
+ * webaz_info L1-2 协议说明(任何 Agent 可调用,了解这是什么)
7
+ * webaz_register 注册账户,获取 api_key
8
+ * webaz_search L1-2 搜索商品
9
+ * webaz_list_product L1-5 卖家上架商品
10
+ * webaz_place_order L1-3 买家下单
11
+ * webaz_update_order L1-6 更新订单状态(发货/揽收/投递/确认/争议)
12
+ * webaz_get_status L1-4 查询订单状态和历史
13
+ * webaz_wallet 查看钱包余额
14
+ */
15
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
16
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
17
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
18
+ import { initDatabase, generateId } from '../../layer0-foundation/L0-1-database/schema.js';
19
+ import { transition, getOrderStatus, initSystemUser, } from '../../layer0-foundation/L0-2-state-machine/engine.js';
20
+ import { initDisputeSchema, createDispute, respondToDispute, arbitrateDispute, getDisputeDetails, getOrderDispute, getOpenDisputes, } from '../../layer3-trust/L3-1-dispute-engine/dispute-engine.js';
21
+ import { initNotificationSchema, notifyTransition, getNotifications, getUnreadCount, markRead, } from '../../layer2-business/L2-6-notifications/notification-engine.js';
22
+ import { initSkillSchema, publishSkill, listSkills, getMySkills, subscribeSkill, unsubscribeSkill, getMySubscriptions, formatSkillForAgent, shouldAutoAccept, SKILL_TYPE_META, } from '../../layer4-economics/L4-4-skill-market/skill-engine.js';
23
+ import { initReputationSchema, recordOrderReputation, recordDisputeReputation, getReputation, getSearchBoost, getStakeDiscount, } from '../../layer4-economics/L4-3-reputation/reputation-engine.js';
24
+ import { generateManifest, getManifestSummary, MANIFEST_URI, } from '../../layer0-foundation/L0-5-manifest/manifest.js';
25
+ import { requireAuth } from './auth.js';
26
+ // ─── 初始化 ──────────────────────────────────────────────────
27
+ const db = initDatabase();
28
+ initSystemUser(db);
29
+ initDisputeSchema(db);
30
+ initNotificationSchema(db);
31
+ initSkillSchema(db);
32
+ initReputationSchema(db);
33
+ // ─── 工具定义(Agent 读这些来理解如何使用协议)────────────────
34
+ const TOOLS = [
35
+ {
36
+ name: 'webaz_info',
37
+ description: `获取 WebAZ的说明和使用指南。
38
+ 这是新 Agent 接入协议时应该调用的第一个工具。
39
+ 返回:协议简介、所有可用工具、每个角色的职责和操作流程。
40
+ 无需任何参数,无需身份验证。`,
41
+ inputSchema: {
42
+ type: 'object',
43
+ properties: {},
44
+ },
45
+ },
46
+ {
47
+ name: 'webaz_register',
48
+ description: `在 WebAZ中注册新账户。
49
+ 注册后获得唯一的 api_key,后续所有操作都需要这个 key。
50
+ 请将 api_key 安全保存,它代表你在协议中的身份。
51
+
52
+ 角色说明:
53
+ - buyer(买家):浏览商品、下单、确认收货
54
+ - seller(卖家):上架商品、接单、发货
55
+ - logistics(物流):揽收包裹、更新运输状态、确认投递
56
+ - reviewer(测评员):对商品进行结构化测评
57
+ - arbitrator(仲裁员):处理争议,做出裁定`,
58
+ inputSchema: {
59
+ type: 'object',
60
+ properties: {
61
+ name: { type: 'string', description: '你的名字或店铺名称' },
62
+ role: {
63
+ type: 'string',
64
+ enum: ['buyer', 'seller', 'logistics', 'reviewer', 'arbitrator'],
65
+ description: '你在协议中的角色',
66
+ },
67
+ initial_balance: {
68
+ type: 'number',
69
+ description: '初始模拟余额(测试用,默认 1000 WAZ)',
70
+ },
71
+ },
72
+ required: ['name', 'role'],
73
+ },
74
+ },
75
+ {
76
+ name: 'webaz_search',
77
+ description: `搜索 WebAZ中的在售商品。
78
+ 无需登录即可搜索,买家或 Agent 可以自由浏览。
79
+ 返回匹配的商品列表,包含价格、卖家信息、库存数量。`,
80
+ inputSchema: {
81
+ type: 'object',
82
+ properties: {
83
+ query: { type: 'string', description: '搜索关键词(商品名称或描述)' },
84
+ category: { type: 'string', description: '商品分类过滤(可选)' },
85
+ max_price: { type: 'number', description: '最高价格过滤(可选)' },
86
+ limit: { type: 'number', description: '返回数量上限,默认 10' },
87
+ },
88
+ },
89
+ },
90
+ {
91
+ name: 'webaz_list_product',
92
+ description: `卖家上架新商品到 WebAZ。
93
+ 需要卖家角色的 api_key。
94
+ 上架时系统会自动计算建议质押金额(商品价格的 15%),用于保障买家权益。
95
+ 商品上架后买家可以搜索到并下单。`,
96
+ inputSchema: {
97
+ type: 'object',
98
+ properties: {
99
+ api_key: { type: 'string', description: '卖家的 api_key' },
100
+ title: { type: 'string', description: '商品名称' },
101
+ description: { type: 'string', description: '商品详细描述' },
102
+ price: { type: 'number', description: '商品价格(WAZ)' },
103
+ stock: { type: 'number', description: '库存数量,默认 1' },
104
+ category: { type: 'string', description: '商品分类(可选)' },
105
+ },
106
+ required: ['api_key', 'title', 'description', 'price'],
107
+ },
108
+ },
109
+ {
110
+ name: 'webaz_place_order',
111
+ description: `买家下单购买商品。
112
+ 需要买家角色的 api_key。
113
+ 下单后资金自动进入协议托管,卖家需在 24 小时内接单。
114
+ 如卖家超时不接单,协议自动退款并记录违约。`,
115
+ inputSchema: {
116
+ type: 'object',
117
+ properties: {
118
+ api_key: { type: 'string', description: '买家的 api_key' },
119
+ product_id: { type: 'string', description: '要购买的商品 ID(从 webaz_search 获得)' },
120
+ quantity: { type: 'number', description: '购买数量,默认 1' },
121
+ shipping_address: { type: 'string', description: '收货地址' },
122
+ notes: { type: 'string', description: '给卖家的备注(可选)' },
123
+ promoter_api_key: {
124
+ type: 'string',
125
+ description: '推荐人的 api_key(可选,如果是通过推荐链接来的)',
126
+ },
127
+ },
128
+ required: ['api_key', 'product_id', 'shipping_address'],
129
+ },
130
+ },
131
+ {
132
+ name: 'webaz_update_order',
133
+ description: `更新订单状态(每个角色只能执行自己的操作)。
134
+
135
+ 卖家可执行的 action:
136
+ - accept:接受订单(付款后 24h 内必须执行)
137
+ - ship:确认发货(需要物流单号,接单后按承诺时间内执行)
138
+
139
+ 物流方可执行的 action:
140
+ - pickup:确认揽收包裹(发货后 48h 内)
141
+ - transit:更新为运输中
142
+ - deliver:确认投递完成(需要投递证明描述)
143
+
144
+ 买家可执行的 action:
145
+ - confirm:确认收货,触发资金结算
146
+ - dispute:发起争议(需要说明原因,会冻结资金等待仲裁)
147
+
148
+ 超过截止时间未操作,协议会自动判定该方违约。`,
149
+ inputSchema: {
150
+ type: 'object',
151
+ properties: {
152
+ api_key: { type: 'string', description: '操作者的 api_key' },
153
+ order_id: { type: 'string', description: '订单 ID' },
154
+ action: {
155
+ type: 'string',
156
+ enum: ['accept', 'ship', 'pickup', 'transit', 'deliver', 'confirm', 'dispute'],
157
+ description: '要执行的操作',
158
+ },
159
+ notes: { type: 'string', description: '操作说明(如物流单号、争议原因等)' },
160
+ evidence_description: {
161
+ type: 'string',
162
+ description: '证据描述(发货/揽收/投递时建议提供,争议时必须提供)',
163
+ },
164
+ },
165
+ required: ['api_key', 'order_id', 'action'],
166
+ },
167
+ },
168
+ {
169
+ name: 'webaz_get_status',
170
+ description: `查询订单的当前状态、完整历史记录和当前责任方。
171
+ 需要参与该订单的 api_key(买家、卖家或物流方均可查询)。
172
+ 返回:当前状态、状态历史(谁在什么时候做了什么)、当前应该由谁操作、截止时间。`,
173
+ inputSchema: {
174
+ type: 'object',
175
+ properties: {
176
+ api_key: { type: 'string', description: '查询者的 api_key' },
177
+ order_id: { type: 'string', description: '订单 ID' },
178
+ },
179
+ required: ['api_key', 'order_id'],
180
+ },
181
+ },
182
+ {
183
+ name: 'webaz_wallet',
184
+ description: `查看自己的钱包余额和收益统计。
185
+ 返回:可用余额、质押中金额、托管中金额、累计总收益。`,
186
+ inputSchema: {
187
+ type: 'object',
188
+ properties: {
189
+ api_key: { type: 'string', description: '你的 api_key' },
190
+ },
191
+ required: ['api_key'],
192
+ },
193
+ },
194
+ {
195
+ name: 'webaz_notifications',
196
+ description: `查询当前用户的通知消息(L2-6 通知系统)。
197
+ Agent 应定期调用此工具检查是否有待处理的订单事件。
198
+ 每次有状态变更(新订单/发货/争议等),相关参与方都会收到通知。`,
199
+ inputSchema: {
200
+ type: 'object',
201
+ properties: {
202
+ api_key: { type: 'string', description: '你的 api_key' },
203
+ unread: { type: 'boolean', description: '只返回未读通知(默认 false)' },
204
+ mark_read: { type: 'boolean', description: '调用后自动标为已读(默认 false)' },
205
+ },
206
+ required: ['api_key'],
207
+ },
208
+ },
209
+ {
210
+ name: 'webaz_dispute',
211
+ description: `管理争议流程(L3 争议系统)。
212
+
213
+ 当买家认为货不对版、货损、卖家欺诈时,可通过 webaz_update_order action=dispute 发起争议,
214
+ 然后用本工具进行后续操作。
215
+
216
+ 协议保障机制(无需人工干预):
217
+ - 被诉方有 48 小时提交反驳证据,否则协议自动判发起方胜诉
218
+ - 仲裁员有 120 小时做出裁定,否则协议默认退款给买家
219
+ - 裁定一旦执行,资金立即自动分配,无法撤销
220
+
221
+ action 说明:
222
+ - view:查看争议详情(任何参与方可调用)
223
+ - list_open:查看所有待处理争议(仅仲裁员)
224
+ - respond:被诉方提交反驳证据(必须在 48h 截止时间前)
225
+ - arbitrate:仲裁员做出裁定并执行资金处置
226
+
227
+ ruling 裁定选项(arbitrate 时使用):
228
+ - refund_buyer:全额退款给买家,扣押卖家部分保证金
229
+ - release_seller:资金释放给卖家(卖家胜诉)
230
+ - partial_refund:部分退款(需指定 refund_amount)`,
231
+ inputSchema: {
232
+ type: 'object',
233
+ properties: {
234
+ api_key: { type: 'string', description: '操作者的 api_key' },
235
+ action: {
236
+ type: 'string',
237
+ enum: ['view', 'list_open', 'respond', 'arbitrate'],
238
+ description: '要执行的操作',
239
+ },
240
+ dispute_id: { type: 'string', description: '争议 ID(respond/arbitrate 时必填,view 时与 order_id 二选一)' },
241
+ order_id: { type: 'string', description: '订单 ID(view 时可替代 dispute_id)' },
242
+ notes: { type: 'string', description: '回应说明 / 反驳理由(respond 时填写)' },
243
+ evidence_description: { type: 'string', description: '证据描述(respond 时建议填写)' },
244
+ ruling: {
245
+ type: 'string',
246
+ enum: ['refund_buyer', 'release_seller', 'partial_refund'],
247
+ description: '裁定结果(arbitrate 时必填)',
248
+ },
249
+ refund_amount: { type: 'number', description: '部分退款金额,仅 ruling=partial_refund 时使用' },
250
+ ruling_reason: { type: 'string', description: '裁定理由(arbitrate 时必填,将永久记录在链上)' },
251
+ },
252
+ required: ['api_key', 'action'],
253
+ },
254
+ },
255
+ {
256
+ name: 'webaz_skill',
257
+ description: `L4-4 Skill 市场——让卖家发布可复用的 Agent 能力插件,买家 Agent 一键订阅。
258
+
259
+ Skill 是解决冷启动的核心机制:现有 Amazon/Shopify 卖家零成本接入 WebAZ,
260
+ 买家 Agent 订阅后可自动发现、优先呈现这些卖家的商品,成交后 Skill 发布者获得推荐佣金。
261
+
262
+ Skill 类型(skill_type):
263
+ - catalog_sync 目录同步:将外部店铺(Amazon/Shopify/自定义)接入 WebAZ 搜索,买家订阅后优先看到
264
+ - auto_accept 自动接单:买家下单后立即接受,无需等待(config: min_amount, max_amount, max_daily_orders)
265
+ - price_negotiation 价格协商:允许 Agent 在限定范围内议价(config: max_discount_pct, min_quantity)
266
+ - quality_guarantee 质量承诺:额外质押保证金,问题可额外赔偿(config: guarantee_amount, coverage_days)
267
+ - instant_ship 极速发货:承诺 24h 内发货(config: ship_within_hours)
268
+
269
+ action 说明:
270
+ - list 浏览 Skill 市场(无需登录)
271
+ - publish 发布新 Skill(仅卖家)
272
+ - subscribe 订阅 Skill(买家订阅后可获得额外好处)
273
+ - unsubscribe 取消订阅
274
+ - my_skills 查看自己发布的 Skill(卖家)
275
+ - my_subs 查看自己订阅的 Skill(买家)`,
276
+ inputSchema: {
277
+ type: 'object',
278
+ properties: {
279
+ api_key: { type: 'string', description: '你的 api_key(list 时可省略)' },
280
+ action: {
281
+ type: 'string',
282
+ enum: ['list', 'publish', 'subscribe', 'unsubscribe', 'my_skills', 'my_subs'],
283
+ description: '要执行的操作',
284
+ },
285
+ // list 过滤参数
286
+ skill_type: {
287
+ type: 'string',
288
+ enum: ['catalog_sync', 'auto_accept', 'price_negotiation', 'quality_guarantee', 'instant_ship'],
289
+ description: '过滤 Skill 类型(list 时可选)',
290
+ },
291
+ query: { type: 'string', description: '关键词搜索(list 时可选)' },
292
+ // publish 参数
293
+ name: { type: 'string', description: 'Skill 名称(publish 时必填)' },
294
+ description: { type: 'string', description: 'Skill 详细描述(publish 时必填)' },
295
+ category: { type: 'string', description: '分类(publish 时可选)' },
296
+ config: {
297
+ type: 'object',
298
+ description: 'Skill 配置(publish 时可选,如 auto_accept 需填 max_daily_orders)',
299
+ },
300
+ // subscribe 参数
301
+ skill_id: { type: 'string', description: 'Skill ID(subscribe/unsubscribe 时必填)' },
302
+ },
303
+ required: ['action'],
304
+ },
305
+ },
306
+ ];
307
+ // ─── 工具处理函数 ─────────────────────────────────────────────
308
+ function handleInfo() {
309
+ const summary = getManifestSummary();
310
+ const stats = (() => {
311
+ try {
312
+ const users = db.prepare("SELECT COUNT(*) as n FROM users WHERE id != 'sys_protocol'").get().n;
313
+ const products = db.prepare("SELECT COUNT(*) as n FROM products WHERE status='active'").get().n;
314
+ const completed = db.prepare("SELECT COUNT(*) as n FROM orders WHERE status='completed'").get().n;
315
+ return { participants: users, active_products: products, completed_orders: completed };
316
+ }
317
+ catch {
318
+ return null;
319
+ }
320
+ })();
321
+ return {
322
+ ...summary,
323
+ description: 'WebAZ 是一个去中心化商业协议。每笔交易通过状态机流转,每个状态转移都需要对应责任方的操作证明。任何超时未操作,协议自动判定该方违约并执行处置。',
324
+ live_stats: stats,
325
+ roles: {
326
+ buyer: '下单、付款、确认收货或发起争议',
327
+ seller: '上架商品、接单、按时发货(质押保证金确保履约)',
328
+ logistics: '揽收包裹、更新运输状态、确认投递(获得 5% 物流费)',
329
+ arbitrator: '处理争议,做出裁定(120h 内必须裁定,否则系统自动退款买家)',
330
+ },
331
+ quick_start: {
332
+ seller: '1. webaz_register(role=seller) → 2. webaz_list_product() → 3. 等通知 webaz_update_order(accept/ship)',
333
+ buyer: '1. webaz_register(role=buyer) → 2. webaz_search() → 3. webaz_place_order() → 4. webaz_update_order(confirm)',
334
+ logistics: '1. webaz_register(role=logistics) → 2. webaz_update_order(pickup) → webaz_update_order(deliver)',
335
+ },
336
+ available_tools: TOOLS.map((t) => ({ name: t.name, description: t.description.split('\n')[0] })),
337
+ full_manifest: `读取 MCP Resource "${MANIFEST_URI}" 获取完整协议规范(状态机/经济模型/争议系统/Skill 市场/声誉系统)`,
338
+ };
339
+ }
340
+ function handleRegister(args) {
341
+ const name = args.name;
342
+ const role = args.role;
343
+ const initialBalance = args.initial_balance ?? 1000;
344
+ const validRoles = ['buyer', 'seller', 'logistics', 'reviewer', 'arbitrator'];
345
+ if (!validRoles.includes(role)) {
346
+ return { error: `无效角色:${role}。可选:${validRoles.join(', ')}` };
347
+ }
348
+ const id = generateId('usr');
349
+ const apiKey = generateId('key');
350
+ db.prepare('INSERT INTO users (id, name, role, api_key) VALUES (?, ?, ?, ?)').run(id, name, role, apiKey);
351
+ db.prepare('INSERT INTO wallets (user_id, balance) VALUES (?, ?)').run(id, initialBalance);
352
+ return {
353
+ success: true,
354
+ message: `注册成功!请妥善保管你的 api_key,这是你在协议中的唯一身份凭证。`,
355
+ user_id: id,
356
+ name,
357
+ role,
358
+ api_key: apiKey,
359
+ initial_balance: initialBalance,
360
+ next_step: role === 'seller'
361
+ ? '现在可以用 webaz_list_product 上架你的第一件商品'
362
+ : role === 'buyer'
363
+ ? '现在可以用 webaz_search 搜索商品'
364
+ : '等待订单分配给你',
365
+ };
366
+ }
367
+ function handleSearch(args) {
368
+ const query = args.query ?? '';
369
+ const category = args.category;
370
+ const maxPrice = args.max_price;
371
+ const limit = args.limit ?? 10;
372
+ let sql = `
373
+ SELECT p.*, u.name as seller_name
374
+ FROM products p
375
+ JOIN users u ON p.seller_id = u.id
376
+ WHERE p.status = 'active' AND p.stock > 0
377
+ `;
378
+ const params = [];
379
+ if (query) {
380
+ sql += ` AND (p.title LIKE ? OR p.description LIKE ?)`;
381
+ params.push(`%${query}%`, `%${query}%`);
382
+ }
383
+ if (category) {
384
+ sql += ` AND p.category = ?`;
385
+ params.push(category);
386
+ }
387
+ if (maxPrice !== undefined) {
388
+ sql += ` AND p.price <= ?`;
389
+ params.push(maxPrice);
390
+ }
391
+ sql += ` ORDER BY p.created_at DESC LIMIT ?`;
392
+ params.push(limit);
393
+ const products = db.prepare(sql).all(...params);
394
+ if (products.length === 0) {
395
+ return { found: 0, message: '没有找到匹配的商品', products: [] };
396
+ }
397
+ const sorted = products
398
+ .map((p) => {
399
+ const boost = getSearchBoost(db, p.seller_id);
400
+ const rep = db.prepare('SELECT total_points, level FROM reputation_scores WHERE user_id = ?').get(p.seller_id);
401
+ return { ...p, _boost: boost, _rep_level: rep?.level ?? 'new', _rep_points: rep?.total_points ?? 0 };
402
+ })
403
+ .sort((a, b) => b._boost - a._boost);
404
+ return {
405
+ found: sorted.length,
406
+ products: sorted.map((p) => {
407
+ const levelMeta = { new: '', trusted: '⭐', quality: '🌟', star: '💫', legend: '🔥' };
408
+ const badge = levelMeta[p._rep_level] ?? '';
409
+ return {
410
+ id: p.id,
411
+ title: p.title,
412
+ description: p.description,
413
+ price: `${p.price} WAZ`,
414
+ stock: p.stock,
415
+ category: p.category,
416
+ seller: badge ? `${badge} ${p.seller_name}` : p.seller_name,
417
+ seller_id: p.seller_id,
418
+ seller_reputation: p._rep_level !== 'new' ? `${badge} ${['', '可信', '优质', '明星', '传奇'][['new', 'trusted', 'quality', 'star', 'legend'].indexOf(p._rep_level)]}(${p._rep_points}分)` : undefined,
419
+ };
420
+ }),
421
+ };
422
+ }
423
+ function handleListProduct(args) {
424
+ const auth = requireAuth(db, args.api_key);
425
+ if ('error' in auth)
426
+ return auth;
427
+ const { user } = auth;
428
+ if (user.role !== 'seller') {
429
+ return { error: `只有 seller 角色可以上架商品,你的角色是:${user.role}` };
430
+ }
431
+ const price = args.price;
432
+ const stakeDiscount = getStakeDiscount(db, user.id);
433
+ const stakeRate = Math.max(0.05, 0.15 - stakeDiscount); // 最低 5%,声誉越高折扣越大
434
+ const stakeAmount = Math.round(price * stakeRate * 100) / 100;
435
+ // 检查卖家是否有足够余额质押
436
+ const wallet = db
437
+ .prepare('SELECT * FROM wallets WHERE user_id = ?')
438
+ .get(user.id);
439
+ if (wallet.balance < stakeAmount) {
440
+ return {
441
+ error: `余额不足:上架此商品需要质押 ${stakeAmount} WAZ,你的余额为 ${wallet.balance} WAZ`,
442
+ };
443
+ }
444
+ const id = generateId('prd');
445
+ db.prepare(`
446
+ INSERT INTO products (id, seller_id, title, description, price, stock, category, stake_amount)
447
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
448
+ `).run(id, user.id, args.title, args.description, price, args.stock ?? 1, args.category ?? null, stakeAmount);
449
+ // 扣除质押金额
450
+ db.prepare(`
451
+ UPDATE wallets SET balance = balance - ?, staked = staked + ? WHERE user_id = ?
452
+ `).run(stakeAmount, stakeAmount, user.id);
453
+ const rep = getReputation(db, user.id);
454
+ return {
455
+ success: true,
456
+ product_id: id,
457
+ title: args.title,
458
+ price: `${price} WAZ`,
459
+ stake_locked: `${stakeAmount} WAZ(质押保证金,交易完成后返还)`,
460
+ stake_rate: stakeDiscount > 0 ? `${(stakeRate * 100).toFixed(0)}%(声誉折扣 -${(stakeDiscount * 100).toFixed(0)}%,原 15%)` : '15%',
461
+ reputation_level: rep.level.label,
462
+ status: 'active(买家现在可以搜索到这件商品)',
463
+ };
464
+ }
465
+ function handlePlaceOrder(args) {
466
+ const auth = requireAuth(db, args.api_key);
467
+ if ('error' in auth)
468
+ return auth;
469
+ const { user } = auth;
470
+ if (user.role !== 'buyer') {
471
+ return { error: `只有 buyer 角色可以下单,你的角色是:${user.role}` };
472
+ }
473
+ const product = db
474
+ .prepare("SELECT p.*, u.name as seller_name, u.id as seller_uid FROM products p JOIN users u ON p.seller_id = u.id WHERE p.id = ? AND p.status = 'active'")
475
+ .get(args.product_id);
476
+ if (!product) {
477
+ return { error: `商品不存在或已下架:${args.product_id}` };
478
+ }
479
+ const quantity = args.quantity ?? 1;
480
+ if (product.stock < quantity) {
481
+ return { error: `库存不足:当前库存 ${product.stock},你要购买 ${quantity}` };
482
+ }
483
+ const totalAmount = product.price * quantity;
484
+ const wallet = db
485
+ .prepare('SELECT * FROM wallets WHERE user_id = ?')
486
+ .get(user.id);
487
+ if (wallet.balance < totalAmount) {
488
+ return {
489
+ error: `余额不足:订单金额 ${totalAmount} WAZ,你的余额 ${wallet.balance} WAZ`,
490
+ };
491
+ }
492
+ const now = new Date();
493
+ const orderId = generateId('ord');
494
+ // 找推荐人
495
+ let promoterId = null;
496
+ if (args.promoter_api_key) {
497
+ const promoter = db
498
+ .prepare('SELECT id FROM users WHERE api_key = ?')
499
+ .get(args.promoter_api_key);
500
+ if (promoter)
501
+ promoterId = promoter.id;
502
+ }
503
+ db.prepare(`
504
+ INSERT INTO orders (
505
+ id, product_id, buyer_id, seller_id, promoter_id,
506
+ quantity, unit_price, total_amount, escrow_amount,
507
+ status, shipping_address, notes,
508
+ pay_deadline, accept_deadline, ship_deadline,
509
+ pickup_deadline, delivery_deadline, confirm_deadline
510
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'created', ?, ?, ?, ?, ?, ?, ?, ?)
511
+ `).run(orderId, product.id, user.id, product.seller_uid, promoterId, quantity, product.price, totalAmount, totalAmount, args.shipping_address, args.notes ?? null, addHours(now, 24), // 买家 24h 内必须付款
512
+ addHours(now, 48), // 卖家 24h 内接单
513
+ addHours(now, 120), // 卖家 72h 内发货
514
+ addHours(now, 168), // 物流 48h 内揽收
515
+ addHours(now, 336), // 物流 7 天内投递
516
+ addHours(now, 408));
517
+ // 扣除库存
518
+ db.prepare('UPDATE products SET stock = stock - ? WHERE id = ?').run(quantity, product.id);
519
+ // 模拟"付款":锁定买家余额
520
+ db.prepare(`
521
+ UPDATE wallets SET balance = balance - ?, escrowed = escrowed + ? WHERE user_id = ?
522
+ `).run(totalAmount, totalAmount, user.id);
523
+ // 直接进入 paid 状态(Phase 0 模拟支付)
524
+ transition(db, orderId, 'paid', user.id, [], '模拟支付完成,资金已托管');
525
+ notifyTransition(db, orderId, 'created', 'paid');
526
+ // 检查卖家是否开启了 auto_accept Skill,若是则自动接单
527
+ let autoAccepted = false;
528
+ if (shouldAutoAccept(db, orderId)) {
529
+ const sysUser = db.prepare("SELECT id FROM users WHERE id = 'sys_protocol'").get();
530
+ const acceptResult = transition(db, orderId, 'accepted', sysUser.id, [], '⚡ auto_accept Skill 自动接单');
531
+ if (acceptResult.success) {
532
+ notifyTransition(db, orderId, 'paid', 'accepted');
533
+ autoAccepted = true;
534
+ }
535
+ }
536
+ return {
537
+ success: true,
538
+ order_id: orderId,
539
+ product: product.title,
540
+ seller: product.seller_name,
541
+ quantity,
542
+ total_amount: `${totalAmount} WAZ(已托管,等待交易完成后自动结算)`,
543
+ status: autoAccepted ? 'accepted' : 'paid',
544
+ auto_accepted: autoAccepted || undefined,
545
+ next: autoAccepted
546
+ ? '⚡ 卖家已开启自动接单,订单已立即接受!等待卖家发货。'
547
+ : '等待卖家 24 小时内接单。卖家超时不接单将自动退款。',
548
+ track: `用 webaz_get_status 查看订单进展`,
549
+ };
550
+ }
551
+ function handleUpdateOrder(args) {
552
+ const auth = requireAuth(db, args.api_key);
553
+ if ('error' in auth)
554
+ return auth;
555
+ const { user } = auth;
556
+ const orderId = args.order_id;
557
+ const action = args.action;
558
+ const notes = args.notes ?? '';
559
+ const evidenceDesc = args.evidence_description ?? '';
560
+ // 验证订单存在且该用户是参与方
561
+ const order = db
562
+ .prepare('SELECT * FROM orders WHERE id = ?')
563
+ .get(orderId);
564
+ if (!order)
565
+ return { error: `订单不存在:${orderId}` };
566
+ const isParticipant = order.buyer_id === user.id ||
567
+ order.seller_id === user.id ||
568
+ order.logistics_id === user.id;
569
+ if (!isParticipant && user.role !== 'arbitrator') {
570
+ return { error: '你不是这笔订单的参与方,无法操作' };
571
+ }
572
+ // 如果是物流首次操作,绑定物流方
573
+ if ((action === 'pickup' || action === 'transit') &&
574
+ order.logistics_id === null &&
575
+ user.role === 'logistics') {
576
+ db.prepare('UPDATE orders SET logistics_id = ? WHERE id = ?').run(user.id, orderId);
577
+ }
578
+ // action → 状态映射
579
+ const actionMap = {
580
+ accept: 'accepted',
581
+ ship: 'shipped',
582
+ pickup: 'picked_up',
583
+ transit: 'in_transit',
584
+ deliver: 'delivered',
585
+ confirm: 'confirmed',
586
+ dispute: 'disputed',
587
+ };
588
+ const toStatus = actionMap[action];
589
+ if (!toStatus)
590
+ return { error: `未知操作:${action}` };
591
+ // 如果有证据描述,先创建证据记录
592
+ const evidenceIds = [];
593
+ if (evidenceDesc) {
594
+ const evidenceId = generateId('evt');
595
+ db.prepare(`
596
+ INSERT INTO evidence (id, order_id, uploader_id, type, description, file_hash)
597
+ VALUES (?, ?, ?, 'description', ?, ?)
598
+ `).run(evidenceId, orderId, user.id, evidenceDesc, `hash_${Date.now()}`);
599
+ evidenceIds.push(evidenceId);
600
+ }
601
+ const result = transition(db, orderId, toStatus, user.id, evidenceIds, notes);
602
+ if (!result.success) {
603
+ return { error: result.error };
604
+ }
605
+ // 通知相关参与方(L2-6)
606
+ notifyTransition(db, orderId, order.status, toStatus);
607
+ // 如果是 dispute,写入 disputes 表(L3-1)
608
+ if (toStatus === 'disputed') {
609
+ const disputeResult = createDispute(db, orderId, user.id, notes || evidenceDesc || '买家发起争议', evidenceIds);
610
+ if (disputeResult.success) {
611
+ return {
612
+ success: true,
613
+ new_status: 'disputed',
614
+ dispute_id: disputeResult.disputeId,
615
+ message: disputeResult.message,
616
+ respond_deadline: disputeResult.respondDeadline,
617
+ next: `用 webaz_dispute action=view dispute_id=${disputeResult.disputeId} 查看争议详情`,
618
+ };
619
+ }
620
+ // 争议记录写入失败不影响状态,仍返回成功
621
+ return { success: true, new_status: 'disputed', message: '争议已发起,资金已冻结', warning: disputeResult.error };
622
+ }
623
+ // 如果是 confirmed,自动触发结算
624
+ if (toStatus === 'confirmed') {
625
+ const sysUser = db
626
+ .prepare("SELECT id FROM users WHERE id = 'sys_protocol'")
627
+ .get();
628
+ transition(db, orderId, 'completed', sysUser.id, [], '系统自动结算');
629
+ settleOrder(db, orderId);
630
+ return {
631
+ success: true,
632
+ new_status: 'completed',
633
+ message: '确认收货成功!资金已自动分配给各参与方。',
634
+ detail: `用 webaz_wallet 查看你的收益`,
635
+ };
636
+ }
637
+ const statusMessages = {
638
+ accepted: '接单成功!请在承诺时间内发货,超时将自动判违约。',
639
+ shipped: '发货成功!物流方 48 小时内需要完成揽收。',
640
+ picked_up: '揽收确认!请尽快安排运输。',
641
+ in_transit: '运输状态已更新。',
642
+ delivered: '投递确认!买家 72 小时内确认收货,超时自动确认。',
643
+ disputed: '争议已发起,资金冻结,等待仲裁员介入。',
644
+ };
645
+ return {
646
+ success: true,
647
+ new_status: result.newStatus,
648
+ message: statusMessages[toStatus] ?? '状态已更新',
649
+ history_record: result.historyId,
650
+ };
651
+ }
652
+ function handleGetStatus(args) {
653
+ const auth = requireAuth(db, args.api_key);
654
+ if ('error' in auth)
655
+ return auth;
656
+ const statusInfo = getOrderStatus(db, args.order_id);
657
+ if (!statusInfo)
658
+ return { error: `订单不存在:${args.order_id}` };
659
+ const { order, history, currentResponsible, activeDeadline, isOverdue } = statusInfo;
660
+ return {
661
+ order_id: order.id,
662
+ current_status: order.status,
663
+ current_responsible: currentResponsible
664
+ ? `${currentResponsible}(当前应由此角色操作)`
665
+ : '无(等待系统处理)',
666
+ deadline: activeDeadline
667
+ ? {
668
+ field: activeDeadline.field,
669
+ time: activeDeadline.deadline,
670
+ overdue: isOverdue ? '⚠️ 已超时!协议将自动判责' : '未超时',
671
+ }
672
+ : null,
673
+ history: history.map((h) => ({
674
+ from: h.from_status,
675
+ to: h.to_status,
676
+ by: `${h.actor_name}(${h.actor_role_name})`,
677
+ at: h.created_at,
678
+ evidence_count: JSON.parse(h.evidence_ids || '[]').length,
679
+ notes: h.notes,
680
+ })),
681
+ };
682
+ }
683
+ function handleWallet(args) {
684
+ const auth = requireAuth(db, args.api_key);
685
+ if ('error' in auth)
686
+ return auth;
687
+ const { user } = auth;
688
+ const wallet = db
689
+ .prepare('SELECT * FROM wallets WHERE user_id = ?')
690
+ .get(user.id);
691
+ if (!wallet)
692
+ return { error: '钱包不存在' };
693
+ const payouts = db
694
+ .prepare('SELECT SUM(amount) as total FROM payouts WHERE recipient_id = ?')
695
+ .get(user.id);
696
+ const rep = getReputation(db, user.id);
697
+ const nextLevel = ['new', 'trusted', 'quality', 'star', 'legend'];
698
+ const nextIdx = nextLevel.indexOf(rep.level.key) + 1;
699
+ const nextLevelDef = nextIdx < nextLevel.length ? { trusted: 200, quality: 800, star: 2000, legend: 5000 }[nextLevel[nextIdx]] : null;
700
+ return {
701
+ user: user.name,
702
+ role: user.role,
703
+ balance: `${wallet.balance} WAZ(可用)`,
704
+ staked: `${wallet.staked} WAZ(质押中,不可用)`,
705
+ escrowed: `${wallet.escrowed} WAZ(托管中,交易完成后结算)`,
706
+ total_earned: `${payouts.total ?? 0} WAZ(历史累计收益)`,
707
+ reputation: {
708
+ level: `${rep.level.icon} ${rep.level.label}`,
709
+ total_points: rep.total_points,
710
+ transactions_done: rep.transactions_done,
711
+ disputes_won: rep.disputes_won,
712
+ disputes_lost: rep.disputes_lost,
713
+ violations: rep.violations,
714
+ stake_discount: rep.level.stakeDiscount > 0 ? `-${(rep.level.stakeDiscount * 100).toFixed(0)}% 质押优惠` : '暂无(升级后享优惠)',
715
+ next_level: nextLevelDef ? `距下一等级还需 ${nextLevelDef - rep.total_points} 分` : '已达最高等级!',
716
+ recent_events: rep.recent_events.slice(0, 5).map(e => `${e.points > 0 ? '+' : ''}${e.points} ${e.reason}`),
717
+ },
718
+ };
719
+ }
720
+ // ─── 通知处理 ─────────────────────────────────────────────────
721
+ function handleNotifications(args) {
722
+ const auth = requireAuth(db, args.api_key);
723
+ if ('error' in auth)
724
+ return auth;
725
+ const { user } = auth;
726
+ const onlyUnread = args.unread === true;
727
+ const notifs = getNotifications(db, user.id, onlyUnread, 30);
728
+ const unread = getUnreadCount(db, user.id);
729
+ if (args.mark_read) {
730
+ markRead(db, user.id);
731
+ }
732
+ return {
733
+ unread_count: unread,
734
+ notifications: notifs.map(n => ({
735
+ id: n.id,
736
+ title: n.title,
737
+ body: n.body,
738
+ order_id: n.order_id,
739
+ read: n.read === 1,
740
+ time: n.created_at,
741
+ })),
742
+ };
743
+ }
744
+ // ─── 争议处理 ─────────────────────────────────────────────────
745
+ function handleDispute(args) {
746
+ const auth = requireAuth(db, args.api_key);
747
+ if ('error' in auth)
748
+ return auth;
749
+ const { user } = auth;
750
+ const action = args.action;
751
+ // ── 查看争议详情 ────────────────────────────────────────────
752
+ if (action === 'view') {
753
+ let dispute = args.dispute_id
754
+ ? getDisputeDetails(db, args.dispute_id)
755
+ : args.order_id
756
+ ? getOrderDispute(db, args.order_id)
757
+ : null;
758
+ if (!dispute)
759
+ return { error: '找不到争议记录,请提供 dispute_id 或 order_id' };
760
+ const evidenceList = (orderId, uploaderRole) => db.prepare(`
761
+ SELECT e.description, e.type, e.file_hash, e.created_at, u.name as uploader
762
+ FROM evidence e JOIN users u ON e.uploader_id = u.id
763
+ WHERE e.order_id = ? AND u.role = ?
764
+ ORDER BY e.created_at ASC
765
+ `).all(orderId, uploaderRole);
766
+ return {
767
+ dispute_id: dispute.id,
768
+ order_id: dispute.order_id,
769
+ status: dispute.status,
770
+ initiator: `${dispute.initiator_name}(${dispute.initiator_role})`,
771
+ defendant: `${dispute.defendant_name}(${dispute.defendant_role})`,
772
+ reason: dispute.reason,
773
+ respond_deadline: dispute.respond_deadline,
774
+ arbitrate_deadline: dispute.arbitrate_deadline,
775
+ plaintiff_evidence: evidenceList(dispute.order_id, dispute.initiator_role),
776
+ defendant_notes: dispute.defendant_notes ?? '(被诉方尚未提交回应)',
777
+ defendant_evidence: JSON.parse(dispute.defendant_evidence_ids || '[]'),
778
+ ruling: dispute.ruling_type
779
+ ? { type: dispute.ruling_type, refund_amount: dispute.refund_amount, reason: dispute.verdict_reason }
780
+ : null,
781
+ resolved_at: dispute.resolved_at,
782
+ };
783
+ }
784
+ // ── 仲裁员查看所有待处理争议 ───────────────────────────────
785
+ if (action === 'list_open') {
786
+ if (user.role !== 'arbitrator') {
787
+ return { error: '只有仲裁员可以查看所有待处理争议' };
788
+ }
789
+ const disputes = getOpenDisputes(db);
790
+ return {
791
+ open_count: disputes.length,
792
+ disputes: disputes.map(d => ({
793
+ dispute_id: d.id,
794
+ order_id: d.order_id,
795
+ status: d.status,
796
+ initiator: `${d.initiator_name}(${d.initiator_role})`,
797
+ defendant: `${d.defendant_name}(${d.defendant_role})`,
798
+ reason: d.reason,
799
+ amount: `${d.total_amount} WAZ`,
800
+ respond_deadline: d.respond_deadline,
801
+ arbitrate_deadline: d.arbitrate_deadline,
802
+ created_at: d.created_at,
803
+ }))
804
+ };
805
+ }
806
+ // ── 被诉方提交反驳 ──────────────────────────────────────────
807
+ if (action === 'respond') {
808
+ if (!args.dispute_id)
809
+ return { error: '请提供 dispute_id' };
810
+ // 如有证据描述,先创建证据记录
811
+ const evidenceIds = [];
812
+ if (args.evidence_description) {
813
+ const dispute = getDisputeDetails(db, args.dispute_id);
814
+ if (dispute) {
815
+ const eid = generateId('evt');
816
+ db.prepare(`
817
+ INSERT INTO evidence (id, order_id, uploader_id, type, description, file_hash)
818
+ VALUES (?, ?, ?, 'description', ?, ?)
819
+ `).run(eid, dispute.order_id, user.id, args.evidence_description, `hash_${Date.now()}`);
820
+ evidenceIds.push(eid);
821
+ }
822
+ }
823
+ return respondToDispute(db, args.dispute_id, user.id, args.notes ?? '', evidenceIds);
824
+ }
825
+ // ── 仲裁员裁定 ─────────────────────────────────────────────
826
+ if (action === 'arbitrate') {
827
+ if (!args.dispute_id)
828
+ return { error: '请提供 dispute_id' };
829
+ if (!args.ruling)
830
+ return { error: '请提供 ruling(refund_buyer / release_seller / partial_refund)' };
831
+ if (!args.ruling_reason)
832
+ return { error: '请提供 ruling_reason(裁定理由将永久记录)' };
833
+ if (args.ruling === 'partial_refund' && !args.refund_amount) {
834
+ return { error: 'partial_refund 需要提供 refund_amount' };
835
+ }
836
+ const result = arbitrateDispute(db, args.dispute_id, user.id, args.ruling, args.ruling_reason, args.refund_amount);
837
+ // L4-3 争议声誉:裁定完成后更新声誉
838
+ if (result.success) {
839
+ const dispute = getDisputeDetails(db, args.dispute_id);
840
+ if (dispute?.order_id) {
841
+ const ruling = args.ruling;
842
+ // refund_buyer → 原告(买家)胜,被告(卖家)败;release_seller → 反之
843
+ const initiatorId = dispute.initiator_id;
844
+ const defendantId = dispute.defendant_id;
845
+ const winnerId = ruling === 'refund_buyer' ? initiatorId : defendantId;
846
+ const loserId = ruling === 'refund_buyer' ? defendantId : initiatorId;
847
+ recordDisputeReputation(db, dispute.order_id, winnerId, loserId);
848
+ }
849
+ }
850
+ return result;
851
+ }
852
+ return { error: `未知 action:${action}` };
853
+ }
854
+ // ─── Skill 市场处理 ────────────────────────────────────────────
855
+ function handleSkill(args) {
856
+ const action = args.action;
857
+ // ── 浏览 Skill 市场 ────────────────────────────────────────
858
+ if (action === 'list') {
859
+ let userId;
860
+ if (args.api_key) {
861
+ const a = requireAuth(db, args.api_key);
862
+ if (!('error' in a))
863
+ userId = a.user.id;
864
+ }
865
+ const skills = listSkills(db, {
866
+ skillType: args.skill_type,
867
+ query: args.query,
868
+ subscriberId: userId,
869
+ limit: 20,
870
+ });
871
+ return {
872
+ total: skills.length,
873
+ skill_types: Object.entries(SKILL_TYPE_META).map(([k, v]) => ({ type: k, label: v.label, icon: v.icon, description: v.description })),
874
+ skills: skills.map(formatSkillForAgent),
875
+ };
876
+ }
877
+ // 以下操作需要身份验证
878
+ const auth = requireAuth(db, args.api_key);
879
+ if ('error' in auth)
880
+ return auth;
881
+ const { user } = auth;
882
+ // ── 发布 Skill ────────────────────────────────────────────
883
+ if (action === 'publish') {
884
+ if (!args.name)
885
+ return { error: '请填写 Skill 名称(name)' };
886
+ if (!args.description)
887
+ return { error: '请填写 Skill 描述(description)' };
888
+ if (!args.skill_type)
889
+ return { error: '请选择 Skill 类型(skill_type)' };
890
+ const skill = publishSkill(db, {
891
+ sellerId: user.id,
892
+ name: args.name,
893
+ description: args.description,
894
+ category: args.category,
895
+ skillType: args.skill_type,
896
+ config: args.config,
897
+ });
898
+ const meta = SKILL_TYPE_META[skill.skill_type];
899
+ return {
900
+ success: true,
901
+ skill_id: skill.id,
902
+ message: `✅ Skill 「${skill.name}」已发布到 WebAZ Skill 市场!买家 Agent 现在可以订阅它。`,
903
+ type: `${meta.icon} ${meta.label}`,
904
+ tip: 'auto_accept Skill 发布后,买家新订单将自动被接受(无需手动操作)',
905
+ };
906
+ }
907
+ // ── 订阅 Skill ────────────────────────────────────────────
908
+ if (action === 'subscribe') {
909
+ if (!args.skill_id)
910
+ return { error: '请提供 skill_id' };
911
+ const result = subscribeSkill(db, user.id, args.skill_id, args.config);
912
+ return { ...result, skill_id: args.skill_id };
913
+ }
914
+ // ── 取消订阅 ──────────────────────────────────────────────
915
+ if (action === 'unsubscribe') {
916
+ if (!args.skill_id)
917
+ return { error: '请提供 skill_id' };
918
+ unsubscribeSkill(db, user.id, args.skill_id);
919
+ return { success: true, message: '已取消订阅' };
920
+ }
921
+ // ── 我发布的 Skill ────────────────────────────────────────
922
+ if (action === 'my_skills') {
923
+ const skills = getMySkills(db, user.id);
924
+ return {
925
+ total: skills.length,
926
+ skills: skills.map(formatSkillForAgent),
927
+ tip: skills.length === 0 ? '还没有发布任何 Skill。用 webaz_skill action=publish 发布你的第一个 Skill。' : undefined,
928
+ };
929
+ }
930
+ // ── 我订阅的 Skill ────────────────────────────────────────
931
+ if (action === 'my_subs') {
932
+ const skills = getMySubscriptions(db, user.id);
933
+ return {
934
+ total: skills.length,
935
+ subscriptions: skills.map(formatSkillForAgent),
936
+ tip: skills.length === 0 ? '还没有订阅任何 Skill。用 webaz_skill action=list 浏览市场。' : undefined,
937
+ };
938
+ }
939
+ return { error: `未知 action:${action}。可选:list, publish, subscribe, unsubscribe, my_skills, my_subs` };
940
+ }
941
+ // ─── 结算逻辑(买家确认后自动执行)──────────────────────────────
942
+ function settleOrder(db, orderId) {
943
+ const order = db.prepare('SELECT * FROM orders WHERE id = ?').get(orderId);
944
+ const totalAmount = order.total_amount;
945
+ const sellerId = order.seller_id;
946
+ const buyerId = order.buyer_id;
947
+ const logisticsId = order.logistics_id;
948
+ const promoterId = order.promoter_id;
949
+ // 分成比例(协议参数,未来可治理调整)
950
+ const protocolFee = Math.round(totalAmount * 0.02 * 100) / 100; // 2% 协议费
951
+ const logisticsFee = Math.round(totalAmount * 0.05 * 100) / 100; // 5% 物流
952
+ const promoterFee = promoterId ? Math.round(totalAmount * 0.03 * 100) / 100 : 0; // 3% 推荐
953
+ const sellerAmount = totalAmount - protocolFee - logisticsFee - promoterFee;
954
+ const payout = (recipientId, role, amount, reason) => {
955
+ if (amount <= 0)
956
+ return;
957
+ db.prepare(`INSERT INTO payouts (id, order_id, recipient_id, role, amount, reason) VALUES (?, ?, ?, ?, ?, ?)`)
958
+ .run(generateId('pay'), orderId, recipientId, role, amount, reason);
959
+ db.prepare(`UPDATE wallets SET balance = balance + ?, earned = earned + ? WHERE user_id = ?`)
960
+ .run(amount, amount, recipientId);
961
+ };
962
+ // 释放买家托管资金(从 escrowed 减掉)
963
+ db.prepare(`UPDATE wallets SET escrowed = escrowed - ? WHERE user_id = ?`).run(totalAmount, buyerId);
964
+ // 按比例分发
965
+ payout(sellerId, 'seller', sellerAmount, 'seller_share');
966
+ if (logisticsId)
967
+ payout(logisticsId, 'logistics', logisticsFee, 'logistics_fee');
968
+ if (promoterId)
969
+ payout(promoterId, 'promoter', promoterFee, 'promoter_fee');
970
+ // 归还卖家质押
971
+ const product = db.prepare('SELECT stake_amount FROM products WHERE id = ?').get(order.product_id);
972
+ db.prepare(`UPDATE wallets SET staked = staked - ?, balance = balance + ? WHERE user_id = ?`)
973
+ .run(product.stake_amount, product.stake_amount, sellerId);
974
+ // L4-3 声誉积分:交易完成后自动更新各方声誉
975
+ recordOrderReputation(db, orderId);
976
+ }
977
+ // ─── MCP Server 主体 ──────────────────────────────────────────
978
+ export async function startMCPServer() {
979
+ const server = new Server({ name: 'dcp-protocol', version: '0.1.0' }, { capabilities: { tools: {}, resources: {} } });
980
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
981
+ // ── MCP Resources:协议 Manifest ─────────────────────────────
982
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
983
+ resources: [
984
+ {
985
+ uri: MANIFEST_URI,
986
+ name: 'WebAZ Protocol Manifest',
987
+ description: '完整的 WebAZ机器可读规范。包含:状态机、经济模型、角色职责、争议系统、Skill 市场、声誉积分、Agent 操作指南。AI Agent 读完即可参与协议,无需额外文档。',
988
+ mimeType: 'application/json',
989
+ },
990
+ ],
991
+ }));
992
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
993
+ if (request.params.uri !== MANIFEST_URI) {
994
+ throw new Error(`未知资源:${request.params.uri}`);
995
+ }
996
+ const manifest = generateManifest(db);
997
+ return {
998
+ contents: [
999
+ {
1000
+ uri: MANIFEST_URI,
1001
+ mimeType: 'application/json',
1002
+ text: JSON.stringify(manifest, null, 2),
1003
+ },
1004
+ ],
1005
+ };
1006
+ });
1007
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1008
+ const { name, arguments: args = {} } = request.params;
1009
+ let result;
1010
+ try {
1011
+ switch (name) {
1012
+ case 'webaz_info':
1013
+ result = handleInfo();
1014
+ break;
1015
+ case 'webaz_register':
1016
+ result = handleRegister(args);
1017
+ break;
1018
+ case 'webaz_search':
1019
+ result = handleSearch(args);
1020
+ break;
1021
+ case 'webaz_list_product':
1022
+ result = handleListProduct(args);
1023
+ break;
1024
+ case 'webaz_place_order':
1025
+ result = handlePlaceOrder(args);
1026
+ break;
1027
+ case 'webaz_update_order':
1028
+ result = handleUpdateOrder(args);
1029
+ break;
1030
+ case 'webaz_get_status':
1031
+ result = handleGetStatus(args);
1032
+ break;
1033
+ case 'webaz_wallet':
1034
+ result = handleWallet(args);
1035
+ break;
1036
+ case 'webaz_dispute':
1037
+ result = handleDispute(args);
1038
+ break;
1039
+ case 'webaz_notifications':
1040
+ result = handleNotifications(args);
1041
+ break;
1042
+ case 'webaz_skill':
1043
+ result = handleSkill(args);
1044
+ break;
1045
+ default: result = { error: `未知工具:${name}` };
1046
+ }
1047
+ }
1048
+ catch (err) {
1049
+ result = { error: `执行出错:${err.message}` };
1050
+ }
1051
+ return {
1052
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
1053
+ };
1054
+ });
1055
+ const transport = new StdioServerTransport();
1056
+ await server.connect(transport);
1057
+ console.error('✅ WebAZ MCP Server 已启动,等待 Agent 连接...');
1058
+ }
1059
+ // ─── 工具函数 ─────────────────────────────────────────────────
1060
+ function addHours(date, hours) {
1061
+ return new Date(date.getTime() + hours * 3_600_000).toISOString();
1062
+ }