@seasonkoh/webaz 0.1.19 → 0.1.21
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 +165 -26
- package/dist/layer0-foundation/L0-2-state-machine/order-chain.js +23 -0
- package/dist/layer0-foundation/L0-2-state-machine/transitions.js +65 -2
- package/dist/layer1-agent/L1-1-mcp-server/server.js +38 -27
- package/dist/layer2-business/L2-8-feedback/build-feedback-engine.js +34 -4
- package/dist/pwa/contract-fingerprint.js +46 -0
- package/dist/pwa/economic-participation.js +122 -0
- package/dist/pwa/endpoint-actions.js +112 -0
- package/dist/pwa/entity-dictionary.js +125 -0
- package/dist/pwa/goal-index.js +60 -0
- package/dist/pwa/integration-contract.js +64 -0
- package/dist/pwa/limits.js +30 -0
- package/dist/pwa/negative-space.js +64 -0
- package/dist/pwa/public/app.js +52 -47
- package/dist/pwa/public/docs/ECONOMIC-MODEL.md +287 -0
- package/dist/pwa/public/docs/INTEGRATOR.md +67 -0
- package/dist/pwa/public/docs/META-RULES-FULL.md +543 -0
- package/dist/pwa/public/i18n.js +44 -41
- package/dist/pwa/routes/disputes-write.js +68 -0
- package/dist/pwa/routes/orders-action.js +93 -1
- package/dist/pwa/routes/orders-read.js +18 -0
- package/dist/pwa/routes/public-utils.js +131 -1
- package/dist/pwa/routes/webauthn.js +9 -1
- package/dist/pwa/server.js +32 -121
- package/dist/pwa/verifiability-index.js +63 -0
- package/dist/version.js +32 -0
- package/package.json +2 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC-011 §② 负空间策略 —— agent【不能做什么】+ 真实 enforced 限额 + 后果阶梯,统一成机读契约面。
|
|
3
|
+
*
|
|
4
|
+
* 正空间(写能力矩阵)已 #126 发布在 /.well-known/webaz-capabilities.json;本面是其【负空间】补全。
|
|
5
|
+
*
|
|
6
|
+
* doc=code:限额表从 ./limits.ts 读(与 server.ts enforcer 同源,零漂移);per-agent 速率从 protocol_params
|
|
7
|
+
* 实时读(治理可调)。禁区对照 META-RULES-FULL.md #3。后果阶梯对应 server.ts issueAgentStrike 状态机。
|
|
8
|
+
*/
|
|
9
|
+
import { SOFTWARE_VERSION, CONTRACT_VERSION } from '../version.js';
|
|
10
|
+
import { capabilityMatrix } from './endpoint-actions.js';
|
|
11
|
+
import { AGENT_RATE_PER_MIN_DEFAULTS, CROSS_USER_READ_DAILY_CAP, MASS_ACTION_DAILY_CAPS, IP_RATE_DEFAULT } from './limits.js';
|
|
12
|
+
const BASE = 'https://webaz.xyz';
|
|
13
|
+
export function buildNegativeSpace(getParam) {
|
|
14
|
+
// per-agent 每分钟速率:实时读 param(治理可调),回落 limits.ts 默认。
|
|
15
|
+
const perAgentRatePerMin = {};
|
|
16
|
+
for (const level of Object.keys(AGENT_RATE_PER_MIN_DEFAULTS)) {
|
|
17
|
+
perAgentRatePerMin[level] = getParam(`agent_rate_${level}_per_min`, AGENT_RATE_PER_MIN_DEFAULTS[level]);
|
|
18
|
+
}
|
|
19
|
+
const cap = capabilityMatrix();
|
|
20
|
+
return {
|
|
21
|
+
contract_version: CONTRACT_VERSION,
|
|
22
|
+
software_version: SOFTWARE_VERSION,
|
|
23
|
+
note: 'RFC-011 §② negative space — what an agent must NOT do + the ENFORCED limits + the consequence ladder. The positive write boundary is the capability matrix (/.well-known/webaz-capabilities.json). Numeric limits are doc=code (shared with the runtime enforcer via src/limits.ts); per-agent rate caps are read live from protocol_params. Crossing a limit returns 429; repeated abuse escalates strikes (see consequence_ladder).',
|
|
24
|
+
// 禁区(质性)—— 元规则 #3,机制 enforce
|
|
25
|
+
forbidden: [
|
|
26
|
+
'rebuild/aggregate a cross-user graph or dataset (user profiling, content farming, scraping) — meta-rule #3',
|
|
27
|
+
'resell or redistribute user data obtained via the protocol — meta-rule #3',
|
|
28
|
+
'impersonate another user or the protocol itself',
|
|
29
|
+
'exceed your declared scope (capability matrix write_actions you did not declare)',
|
|
30
|
+
'self-register accounts to bypass invite/captcha/real-person accountability (NETWORK mode blocks agent self-register)',
|
|
31
|
+
],
|
|
32
|
+
forbidden_enforced_by: [
|
|
33
|
+
'default-deny write boundary (undeclared agent → AGENT_SCOPE_UNDECLARED)',
|
|
34
|
+
'cross-user read daily cap (distinct other-user reads; humans capped too)',
|
|
35
|
+
'sensitive cross-user read scopes (search / profile) constrain declared agents',
|
|
36
|
+
'accountability strikes → 3-strike block; api-key revocation',
|
|
37
|
+
],
|
|
38
|
+
// enforced 限额
|
|
39
|
+
rate_limits: {
|
|
40
|
+
per_agent_per_min: { by_trust_level: perAgentRatePerMin, param: 'agent_rate_<level>_per_min (live)', on_exceed: '429; sustained abuse → strike (reason rate_limit_abuse)' },
|
|
41
|
+
cross_user_read_daily: { unit: 'distinct other-user reads per day (e.g. /api/users/:id/*)', by_trust_level: CROSS_USER_READ_DAILY_CAP, note: 'passkey_human is capped too (a scraper using a real account does not bypass)', on_exceed: '403 CROSS_USER_READ_DAILY_CAP' },
|
|
42
|
+
mass_action_daily: { unit: 'social-write actions per day', by_action_and_level: MASS_ACTION_DAILY_CAPS, on_exceed: '429 AGENT_DAILY_CAP; ≥3 overruns/24h → strike (warning)' },
|
|
43
|
+
anonymous_ip: { max: IP_RATE_DEFAULT.max, window_ms: IP_RATE_DEFAULT.window_ms, note: 'per-IP default for public/unauthenticated endpoints' },
|
|
44
|
+
},
|
|
45
|
+
read_scopes: cap.read_scopes, // 敏感跨用户读门(search / profile),与 ② 同源
|
|
46
|
+
// 后果阶梯(对应 issueAgentStrike 状态机)
|
|
47
|
+
consequence_ladder: {
|
|
48
|
+
model: '3-strike state machine on the agent api_key (→ passport, → custodian).',
|
|
49
|
+
steps: [
|
|
50
|
+
{ level: 'warning', effect: 'recorded; expires ~24h', escalates: 'a 2nd warning within 7 days → suspend_7d' },
|
|
51
|
+
{ level: 'suspend_7d', effect: '7-day suspension; active skills auto-disabled', escalates: 'a 3rd suspension within 30 days → permanent' },
|
|
52
|
+
{ level: 'permanent', effect: 'permanent block of the api_key' },
|
|
53
|
+
],
|
|
54
|
+
appeal: `POST ${BASE}/api/me/agents/strikes/:id/appeal (reason ≥10 chars)`,
|
|
55
|
+
enforced_by: 'src/pwa/server.ts issueAgentStrike + agent_strikes table',
|
|
56
|
+
},
|
|
57
|
+
iron_rule: 'arbitrate / vote / agent_revoke / delete_passkey / large withdraw require a live WebAuthn ceremony regardless of declared scope (CHARTER §4 iron-rule) — no scope or rate budget overrides it.',
|
|
58
|
+
references: {
|
|
59
|
+
meta_rules: `${BASE}/docs/META-RULES-FULL.md`, // 协议自服务 —— agent 必须能读到约束它的规则
|
|
60
|
+
capability_matrix: `${BASE}/.well-known/webaz-capabilities.json`,
|
|
61
|
+
integrator_guide: `${BASE}/docs/INTEGRATOR.md`,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
package/dist/pwa/public/app.js
CHANGED
|
@@ -3256,7 +3256,7 @@ async function renderAdminDashboard(app) {
|
|
|
3256
3256
|
])
|
|
3257
3257
|
const kpiTokenomics2 = kpiGrid([
|
|
3258
3258
|
{ label: t('累计分享分润'),value: Number(tk.commission_total || 0).toFixed(2), unit: 'WAZ' },
|
|
3259
|
-
{ label: t('
|
|
3259
|
+
{ label: t('累计匹配发放'),value: Number(tk.binary_waz_total || 0).toFixed(2), unit: 'WAZ' },
|
|
3260
3260
|
{ label: t('PV 待处理'), value: tk.ledger_pending ?? 0 },
|
|
3261
3261
|
])
|
|
3262
3262
|
// 异常告警 banner — 多条件聚合
|
|
@@ -3298,7 +3298,7 @@ async function renderAdminDashboard(app) {
|
|
|
3298
3298
|
</div>
|
|
3299
3299
|
<div style="font-size:13px;color:#6b7280;margin:16px 0 8px">⚛ ${t('Tokenomics')}</div>
|
|
3300
3300
|
<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-bottom:16px">
|
|
3301
|
-
${quickAction('#admin/tokenomics', '⚛', t('
|
|
3301
|
+
${quickAction('#admin/tokenomics', '⚛', t('积分基金 / Tier 配置 / 高额榜'))}
|
|
3302
3302
|
</div>`
|
|
3303
3303
|
|
|
3304
3304
|
// A5 重设:渐变标题 + 分区标题 + 颜色块分组
|
|
@@ -4352,7 +4352,7 @@ async function renderApplyRewards(app) {
|
|
|
4352
4352
|
<div class="card" style="margin-bottom:16px;background:#fef2f2;border-color:#fca5a5">
|
|
4353
4353
|
<div style="font-weight:600;color:#991b1b;margin-bottom:6px;font-size:14px">⚠️ ${t('本流程与购物无关')}</div>
|
|
4354
4354
|
<div style="font-size:12px;color:#7f1d1d;line-height:1.6">
|
|
4355
|
-
${t('你可以随时退出,不影响任何已下单或未来订单。本流程涉及经济关系登记(三级佣金 +
|
|
4355
|
+
${t('你可以随时退出,不影响任何已下单或未来订单。本流程涉及经济关系登记(三级佣金 + 积分配对),请仔细阅读全部条款。')}<br>
|
|
4356
4356
|
<span style="opacity:0.85">This flow is not part of shopping. You can leave anytime without affecting any orders. This is an economic-relationship registration — please read all terms.</span>
|
|
4357
4357
|
</div>
|
|
4358
4358
|
</div>
|
|
@@ -5536,7 +5536,7 @@ async function renderAdminTokenomics(app) {
|
|
|
5536
5536
|
<h2 style="font-size:15px;font-weight:600;margin:16px 0 8px">🎁 ${t('管理津贴池')}</h2>
|
|
5537
5537
|
<div class="card" style="margin-bottom:12px">
|
|
5538
5538
|
<div style="font-size:18px;font-weight:700">${Number(mb.balance ?? 0).toFixed(2)} WAZ</div>
|
|
5539
|
-
<div style="font-size:11px;color:#9ca3af;margin-top:4px">${t('来自协议费 50
|
|
5539
|
+
<div style="font-size:11px;color:#9ca3af;margin-top:4px">${t('来自协议费 50%,用于大博主匹配团队 10/5/2% 补贴')}</div>
|
|
5540
5540
|
<div id="mgmt-bonus-control" style="margin-top:12px;padding:10px;background:#fef9c3;border:1px solid #fde047;border-radius:6px">
|
|
5541
5541
|
<div style="font-size:12px;font-weight:600;margin-bottom:6px">⚠️ ${t('津贴门控')}</div>
|
|
5542
5542
|
<div id="mgmt-bonus-status" style="font-size:11px;color:#92400e;margin-bottom:8px">${t('加载中...')}</div>
|
|
@@ -5560,7 +5560,7 @@ async function renderAdminTokenomics(app) {
|
|
|
5560
5560
|
<div class="card" style="margin-bottom:12px">
|
|
5561
5561
|
<div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px">
|
|
5562
5562
|
<button class="btn btn-outline btn-sm" style="font-size:11px" onclick="doTkProcess()">${t('处理 PV 流水')}</button>
|
|
5563
|
-
<button class="btn btn-outline btn-sm" style="font-size:11px" onclick="doTkSettle()">${t('
|
|
5563
|
+
<button class="btn btn-outline btn-sm" style="font-size:11px" onclick="doTkSettle()">${t('触发匹配结算')}</button>
|
|
5564
5564
|
<button class="btn btn-outline btn-sm" style="font-size:11px;color:#dc2626;border-color:#dc2626" onclick="if(confirm('${t('确认分发 WAZ?这将清空池子按 N 比例分配')}'))doTkDistribute()">${t('分发 WAZ(清池)')}</button>
|
|
5565
5565
|
</div>
|
|
5566
5566
|
<div style="font-size:12px;color:#6b7280">${t('PV 待处理流水')}: ${data.pv_ledger?.pending ?? 0} ${t('条')} · ${t('待累积 PV')}: ${Number(data.pv_ledger?.pending_pv ?? 0).toLocaleString()}</div>
|
|
@@ -5580,10 +5580,10 @@ async function renderAdminTokenomics(app) {
|
|
|
5580
5580
|
<div style="padding:8px 12px;font-size:11px;color:#6b7280">${t('调整:POST /api/admin/tokenomics/tier with body {tier, pv_threshold, score_per_hit, active}')}</div>
|
|
5581
5581
|
</div>
|
|
5582
5582
|
|
|
5583
|
-
<h2 style="font-size:15px;font-weight:600;margin:16px 0 8px">🏆 ${t('Top
|
|
5583
|
+
<h2 style="font-size:15px;font-weight:600;margin:16px 0 8px">🏆 ${t('Top 三级佣金')} (Top 10)</h2>
|
|
5584
5584
|
<div class="card" style="padding:0;margin-bottom:12px">${commRows}</div>
|
|
5585
5585
|
|
|
5586
|
-
<h2 style="font-size:15px;font-weight:600;margin:16px 0 8px">🏆 ${t('Top
|
|
5586
|
+
<h2 style="font-size:15px;font-weight:600;margin:16px 0 8px">🏆 ${t('Top 匹配收益')} (Top 10)</h2>
|
|
5587
5587
|
<div class="card" style="padding:0;margin-bottom:12px">${binaryRows}</div>
|
|
5588
5588
|
`, 'admin')
|
|
5589
5589
|
setTimeout(loadRequireRefStatus, 100)
|
|
@@ -5643,7 +5643,7 @@ window.doTkProcess = async () => {
|
|
|
5643
5643
|
window.doTkSettle = async () => {
|
|
5644
5644
|
const res = await POST('/admin/atomic/run-settlement', {})
|
|
5645
5645
|
const msg = document.getElementById('tk-msg')
|
|
5646
|
-
if (msg) msg.innerHTML = res.error ? alert$('error', res.error) : alert$('success', `${t('
|
|
5646
|
+
if (msg) msg.innerHTML = res.error ? alert$('error', res.error) : alert$('success', `${t('已触发匹配')}: ${res.settled}`)
|
|
5647
5647
|
setTimeout(() => renderAdminTokenomics(document.getElementById('app')), 800)
|
|
5648
5648
|
}
|
|
5649
5649
|
|
|
@@ -6716,10 +6716,10 @@ async function renderPromoter(app) {
|
|
|
6716
6716
|
const _kpiRestricted = _mlmMax <= 1
|
|
6717
6717
|
// 两个语义独立的 gate(三级奖励 ≠ PV 双轨系统)—— 2026-06-04 已解耦:
|
|
6718
6718
|
// - _kpiRestricted (max ≤ 1):紧张地区 — KPI 改时间线 / team 隐 WAZ
|
|
6719
|
-
// - _pvAllowed:PV
|
|
6719
|
+
// - _pvAllowed:PV 双轨/匹配系统是否开启,读 region_pv_enabled(独立旋钮,不再绑 max≥3)
|
|
6720
6720
|
// commission 层级(max_levels) 与 PV 系统(pv_enabled) 分离:可单独开某辖区到 L2/L3 而 PV 仍关。
|
|
6721
6721
|
const _pvAllowed = Number(state.user?.region_pv_enabled ?? 0) === 1
|
|
6722
|
-
// 紧张地区:拼装最近奖励时间线(commission +
|
|
6722
|
+
// 紧张地区:拼装最近奖励时间线(commission + 匹配 binary 混合,按时间倒序取 5 条)
|
|
6723
6723
|
let kpiBar = ''
|
|
6724
6724
|
if (_kpiRestricted) {
|
|
6725
6725
|
const _cm = (data.recent || []).slice(0, 10).map(r => ({
|
|
@@ -7051,13 +7051,13 @@ async function renderPromoter(app) {
|
|
|
7051
7051
|
|
|
7052
7052
|
// ─── ⑥ WebAZ 发展奖(默认折叠;紧张 PV 地区精简版)───
|
|
7053
7053
|
// 合规:max_levels ≤ 1 的地区(GCC/越南/印尼/菲律宾 + 未审计地区)
|
|
7054
|
-
// 只显示"累计推广 N 人"客观数字,全部金钱/tier
|
|
7054
|
+
// 只显示"累计推广 N 人"客观数字,全部金钱/tier/匹配话术隐藏
|
|
7055
7055
|
// 复用顶部 _kpiRestricted(同一含义,避免重复定义)
|
|
7056
7056
|
const _totalRecruits = Number(data.team?.l1 || 0) + Number(data.team?.l2 || 0) + Number(data.team?.l3 || 0)
|
|
7057
7057
|
// 最右侧地区显示(所有模式都加)
|
|
7058
7058
|
const _userRegion = state.user?.region || 'global'
|
|
7059
7059
|
const _regionChip = `<span style="font-size:11px;color:#6b7280;white-space:nowrap;font-weight:400">${regionLabel(_userRegion)}</span>`
|
|
7060
|
-
// PV 双轨系统:只在 _pvAllowed(region_pv_enabled=1,2026-06-04 解耦,独立于 max_levels)才显示完整 tier
|
|
7060
|
+
// PV 双轨系统:只在 _pvAllowed(region_pv_enabled=1,2026-06-04 解耦,独立于 max_levels)才显示完整 tier/匹配/弱侧;
|
|
7061
7061
|
// 未开启 PV 的地区 → 走精简卡(分数仍后台计算累积,待 pv_enabled 开启/迁移后兑现)
|
|
7062
7062
|
const atomicSection = atomic.left_invite_url
|
|
7063
7063
|
? (!_pvAllowed
|
|
@@ -7080,7 +7080,7 @@ async function renderPromoter(app) {
|
|
|
7080
7080
|
<span>
|
|
7081
7081
|
🌟 ${t('WebAZ 发展奖')}
|
|
7082
7082
|
<span style="font-weight:400;font-size:12px;color:#6b7280;margin-left:6px">
|
|
7083
|
-
${t('
|
|
7083
|
+
${t('弱侧')} ${weak.toLocaleString()} PV · ${t('累计')} ${(atomic.score?.settled_waz || 0).toFixed(2)} WAZ
|
|
7084
7084
|
</span>
|
|
7085
7085
|
</span>
|
|
7086
7086
|
${_regionChip}
|
|
@@ -7266,7 +7266,7 @@ window.setBoughtKw = (kw) => {
|
|
|
7266
7266
|
}, 200)
|
|
7267
7267
|
}
|
|
7268
7268
|
|
|
7269
|
-
//
|
|
7269
|
+
// 积分匹配内部内容(折叠展开后渲染,从 renderAtomicSection 简化抽出)
|
|
7270
7270
|
function renderAtomicInner(a, leftPv, rightPv, weak, nextTier, nextProgress) {
|
|
7271
7271
|
const tiers = a.tier_config || []
|
|
7272
7272
|
const tierTable = tiers.map(x => `
|
|
@@ -7276,7 +7276,7 @@ function renderAtomicInner(a, leftPv, rightPv, weak, nextTier, nextProgress) {
|
|
|
7276
7276
|
<td style="padding:5px 8px;font-size:12px;text-align:right;font-weight:600">${x.score_per_hit}</td>
|
|
7277
7277
|
</tr>`).join('')
|
|
7278
7278
|
|
|
7279
|
-
//
|
|
7279
|
+
// 本月匹配次数 + WAZ
|
|
7280
7280
|
const now = new Date()
|
|
7281
7281
|
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1).toISOString().slice(0, 10)
|
|
7282
7282
|
const monthBinary = (a.recent_binary || []).filter(r => (r.created_at || '').slice(0, 10) >= monthStart)
|
|
@@ -7293,7 +7293,7 @@ function renderAtomicInner(a, leftPv, rightPv, weak, nextTier, nextProgress) {
|
|
|
7293
7293
|
<div>tier ${r.tier} · Score <strong>${r.score}</strong> · ${r.settled_at ? `<span style="color:#16a34a">${t('已结')} ${Number(r.waz_amount).toFixed(2)}</span>` : `<span style="color:#d97706">${t('待分配')}</span>`}</div>
|
|
7294
7294
|
<div style="color:#9ca3af">${fmtTime(r.created_at)}</div>
|
|
7295
7295
|
</div>`).join('')
|
|
7296
|
-
: `<div style="text-align:center;color:#9ca3af;padding:10px;font-size:12px">${t('
|
|
7296
|
+
: `<div style="text-align:center;color:#9ca3af;padding:10px;font-size:12px">${t('暂无匹配记录')}</div>`
|
|
7297
7297
|
|
|
7298
7298
|
return `
|
|
7299
7299
|
<!-- 我的位置 -->
|
|
@@ -7302,7 +7302,7 @@ function renderAtomicInner(a, leftPv, rightPv, weak, nextTier, nextProgress) {
|
|
|
7302
7302
|
📍 ${t('我挂位置')}: <strong>${escHtml(a.my_placement.name)}</strong> 的 ${a.my_placement.side === 'left' ? '🔵 左侧' : '🟢 右侧'}
|
|
7303
7303
|
</div>` : `
|
|
7304
7304
|
<div style="background:#f9fafb;border:1px solid #e5e7eb;border-radius:6px;padding:8px 10px;font-size:12px;color:#9ca3af;margin-bottom:10px">
|
|
7305
|
-
${t('
|
|
7305
|
+
${t('你还没加入任何上级的积分树(独立根节点)')}
|
|
7306
7306
|
</div>`}
|
|
7307
7307
|
|
|
7308
7308
|
<!-- 左/右 PV -->
|
|
@@ -7319,25 +7319,25 @@ function renderAtomicInner(a, leftPv, rightPv, weak, nextTier, nextProgress) {
|
|
|
7319
7319
|
</div>
|
|
7320
7320
|
</div>
|
|
7321
7321
|
|
|
7322
|
-
<!--
|
|
7322
|
+
<!-- 本月匹配统计 + 总匹配 -->
|
|
7323
7323
|
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:6px;margin-bottom:10px">
|
|
7324
7324
|
<div style="text-align:center;background:#fef3c7;border-radius:6px;padding:8px 4px">
|
|
7325
|
-
<div style="font-size:10px;color:#92400e">${t('
|
|
7325
|
+
<div style="font-size:10px;color:#92400e">${t('本月匹配次数')}</div>
|
|
7326
7326
|
<div style="font-size:14px;font-weight:700;color:#78350f">${monthHits}</div>
|
|
7327
7327
|
</div>
|
|
7328
7328
|
<div style="text-align:center;background:#dcfce7;border-radius:6px;padding:8px 4px">
|
|
7329
|
-
<div style="font-size:10px;color:#166534">${t('
|
|
7329
|
+
<div style="font-size:10px;color:#166534">${t('本月匹配 WAZ')}</div>
|
|
7330
7330
|
<div style="font-size:14px;font-weight:700;color:#14532d">${monthWaz.toFixed(2)}</div>
|
|
7331
7331
|
</div>
|
|
7332
7332
|
<div style="text-align:center;background:#eef2ff;border-radius:6px;padding:8px 4px">
|
|
7333
|
-
<div style="font-size:10px;color:#4338ca">${t('
|
|
7333
|
+
<div style="font-size:10px;color:#4338ca">${t('累计匹配次数')}</div>
|
|
7334
7334
|
<div style="font-size:14px;font-weight:700;color:#3730a3">${totalHits}</div>
|
|
7335
7335
|
</div>
|
|
7336
7336
|
</div>
|
|
7337
7337
|
|
|
7338
7338
|
${renderBinaryTree(a.binary_tree)}
|
|
7339
7339
|
|
|
7340
|
-
<div style="font-size:12px;color:#6b7280;margin-bottom:6px">${t('
|
|
7340
|
+
<div style="font-size:12px;color:#6b7280;margin-bottom:6px">${t('弱侧匹配量')}: <strong>${weak.toLocaleString()}</strong> PV</div>
|
|
7341
7341
|
${nextTier ? `
|
|
7342
7342
|
<div style="font-size:11px;color:#6b7280;margin-bottom:4px">${t('距离 tier')} ${nextTier.tier} ${t('门槛')} ${Number(nextTier.pv_threshold).toLocaleString()} PV (+${nextTier.score_per_hit} Score)</div>
|
|
7343
7343
|
<div style="background:#f3f4f6;height:6px;border-radius:3px;overflow:hidden;margin-bottom:10px">
|
|
@@ -7345,7 +7345,7 @@ function renderAtomicInner(a, leftPv, rightPv, weak, nextTier, nextProgress) {
|
|
|
7345
7345
|
</div>` : ''}
|
|
7346
7346
|
|
|
7347
7347
|
<details style="margin-bottom:10px">
|
|
7348
|
-
<summary style="font-size:12px;color:#6366f1;cursor:pointer">${t('
|
|
7348
|
+
<summary style="font-size:12px;color:#6366f1;cursor:pointer">${t('匹配档位表')}</summary>
|
|
7349
7349
|
<table style="width:100%;margin-top:6px;border-collapse:collapse">
|
|
7350
7350
|
<tr style="background:#f9fafb;font-size:11px;color:#6b7280">
|
|
7351
7351
|
<th style="padding:5px 8px;text-align:left">Tier</th>
|
|
@@ -7355,7 +7355,7 @@ function renderAtomicInner(a, leftPv, rightPv, weak, nextTier, nextProgress) {
|
|
|
7355
7355
|
${tierTable}
|
|
7356
7356
|
</table>
|
|
7357
7357
|
</details>
|
|
7358
|
-
<h4 style="font-size:12px;font-weight:600;margin:6px 0">📊 ${t('
|
|
7358
|
+
<h4 style="font-size:12px;font-weight:600;margin:6px 0">📊 ${t('最近匹配')}</h4>
|
|
7359
7359
|
${recentRows}`
|
|
7360
7360
|
}
|
|
7361
7361
|
|
|
@@ -7510,10 +7510,10 @@ function renderAtomicSection(a) {
|
|
|
7510
7510
|
<div>tier ${r.tier} · Score <strong>${r.score}</strong> · ${r.settled_at ? `<span style="color:#16a34a">已结 ${Number(r.waz_amount).toFixed(2)} WAZ</span>` : `<span style="color:#d97706">待分配</span>`}</div>
|
|
7511
7511
|
<div style="color:#9ca3af">${fmtTime(r.created_at)}</div>
|
|
7512
7512
|
</div>`).join('')
|
|
7513
|
-
: `<div style="text-align:center;color:#9ca3af;padding:16px;font-size:12px">${t('
|
|
7513
|
+
: `<div style="text-align:center;color:#9ca3af;padding:16px;font-size:12px">${t('暂无匹配记录')}</div>`
|
|
7514
7514
|
|
|
7515
7515
|
return `
|
|
7516
|
-
<h2 style="font-size:15px;font-weight:600;margin:24px 0 8px">⚛ ${t('
|
|
7516
|
+
<h2 style="font-size:15px;font-weight:600;margin:24px 0 8px">⚛ ${t('积分 — 积分匹配')}</h2>
|
|
7517
7517
|
|
|
7518
7518
|
<div class="card" style="margin-bottom:12px;background:linear-gradient(135deg,#dbeafe,#f0fdf4)">
|
|
7519
7519
|
<div style="font-size:13px;color:#6b7280;margin-bottom:6px">🔗 ${t('左右码(完全对称 / 末端垂直挂靠)')}</div>
|
|
@@ -7568,7 +7568,7 @@ function renderAtomicSection(a) {
|
|
|
7568
7568
|
${renderBinaryTree(a.binary_tree)}
|
|
7569
7569
|
|
|
7570
7570
|
<div class="card" style="margin-bottom:12px">
|
|
7571
|
-
<div style="font-size:13px;color:#6b7280;margin-bottom:6px">${t('
|
|
7571
|
+
<div style="font-size:13px;color:#6b7280;margin-bottom:6px">${t('弱侧匹配量')}: <strong>${pair.toLocaleString()}</strong> PV</div>
|
|
7572
7572
|
${nextTier ? `
|
|
7573
7573
|
<div style="font-size:11px;color:#6b7280;margin-bottom:4px">${t('距离 tier')} ${nextTier.tier} ${t('门槛')} ${Number(nextTier.pv_threshold).toLocaleString()} PV (${nextTier.score_per_hit} Score)</div>
|
|
7574
7574
|
<div style="background:#f3f4f6;height:6px;border-radius:3px;overflow:hidden">
|
|
@@ -7588,7 +7588,7 @@ function renderAtomicSection(a) {
|
|
|
7588
7588
|
</div>
|
|
7589
7589
|
|
|
7590
7590
|
<details style="margin-bottom:12px">
|
|
7591
|
-
<summary style="font-size:12px;color:#6366f1;cursor:pointer">${t('
|
|
7591
|
+
<summary style="font-size:12px;color:#6366f1;cursor:pointer">${t('匹配档位表')}</summary>
|
|
7592
7592
|
<table style="width:100%;margin-top:8px;border-collapse:collapse">
|
|
7593
7593
|
<tr style="background:#f9fafb;font-size:11px;color:#6b7280">
|
|
7594
7594
|
<th style="padding:6px 8px;text-align:left">Tier</th>
|
|
@@ -7599,13 +7599,13 @@ function renderAtomicSection(a) {
|
|
|
7599
7599
|
</table>
|
|
7600
7600
|
</details>
|
|
7601
7601
|
|
|
7602
|
-
<h3 style="font-size:13px;font-weight:600;margin:8px 0">📊 ${t('
|
|
7602
|
+
<h3 style="font-size:13px;font-weight:600;margin:8px 0">📊 ${t('最近匹配')}</h3>
|
|
7603
7603
|
<div class="card" style="padding:0">
|
|
7604
7604
|
${recentRows}
|
|
7605
7605
|
</div>`
|
|
7606
7606
|
}
|
|
7607
7607
|
|
|
7608
|
-
// P12:
|
|
7608
|
+
// P12: 三层积分树(你 + 左右 + 各自左右)
|
|
7609
7609
|
function renderBinaryTree(tree) {
|
|
7610
7610
|
if (!tree || !tree.me) return ''
|
|
7611
7611
|
const node = (n, bg, fg, label) => n
|
|
@@ -7617,7 +7617,7 @@ function renderBinaryTree(tree) {
|
|
|
7617
7617
|
|
|
7618
7618
|
return `
|
|
7619
7619
|
<details style="margin-bottom:12px" open>
|
|
7620
|
-
<summary style="font-size:12px;color:#6366f1;cursor:pointer;margin-bottom:6px">🌳 ${t('
|
|
7620
|
+
<summary style="font-size:12px;color:#6366f1;cursor:pointer;margin-bottom:6px">🌳 ${t('团队组织图(3 层)')} <span style="color:#9ca3af;font-size:10px">${t('点击节点查看 PV')}</span></summary>
|
|
7621
7621
|
<div style="padding:8px;background:#fff;border:1px solid #e5e7eb;border-radius:8px">
|
|
7622
7622
|
<!-- L0: 你 -->
|
|
7623
7623
|
<div style="margin-bottom:6px">
|
|
@@ -7693,8 +7693,8 @@ window.showNodePvModal = async (userId) => {
|
|
|
7693
7693
|
</div>
|
|
7694
7694
|
|
|
7695
7695
|
<div style="background:#f9fafb;border-radius:6px;padding:8px 10px;font-size:11px;color:#6b7280;line-height:1.7;margin-bottom:10px">
|
|
7696
|
-
${t('
|
|
7697
|
-
${t('
|
|
7696
|
+
${t('弱侧匹配量')}: <strong style="color:#374151">${weak.toLocaleString()}</strong> PV · ${t('双腿均衡')} ${ratio}%<br>
|
|
7697
|
+
${t('累计匹配')}: <strong style="color:#374151">${data.total_hits || 0}</strong> ${t('次')} · ${t('累计获 WAZ')}: <strong style="color:#16a34a">${Number(data.settled_waz || 0).toFixed(2)}</strong><br>
|
|
7698
7698
|
${t('待结 Score')}: <strong style="color:#d97706">${Number(data.pending_score || 0).toFixed(1)}</strong>
|
|
7699
7699
|
</div>
|
|
7700
7700
|
|
|
@@ -7829,11 +7829,11 @@ async function maybePromptPlacementBind() {
|
|
|
7829
7829
|
const st = await GET('/profile/placement-status')
|
|
7830
7830
|
if (!st.can_bind) return // 已有 placement 或 有下线 → 不弹
|
|
7831
7831
|
setTimeout(() => {
|
|
7832
|
-
const ok = confirm(`📍 ${t('系统检测到邀请链接')}\n\n${t('inviter')}: ${inviter}\n\n${t('
|
|
7832
|
+
const ok = confirm(`📍 ${t('系统检测到邀请链接')}\n\n${t('inviter')}: ${inviter}\n\n${t('是否加入对方的积分配对?')}\n${t('系统将按推荐人偏好自动选择左/右区。一旦加入永久不变。')}`)
|
|
7833
7833
|
if (ok) {
|
|
7834
7834
|
POST('/profile/bind-placement', { inviter_id: inviter, side: hint.placement_side || undefined }).then(res => {
|
|
7835
7835
|
if (res.error) alert(`✗ ${res.error}`)
|
|
7836
|
-
else alert(`✓ ${t('
|
|
7836
|
+
else alert(`✓ ${t('已加入积分树')}\n${t('侧')}: ${res.side}\n${t('深度')}: ${res.depth}`)
|
|
7837
7837
|
})
|
|
7838
7838
|
}
|
|
7839
7839
|
localStorage.setItem(askedKey, Date.now().toString())
|
|
@@ -9748,6 +9748,11 @@ async function renderMyBuildFeedback(app) {
|
|
|
9748
9748
|
<div style="font-size:13px;color:#374151">${escHtml(String(f.body || ''))}</div>
|
|
9749
9749
|
${f.resolution ? `<div style="font-size:11px;color:#6b7280;margin-top:6px;padding-top:6px;border-top:1px solid #f3f4f6">↳ ${escHtml(String(f.resolution))}</div>` : ''}
|
|
9750
9750
|
${Number(f.credited_points) > 0 ? `<div style="font-size:11px;color:#16a34a;margin-top:4px">🏅 +${f.credited_points} ${t('共建信誉')}</div>` : ''}
|
|
9751
|
+
${(f.credit_pending_anchor && !(Number(f.credited_points) > 0)) ? `
|
|
9752
|
+
<div style="font-size:11px;color:#854d0e;background:#fef3c7;border-radius:6px;padding:8px;margin-top:6px">
|
|
9753
|
+
🔐 ${t('这条贡献已被采纳 —— 绑定 Passkey 即可领取建设信誉(奖励需锚定可问责真人)')}
|
|
9754
|
+
<button class="btn btn-sm" style="margin-top:6px;font-size:11px;padding:4px 10px;background:#7c3aed;color:#fff;border-color:transparent" onclick="(async()=>{ if(await doRegisterPasskey('')){ alert(t('已绑定,建设信誉已补发')); navigate('#build-feedback') } })()">${t('绑定 Passkey 领取')}</button>
|
|
9755
|
+
</div>` : ''}
|
|
9751
9756
|
</div>`).join('')
|
|
9752
9757
|
app.innerHTML = shell(`
|
|
9753
9758
|
<h1 class="page-title">💬 ${t('我的反馈')}</h1>
|
|
@@ -10204,7 +10209,7 @@ async function renderMyAgents(app) {
|
|
|
10204
10209
|
<summary style="cursor:pointer;color:#6366f1;padding:4px 0">📊 ${t('为什么是')} ${lvlLabel} ${t('级?')}</summary>
|
|
10205
10210
|
<div style="background:#f9fafb;border-radius:6px;padding:8px 10px;margin-top:4px;font-family:monospace;line-height:1.6">
|
|
10206
10211
|
<div>${t('加分')}:${t('账号年龄')} +${rep.signals.age_pts} · ${t('订单数')} +${rep.signals.order_pts} · ${t('分享转化')} +${rep.signals.share_pts} · ${t('调用多样性')} +${rep.signals.diversity_pts}</div>
|
|
10207
|
-
${(rep.signals.dispute_penalty + rep.signals.sybil_penalty + rep.signals.cross_penalty + rep.signals.ratelimit_penalty) < 0 ? `<div style="color:#dc2626">${t('扣分')}:${t('争议败诉')} ${rep.signals.dispute_penalty} · ${t('同 IP 异户')} ${rep.signals.sybil_penalty} · ${t('
|
|
10212
|
+
${(rep.signals.dispute_penalty + rep.signals.sybil_penalty + rep.signals.cross_penalty + rep.signals.ratelimit_penalty) < 0 ? `<div style="color:#dc2626">${t('扣分')}:${t('争议败诉')} ${rep.signals.dispute_penalty} · ${t('同 IP 异户')} ${rep.signals.sybil_penalty} · ${t('匹配命中')} ${rep.signals.cross_penalty} · ${t('限速命中')} ${rep.signals.ratelimit_penalty}</div>` : ''}
|
|
10208
10213
|
<div style="color:#9ca3af;margin-top:4px">${t('阈值')}:new<20 · trusted≥20 · quality≥50 · legend≥80</div>
|
|
10209
10214
|
</div>
|
|
10210
10215
|
</details>
|
|
@@ -11683,8 +11688,8 @@ window.doLoginByPassword = async () => {
|
|
|
11683
11688
|
|
|
11684
11689
|
|
|
11685
11690
|
// 从 URL 解析分享 hint(30 天 cookie 持久化)
|
|
11686
|
-
// ?ref=usr_xxx →
|
|
11687
|
-
// ?placement=usr_xxx&side=left|right →
|
|
11691
|
+
// ?ref=usr_xxx → 仅三级佣金 sponsor
|
|
11692
|
+
// ?placement=usr_xxx&side=left|right → 仅积分挂靠
|
|
11688
11693
|
// ?ref=xxx&side=left|right → 两轨道(ref 同时作 placement)
|
|
11689
11694
|
// ?ref=xxx&placement=xxx&side=... → 显式两轨道
|
|
11690
11695
|
function readShareHint() {
|
|
@@ -12054,10 +12059,10 @@ function showRegisterSuccessModal(res) {
|
|
|
12054
12059
|
const targetBtn = (intended && intended !== '#' && intended !== '')
|
|
12055
12060
|
? `<button class="btn btn-primary" style="width:100%;margin-bottom:10px" onclick="window._closeRegModal(true)">🎯 ${t('回到原页')} ${tgt?.title ? '— ' + escHtml(tgt.title) : ''} →</button>`
|
|
12056
12061
|
: ''
|
|
12057
|
-
// P1 (QA 轮 14.a): 区分"邀请人"(referral sponsor) 与"
|
|
12062
|
+
// P1 (QA 轮 14.a): 区分"邀请人"(referral sponsor) 与"积分树挂靠"(binary placement)
|
|
12058
12063
|
// 旧版挤一行 "已绑定邀请人 X · 左区 depth N" → 用户误以为 depth 是相对邀请人
|
|
12059
12064
|
const placementLine = res.placement
|
|
12060
|
-
? `<div style="font-size:11px;color:#9ca3af;margin-bottom:14px">${t('
|
|
12065
|
+
? `<div style="font-size:11px;color:#9ca3af;margin-bottom:14px">${t('积分树挂靠')}: ${res.placement.side === 'left' ? '🔵 ' + t('左区') : '🟢 ' + t('右区')} · ${t('深度')} ${res.placement.depth}<br><span style="font-size:10px">${t('(挂靠位置由系统按弱区自动决定,与邀请人不一定相邻)')}</span></div>`
|
|
12061
12066
|
: ''
|
|
12062
12067
|
_openModal(`
|
|
12063
12068
|
<h2 style="font-size:18px;font-weight:600;margin-bottom:8px;color:#16a34a">🎉 ${t('注册成功!')}</h2>
|
|
@@ -13565,7 +13570,7 @@ async function renderUserProfile(app, userId) {
|
|
|
13565
13570
|
<input type="checkbox" id="feed-visible-tg" style="width:16px;height:16px" ${data.feed_visible ? 'checked' : ''} onchange="toggleFeedVisible(this.checked)">
|
|
13566
13571
|
<label for="feed-visible-tg" style="font-size:13px;cursor:pointer">${t('在公开动态流显示我的活动')}</label>
|
|
13567
13572
|
</div>
|
|
13568
|
-
<p style="font-size:11px;color:#9ca3af;margin-top:4px;margin-left:26px">${t('
|
|
13573
|
+
<p style="font-size:11px;color:#9ca3af;margin-top:4px;margin-left:26px">${t('关闭后,你的购买/匹配/分润事件不会出现在 发现好物 > 动态')}</p>
|
|
13569
13574
|
|
|
13570
13575
|
<div id="social-msg" style="margin-top:10px"></div>
|
|
13571
13576
|
|
|
@@ -21731,13 +21736,13 @@ async function renderSeller(app) {
|
|
|
21731
21736
|
)}
|
|
21732
21737
|
${featureCard(
|
|
21733
21738
|
'🎯',
|
|
21734
|
-
t('买家 Agent
|
|
21739
|
+
t('买家 Agent 意图匹配'),
|
|
21735
21740
|
t('零推广费'),
|
|
21736
21741
|
t('全网买家 Agent 携精准需求(Alias)全天候巡航,与商品语义刚需匹配,订单全自动闭合成交。')
|
|
21737
21742
|
)}
|
|
21738
21743
|
${featureCard(
|
|
21739
21744
|
'🌊',
|
|
21740
|
-
t('
|
|
21745
|
+
t('智能体扩散分润'),
|
|
21741
21746
|
t('秒级自动分账'),
|
|
21742
21747
|
t('商品由其他节点或达人 Agent 智能分享成交,下游分润通过智能合约去中心化自动清算,即刻到账。')
|
|
21743
21748
|
)}
|
|
@@ -24728,7 +24733,7 @@ function renderTrustCard(trust) {
|
|
|
24728
24733
|
const neg = []
|
|
24729
24734
|
if (s.dispute_penalty < 0) neg.push(`${t('仲裁败诉')} ${s.dispute_penalty}`)
|
|
24730
24735
|
if (s.sybil_penalty < 0) neg.push(`${t('同 IP 多号')} ${s.sybil_penalty}`)
|
|
24731
|
-
if (s.cross_penalty < 0) neg.push(`${t('
|
|
24736
|
+
if (s.cross_penalty < 0) neg.push(`${t('同支')} ${s.cross_penalty}`)
|
|
24732
24737
|
if (s.ratelimit_penalty < 0) neg.push(`${t('限速命中')} ${s.ratelimit_penalty}`)
|
|
24733
24738
|
|
|
24734
24739
|
const nextHint = trust.trust_score < trust.raw_mode_min_trust
|
|
@@ -24907,7 +24912,7 @@ async function renderWallet(app) {
|
|
|
24907
24912
|
</div>
|
|
24908
24913
|
</div>
|
|
24909
24914
|
|
|
24910
|
-
<!-- 卖家收入分类速览(仅 seller 显示)- 2026-05-24
|
|
24915
|
+
<!-- 卖家收入分类速览(仅 seller 显示)- 2026-05-24 匹配列只在 PV 允许地区显示 -->
|
|
24911
24916
|
${state.user?.role === 'seller' ? (() => {
|
|
24912
24917
|
const _pvOK = Number(state.user?.region_pv_enabled ?? 0) === 1 // 2026-06-04 解耦:PV 列读 pv_enabled,不再绑 max≥3
|
|
24913
24918
|
const cols = _pvOK ? 4 : 3
|
|
@@ -24926,7 +24931,7 @@ async function renderWallet(app) {
|
|
|
24926
24931
|
<div style="font-size:9px;color:#9ca3af">${((inc.commissions?.l1?.count||0)+(inc.commissions?.l2?.count||0)+(inc.commissions?.l3?.count||0))} ${t('单')}</div>
|
|
24927
24932
|
</div>
|
|
24928
24933
|
${_pvOK ? `<div style="background:#fefce8;border-radius:6px;padding:8px">
|
|
24929
|
-
<div style="font-size:9px;color:#854d0e">⚛ ${t('
|
|
24934
|
+
<div style="font-size:9px;color:#854d0e">⚛ ${t('匹配已结')}</div>
|
|
24930
24935
|
<div style="font-size:14px;font-weight:700;color:#a16207;margin-top:2px">${(inc.binary?.settled_waz||0).toFixed(2)}</div>
|
|
24931
24936
|
<div style="font-size:9px;color:#9ca3af">${inc.binary?.settled_count||0} ${t('次')}</div>
|
|
24932
24937
|
</div>` : ''}
|
|
@@ -24962,7 +24967,7 @@ async function renderWallet(app) {
|
|
|
24962
24967
|
})()}
|
|
24963
24968
|
${getMaxLevels() >= 3 ? `
|
|
24964
24969
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-bottom:10px">
|
|
24965
|
-
<div style="background:#fefce8;border-radius:6px;padding:10px"><div style="font-size:10px;color:#854d0e">⚛ ${t('
|
|
24970
|
+
<div style="background:#fefce8;border-radius:6px;padding:10px"><div style="font-size:10px;color:#854d0e">⚛ ${t('积分已结算')}</div><div style="font-size:15px;font-weight:700;color:#a16207">${inc.binary.settled_waz.toFixed(2)} WAZ</div><div style="font-size:9px;color:#9ca3af">${inc.binary.settled_count} ${t('次')}</div></div>
|
|
24966
24971
|
<div style="background:#fef3c7;border-radius:6px;padding:10px"><div style="font-size:10px;color:#92400e">⏳ ${t('待结 Score')}</div><div style="font-size:15px;font-weight:700;color:#d97706">${inc.binary.pending_score.toFixed(1)}</div><div style="font-size:9px;color:#9ca3af">${t('每月结算')}</div></div>
|
|
24967
24972
|
</div>` : ''}
|
|
24968
24973
|
${inc.sales.count > 0 ? `
|