@seasonkoh/webaz 0.1.21 → 0.1.23
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/dist/layer0-foundation/L0-2-state-machine/engine.js +81 -93
- package/dist/layer1-agent/L1-1-mcp-server/server.js +682 -236
- package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +138 -168
- package/dist/layer4-economics/L4-4-skill-market/skill-engine.js +6 -4
- package/dist/layer4-economics/L4-4-skill-market/skill-listing-engine.js +12 -8
- package/dist/ledger.js +58 -0
- package/dist/money.js +106 -0
- package/dist/pwa/acp-feed.js +103 -0
- package/dist/pwa/economic-participation.js +1 -1
- package/dist/pwa/integration-contract.js +4 -0
- package/dist/pwa/public/app.js +8 -7
- package/dist/pwa/public/index.html +27 -1
- package/dist/pwa/routes/auction.js +9 -6
- package/dist/pwa/routes/disputes-write.js +12 -11
- package/dist/pwa/routes/orders-create.js +24 -13
- package/dist/pwa/routes/public-utils.js +28 -0
- package/dist/pwa/routes/referral.js +81 -0
- package/dist/pwa/server.js +57 -39
- package/dist/settlement-math.js +27 -0
- package/package.json +1 -1
|
@@ -60,6 +60,42 @@ const NETWORK_TOOLS = new Set([
|
|
|
60
60
|
'webaz_get_status',
|
|
61
61
|
'webaz_feedback',
|
|
62
62
|
'webaz_contribute',
|
|
63
|
+
// Batch 1(只读 + 低危自身写):走 webaz.xyz Bearer api_key。
|
|
64
|
+
'webaz_notifications',
|
|
65
|
+
'webaz_nearby',
|
|
66
|
+
'webaz_profile',
|
|
67
|
+
'webaz_shareables',
|
|
68
|
+
'webaz_mykey',
|
|
69
|
+
// Batch 2(低危写,无钱无 escrow):走 webaz.xyz Bearer api_key。
|
|
70
|
+
// 注:share_link 暂不迁(无对应服务端端点,需新建,留待后续)。
|
|
71
|
+
'webaz_follows',
|
|
72
|
+
'webaz_like',
|
|
73
|
+
'webaz_blocklist',
|
|
74
|
+
'webaz_default_address',
|
|
75
|
+
'webaz_chat',
|
|
76
|
+
'webaz_rfq',
|
|
77
|
+
'webaz_referral',
|
|
78
|
+
// Batch 3(商务):secondhand/skill_market/auction 纯 pwaApi(mode-aware 自动走网络);
|
|
79
|
+
// skill 直连本地引擎,加了显式 apiCall network 分支。
|
|
80
|
+
'webaz_secondhand',
|
|
81
|
+
'webaz_skill',
|
|
82
|
+
'webaz_skill_market',
|
|
83
|
+
'webaz_auction',
|
|
84
|
+
// Batch 4(资金/质押,守恒由服务端 RFC-014 保证;wallet 只读,写=Passkey 仅 PWA):
|
|
85
|
+
'webaz_wallet',
|
|
86
|
+
'webaz_trial',
|
|
87
|
+
'webaz_charity',
|
|
88
|
+
'webaz_bid',
|
|
89
|
+
'webaz_auto_bid',
|
|
90
|
+
// Batch 5(铁律/敏感):claim_verify 纯 pwaApi(真人门由服务端 require_human_presence 强制);
|
|
91
|
+
// dispute view/list_open/respond/add_evidence 走网络,arbitrate 仅返回 Passkey 指引;
|
|
92
|
+
// rotate_key/revoke_key 仅返回 Passkey 指引(不本地校验)。
|
|
93
|
+
'webaz_dispute',
|
|
94
|
+
'webaz_claim_verify',
|
|
95
|
+
'webaz_rotate_key',
|
|
96
|
+
'webaz_revoke_key',
|
|
97
|
+
// #1122:share_link 现有服务端端点 /api/share-link,可走网络。
|
|
98
|
+
'webaz_share_link',
|
|
63
99
|
]);
|
|
64
100
|
const recentCalls = [];
|
|
65
101
|
function pushRecentCall(c) {
|
|
@@ -71,6 +107,24 @@ function pushRecentCall(c) {
|
|
|
71
107
|
function toolBackend(tool) {
|
|
72
108
|
return (MODE === 'network' && NETWORK_TOOLS.has(tool)) ? 'network' : 'sandbox';
|
|
73
109
|
}
|
|
110
|
+
// 未在 NETWORK_TOOLS 名单、但 NETWORK 模式下仍可本地运行的"自省/引导"工具(非数据操作)。
|
|
111
|
+
// info = 本地自省(并拉 live 网络状态);register = 引导真人去 webaz.xyz。其余未迁工具一律硬失败。
|
|
112
|
+
const NETWORK_SELF_AWARE = new Set(['webaz_info', 'webaz_register']);
|
|
113
|
+
// RFC-003 Batch 0 安全网:NETWORK 模式下调用【未迁移】工具时的诚实拒绝(而非静默落本地沙盒)。
|
|
114
|
+
// 否则带 key 的用户调未迁工具会被悄悄喂本地结果——写操作=幻影操作(根本没到 webaz.xyz)。
|
|
115
|
+
function networkMigrationPending(tool) {
|
|
116
|
+
const short = tool.replace(/^webaz_/, '');
|
|
117
|
+
return {
|
|
118
|
+
_mode: 'network',
|
|
119
|
+
not_on_network_yet: true,
|
|
120
|
+
error: `${tool} 尚未接入 webaz.xyz 共享网络(迁移进行中)。NETWORK 模式下拒绝把它落到本机沙盒——本地结果不会到达 webaz.xyz,写操作会变成"幻影操作"。 / ${tool} is not on the live network yet (migration in progress); refusing to run it against your local sandbox while in NETWORK mode — a local result would NOT reach webaz.xyz.`,
|
|
121
|
+
what_to_do: [
|
|
122
|
+
`现在就用网页完成此动作:${WEBAZ_API_URL}(PWA) / Use the web app for this action now.`,
|
|
123
|
+
`只想本地试玩/测试?设环境变量 WEBAZ_MODE=sandbox 显式进沙盒。 / Set WEBAZ_MODE=sandbox to use the local sandbox explicitly.`,
|
|
124
|
+
],
|
|
125
|
+
migration: `RFC-003 渐进迁移:webaz_${short} 将在后续批次获得网络支持。 / incremental migration; network support for this tool lands in an upcoming batch.`,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
74
128
|
// 统一 API helper(P1/P2 迁移工具时使用)。Bearer api_key + 15s 超时 + 错误映射。
|
|
75
129
|
async function apiCall(path, opts = {}) {
|
|
76
130
|
const { method = 'GET', body } = opts;
|
|
@@ -1475,8 +1529,24 @@ async function handleContribute(args) {
|
|
|
1475
1529
|
const q = args.area ? '?status=open&area=' + encodeURIComponent(String(args.area)) : '?status=open';
|
|
1476
1530
|
return apiCall('/api/build-tasks' + q, { apiKey });
|
|
1477
1531
|
}
|
|
1478
|
-
function handleInfo() {
|
|
1532
|
+
async function handleInfo() {
|
|
1479
1533
|
const summary = getManifestSummary();
|
|
1534
|
+
// RFC-003 Batch 0:NETWORK 模式下,best-effort 拉 webaz.xyz 的 live 协议状态,
|
|
1535
|
+
// 让带 key 的 agent 拿到【真网络】数字,而非只看本机本地 live_stats(下方仍保留并标注为本地)。
|
|
1536
|
+
let network_live = null;
|
|
1537
|
+
if (MODE === 'network') {
|
|
1538
|
+
try {
|
|
1539
|
+
const ps = await apiCall('/api/protocol-status');
|
|
1540
|
+
network_live = { source: `${WEBAZ_API_URL}/api/protocol-status (live, fetched this call)`, ...ps };
|
|
1541
|
+
}
|
|
1542
|
+
catch (e) {
|
|
1543
|
+
network_live = {
|
|
1544
|
+
source: `${WEBAZ_API_URL}/api/protocol-status`,
|
|
1545
|
+
error: `couldn't reach live network this call: ${e.message}`,
|
|
1546
|
+
note: 'live_stats below is LOCAL-only (this MCP server\'s SQLite), not protocol-wide.',
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1480
1550
|
// QA 轮 3 抓到:live_stats 不是 hardcoded、不是 remote — 就是本地 SQLite count。这里加 source 字段澄清。
|
|
1481
1551
|
const stats = (() => {
|
|
1482
1552
|
try {
|
|
@@ -1520,7 +1590,7 @@ function handleInfo() {
|
|
|
1520
1590
|
mode: MODE, // 'network' | 'sandbox'
|
|
1521
1591
|
mode_banner: modeBanner(),
|
|
1522
1592
|
mode_meaning: MODE === 'network'
|
|
1523
|
-
? '🟢 NETWORK:核心交易工具(下单/上架/履约/比价等)走 webaz.xyz
|
|
1593
|
+
? '🟢 NETWORK:核心交易工具(下单/上架/履约/比价等)走 webaz.xyz 共享生产网络。真网络规模见下方 network_live(本次实时拉取);live_stats 仍是本机本地缓存,仅供参考。'
|
|
1524
1594
|
: '🟡 SANDBOX:所有工具都在本机本地 SQLite 运行,与 webaz.xyz 全网隔离。任何计数 / 账号 / 订单仅本机有效,不是真实网络状态。设 WEBAZ_API_KEY 可切到 NETWORK。',
|
|
1525
1595
|
phase: 'pre_launch',
|
|
1526
1596
|
real_users_on_canonical: 0,
|
|
@@ -1529,6 +1599,20 @@ function handleInfo() {
|
|
|
1529
1599
|
disclaimer: '本协议尚未公开上线,prod 真实用户≈0,无真实经济流转。下方 live_stats / 工具示例所有计数均来自**本机 MCP 服务器的本地 SQLite**(~/.webaz/webaz.db),仅供 dev / demo,不代表协议全网真实状态。请勿据此评估市场规模、做投资决策、或替终端用户承诺任何经济关系。',
|
|
1530
1600
|
},
|
|
1531
1601
|
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 是去中心化商业协议;每笔交易通过状态机流转,每次状态转移需对应责任方的操作证明;超时未操作则协议自动判违约并执行处置。',
|
|
1602
|
+
// 给【终端用户/买家】的价值主张 —— 陌生 agent / 普通买家先要知道"对我有什么用",而非只读抽象 tagline + 技术 description。
|
|
1603
|
+
for_end_user: {
|
|
1604
|
+
note: 'What WebAZ does FOR a buyer (why use it): / 这个协议对买家有什么用(为什么用它):',
|
|
1605
|
+
value: [
|
|
1606
|
+
'Escrow on every order — your money is held by the protocol and only released to the seller after you confirm receipt (or an auto-confirm window). / 每笔订单托管:钱由协议托管,你确认收货(或到自动确认期)才放款给卖家。',
|
|
1607
|
+
'Automatic fault ruling — if the seller does not accept / ship / deliver in time, the protocol auto-refunds you, no haggling. / 自动判责:卖家不接单/不发货/不送达超时,协议自动退款给你,无需扯皮。',
|
|
1608
|
+
'Disputes with evidence + arbitration — open a dispute with proof; a neutral process decides. / 争议可凭证据发起 + 中立仲裁裁决。',
|
|
1609
|
+
'Decision-ready transparency — price history, seller reputation, win/loss record and arbitration precedents are public before you buy. / 知情决策:价格历史、卖家信誉、胜诉/败诉记录、仲裁判例,下单前都公开可查。',
|
|
1610
|
+
'Agent-native — your AI agent can compare prices, place orders, and track fulfillment for you via the MCP tools. / agent 原生:你的 AI 可经 MCP 工具替你比价、下单、跟踪履约。',
|
|
1611
|
+
],
|
|
1612
|
+
honesty: 'Pre-launch: WAZ is a simulated test currency, no real money settles yet. Don\'t treat balances as real value. / 尚未上线:WAZ 是模拟测试币,暂无真实资金结算,余额勿当真实价值。',
|
|
1613
|
+
try_it: 'Browse without an account at https://webaz.xyz/#discover ; the protocol state is public at https://webaz.xyz/.well-known/webaz-protocol.json',
|
|
1614
|
+
get_access: 'Pre-launch is invite-gated — request an invite at https://webaz.xyz/#welcome (browsing/reading needs no invite). / 上线前邀请制:到 #welcome 申请邀请,浏览/查看无需邀请。',
|
|
1615
|
+
},
|
|
1532
1616
|
// 连接两个场景:用协议(本工具) ↔ 改协议(开发协作)。想改 WebAZ 本身的 agent 从这里进。
|
|
1533
1617
|
for_contributors: {
|
|
1534
1618
|
note: 'Want to change WebAZ itself (not just use it)? This is an open, agent-native protocol — AI-authored PRs are welcome, with accountability. / 想改 WebAZ 本身(不只是用)?这是开放的 agent 原生协议,欢迎 AI 提 PR,但需问责。',
|
|
@@ -1536,6 +1620,8 @@ function handleInfo() {
|
|
|
1536
1620
|
start_here: 'AGENTS.md (project map + before-you-code + PR flow) → CONTRIBUTING.md (full guide)',
|
|
1537
1621
|
ai_accountability: 'AI-authored PRs: add 🤖🤖🤖 to the PR title; the agent must be triggered by a Passkey-bound human (webazer) who is accountable. / AI 提 PR:标题加 🤖🤖🤖,且须由已绑 Passkey 的真人(webazer)触发并担责。',
|
|
1538
1622
|
},
|
|
1623
|
+
// NETWORK 模式:真网络 live 状态(best-effort 拉自 webaz.xyz);SANDBOX 模式为 null。
|
|
1624
|
+
network_live,
|
|
1539
1625
|
live_stats: stats,
|
|
1540
1626
|
economics,
|
|
1541
1627
|
// 佣金机制 —— 纯功能性描述(怎么运作),不做"自证清白"式辩护。
|
|
@@ -2519,6 +2605,19 @@ async function handleWallet(args) {
|
|
|
2519
2605
|
const apiKey = args.api_key;
|
|
2520
2606
|
if (!apiKey)
|
|
2521
2607
|
return { error: 'api_key required' };
|
|
2608
|
+
// RFC-003 Batch 4:NETWORK 模式 → webaz.xyz 真网络【只读】(Bearer api_key)。
|
|
2609
|
+
// 写动作(withdraw/topup/whitelist/connect)是 Passkey+OTP 多步流,MCP 不暴露,仅 PWA。
|
|
2610
|
+
if (toolBackend('webaz_wallet') === 'network') {
|
|
2611
|
+
if (action === 'view')
|
|
2612
|
+
return await apiCall('/api/wallet', { apiKey });
|
|
2613
|
+
if (action === 'deposits')
|
|
2614
|
+
return await apiCall('/api/wallet/deposits', { apiKey });
|
|
2615
|
+
if (action === 'withdrawals')
|
|
2616
|
+
return await apiCall('/api/wallet/withdrawals', { apiKey });
|
|
2617
|
+
if (action === 'income')
|
|
2618
|
+
return await apiCall('/api/wallet/income', { apiKey });
|
|
2619
|
+
return { error: `unknown action: ${action}. Valid: view | deposits | withdrawals | income. 提现/充值/白名单需 Passkey+OTP,仅 PWA Web 端。` };
|
|
2620
|
+
}
|
|
2522
2621
|
if (action === 'deposits')
|
|
2523
2622
|
return await pwaApi('GET', '/wallet/deposits', apiKey);
|
|
2524
2623
|
if (action === 'withdrawals')
|
|
@@ -2568,7 +2667,16 @@ async function handleWallet(args) {
|
|
|
2568
2667
|
};
|
|
2569
2668
|
}
|
|
2570
2669
|
// ─── 通知处理 ─────────────────────────────────────────────────
|
|
2571
|
-
function handleNotifications(args) {
|
|
2670
|
+
async function handleNotifications(args) {
|
|
2671
|
+
// RFC-003 Batch 1:NETWORK 模式 → 调 webaz.xyz 真网络通知端点(Bearer api_key);SANDBOX 走本地。
|
|
2672
|
+
if (toolBackend('webaz_notifications') === 'network') {
|
|
2673
|
+
const apiKey = String(args.api_key || '');
|
|
2674
|
+
if (!apiKey)
|
|
2675
|
+
return { error: 'api_key required' };
|
|
2676
|
+
if (args.mark_read)
|
|
2677
|
+
await apiCall('/api/notifications/read', { method: 'POST', apiKey });
|
|
2678
|
+
return await apiCall('/api/notifications' + (args.unread === true ? '?unread=1' : ''), { apiKey });
|
|
2679
|
+
}
|
|
2572
2680
|
const auth = requireAuth(db, args.api_key);
|
|
2573
2681
|
if ('error' in auth)
|
|
2574
2682
|
return auth;
|
|
@@ -2596,11 +2704,69 @@ async function handleDispute(args) {
|
|
|
2596
2704
|
const apiKey = args.api_key;
|
|
2597
2705
|
if (!apiKey)
|
|
2598
2706
|
return { error: 'api_key required' };
|
|
2707
|
+
const action = args.action;
|
|
2708
|
+
// ── 仲裁裁定(Iron-Rule)──────────────────────────────────────
|
|
2709
|
+
// 两种模式都只登记意图 + 返回 PWA+Passkey 指引;不执行、不碰 db。放在 auth 之前。
|
|
2710
|
+
if (action === 'arbitrate') {
|
|
2711
|
+
if (!args.dispute_id)
|
|
2712
|
+
return { error: '请提供 dispute_id' };
|
|
2713
|
+
if (!args.ruling)
|
|
2714
|
+
return { error: '请提供 ruling(refund_buyer / release_seller / partial_refund / liability_split)' };
|
|
2715
|
+
if (!args.ruling_reason)
|
|
2716
|
+
return { error: '请提供 ruling_reason(裁定理由将永久记录)' };
|
|
2717
|
+
if (args.ruling === 'partial_refund' && !args.refund_amount && !args.liable_party) {
|
|
2718
|
+
return { error: 'partial_refund 需要提供 refund_amount,或 liable_party(第三方责任方)' };
|
|
2719
|
+
}
|
|
2720
|
+
if (args.ruling === 'liability_split' && (!Array.isArray(args.liability_parties) || args.liability_parties.length === 0)) {
|
|
2721
|
+
return { error: 'liability_split 需要提供 liability_parties 数组,每项 { user_id, amount }' };
|
|
2722
|
+
}
|
|
2723
|
+
return {
|
|
2724
|
+
success: false,
|
|
2725
|
+
requires_human_action: true,
|
|
2726
|
+
iron_rule: 'Arbitration ruling is irreversible, affects multiple parties, and locks fund distribution permanently. Agent cannot execute unilaterally. Same Iron-Rule gating as webaz_revoke_key / claim_verify vote.',
|
|
2727
|
+
action: 'arbitrate_dispute',
|
|
2728
|
+
dispute_id: args.dispute_id,
|
|
2729
|
+
proposed_ruling: {
|
|
2730
|
+
ruling: args.ruling,
|
|
2731
|
+
reason: args.ruling_reason,
|
|
2732
|
+
...(args.refund_amount !== undefined ? { refund_amount: args.refund_amount } : {}),
|
|
2733
|
+
...(args.liable_party ? { liable_party: args.liable_party } : {}),
|
|
2734
|
+
...(args.liability_parties ? { liability_parties: args.liability_parties } : {}),
|
|
2735
|
+
},
|
|
2736
|
+
next_step: {
|
|
2737
|
+
via: 'PWA + Passkey (arbitrator role)',
|
|
2738
|
+
url: `https://webaz.xyz/arbitrate?dispute=${encodeURIComponent(args.dispute_id)}`,
|
|
2739
|
+
instructions: '1) Open URL in browser 2) Sign in as arbitrator 3) Review evidence 4) Confirm with Passkey 5) Submit final ruling. Action is irreversible after confirmation.',
|
|
2740
|
+
},
|
|
2741
|
+
};
|
|
2742
|
+
}
|
|
2743
|
+
// RFC-003 Batch 5:NETWORK 模式 → webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
2744
|
+
if (toolBackend('webaz_dispute') === 'network') {
|
|
2745
|
+
if (action === 'view') {
|
|
2746
|
+
if (!args.dispute_id)
|
|
2747
|
+
return { error: '网络模式请提供 dispute_id(order_id 查询仅 PWA 支持)。 / provide dispute_id on the live network.' };
|
|
2748
|
+
return await apiCall('/api/disputes/' + encodeURIComponent(String(args.dispute_id)), { apiKey });
|
|
2749
|
+
}
|
|
2750
|
+
if (action === 'list_open')
|
|
2751
|
+
return await apiCall('/api/disputes', { apiKey });
|
|
2752
|
+
if (action === 'respond') {
|
|
2753
|
+
if (!args.dispute_id)
|
|
2754
|
+
return { error: '请提供 dispute_id' };
|
|
2755
|
+
return await apiCall('/api/disputes/' + encodeURIComponent(String(args.dispute_id)) + '/respond', { method: 'POST', apiKey, body: { notes: args.notes ?? '', evidence_description: args.evidence_description ?? '' } });
|
|
2756
|
+
}
|
|
2757
|
+
if (action === 'add_evidence') {
|
|
2758
|
+
if (!args.dispute_id)
|
|
2759
|
+
return { error: '请提供 dispute_id' };
|
|
2760
|
+
if (!args.evidence_description)
|
|
2761
|
+
return { error: '请提供 evidence_description(证据描述)' };
|
|
2762
|
+
return await apiCall('/api/disputes/' + encodeURIComponent(String(args.dispute_id)) + '/add-evidence', { method: 'POST', apiKey, body: { description: args.evidence_description, evidence_type: 'text' } });
|
|
2763
|
+
}
|
|
2764
|
+
return { error: `未知 action:${action}。Valid: view | list_open | respond | add_evidence | arbitrate` };
|
|
2765
|
+
}
|
|
2599
2766
|
const auth = requireAuth(db, apiKey);
|
|
2600
2767
|
if ('error' in auth)
|
|
2601
2768
|
return auth;
|
|
2602
2769
|
const { user } = auth;
|
|
2603
|
-
const action = args.action;
|
|
2604
2770
|
// ── 查看争议详情 ────────────────────────────────────────────
|
|
2605
2771
|
if (action === 'view') {
|
|
2606
2772
|
let dispute = args.dispute_id
|
|
@@ -2694,43 +2860,6 @@ async function handleDispute(args) {
|
|
|
2694
2860
|
evidence_type: 'text',
|
|
2695
2861
|
});
|
|
2696
2862
|
}
|
|
2697
|
-
// ── 仲裁员裁定(Iron-Rule:MCP 仅登记意图,真正裁定需 PWA + Passkey 二次确认)─
|
|
2698
|
-
// QA 轮 7 P1 修复:旧版直接调 PWA HTTP /api/disputes/:id/arbitrate,
|
|
2699
|
-
// 1) 违反 Iron-Rule 模型(应该跟 revoke_key/rotate_key 同模式 — agent 不能单方面执行不可逆操作)
|
|
2700
|
-
// 2) 本地 dev 没起 PWA 就挂
|
|
2701
|
-
if (action === 'arbitrate') {
|
|
2702
|
-
if (!args.dispute_id)
|
|
2703
|
-
return { error: '请提供 dispute_id' };
|
|
2704
|
-
if (!args.ruling)
|
|
2705
|
-
return { error: '请提供 ruling(refund_buyer / release_seller / partial_refund / liability_split)' };
|
|
2706
|
-
if (!args.ruling_reason)
|
|
2707
|
-
return { error: '请提供 ruling_reason(裁定理由将永久记录)' };
|
|
2708
|
-
if (args.ruling === 'partial_refund' && !args.refund_amount && !args.liable_party) {
|
|
2709
|
-
return { error: 'partial_refund 需要提供 refund_amount,或 liable_party(第三方责任方)' };
|
|
2710
|
-
}
|
|
2711
|
-
if (args.ruling === 'liability_split' && (!Array.isArray(args.liability_parties) || args.liability_parties.length === 0)) {
|
|
2712
|
-
return { error: 'liability_split 需要提供 liability_parties 数组,每项 { user_id, amount }' };
|
|
2713
|
-
}
|
|
2714
|
-
return {
|
|
2715
|
-
success: false,
|
|
2716
|
-
requires_human_action: true,
|
|
2717
|
-
iron_rule: 'Arbitration ruling is irreversible, affects multiple parties, and locks fund distribution permanently. Agent cannot execute unilaterally. Same Iron-Rule gating as webaz_revoke_key / claim_verify vote.',
|
|
2718
|
-
action: 'arbitrate_dispute',
|
|
2719
|
-
dispute_id: args.dispute_id,
|
|
2720
|
-
proposed_ruling: {
|
|
2721
|
-
ruling: args.ruling,
|
|
2722
|
-
reason: args.ruling_reason,
|
|
2723
|
-
...(args.refund_amount !== undefined ? { refund_amount: args.refund_amount } : {}),
|
|
2724
|
-
...(args.liable_party ? { liable_party: args.liable_party } : {}),
|
|
2725
|
-
...(args.liability_parties ? { liability_parties: args.liability_parties } : {}),
|
|
2726
|
-
},
|
|
2727
|
-
next_step: {
|
|
2728
|
-
via: 'PWA + Passkey (arbitrator role)',
|
|
2729
|
-
url: `https://webaz.xyz/arbitrate?dispute=${encodeURIComponent(args.dispute_id)}`,
|
|
2730
|
-
instructions: '1) Open URL in browser 2) Sign in as arbitrator 3) Review evidence 4) Confirm with Passkey 5) Submit final ruling. Action is irreversible after confirmation.',
|
|
2731
|
-
},
|
|
2732
|
-
};
|
|
2733
|
-
}
|
|
2734
2863
|
return { error: `未知 action:${action}。Valid: view | list_open | respond | add_evidence | arbitrate` };
|
|
2735
2864
|
}
|
|
2736
2865
|
// ─── 索赔验证(claim-verification)处理 — Wave 6 新增 ────────────
|
|
@@ -2819,8 +2948,44 @@ async function handleClaimVerify(args) {
|
|
|
2819
2948
|
return { error: `未知 action:${action}。Valid: create | view | mine | submit_seller_evidence | available | vote | eligibility | verifier_status | apply | withdraw_application | appeal` };
|
|
2820
2949
|
}
|
|
2821
2950
|
// ─── Skill 市场处理 ────────────────────────────────────────────
|
|
2822
|
-
function handleSkill(args) {
|
|
2951
|
+
async function handleSkill(args) {
|
|
2823
2952
|
const action = args.action;
|
|
2953
|
+
// RFC-003 Batch 3:NETWORK 模式 → webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地引擎。
|
|
2954
|
+
if (toolBackend('webaz_skill') === 'network') {
|
|
2955
|
+
const apiKey = String(args.api_key || '');
|
|
2956
|
+
if (action === 'list') {
|
|
2957
|
+
const qs = new URLSearchParams();
|
|
2958
|
+
if (args.skill_type)
|
|
2959
|
+
qs.set('skill_type', String(args.skill_type));
|
|
2960
|
+
if (args.query)
|
|
2961
|
+
qs.set('q', String(args.query));
|
|
2962
|
+
const q = qs.toString();
|
|
2963
|
+
return await apiCall('/api/skills' + (q ? '?' + q : ''), { apiKey });
|
|
2964
|
+
}
|
|
2965
|
+
if (!apiKey)
|
|
2966
|
+
return { error: 'api_key required' };
|
|
2967
|
+
if (action === 'publish') {
|
|
2968
|
+
return await apiCall('/api/skills', { method: 'POST', apiKey, body: {
|
|
2969
|
+
name: args.name, description: args.description, category: args.category,
|
|
2970
|
+
skill_type: args.skill_type, config: args.config,
|
|
2971
|
+
} });
|
|
2972
|
+
}
|
|
2973
|
+
if (action === 'subscribe') {
|
|
2974
|
+
if (!args.skill_id)
|
|
2975
|
+
return { error: '请提供 skill_id' };
|
|
2976
|
+
return await apiCall('/api/skills/' + encodeURIComponent(String(args.skill_id)) + '/subscribe', { method: 'POST', apiKey, body: { config: args.config } });
|
|
2977
|
+
}
|
|
2978
|
+
if (action === 'unsubscribe') {
|
|
2979
|
+
if (!args.skill_id)
|
|
2980
|
+
return { error: '请提供 skill_id' };
|
|
2981
|
+
return await apiCall('/api/skills/' + encodeURIComponent(String(args.skill_id)) + '/subscribe', { method: 'DELETE', apiKey });
|
|
2982
|
+
}
|
|
2983
|
+
if (action === 'my_skills')
|
|
2984
|
+
return await apiCall('/api/skills/mine', { apiKey });
|
|
2985
|
+
if (action === 'my_subs')
|
|
2986
|
+
return await apiCall('/api/skills/subscriptions', { apiKey });
|
|
2987
|
+
return { error: `未知 action:${action}。可选:list, publish, subscribe, unsubscribe, my_skills, my_subs` };
|
|
2988
|
+
}
|
|
2824
2989
|
// ── 浏览 Skill 市场 ────────────────────────────────────────
|
|
2825
2990
|
if (action === 'list') {
|
|
2826
2991
|
let userId;
|
|
@@ -2916,6 +3081,21 @@ function redactKey(key) {
|
|
|
2916
3081
|
return `${key.slice(0, 8)}***${key.slice(-4)}`;
|
|
2917
3082
|
}
|
|
2918
3083
|
function handleMyKey(args) {
|
|
3084
|
+
// RFC-003 Batch 1:NETWORK 模式下,账号找回是 Passkey 门控(Iron-Rule)——handle+permanent_code
|
|
3085
|
+
// 查询不作为网络端点暴露(防枚举)。诚实引导到 PWA 的 Passkey 找回流,不在本地假装查到。
|
|
3086
|
+
if (toolBackend('webaz_mykey') === 'network') {
|
|
3087
|
+
return {
|
|
3088
|
+
_mode: 'network',
|
|
3089
|
+
found: null,
|
|
3090
|
+
message: 'On the live network, account recovery is Passkey-gated for security (Iron-Rule). handle + permanent_code lookup is not exposed as a network endpoint (anti-enumeration).',
|
|
3091
|
+
recover: {
|
|
3092
|
+
via: 'PWA + Passkey',
|
|
3093
|
+
start_url: `${WEBAZ_API_URL}/recover`,
|
|
3094
|
+
note: 'Open in a browser and verify with your Passkey to recover or rotate your api_key.',
|
|
3095
|
+
},
|
|
3096
|
+
rotate_hint: 'Already have your api_key but want to replace it? Use webaz_rotate_key.',
|
|
3097
|
+
};
|
|
3098
|
+
}
|
|
2919
3099
|
const handle = args.handle?.trim();
|
|
2920
3100
|
const permaCode = args.permanent_code?.trim()?.toUpperCase();
|
|
2921
3101
|
if (!handle || !permaCode) {
|
|
@@ -2969,6 +3149,33 @@ function handleMyKey(args) {
|
|
|
2969
3149
|
async function handleProfile(args) {
|
|
2970
3150
|
const action = args.action;
|
|
2971
3151
|
const apiKey = String(args.api_key || '');
|
|
3152
|
+
// RFC-003 Batch 1:NETWORK 模式 → 全部 action 调 webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3153
|
+
if (toolBackend('webaz_profile') === 'network') {
|
|
3154
|
+
if (action === 'view_user') {
|
|
3155
|
+
if (!args.user_id)
|
|
3156
|
+
return { error: 'user_id required' };
|
|
3157
|
+
return await apiCall('/api/users/' + encodeURIComponent(String(args.user_id)), { apiKey });
|
|
3158
|
+
}
|
|
3159
|
+
if (action === 'feed') {
|
|
3160
|
+
if (!args.user_id || !args.feed)
|
|
3161
|
+
return { error: 'user_id + feed required' };
|
|
3162
|
+
const FEED_PATH = {
|
|
3163
|
+
secondhand: 'secondhand', auctions: 'auctions', reviews: 'reviews', products: 'products',
|
|
3164
|
+
shares: 'shareables', reputation: 'reputation', pv: 'pv-summary', liked: 'liked-shareables',
|
|
3165
|
+
};
|
|
3166
|
+
const seg = FEED_PATH[String(args.feed)];
|
|
3167
|
+
if (!seg)
|
|
3168
|
+
return { error: `unknown feed: ${args.feed}. options: ${Object.keys(FEED_PATH).join(', ')}` };
|
|
3169
|
+
return await apiCall('/api/users/' + encodeURIComponent(String(args.user_id)) + '/' + seg, { apiKey });
|
|
3170
|
+
}
|
|
3171
|
+
if (action === 'view')
|
|
3172
|
+
return await apiCall('/api/me', { apiKey });
|
|
3173
|
+
if (action === 'add_role')
|
|
3174
|
+
return await apiCall('/api/profile/add-role', { method: 'POST', apiKey, body: { role: args.role } });
|
|
3175
|
+
if (action === 'switch_role')
|
|
3176
|
+
return await apiCall('/api/profile/switch-role', { method: 'POST', apiKey, body: { role: args.role } });
|
|
3177
|
+
return { error: `Unknown action: ${action}. Options: view, add_role, switch_role, view_user, feed` };
|
|
3178
|
+
}
|
|
2972
3179
|
// 看他人公开主页 / 内容流
|
|
2973
3180
|
if (action === 'view_user') {
|
|
2974
3181
|
if (!args.user_id)
|
|
@@ -3036,6 +3243,26 @@ async function handleProfile(args) {
|
|
|
3036
3243
|
// 这里给 agent 两个"声明意图"工具:MCP 验 api_key 合法 → 返回 PWA URL 让用户 Passkey 二次确认。
|
|
3037
3244
|
// 真正改 DB 的动作放 PWA endpoint,跟 claim_verify / arbitrate 同模型。
|
|
3038
3245
|
function handleRevokeKey(args) {
|
|
3246
|
+
// RFC-003 Batch 5:NETWORK 模式 → 不本地校验 key(PWA 会鉴权),直接返回 Passkey 撤销指引。
|
|
3247
|
+
if (toolBackend('webaz_revoke_key') === 'network') {
|
|
3248
|
+
const apiKey = String(args.api_key || '');
|
|
3249
|
+
const reason = (args.reason || 'unspecified').trim().slice(0, 100);
|
|
3250
|
+
return {
|
|
3251
|
+
_mode: 'network',
|
|
3252
|
+
success: false,
|
|
3253
|
+
requires_human_action: true,
|
|
3254
|
+
iron_rule: 'API key revocation is a destructive, irreversible operation. Agent cannot execute unilaterally. Same gating as claim_verify and arbitrate.',
|
|
3255
|
+
action: 'revoke_api_key',
|
|
3256
|
+
api_key_hint: redactKey(apiKey),
|
|
3257
|
+
reason_logged: reason,
|
|
3258
|
+
next_step: {
|
|
3259
|
+
via: 'PWA + Passkey',
|
|
3260
|
+
url: 'https://webaz.xyz/revoke',
|
|
3261
|
+
instructions: '1) Open URL in browser 2) Sign in 3) Confirm with Passkey 4) Click "Revoke". After confirm the old api_key returns 401 on all tools.',
|
|
3262
|
+
warning: 'After revoke you cannot call any auth\'d tool until you have a new api_key. Use webaz_rotate_key instead for atomic invalidate + re-issue.',
|
|
3263
|
+
},
|
|
3264
|
+
};
|
|
3265
|
+
}
|
|
3039
3266
|
const auth = requireAuth(db, args.api_key);
|
|
3040
3267
|
if ('error' in auth)
|
|
3041
3268
|
return auth;
|
|
@@ -3058,6 +3285,25 @@ function handleRevokeKey(args) {
|
|
|
3058
3285
|
};
|
|
3059
3286
|
}
|
|
3060
3287
|
function handleRotateKey(args) {
|
|
3288
|
+
// RFC-003 Batch 5:NETWORK 模式 → 不本地校验 key(PWA 会鉴权),直接返回 Passkey 轮换指引。
|
|
3289
|
+
if (toolBackend('webaz_rotate_key') === 'network') {
|
|
3290
|
+
const apiKey = String(args.api_key || '');
|
|
3291
|
+
const reason = (args.reason || 'rotation').trim().slice(0, 100);
|
|
3292
|
+
return {
|
|
3293
|
+
_mode: 'network',
|
|
3294
|
+
success: false,
|
|
3295
|
+
requires_human_action: true,
|
|
3296
|
+
iron_rule: 'API key rotation requires Passkey verification (Iron-Rule). MCP registers intent only.',
|
|
3297
|
+
action: 'rotate_api_key',
|
|
3298
|
+
old_api_key_hint: redactKey(apiKey),
|
|
3299
|
+
reason_logged: reason,
|
|
3300
|
+
next_step: {
|
|
3301
|
+
via: 'PWA + Passkey',
|
|
3302
|
+
url: 'https://webaz.xyz/rotate',
|
|
3303
|
+
instructions: '1) Open URL 2) Sign in 3) Confirm with Passkey 4) PWA returns new api_key — copy immediately, shown once. Old key invalidated atomically with new key issuance.',
|
|
3304
|
+
},
|
|
3305
|
+
};
|
|
3306
|
+
}
|
|
3061
3307
|
const auth = requireAuth(db, args.api_key);
|
|
3062
3308
|
if ('error' in auth)
|
|
3063
3309
|
return auth;
|
|
@@ -3079,7 +3325,14 @@ function handleRotateKey(args) {
|
|
|
3079
3325
|
};
|
|
3080
3326
|
}
|
|
3081
3327
|
// ─── 推广 / 双轨 (Tokenomics) ───────────────────────────────────
|
|
3082
|
-
function handleReferral(args) {
|
|
3328
|
+
async function handleReferral(args) {
|
|
3329
|
+
// RFC-003 Batch 2:NETWORK 模式 → webaz.xyz 真网络聚合(Bearer api_key);SANDBOX 走本地。
|
|
3330
|
+
if (toolBackend('webaz_referral') === 'network') {
|
|
3331
|
+
const apiKey = String(args.api_key || '');
|
|
3332
|
+
if (!apiKey)
|
|
3333
|
+
return { error: 'api_key required' };
|
|
3334
|
+
return await apiCall('/api/referral/me', { apiKey });
|
|
3335
|
+
}
|
|
3083
3336
|
const auth = requireAuth(db, args.api_key);
|
|
3084
3337
|
if ('error' in auth)
|
|
3085
3338
|
return auth;
|
|
@@ -3175,7 +3428,19 @@ function handleReferral(args) {
|
|
|
3175
3428
|
: 'Complete 1 purchase first, then your share link will earn 3-tier commission. Until then, your share builds points-matching only.',
|
|
3176
3429
|
};
|
|
3177
3430
|
}
|
|
3178
|
-
function handleShareLink(args) {
|
|
3431
|
+
async function handleShareLink(args) {
|
|
3432
|
+
// RFC-003 #1122:NETWORK 模式 → 调 webaz.xyz 的 /api/share-link(服务端同款计算);SANDBOX 走本地。
|
|
3433
|
+
if (toolBackend('webaz_share_link') === 'network') {
|
|
3434
|
+
const apiKey = String(args.api_key || '');
|
|
3435
|
+
if (!apiKey)
|
|
3436
|
+
return { error: 'api_key required' };
|
|
3437
|
+
if (!args.product_id)
|
|
3438
|
+
return { error: 'product_id required' };
|
|
3439
|
+
const qs = new URLSearchParams({ product_id: String(args.product_id) });
|
|
3440
|
+
if (args.side)
|
|
3441
|
+
qs.set('side', String(args.side));
|
|
3442
|
+
return await apiCall('/api/share-link?' + qs.toString(), { apiKey });
|
|
3443
|
+
}
|
|
3179
3444
|
const auth = requireAuth(db, args.api_key);
|
|
3180
3445
|
if ('error' in auth)
|
|
3181
3446
|
return auth;
|
|
@@ -3259,7 +3524,24 @@ function handleShareLink(args) {
|
|
|
3259
3524
|
};
|
|
3260
3525
|
}
|
|
3261
3526
|
// ─── 黑名单 / 关注 / 雷达 / 默认地址 / shareables ─────────
|
|
3262
|
-
function handleBlocklist(args) {
|
|
3527
|
+
async function handleBlocklist(args) {
|
|
3528
|
+
// RFC-003 Batch 2:NETWORK 模式 → webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3529
|
+
if (toolBackend('webaz_blocklist') === 'network') {
|
|
3530
|
+
const apiKey = String(args.api_key || '');
|
|
3531
|
+
if (!apiKey)
|
|
3532
|
+
return { error: 'api_key required' };
|
|
3533
|
+
const act = String(args.action || '');
|
|
3534
|
+
if (act === 'list')
|
|
3535
|
+
return await apiCall('/api/blocklist', { apiKey });
|
|
3536
|
+
const uid = args.user_id ? encodeURIComponent(String(args.user_id)) : '';
|
|
3537
|
+
if (!uid)
|
|
3538
|
+
return { error: 'user_id required for block/unblock' };
|
|
3539
|
+
if (act === 'block')
|
|
3540
|
+
return await apiCall('/api/blocklist/' + uid, { method: 'POST', apiKey, body: { reason: args.reason } });
|
|
3541
|
+
if (act === 'unblock')
|
|
3542
|
+
return await apiCall('/api/blocklist/' + uid, { method: 'DELETE', apiKey });
|
|
3543
|
+
return { error: `unknown action: ${act}` };
|
|
3544
|
+
}
|
|
3263
3545
|
const auth = requireAuth(db, args.api_key);
|
|
3264
3546
|
if ('error' in auth)
|
|
3265
3547
|
return auth;
|
|
@@ -3296,7 +3578,26 @@ function handleBlocklist(args) {
|
|
|
3296
3578
|
}
|
|
3297
3579
|
return { error: `unknown action: ${action}` };
|
|
3298
3580
|
}
|
|
3299
|
-
function handleFollows(args) {
|
|
3581
|
+
async function handleFollows(args) {
|
|
3582
|
+
// RFC-003 Batch 2:NETWORK 模式 → webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3583
|
+
if (toolBackend('webaz_follows') === 'network') {
|
|
3584
|
+
const apiKey = String(args.api_key || '');
|
|
3585
|
+
if (!apiKey)
|
|
3586
|
+
return { error: 'api_key required' };
|
|
3587
|
+
const act = String(args.action || '');
|
|
3588
|
+
if (act === 'list')
|
|
3589
|
+
return await apiCall('/api/follows/me', { apiKey });
|
|
3590
|
+
const uid = args.user_id ? encodeURIComponent(String(args.user_id)) : '';
|
|
3591
|
+
if (!uid)
|
|
3592
|
+
return { error: 'user_id required' };
|
|
3593
|
+
if (act === 'follow')
|
|
3594
|
+
return await apiCall('/api/follows/' + uid, { method: 'POST', apiKey });
|
|
3595
|
+
if (act === 'unfollow')
|
|
3596
|
+
return await apiCall('/api/follows/' + uid, { method: 'DELETE', apiKey });
|
|
3597
|
+
if (act === 'status')
|
|
3598
|
+
return await apiCall('/api/follows/' + uid + '/status', { apiKey });
|
|
3599
|
+
return { error: `unknown action: ${act}` };
|
|
3600
|
+
}
|
|
3300
3601
|
const auth = requireAuth(db, args.api_key);
|
|
3301
3602
|
if ('error' in auth)
|
|
3302
3603
|
return auth;
|
|
@@ -3342,12 +3643,32 @@ function handleFollows(args) {
|
|
|
3342
3643
|
}
|
|
3343
3644
|
return { error: `unknown action: ${action}` };
|
|
3344
3645
|
}
|
|
3345
|
-
function handleNearby(args) {
|
|
3646
|
+
async function handleNearby(args) {
|
|
3647
|
+
const action = String(args.action || '');
|
|
3648
|
+
// RFC-003 Batch 1:NETWORK 模式 → 调 webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3649
|
+
if (toolBackend('webaz_nearby') === 'network') {
|
|
3650
|
+
const apiKey = String(args.api_key || '');
|
|
3651
|
+
if (!apiKey)
|
|
3652
|
+
return { error: 'api_key required' };
|
|
3653
|
+
if (action === 'set_location')
|
|
3654
|
+
return await apiCall('/api/profile/set-location', { method: 'POST', apiKey, body: { lat: args.lat, lng: args.lng } });
|
|
3655
|
+
if (action === 'clear_location')
|
|
3656
|
+
return await apiCall('/api/profile/clear-location', { method: 'POST', apiKey });
|
|
3657
|
+
if (action === 'query') {
|
|
3658
|
+
const qs = new URLSearchParams();
|
|
3659
|
+
if (args.scope)
|
|
3660
|
+
qs.set('scope', String(args.scope));
|
|
3661
|
+
if (args.window)
|
|
3662
|
+
qs.set('window', String(args.window));
|
|
3663
|
+
const q = qs.toString();
|
|
3664
|
+
return await apiCall('/api/nearby' + (q ? '?' + q : ''), { apiKey });
|
|
3665
|
+
}
|
|
3666
|
+
return { error: `unknown action: ${action}` };
|
|
3667
|
+
}
|
|
3346
3668
|
const auth = requireAuth(db, args.api_key);
|
|
3347
3669
|
if ('error' in auth)
|
|
3348
3670
|
return auth;
|
|
3349
3671
|
const { user } = auth;
|
|
3350
|
-
const action = String(args.action || '');
|
|
3351
3672
|
if (action === 'set_location') {
|
|
3352
3673
|
const lat = Number(args.lat), lng = Number(args.lng);
|
|
3353
3674
|
if (!Number.isFinite(lat) || !Number.isFinite(lng))
|
|
@@ -3392,7 +3713,28 @@ function handleNearby(args) {
|
|
|
3392
3713
|
}
|
|
3393
3714
|
return { error: `unknown action: ${action}` };
|
|
3394
3715
|
}
|
|
3395
|
-
function handleDefaultAddress(args) {
|
|
3716
|
+
async function handleDefaultAddress(args) {
|
|
3717
|
+
// RFC-003 Batch 2:NETWORK 模式 → webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3718
|
+
if (toolBackend('webaz_default_address') === 'network') {
|
|
3719
|
+
const apiKey = String(args.api_key || '');
|
|
3720
|
+
if (!apiKey)
|
|
3721
|
+
return { error: 'api_key required' };
|
|
3722
|
+
const act = String(args.action || '');
|
|
3723
|
+
if (act === 'read') {
|
|
3724
|
+
const me = await apiCall('/api/me', { apiKey });
|
|
3725
|
+
if (me.error)
|
|
3726
|
+
return me;
|
|
3727
|
+
return { address_text: me.default_address_text ?? null, address_region: me.default_address_region ?? null };
|
|
3728
|
+
}
|
|
3729
|
+
if (act === 'set') {
|
|
3730
|
+
const text = (args.text || '').trim().slice(0, 200);
|
|
3731
|
+
const region = (args.region || '').trim().slice(0, 40);
|
|
3732
|
+
if (!text)
|
|
3733
|
+
return { error: 'missing_text', error_code: 'TEXT_REQUIRED', message: 'action=set 需要 "text" 字段(自由格式地址,≤200);可选 "region"。' };
|
|
3734
|
+
return await apiCall('/api/profile/default-address', { method: 'POST', apiKey, body: { text, region: region || null } });
|
|
3735
|
+
}
|
|
3736
|
+
return { error: `unknown action: ${act}` };
|
|
3737
|
+
}
|
|
3396
3738
|
const auth = requireAuth(db, args.api_key);
|
|
3397
3739
|
if ('error' in auth)
|
|
3398
3740
|
return auth;
|
|
@@ -3430,12 +3772,42 @@ function handleDefaultAddress(args) {
|
|
|
3430
3772
|
}
|
|
3431
3773
|
return { error: `unknown action: ${action}` };
|
|
3432
3774
|
}
|
|
3433
|
-
function handleShareables(args) {
|
|
3775
|
+
async function handleShareables(args) {
|
|
3776
|
+
const action = String(args.action || '');
|
|
3777
|
+
// RFC-003 Batch 1:NETWORK 模式 → 调 webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3778
|
+
if (toolBackend('webaz_shareables') === 'network') {
|
|
3779
|
+
const apiKey = String(args.api_key || '');
|
|
3780
|
+
if (!apiKey)
|
|
3781
|
+
return { error: 'api_key required' };
|
|
3782
|
+
if (action === 'list_mine')
|
|
3783
|
+
return await apiCall('/api/shareables/me', { apiKey });
|
|
3784
|
+
if (action === 'by_product') {
|
|
3785
|
+
if (!args.related_product_id)
|
|
3786
|
+
return { error: 'related_product_id required' };
|
|
3787
|
+
return await apiCall('/api/shareables/by-product/' + encodeURIComponent(String(args.related_product_id)), { apiKey });
|
|
3788
|
+
}
|
|
3789
|
+
if (action === 'by_anchor') {
|
|
3790
|
+
if (!args.related_anchor)
|
|
3791
|
+
return { error: 'related_anchor required' };
|
|
3792
|
+
return await apiCall('/api/shareables/by-anchor/' + encodeURIComponent(String(args.related_anchor)), { apiKey });
|
|
3793
|
+
}
|
|
3794
|
+
if (action === 'add') {
|
|
3795
|
+
return await apiCall('/api/shareables', { method: 'POST', apiKey, body: {
|
|
3796
|
+
external_url: args.external_url, related_product_id: args.related_product_id,
|
|
3797
|
+
related_anchor: args.related_anchor, title: args.title, description: args.description,
|
|
3798
|
+
} });
|
|
3799
|
+
}
|
|
3800
|
+
if (action === 'delete') {
|
|
3801
|
+
if (!args.shareable_id)
|
|
3802
|
+
return { error: 'shareable_id required' };
|
|
3803
|
+
return await apiCall('/api/shareables/' + encodeURIComponent(String(args.shareable_id)), { method: 'DELETE', apiKey });
|
|
3804
|
+
}
|
|
3805
|
+
return { error: `unknown action: ${action}` };
|
|
3806
|
+
}
|
|
3434
3807
|
const auth = requireAuth(db, args.api_key);
|
|
3435
3808
|
if ('error' in auth)
|
|
3436
3809
|
return auth;
|
|
3437
3810
|
const { user } = auth;
|
|
3438
|
-
const action = String(args.action || '');
|
|
3439
3811
|
if (action === 'list_mine') {
|
|
3440
3812
|
const rows = db.prepare(`
|
|
3441
3813
|
SELECT s.*, p.title as product_title FROM shareables s
|
|
@@ -3547,6 +3919,11 @@ async function readEndpoint(tool, subpath) {
|
|
|
3547
3919
|
}
|
|
3548
3920
|
}
|
|
3549
3921
|
async function pwaApi(method, path, apiKey, body) {
|
|
3922
|
+
// RFC-003:NETWORK 模式 → 走 webaz.xyz 共享网络(Bearer api_key)。仅 NETWORK_TOOLS 里的工具会到这里
|
|
3923
|
+
// (其余未迁工具在 dispatch 被 Batch 0 守卫拦下);SANDBOX 模式仍转发本地 PWA(localhost)。
|
|
3924
|
+
if (MODE === 'network') {
|
|
3925
|
+
return apiCall(path.startsWith('/api') ? path : '/api' + path, { method, apiKey, body });
|
|
3926
|
+
}
|
|
3550
3927
|
const opts = {
|
|
3551
3928
|
method,
|
|
3552
3929
|
headers: {
|
|
@@ -3589,9 +3966,12 @@ async function handleSecondhand(args) {
|
|
|
3589
3966
|
if (!isPublic) {
|
|
3590
3967
|
if (!apiKey)
|
|
3591
3968
|
return { error: 'api_key required' };
|
|
3592
|
-
|
|
3593
|
-
if ('
|
|
3594
|
-
|
|
3969
|
+
// NETWORK 模式由 webaz.xyz 端点鉴权;只有 SANDBOX 才查本地库(否则真网络用户的 key 不在本地会误拒)。
|
|
3970
|
+
if (toolBackend('webaz_secondhand') !== 'network') {
|
|
3971
|
+
const auth = requireAuth(db, apiKey);
|
|
3972
|
+
if ('error' in auth)
|
|
3973
|
+
return auth;
|
|
3974
|
+
}
|
|
3595
3975
|
}
|
|
3596
3976
|
const iid = () => encodeURIComponent(String(args.item_id || ''));
|
|
3597
3977
|
switch (action) {
|
|
@@ -3657,9 +4037,11 @@ async function handleTrial(args) {
|
|
|
3657
4037
|
if (!isPublic) {
|
|
3658
4038
|
if (!apiKey)
|
|
3659
4039
|
return { error: 'api_key required' };
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
4040
|
+
if (toolBackend('webaz_trial') !== 'network') {
|
|
4041
|
+
const auth = requireAuth(db, apiKey);
|
|
4042
|
+
if ('error' in auth)
|
|
4043
|
+
return auth;
|
|
4044
|
+
}
|
|
3663
4045
|
}
|
|
3664
4046
|
const pid = () => encodeURIComponent(String(args.product_id || ''));
|
|
3665
4047
|
switch (action) {
|
|
@@ -3708,9 +4090,11 @@ async function handleSkillMarket(args) {
|
|
|
3708
4090
|
if (!isPublic) {
|
|
3709
4091
|
if (!apiKey)
|
|
3710
4092
|
return { error: 'api_key required' };
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
4093
|
+
if (toolBackend('webaz_skill_market') !== 'network') {
|
|
4094
|
+
const auth = requireAuth(db, apiKey);
|
|
4095
|
+
if ('error' in auth)
|
|
4096
|
+
return auth;
|
|
4097
|
+
}
|
|
3714
4098
|
}
|
|
3715
4099
|
const sid = () => encodeURIComponent(String(args.skill_id || ''));
|
|
3716
4100
|
switch (action) {
|
|
@@ -3777,9 +4161,11 @@ async function handleRfq(args) {
|
|
|
3777
4161
|
const action = String(args.action || '');
|
|
3778
4162
|
if (!apiKey)
|
|
3779
4163
|
return { error: 'api_key required' };
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
4164
|
+
if (toolBackend('webaz_rfq') !== 'network') {
|
|
4165
|
+
const auth = requireAuth(db, apiKey);
|
|
4166
|
+
if ('error' in auth)
|
|
4167
|
+
return auth;
|
|
4168
|
+
}
|
|
3783
4169
|
switch (action) {
|
|
3784
4170
|
case 'create': return await pwaApi('POST', '/rfqs', apiKey, {
|
|
3785
4171
|
title: args.title, qty: args.qty, max_price: args.max_price,
|
|
@@ -3824,6 +4210,36 @@ async function handleBid(args) {
|
|
|
3824
4210
|
const action = String(args.action || '');
|
|
3825
4211
|
if (!apiKey)
|
|
3826
4212
|
return { error: 'api_key required' };
|
|
4213
|
+
// RFC-003 Batch 4:NETWORK 模式 → webaz.xyz 真网络(Bearer api_key);质押由服务端结算。
|
|
4214
|
+
if (toolBackend('webaz_bid') === 'network') {
|
|
4215
|
+
if (action === 'submit') {
|
|
4216
|
+
if (!args.rfq_id || !args.price)
|
|
4217
|
+
return { error: 'rfq_id + price required' };
|
|
4218
|
+
return await apiCall('/api/rfqs/' + encodeURIComponent(String(args.rfq_id)) + '/bids', { method: 'POST', apiKey, body: {
|
|
4219
|
+
price: args.price, qty_offered: args.qty_offered, eta_hours: args.eta_hours,
|
|
4220
|
+
fulfillment_type: args.fulfillment_type ?? 'standard', note: args.note, offer_id: args.offer_id,
|
|
4221
|
+
} });
|
|
4222
|
+
}
|
|
4223
|
+
if (action === 'patch') {
|
|
4224
|
+
if (!args.bid_id)
|
|
4225
|
+
return { error: 'bid_id required' };
|
|
4226
|
+
const body = {};
|
|
4227
|
+
for (const k of ['price', 'qty_offered', 'eta_hours', 'fulfillment_type', 'note'])
|
|
4228
|
+
if (args[k] !== undefined)
|
|
4229
|
+
body[k] = args[k];
|
|
4230
|
+
return await apiCall('/api/bids/' + encodeURIComponent(String(args.bid_id)), { method: 'PATCH', apiKey, body });
|
|
4231
|
+
}
|
|
4232
|
+
if (action === 'cancel') {
|
|
4233
|
+
if (!args.bid_id)
|
|
4234
|
+
return { error: 'bid_id required' };
|
|
4235
|
+
return await apiCall('/api/bids/' + encodeURIComponent(String(args.bid_id)), { method: 'DELETE', apiKey });
|
|
4236
|
+
}
|
|
4237
|
+
if (action === 'list_mine') {
|
|
4238
|
+
return { _mode: 'network', not_available_on_network: true,
|
|
4239
|
+
error: 'webaz_bid list_mine 暂无网络端点(webaz.xyz 未提供 my-bids GET)。请用 webaz_rfq action=detail 查看具体 RFQ 的出价,或到 PWA 查看。 / no my-bids GET on the network yet; use webaz_rfq detail or the PWA.' };
|
|
4240
|
+
}
|
|
4241
|
+
return { error: `unknown action: ${action}` };
|
|
4242
|
+
}
|
|
3827
4243
|
const auth = requireAuth(db, apiKey);
|
|
3828
4244
|
if ('error' in auth)
|
|
3829
4245
|
return auth;
|
|
@@ -3868,9 +4284,11 @@ async function handleChat(args) {
|
|
|
3868
4284
|
const action = String(args.action || '');
|
|
3869
4285
|
if (!apiKey)
|
|
3870
4286
|
return { error: 'api_key required' };
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
4287
|
+
if (toolBackend('webaz_chat') !== 'network') {
|
|
4288
|
+
const auth = requireAuth(db, apiKey);
|
|
4289
|
+
if ('error' in auth)
|
|
4290
|
+
return auth;
|
|
4291
|
+
}
|
|
3874
4292
|
switch (action) {
|
|
3875
4293
|
case 'start': {
|
|
3876
4294
|
if (!args.kind || !args.context_id)
|
|
@@ -3908,6 +4326,52 @@ async function handleAutoBidSkill(args) {
|
|
|
3908
4326
|
const action = String(args.action || '');
|
|
3909
4327
|
if (!apiKey)
|
|
3910
4328
|
return { error: 'api_key required' };
|
|
4329
|
+
const buildAutoBidConfig = () => ({
|
|
4330
|
+
enabled: args.enabled !== false,
|
|
4331
|
+
categories: Array.isArray(args.categories) ? args.categories : ['standard'],
|
|
4332
|
+
regions: Array.isArray(args.regions) ? args.regions : [],
|
|
4333
|
+
max_eta_h: Number(args.max_eta_h ?? 24),
|
|
4334
|
+
fulfillment_type: String(args.fulfillment_type ?? 'standard'),
|
|
4335
|
+
bid_strategy: String(args.bid_strategy ?? 'cheapest_undercut'),
|
|
4336
|
+
undercut_pct: Math.max(0, Math.min(0.5, Number(args.undercut_pct ?? 0.05))),
|
|
4337
|
+
max_price_cap: args.max_price_cap ?? null,
|
|
4338
|
+
daily_cap: Number(args.daily_cap ?? 20),
|
|
4339
|
+
cooldown_min: Number(args.cooldown_min ?? 60),
|
|
4340
|
+
});
|
|
4341
|
+
// RFC-003 Batch 4:NETWORK 模式 → 先从 webaz.xyz 取既有 auto_bid skill,再 PATCH/POST/disable。
|
|
4342
|
+
if (toolBackend('webaz_auto_bid') === 'network') {
|
|
4343
|
+
const mine = await apiCall('/api/skills/mine', { apiKey });
|
|
4344
|
+
if (mine.error)
|
|
4345
|
+
return mine;
|
|
4346
|
+
const list = (Array.isArray(mine) ? mine : (Array.isArray(mine.skills) ? mine.skills : []));
|
|
4347
|
+
const existingNet = list.find(s => s.skill_type === 'auto_bid');
|
|
4348
|
+
if (action === 'get') {
|
|
4349
|
+
if (!existingNet)
|
|
4350
|
+
return { exists: false };
|
|
4351
|
+
let cfg = existingNet.config;
|
|
4352
|
+
if (typeof cfg === 'string') {
|
|
4353
|
+
try {
|
|
4354
|
+
cfg = JSON.parse(cfg || '{}');
|
|
4355
|
+
}
|
|
4356
|
+
catch {
|
|
4357
|
+
cfg = {};
|
|
4358
|
+
}
|
|
4359
|
+
}
|
|
4360
|
+
return { exists: true, id: existingNet.id, active: existingNet.active, config: cfg };
|
|
4361
|
+
}
|
|
4362
|
+
if (action === 'set') {
|
|
4363
|
+
const config = buildAutoBidConfig();
|
|
4364
|
+
if (existingNet)
|
|
4365
|
+
return await apiCall('/api/skills/' + encodeURIComponent(String(existingNet.id)), { method: 'PATCH', apiKey, body: { config, active: config.enabled ? 1 : 0 } });
|
|
4366
|
+
return await apiCall('/api/skills', { method: 'POST', apiKey, body: { name: '我的自动报价 (MCP)', description: 'auto_bid via MCP', category: 'rfq', skill_type: 'auto_bid', config } });
|
|
4367
|
+
}
|
|
4368
|
+
if (action === 'disable') {
|
|
4369
|
+
if (!existingNet)
|
|
4370
|
+
return { error: '尚未创建 auto_bid Skill' };
|
|
4371
|
+
return await apiCall('/api/skills/' + encodeURIComponent(String(existingNet.id)) + '/disable', { method: 'POST', apiKey });
|
|
4372
|
+
}
|
|
4373
|
+
return { error: `unknown action: ${action}` };
|
|
4374
|
+
}
|
|
3911
4375
|
const auth = requireAuth(db, apiKey);
|
|
3912
4376
|
if ('error' in auth)
|
|
3913
4377
|
return auth;
|
|
@@ -3959,6 +4423,7 @@ async function handlePriceHistory(args) {
|
|
|
3959
4423
|
}
|
|
3960
4424
|
async function handleCharity(args) {
|
|
3961
4425
|
const action = String(args.action || '');
|
|
4426
|
+
// RFC-003 Batch 4:公开读走 readEndpoint(network → webaz.xyz / sandbox → 本地);写走 pwaApi(mode-aware)。
|
|
3962
4427
|
if (action === 'list') {
|
|
3963
4428
|
const qs = new URLSearchParams();
|
|
3964
4429
|
if (args.category)
|
|
@@ -3967,58 +4432,27 @@ async function handleCharity(args) {
|
|
|
3967
4432
|
qs.set('target_kind', String(args.target_kind));
|
|
3968
4433
|
if (args.limit)
|
|
3969
4434
|
qs.set('limit', String(args.limit));
|
|
3970
|
-
|
|
3971
|
-
const r = await fetch(PWA_API_BASE + '/wishes' + (qs.toString() ? '?' + qs : ''));
|
|
3972
|
-
return await r.json();
|
|
3973
|
-
}
|
|
3974
|
-
catch (e) {
|
|
3975
|
-
return { error: String(e.message) };
|
|
3976
|
-
}
|
|
4435
|
+
return readEndpoint('webaz_charity', '/wishes' + (qs.toString() ? '?' + qs : ''));
|
|
3977
4436
|
}
|
|
3978
4437
|
if (action === 'detail') {
|
|
3979
4438
|
if (!args.wish_id)
|
|
3980
4439
|
return { error: 'wish_id required' };
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
if (action === 'stories') {
|
|
3990
|
-
try {
|
|
3991
|
-
const r = await fetch(PWA_API_BASE + '/charity/stories');
|
|
3992
|
-
return await r.json();
|
|
3993
|
-
}
|
|
3994
|
-
catch (e) {
|
|
3995
|
-
return { error: String(e.message) };
|
|
3996
|
-
}
|
|
3997
|
-
}
|
|
3998
|
-
if (action === 'leaderboard') {
|
|
3999
|
-
try {
|
|
4000
|
-
const r = await fetch(PWA_API_BASE + '/charity/leaderboard');
|
|
4001
|
-
return await r.json();
|
|
4002
|
-
}
|
|
4003
|
-
catch (e) {
|
|
4004
|
-
return { error: String(e.message) };
|
|
4005
|
-
}
|
|
4006
|
-
}
|
|
4007
|
-
if (action === 'fund') {
|
|
4008
|
-
try {
|
|
4009
|
-
const r = await fetch(PWA_API_BASE + '/charity/fund');
|
|
4010
|
-
return await r.json();
|
|
4011
|
-
}
|
|
4012
|
-
catch (e) {
|
|
4013
|
-
return { error: String(e.message) };
|
|
4014
|
-
}
|
|
4015
|
-
}
|
|
4440
|
+
return readEndpoint('webaz_charity', '/wishes/' + encodeURIComponent(String(args.wish_id)));
|
|
4441
|
+
}
|
|
4442
|
+
if (action === 'stories')
|
|
4443
|
+
return readEndpoint('webaz_charity', '/charity/stories');
|
|
4444
|
+
if (action === 'leaderboard')
|
|
4445
|
+
return readEndpoint('webaz_charity', '/charity/leaderboard');
|
|
4446
|
+
if (action === 'fund')
|
|
4447
|
+
return readEndpoint('webaz_charity', '/charity/fund');
|
|
4016
4448
|
const apiKey = String(args.api_key || '');
|
|
4017
4449
|
if (!apiKey)
|
|
4018
4450
|
return { error: 'api_key required for this action' };
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4451
|
+
if (toolBackend('webaz_charity') !== 'network') {
|
|
4452
|
+
const auth = requireAuth(db, apiKey);
|
|
4453
|
+
if ('error' in auth)
|
|
4454
|
+
return auth;
|
|
4455
|
+
}
|
|
4022
4456
|
if (action === 'create') {
|
|
4023
4457
|
return await pwaApi('POST', '/wishes', apiKey, {
|
|
4024
4458
|
title: args.title, content: args.content, category: args.category,
|
|
@@ -4106,9 +4540,11 @@ async function handleLike(args) {
|
|
|
4106
4540
|
const sid = String(args.shareable_id || '');
|
|
4107
4541
|
if (!apiKey || !sid)
|
|
4108
4542
|
return { error: 'api_key + shareable_id required' };
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4543
|
+
if (toolBackend('webaz_like') !== 'network') {
|
|
4544
|
+
const auth = requireAuth(db, apiKey);
|
|
4545
|
+
if ('error' in auth)
|
|
4546
|
+
return auth;
|
|
4547
|
+
}
|
|
4112
4548
|
if (action === 'toggle')
|
|
4113
4549
|
return await pwaApi('POST', '/shareables/' + encodeURIComponent(sid) + '/like', apiKey);
|
|
4114
4550
|
if (action === 'status')
|
|
@@ -4129,9 +4565,11 @@ async function handleAuction(args) {
|
|
|
4129
4565
|
const action = String(args.action || '');
|
|
4130
4566
|
if (!apiKey)
|
|
4131
4567
|
return { error: 'api_key required' };
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4568
|
+
if (toolBackend('webaz_auction') !== 'network') {
|
|
4569
|
+
const auth = requireAuth(db, apiKey);
|
|
4570
|
+
if ('error' in auth)
|
|
4571
|
+
return auth;
|
|
4572
|
+
}
|
|
4135
4573
|
switch (action) {
|
|
4136
4574
|
case 'create': return await pwaApi('POST', '/auctions', apiKey, {
|
|
4137
4575
|
title: args.title, qty: args.qty, category: args.category,
|
|
@@ -4407,126 +4845,134 @@ export async function startMCPServer() {
|
|
|
4407
4845
|
const t0 = Date.now();
|
|
4408
4846
|
let result;
|
|
4409
4847
|
try {
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
break;
|
|
4417
|
-
case 'webaz_search':
|
|
4418
|
-
result = await handleSearch(args);
|
|
4419
|
-
break;
|
|
4420
|
-
case 'webaz_verify_price':
|
|
4421
|
-
result = await handleVerifyPrice(args);
|
|
4422
|
-
break;
|
|
4423
|
-
case 'webaz_list_product':
|
|
4424
|
-
result = await handleListProduct(args);
|
|
4425
|
-
break;
|
|
4426
|
-
case 'webaz_place_order':
|
|
4427
|
-
result = await handlePlaceOrder(args);
|
|
4428
|
-
break;
|
|
4429
|
-
case 'webaz_update_order':
|
|
4430
|
-
result = await handleUpdateOrder(args);
|
|
4431
|
-
break;
|
|
4432
|
-
case 'webaz_get_status':
|
|
4433
|
-
result = await handleGetStatus(args);
|
|
4434
|
-
break;
|
|
4435
|
-
case 'webaz_feedback':
|
|
4436
|
-
result = await handleFeedback(args);
|
|
4437
|
-
break;
|
|
4438
|
-
case 'webaz_contribute':
|
|
4439
|
-
result = await handleContribute(args);
|
|
4440
|
-
break;
|
|
4441
|
-
case 'webaz_wallet':
|
|
4442
|
-
result = await handleWallet(args);
|
|
4443
|
-
break;
|
|
4444
|
-
case 'webaz_dispute':
|
|
4445
|
-
result = await handleDispute(args);
|
|
4446
|
-
break;
|
|
4447
|
-
case 'webaz_claim_verify':
|
|
4448
|
-
result = await handleClaimVerify(args);
|
|
4449
|
-
break;
|
|
4450
|
-
case 'webaz_notifications':
|
|
4451
|
-
result = handleNotifications(args);
|
|
4452
|
-
break;
|
|
4453
|
-
case 'webaz_skill':
|
|
4454
|
-
result = handleSkill(args);
|
|
4455
|
-
break;
|
|
4456
|
-
case 'webaz_skill_market':
|
|
4457
|
-
result = await handleSkillMarket(args);
|
|
4458
|
-
break;
|
|
4459
|
-
case 'webaz_secondhand':
|
|
4460
|
-
result = await handleSecondhand(args);
|
|
4461
|
-
break;
|
|
4462
|
-
case 'webaz_trial':
|
|
4463
|
-
result = await handleTrial(args);
|
|
4464
|
-
break;
|
|
4465
|
-
case 'webaz_mykey':
|
|
4466
|
-
result = handleMyKey(args);
|
|
4467
|
-
break;
|
|
4468
|
-
case 'webaz_profile':
|
|
4469
|
-
result = await handleProfile(args);
|
|
4470
|
-
break;
|
|
4471
|
-
case 'webaz_revoke_key':
|
|
4472
|
-
result = handleRevokeKey(args);
|
|
4473
|
-
break;
|
|
4474
|
-
case 'webaz_rotate_key':
|
|
4475
|
-
result = handleRotateKey(args);
|
|
4476
|
-
break;
|
|
4477
|
-
case 'webaz_referral':
|
|
4478
|
-
result = handleReferral(args);
|
|
4479
|
-
break;
|
|
4480
|
-
case 'webaz_share_link':
|
|
4481
|
-
result = handleShareLink(args);
|
|
4482
|
-
break;
|
|
4483
|
-
case 'webaz_blocklist':
|
|
4484
|
-
result = handleBlocklist(args);
|
|
4485
|
-
break;
|
|
4486
|
-
case 'webaz_follows':
|
|
4487
|
-
result = handleFollows(args);
|
|
4488
|
-
break;
|
|
4489
|
-
case 'webaz_nearby':
|
|
4490
|
-
result = handleNearby(args);
|
|
4491
|
-
break;
|
|
4492
|
-
case 'webaz_default_address':
|
|
4493
|
-
result = handleDefaultAddress(args);
|
|
4494
|
-
break;
|
|
4495
|
-
case 'webaz_shareables':
|
|
4496
|
-
result = handleShareables(args);
|
|
4497
|
-
break;
|
|
4498
|
-
case 'webaz_rfq':
|
|
4499
|
-
result = await handleRfq(args);
|
|
4500
|
-
break;
|
|
4501
|
-
case 'webaz_bid':
|
|
4502
|
-
result = await handleBid(args);
|
|
4503
|
-
break;
|
|
4504
|
-
case 'webaz_chat':
|
|
4505
|
-
result = await handleChat(args);
|
|
4506
|
-
break;
|
|
4507
|
-
case 'webaz_auto_bid':
|
|
4508
|
-
result = await handleAutoBidSkill(args);
|
|
4509
|
-
break;
|
|
4510
|
-
case 'webaz_auction':
|
|
4511
|
-
result = await handleAuction(args);
|
|
4512
|
-
break;
|
|
4513
|
-
case 'webaz_like':
|
|
4514
|
-
result = await handleLike(args);
|
|
4515
|
-
break;
|
|
4516
|
-
case 'webaz_p2p_product':
|
|
4517
|
-
result = await handleP2pProduct(args);
|
|
4518
|
-
break;
|
|
4519
|
-
case 'webaz_charity':
|
|
4520
|
-
result = await handleCharity(args);
|
|
4521
|
-
break;
|
|
4522
|
-
case 'webaz_price_history':
|
|
4523
|
-
result = await handlePriceHistory(args);
|
|
4524
|
-
break;
|
|
4525
|
-
case 'webaz_leaderboard':
|
|
4526
|
-
result = await handleLeaderboard(args);
|
|
4527
|
-
break;
|
|
4528
|
-
default: result = { error: `未知工具:${name}` };
|
|
4848
|
+
// ─── RFC-003 Batch 0 安全网:NETWORK 模式下未迁移的工具【硬失败】,不静默落本地沙盒 ───
|
|
4849
|
+
// 例外:info / register(NETWORK_SELF_AWARE)有专门 network-aware 处理,照常放行。
|
|
4850
|
+
let handled = false;
|
|
4851
|
+
if (MODE === 'network' && !NETWORK_TOOLS.has(name) && !NETWORK_SELF_AWARE.has(name)) {
|
|
4852
|
+
result = networkMigrationPending(name);
|
|
4853
|
+
handled = true;
|
|
4529
4854
|
}
|
|
4855
|
+
if (!handled)
|
|
4856
|
+
switch (name) {
|
|
4857
|
+
case 'webaz_info':
|
|
4858
|
+
result = await handleInfo();
|
|
4859
|
+
break;
|
|
4860
|
+
case 'webaz_register':
|
|
4861
|
+
result = handleRegister(args);
|
|
4862
|
+
break;
|
|
4863
|
+
case 'webaz_search':
|
|
4864
|
+
result = await handleSearch(args);
|
|
4865
|
+
break;
|
|
4866
|
+
case 'webaz_verify_price':
|
|
4867
|
+
result = await handleVerifyPrice(args);
|
|
4868
|
+
break;
|
|
4869
|
+
case 'webaz_list_product':
|
|
4870
|
+
result = await handleListProduct(args);
|
|
4871
|
+
break;
|
|
4872
|
+
case 'webaz_place_order':
|
|
4873
|
+
result = await handlePlaceOrder(args);
|
|
4874
|
+
break;
|
|
4875
|
+
case 'webaz_update_order':
|
|
4876
|
+
result = await handleUpdateOrder(args);
|
|
4877
|
+
break;
|
|
4878
|
+
case 'webaz_get_status':
|
|
4879
|
+
result = await handleGetStatus(args);
|
|
4880
|
+
break;
|
|
4881
|
+
case 'webaz_feedback':
|
|
4882
|
+
result = await handleFeedback(args);
|
|
4883
|
+
break;
|
|
4884
|
+
case 'webaz_contribute':
|
|
4885
|
+
result = await handleContribute(args);
|
|
4886
|
+
break;
|
|
4887
|
+
case 'webaz_wallet':
|
|
4888
|
+
result = await handleWallet(args);
|
|
4889
|
+
break;
|
|
4890
|
+
case 'webaz_dispute':
|
|
4891
|
+
result = await handleDispute(args);
|
|
4892
|
+
break;
|
|
4893
|
+
case 'webaz_claim_verify':
|
|
4894
|
+
result = await handleClaimVerify(args);
|
|
4895
|
+
break;
|
|
4896
|
+
case 'webaz_notifications':
|
|
4897
|
+
result = await handleNotifications(args);
|
|
4898
|
+
break;
|
|
4899
|
+
case 'webaz_skill':
|
|
4900
|
+
result = await handleSkill(args);
|
|
4901
|
+
break;
|
|
4902
|
+
case 'webaz_skill_market':
|
|
4903
|
+
result = await handleSkillMarket(args);
|
|
4904
|
+
break;
|
|
4905
|
+
case 'webaz_secondhand':
|
|
4906
|
+
result = await handleSecondhand(args);
|
|
4907
|
+
break;
|
|
4908
|
+
case 'webaz_trial':
|
|
4909
|
+
result = await handleTrial(args);
|
|
4910
|
+
break;
|
|
4911
|
+
case 'webaz_mykey':
|
|
4912
|
+
result = handleMyKey(args);
|
|
4913
|
+
break;
|
|
4914
|
+
case 'webaz_profile':
|
|
4915
|
+
result = await handleProfile(args);
|
|
4916
|
+
break;
|
|
4917
|
+
case 'webaz_revoke_key':
|
|
4918
|
+
result = handleRevokeKey(args);
|
|
4919
|
+
break;
|
|
4920
|
+
case 'webaz_rotate_key':
|
|
4921
|
+
result = handleRotateKey(args);
|
|
4922
|
+
break;
|
|
4923
|
+
case 'webaz_referral':
|
|
4924
|
+
result = await handleReferral(args);
|
|
4925
|
+
break;
|
|
4926
|
+
case 'webaz_share_link':
|
|
4927
|
+
result = await handleShareLink(args);
|
|
4928
|
+
break;
|
|
4929
|
+
case 'webaz_blocklist':
|
|
4930
|
+
result = await handleBlocklist(args);
|
|
4931
|
+
break;
|
|
4932
|
+
case 'webaz_follows':
|
|
4933
|
+
result = await handleFollows(args);
|
|
4934
|
+
break;
|
|
4935
|
+
case 'webaz_nearby':
|
|
4936
|
+
result = await handleNearby(args);
|
|
4937
|
+
break;
|
|
4938
|
+
case 'webaz_default_address':
|
|
4939
|
+
result = await handleDefaultAddress(args);
|
|
4940
|
+
break;
|
|
4941
|
+
case 'webaz_shareables':
|
|
4942
|
+
result = await handleShareables(args);
|
|
4943
|
+
break;
|
|
4944
|
+
case 'webaz_rfq':
|
|
4945
|
+
result = await handleRfq(args);
|
|
4946
|
+
break;
|
|
4947
|
+
case 'webaz_bid':
|
|
4948
|
+
result = await handleBid(args);
|
|
4949
|
+
break;
|
|
4950
|
+
case 'webaz_chat':
|
|
4951
|
+
result = await handleChat(args);
|
|
4952
|
+
break;
|
|
4953
|
+
case 'webaz_auto_bid':
|
|
4954
|
+
result = await handleAutoBidSkill(args);
|
|
4955
|
+
break;
|
|
4956
|
+
case 'webaz_auction':
|
|
4957
|
+
result = await handleAuction(args);
|
|
4958
|
+
break;
|
|
4959
|
+
case 'webaz_like':
|
|
4960
|
+
result = await handleLike(args);
|
|
4961
|
+
break;
|
|
4962
|
+
case 'webaz_p2p_product':
|
|
4963
|
+
result = await handleP2pProduct(args);
|
|
4964
|
+
break;
|
|
4965
|
+
case 'webaz_charity':
|
|
4966
|
+
result = await handleCharity(args);
|
|
4967
|
+
break;
|
|
4968
|
+
case 'webaz_price_history':
|
|
4969
|
+
result = await handlePriceHistory(args);
|
|
4970
|
+
break;
|
|
4971
|
+
case 'webaz_leaderboard':
|
|
4972
|
+
result = await handleLeaderboard(args);
|
|
4973
|
+
break;
|
|
4974
|
+
default: result = { error: `未知工具:${name}` };
|
|
4975
|
+
}
|
|
4530
4976
|
}
|
|
4531
4977
|
catch (err) {
|
|
4532
4978
|
result = { error: `执行出错:${err.message}` };
|