@seasonkoh/webaz 0.1.25 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/layer1-agent/L1-1-mcp-server/server.js +129 -150
- package/dist/layer2-business/L2-9-contribution/build-task-agent-metadata-store.js +9 -0
- package/dist/layer2-business/L2-9-contribution/build-tasks-engine.js +1 -1
- package/dist/layer2-business/L2-9-contribution/identity-claim-discovery.js +55 -0
- package/dist/layer2-business/L2-9-contribution/task-proposal-ai-store.js +99 -0
- package/dist/layer2-business/L2-9-contribution/task-proposal-draft.js +191 -0
- package/dist/pwa/admin-bearer-auth.js +21 -0
- package/dist/pwa/email-delivery.js +127 -0
- package/dist/pwa/public/app.js +940 -245
- package/dist/pwa/public/i18n.js +269 -40
- package/dist/pwa/public/openapi.json +4 -4
- package/dist/pwa/public/whitepaper/en/index.html +153 -0
- package/dist/pwa/public/whitepaper/zh-CN/index.html +153 -0
- package/dist/pwa/routes/admin-atomic.js +10 -4
- package/dist/pwa/routes/admin-moderation.js +25 -1
- package/dist/pwa/routes/admin-ops.js +13 -2
- package/dist/pwa/routes/admin-users-query.js +12 -1
- package/dist/pwa/routes/admin-wallet-ops.js +26 -3
- package/dist/pwa/routes/auction.js +4 -2
- package/dist/pwa/routes/auth-read.js +10 -1
- package/dist/pwa/routes/auth-register.js +82 -12
- package/dist/pwa/routes/contribution-identity.js +17 -0
- package/dist/pwa/routes/growth.js +1 -1
- package/dist/pwa/routes/orders-action.js +19 -13
- package/dist/pwa/routes/profile-credentials.js +7 -4
- package/dist/pwa/routes/profile-placement.js +7 -8
- package/dist/pwa/routes/promoter.js +3 -17
- package/dist/pwa/routes/ratings.js +64 -4
- package/dist/pwa/routes/recover-key.js +58 -19
- package/dist/pwa/routes/referral.js +4 -24
- package/dist/pwa/routes/share-redirects.js +4 -3
- package/dist/pwa/routes/shop-referral.js +6 -5
- package/dist/pwa/routes/shops.js +5 -2
- package/dist/pwa/routes/task-proposals.js +76 -0
- package/dist/pwa/routes/trial.js +4 -2
- package/dist/pwa/routes/users-public.js +2 -12
- package/dist/pwa/routes/wallet-read.js +1 -1
- package/dist/pwa/server.js +67 -9
- package/package.json +31 -3
|
@@ -155,14 +155,15 @@ export function registerShareRedirectsRoutes(app, deps) {
|
|
|
155
155
|
`, [s.owner_id, s.id, expiresAt, s.id, s.related_product_id, user.id]);
|
|
156
156
|
res.json({ ok: true, attributed: true, refreshed: true, sharer_id: s.owner_id });
|
|
157
157
|
});
|
|
158
|
-
// 邀请短链 /i/CODE — invite-code ONLY (permanent_code
|
|
159
|
-
// 一律 404(不再做 handle 解析)
|
|
158
|
+
// 邀请短链 /i/CODE — invite-code ONLY (permanent_code, 兼容旧的 -L/-R 后缀). usr_xxx / @handle / 裸 handle
|
|
159
|
+
// 一律 404(不再做 handle 解析)。pre-public 去左右码:/i/CODE 与旧 /i/CODE-L、/i/CODE-R 一律
|
|
160
|
+
// 规范化重定向到 /?ref=CODE(丢弃 side;放置侧别由注册时系统自动决定),旧链接/二维码仍可用。
|
|
160
161
|
// 不受 invite_rotation_enabled 影响:已有用户分享出的 /i/CODE 链接和二维码必须始终可用。
|
|
161
162
|
app.get('/i/:code', (req, res) => {
|
|
162
163
|
const ref = resolveInviteCodeRef(String(req.params.code || ''));
|
|
163
164
|
if (!ref)
|
|
164
165
|
return void res.status(404).send('Invitation link not found.');
|
|
165
|
-
const target = `/?ref=${encodeURIComponent(ref.code)}
|
|
166
|
+
const target = `/?ref=${encodeURIComponent(ref.code)}`;
|
|
166
167
|
res.redirect(302, target);
|
|
167
168
|
});
|
|
168
169
|
}
|
|
@@ -6,18 +6,19 @@ export function registerShopReferralRoutes(app, deps) {
|
|
|
6
6
|
if (!user)
|
|
7
7
|
return;
|
|
8
8
|
const recipientId = user.id;
|
|
9
|
-
const { seller_identifier, ref_code
|
|
9
|
+
const { seller_identifier, ref_code } = req.body || {};
|
|
10
10
|
if (!seller_identifier || typeof seller_identifier !== 'string')
|
|
11
11
|
return void errorRes(res, 400, 'SELLER_REQUIRED', 'seller_identifier required');
|
|
12
12
|
if (!ref_code || typeof ref_code !== 'string')
|
|
13
13
|
return void errorRes(res, 400, 'REF_REQUIRED', 'ref_code required');
|
|
14
|
-
// referrer = invite code ONLY (permanent_code
|
|
14
|
+
// referrer = invite code ONLY (permanent_code); usr_xxx / @handle / handle rejected.
|
|
15
|
+
// pre-public 去左右码:旧 -L/-R 后缀仍被接受但归一化为基础码(ref.code),side 一律忽略。
|
|
15
16
|
const ref = resolveInviteCodeRef(ref_code);
|
|
16
17
|
if (!ref)
|
|
17
|
-
return void errorRes(res, 400, 'INVALID_REF_CODE', '邀请码无效(仅 6-7
|
|
18
|
+
return void errorRes(res, 400, 'INVALID_REF_CODE', '邀请码无效(仅 6-7 位永久码)');
|
|
18
19
|
const referrerId = ref.userId;
|
|
19
|
-
//
|
|
20
|
-
const finalSide =
|
|
20
|
+
// pre-public 去左右码:不再按 side 归属(忽略 body.side 与邀请码 -L/-R),统一存 null
|
|
21
|
+
const finalSide = null;
|
|
21
22
|
// seller 定位:个人页多形态(usr_xxx / @handle / handle / permanent_code)。
|
|
22
23
|
// 必须是真实 seller 店铺 —— 普通 buyer / admin / 其它角色不能被写成 shop_referral_attribution.seller_id。
|
|
23
24
|
const sellerId = resolveUserRef(seller_identifier);
|
package/dist/pwa/routes/shops.js
CHANGED
|
@@ -26,8 +26,11 @@ export function registerShopsRoutes(app, deps) {
|
|
|
26
26
|
ORDER BY sales_count DESC, p.created_at DESC
|
|
27
27
|
LIMIT 50
|
|
28
28
|
`, [sellerId]);
|
|
29
|
+
// 双盲铁律(店铺主页公开面):rating agg + 最近评价只算/只展示已揭晓的评价。
|
|
30
|
+
// 揭晓 = 双方都评过(buyer_ratings 存在) OR 无盲评窗口 OR 盲评期已过 —— 与 /products|sellers/:id/ratings 同条件。
|
|
31
|
+
const blindOpen = `(EXISTS (SELECT 1 FROM buyer_ratings br WHERE br.order_id = r.order_id) OR r.hidden_until IS NULL OR datetime(r.hidden_until) <= datetime('now'))`;
|
|
29
32
|
const ratingsAgg = (await dbOne(`
|
|
30
|
-
SELECT COUNT(*) as cnt, COALESCE(AVG(stars), 0) as avg_stars FROM order_ratings WHERE seller_id = ?
|
|
33
|
+
SELECT COUNT(*) as cnt, COALESCE(AVG(stars), 0) as avg_stars FROM order_ratings r WHERE r.seller_id = ? AND ${blindOpen}
|
|
31
34
|
`, [sellerId]));
|
|
32
35
|
const followers = (await dbOne(`SELECT COUNT(*) as n FROM follows WHERE followee_id = ?`, [sellerId])).n;
|
|
33
36
|
const completedOrders = (await dbOne(`SELECT COUNT(*) as n FROM orders WHERE seller_id = ? AND status = 'completed'`, [sellerId])).n;
|
|
@@ -48,7 +51,7 @@ export function registerShopsRoutes(app, deps) {
|
|
|
48
51
|
FROM order_ratings r
|
|
49
52
|
JOIN users u ON u.id = r.buyer_id
|
|
50
53
|
JOIN products p ON p.id = r.product_id
|
|
51
|
-
WHERE r.seller_id = ?
|
|
54
|
+
WHERE r.seller_id = ? AND ${blindOpen}
|
|
52
55
|
ORDER BY r.created_at DESC LIMIT 5
|
|
53
56
|
`, [sellerId]);
|
|
54
57
|
res.json({
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { validateProposalInput, insertTaskProposal, listTaskProposals, reviewTaskProposal } from '../../layer2-business/L2-9-contribution/task-proposal-store.js';
|
|
2
2
|
import { withUncommittedValueBoundary } from '../../layer2-business/L2-9-contribution/contribution-display-envelope.js';
|
|
3
3
|
import { getCanonicalContributionTarget } from '../../layer2-business/L2-9-contribution/canonical-contribution-target.js';
|
|
4
|
+
import { createDraftFromProposal, listDraftBuildTasks, publishDraftBuildTask } from '../../layer2-business/L2-9-contribution/task-proposal-draft.js';
|
|
5
|
+
import { recommendForProposal, insertAiSuggestion, listAiSuggestions, getProposalLite } from '../../layer2-business/L2-9-contribution/task-proposal-ai-store.js';
|
|
6
|
+
const AI_NOTICE = 'AI suggestion — assistant only, NOT a decision. A human maintainer must explicitly create / publish / reject the formal task. AI never auto-publishes, auto-rejects, hides proposals, or assigns reward / credit.';
|
|
4
7
|
const PROPOSAL_NOTICE = 'A task proposal is a SUGGESTION in the maintainer review inbox. It is NOT a contribution fact, formal participation, or any reward / payout / score, and it never appears on the public task board until a maintainer reviews and (manually) converts it. source_ref is a reference only; the canonical contribution target is fixed by trusted config.';
|
|
5
8
|
function withProposalEnvelope(payload) {
|
|
6
9
|
return withUncommittedValueBoundary({ ...payload, proposal_notice: PROPOSAL_NOTICE, canonical_contribution_target: getCanonicalContributionTarget() });
|
|
@@ -42,4 +45,77 @@ export function registerTaskProposalsRoutes(app, deps) {
|
|
|
42
45
|
}
|
|
43
46
|
res.json(withProposalEnvelope({ proposal: result }));
|
|
44
47
|
});
|
|
48
|
+
// ── AI-assist (ASSISTANT ONLY) ─────────────────────────────────────────────
|
|
49
|
+
// Classify + suggest draft fields; stored as recommendation/evidence with accountability metadata.
|
|
50
|
+
// NEVER a decision: no auto-publish / auto-reject / hide / reward. A human admin must act explicitly.
|
|
51
|
+
app.post('/api/admin/task-proposals/:id/ai-assist', (req, res) => {
|
|
52
|
+
const admin = requireSupportAdmin(req, res);
|
|
53
|
+
if (!admin)
|
|
54
|
+
return;
|
|
55
|
+
const p = getProposalLite(db, String(req.params.id));
|
|
56
|
+
if (!p)
|
|
57
|
+
return void errorRes(res, 404, 'NOT_FOUND', 'proposal not found');
|
|
58
|
+
const { recommendation, model, provider } = recommendForProposal(db, p);
|
|
59
|
+
const stored = insertAiSuggestion(db, { proposalId: p.id, reviewerType: 'ai', model, provider,
|
|
60
|
+
inputSummary: `${p.title}\n${p.summary}`, outputJson: JSON.stringify(recommendation) });
|
|
61
|
+
res.json(withProposalEnvelope({ ai_suggestion: recommendation, model, provider, suggestion_id: stored.id, requested_by: admin.id, ai_notice: AI_NOTICE }));
|
|
62
|
+
});
|
|
63
|
+
// stored AI suggestions (evidence) for a proposal
|
|
64
|
+
app.get('/api/admin/task-proposals/:id/ai-suggestions', (req, res) => {
|
|
65
|
+
const admin = requireSupportAdmin(req, res);
|
|
66
|
+
if (!admin)
|
|
67
|
+
return;
|
|
68
|
+
res.json(withProposalEnvelope({ suggestions: listAiSuggestions(db, String(req.params.id)), ai_notice: AI_NOTICE }));
|
|
69
|
+
});
|
|
70
|
+
// ── create an UNPUBLISHED formal task draft from a proposal — explicit maintainer action ──
|
|
71
|
+
// No auto-publish (draft is internal/unclaimable until an explicit publish); no reward/credit side effect.
|
|
72
|
+
app.post('/api/admin/task-proposals/:id/create-task-draft', (req, res) => {
|
|
73
|
+
const admin = requireSupportAdmin(req, res);
|
|
74
|
+
if (!admin)
|
|
75
|
+
return;
|
|
76
|
+
const b = (req.body ?? {});
|
|
77
|
+
if (!b.title || String(b.title).trim().length < 3)
|
|
78
|
+
return void errorRes(res, 400, 'TITLE_REQUIRED', 'title is required (>=3 chars) — the maintainer must review/confirm the formal title');
|
|
79
|
+
const r = createDraftFromProposal(db, {
|
|
80
|
+
proposalId: String(req.params.id), adminId: admin.id,
|
|
81
|
+
title: String(b.title), area: b.area ?? null, description: b.description ?? null,
|
|
82
|
+
sourceRef: b.source_ref ?? null,
|
|
83
|
+
acceptanceCriteria: Array.isArray(b.acceptance_criteria) ? b.acceptance_criteria : [],
|
|
84
|
+
verificationCommands: Array.isArray(b.verification_commands) ? b.verification_commands : [],
|
|
85
|
+
deliverables: Array.isArray(b.deliverables) ? b.deliverables : [],
|
|
86
|
+
allowedPaths: Array.isArray(b.allowed_paths) ? b.allowed_paths : [],
|
|
87
|
+
forbiddenPaths: Array.isArray(b.forbidden_paths) ? b.forbidden_paths : [],
|
|
88
|
+
forbiddenActions: Array.isArray(b.forbidden_actions) ? b.forbidden_actions : [],
|
|
89
|
+
requiredCapabilities: Array.isArray(b.required_capabilities) ? b.required_capabilities : [],
|
|
90
|
+
definitionOfDone: b.definition_of_done ?? null,
|
|
91
|
+
expectedResults: b.expected_results ?? null,
|
|
92
|
+
autoClaimable: b.auto_claimable === false ? false : undefined,
|
|
93
|
+
riskLevel: b.risk_level, taskType: b.task_type, note: b.note ?? null,
|
|
94
|
+
});
|
|
95
|
+
if ('error' in r) {
|
|
96
|
+
const code = r.error_code === 'PROPOSAL_NOT_FOUND' ? 404 : r.error_code === 'PROPOSAL_TERMINAL' ? 409 : r.error_code === 'RATE_LIMITED' ? 429 : 400;
|
|
97
|
+
return void errorRes(res, code, r.error_code, r.error);
|
|
98
|
+
}
|
|
99
|
+
res.json(withProposalEnvelope({ draft: { draft_task_id: r.draft_task_id, status: 'draft', audience: 'internal', published: false }, created_by: admin.id }));
|
|
100
|
+
});
|
|
101
|
+
// admin list of UNPUBLISHED drafts (internal, open) + source proposal id
|
|
102
|
+
app.get('/api/admin/build-task-drafts', (req, res) => {
|
|
103
|
+
const admin = requireSupportAdmin(req, res);
|
|
104
|
+
if (!admin)
|
|
105
|
+
return;
|
|
106
|
+
res.json(withProposalEnvelope({ drafts: listDraftBuildTasks(db) }));
|
|
107
|
+
});
|
|
108
|
+
// PUBLISH a draft → public open task — explicit human/admin action; records the acting admin
|
|
109
|
+
app.post('/api/admin/build-task-drafts/:id/publish', (req, res) => {
|
|
110
|
+
const admin = requireSupportAdmin(req, res);
|
|
111
|
+
if (!admin)
|
|
112
|
+
return;
|
|
113
|
+
const r = publishDraftBuildTask(db, String(req.params.id), admin.id);
|
|
114
|
+
if ('error' in r) {
|
|
115
|
+
const code = r.error_code === 'NOT_FOUND' ? 404
|
|
116
|
+
: (r.error_code === 'PROPOSAL_REJECTED' || r.error_code === 'PROPOSAL_CONVERTED_ELSEWHERE') ? 409 : 400;
|
|
117
|
+
return void errorRes(res, code, r.error_code, r.error, r.missing ? { missing: r.missing } : undefined);
|
|
118
|
+
}
|
|
119
|
+
res.json(withProposalEnvelope({ published: { task_id: r.task_id, published: true }, published_by: admin.id }));
|
|
120
|
+
});
|
|
45
121
|
}
|
package/dist/pwa/routes/trial.js
CHANGED
|
@@ -114,7 +114,7 @@ export async function evaluateTrialClaims(db, generateId) {
|
|
|
114
114
|
return { evaluated, refunded, expired };
|
|
115
115
|
}
|
|
116
116
|
export function registerTrialRoutes(app, deps) {
|
|
117
|
-
const { db, generateId, auth, clientIpHash, clientUaHash, requireProtocolAdmin } = deps;
|
|
117
|
+
const { db, generateId, auth, clientIpHash, clientUaHash, requireProtocolAdmin, logAdminAction } = deps;
|
|
118
118
|
// 卖家:开/更新活动
|
|
119
119
|
app.post('/api/products/:product_id/trial-campaign', async (req, res) => {
|
|
120
120
|
const user = auth(req, res);
|
|
@@ -346,6 +346,8 @@ export function registerTrialRoutes(app, deps) {
|
|
|
346
346
|
const admin = requireProtocolAdmin(req, res);
|
|
347
347
|
if (!admin)
|
|
348
348
|
return;
|
|
349
|
-
|
|
349
|
+
const result = await evaluateTrialClaims(db, generateId);
|
|
350
|
+
logAdminAction(admin.id, 'trial_run_eval', 'protocol', null, { result });
|
|
351
|
+
res.json(result);
|
|
350
352
|
});
|
|
351
353
|
}
|
|
@@ -60,16 +60,10 @@ export function registerUsersPublicRoutes(app, deps) {
|
|
|
60
60
|
const placementName = u.placement_id ? (await dbOne("SELECT name FROM users WHERE id = ?", [u.placement_id]))?.name : null;
|
|
61
61
|
const leftChildName = u.left_child_id ? (await dbOne("SELECT name FROM users WHERE id = ?", [u.left_child_id]))?.name : null;
|
|
62
62
|
const rightChildName = u.right_child_id ? (await dbOne("SELECT name FROM users WHERE id = ?", [u.right_child_id]))?.name : null;
|
|
63
|
-
const score = (await dbOne(`
|
|
64
|
-
SELECT
|
|
65
|
-
COALESCE(SUM(CASE WHEN settled_at IS NULL THEN score ELSE 0 END),0) AS pending_score,
|
|
66
|
-
COALESCE(SUM(CASE WHEN settled_at IS NOT NULL THEN waz_amount ELSE 0 END),0) AS settled_waz,
|
|
67
|
-
COUNT(*) AS total_hits
|
|
68
|
-
FROM binary_score_records WHERE user_id = ?
|
|
69
|
-
`, [targetId]));
|
|
70
63
|
const leftPv = Number(u.total_left_pv ?? 0);
|
|
71
64
|
const rightPv = Number(u.total_right_pv ?? 0);
|
|
72
|
-
|
|
65
|
+
// pre-public de-MLM: PV 对碰为 pre-launch、未启用 —— public 端口不再暴露弱腿 / 对碰收益指标
|
|
66
|
+
// (weak_leg_pv / pending_score / settled_waz / total_hits)。位置 + 左右区 PV 仅作为参与记录保留。
|
|
73
67
|
res.json({
|
|
74
68
|
id: u.id,
|
|
75
69
|
name: u.name,
|
|
@@ -80,10 +74,6 @@ export function registerUsersPublicRoutes(app, deps) {
|
|
|
80
74
|
right_child: u.right_child_id ? { id: u.right_child_id, name: rightChildName } : null,
|
|
81
75
|
total_left_pv: leftPv,
|
|
82
76
|
total_right_pv: rightPv,
|
|
83
|
-
weak_leg_pv: weak,
|
|
84
|
-
pending_score: Number(score.pending_score),
|
|
85
|
-
settled_waz: Number(score.settled_waz),
|
|
86
|
-
total_hits: Number(score.total_hits),
|
|
87
77
|
joined_at: u.created_at,
|
|
88
78
|
});
|
|
89
79
|
});
|
|
@@ -160,7 +160,7 @@ export function registerWalletReadRoutes(app, deps) {
|
|
|
160
160
|
});
|
|
161
161
|
res.json(enriched);
|
|
162
162
|
});
|
|
163
|
-
//
|
|
163
|
+
// 收入构成:销售 / 分享归因 / PV 记录(pre-launch,若适用)
|
|
164
164
|
app.get('/api/wallet/income', async (req, res) => {
|
|
165
165
|
const user = auth(req, res);
|
|
166
166
|
if (!user)
|
package/dist/pwa/server.js
CHANGED
|
@@ -47,6 +47,7 @@ import { createPublicClient, createWalletClient, http, parseAbiItem, parseAbi, p
|
|
|
47
47
|
import { baseSepolia, base } from 'viem/chains';
|
|
48
48
|
import { createHmac, createHash, randomBytes, scryptSync, timingSafeEqual } from 'node:crypto';
|
|
49
49
|
import { safeFetch } from './security/ssrf.js';
|
|
50
|
+
import { deliverVerificationCode, emailDeliveryNotConfigured, isVerificationEmailReady, } from './email-delivery.js';
|
|
50
51
|
// @simplewebauthn/server 已迁出到 src/pwa/routes/webauthn.ts (#1013 Phase 1)
|
|
51
52
|
import { registerWebauthnRoutes } from './routes/webauthn.js';
|
|
52
53
|
import { createHumanPresence } from './human-presence.js';
|
|
@@ -201,6 +202,7 @@ import { registerAdminEventsRoutes } from './routes/admin-events.js';
|
|
|
201
202
|
import { registerAdminModerationRoutes } from './routes/admin-moderation.js';
|
|
202
203
|
// Admin 钱包运维 (#1013 Phase 69) — hot-wallet 2 + withdrawals 2 = 4 endpoints
|
|
203
204
|
import { registerAdminWalletOpsRoutes } from './routes/admin-wallet-ops.js';
|
|
205
|
+
import { resolveBearerProtocolAdmin } from './admin-bearer-auth.js';
|
|
204
206
|
// Admin Catalog (#1013 Phase 70) — categories 2 + products 2 = 4 endpoints
|
|
205
207
|
import { registerAdminCatalogRoutes } from './routes/admin-catalog.js';
|
|
206
208
|
// Reputation 公开查询 (#1013 Phase 71) — 2 endpoints
|
|
@@ -310,6 +312,8 @@ import { registerPublicBuildTasksRoutes } from './routes/public-build-tasks.js';
|
|
|
310
312
|
import { initBuildTasksSchema } from '../layer2-business/L2-9-contribution/build-tasks-engine.js';
|
|
311
313
|
import { initBuildTaskAgentMetadataSchema } from '../layer2-business/L2-9-contribution/build-task-agent-metadata-store.js';
|
|
312
314
|
import { initTaskProposalSchema } from '../layer2-business/L2-9-contribution/task-proposal-store.js';
|
|
315
|
+
import { initTaskProposalAiSchema } from '../layer2-business/L2-9-contribution/task-proposal-ai-store.js';
|
|
316
|
+
import { initTaskProposalDraftLinkSchema } from '../layer2-business/L2-9-contribution/task-proposal-draft.js';
|
|
313
317
|
import { registerTaskProposalsRoutes } from './routes/task-proposals.js';
|
|
314
318
|
import { createSlidingWindowLimiter } from './rate-limit.js';
|
|
315
319
|
import { registerBuildReputationRoutes } from './routes/build-reputation.js';
|
|
@@ -375,6 +379,8 @@ initBuildFeedbackSchema(db); // RFC-004 build_feedback
|
|
|
375
379
|
initBuildTasksSchema(db); // RFC-006 build_tasks(协调层)
|
|
376
380
|
initBuildTaskAgentMetadataSchema(db); // PR9B — agent-ready task metadata satellite(schema only;FUTURE-TASK-BOARD-V1-DESIGN #326)
|
|
377
381
|
initTaskProposalSchema(db); // Task Proposal Inbox v1 — suggestion inbox(maintainer review;never auto build_task)
|
|
382
|
+
initTaskProposalAiSchema(db); // Task Proposal AI-assist — assistant-only recommendation/evidence(human decides)
|
|
383
|
+
initTaskProposalDraftLinkSchema(db); // Task Proposal draft links — source proposal ↔ draft task(converted at publish)
|
|
378
384
|
initBuildReputationSchema(db); // RFC-006 build_reputation(独立池 + 贡献者看板)
|
|
379
385
|
initGithubCredentialStoreSchema(db); // PR 3B-3a — GitHub credential store + RFC-017 fact layer (schema only)
|
|
380
386
|
initIdentityBindingSchema(db); // PR 4a — GitHub identity → WebAZ account binding (append-only events + active projection)
|
|
@@ -5850,6 +5856,12 @@ registerAuthRegisterRoutes(app, {
|
|
|
5850
5856
|
pickPreferredSide, joinPowerLeg,
|
|
5851
5857
|
get INVITE_ROTATION_HANDLES() { return INVITE_ROTATION_HANDLES; },
|
|
5852
5858
|
inviteRotationLookup,
|
|
5859
|
+
// 邮箱验证优先注册 — issueCode/findActiveCode 是 hoisted 函数声明、isVerificationEmailReady/
|
|
5860
|
+
// emailDeliveryNotConfigured 是 import,均可在此安全引用;CODE_TTL_MIN/MAX_CODE_ATTEMPTS 是后置 const,
|
|
5861
|
+
// 走 getter 延迟读避免 TDZ。
|
|
5862
|
+
issueCode, findActiveCode, canDeliverCodes: isVerificationEmailReady, emailDeliveryNotConfigured,
|
|
5863
|
+
get CODE_TTL_MIN() { return CODE_TTL_MIN; },
|
|
5864
|
+
get MAX_CODE_ATTEMPTS() { return MAX_CODE_ATTEMPTS; },
|
|
5853
5865
|
recordSession, broadcastSystemEvent,
|
|
5854
5866
|
});
|
|
5855
5867
|
// #1013 Phase 116: me + profile 已迁出
|
|
@@ -6027,23 +6039,30 @@ registerAuthLoginRoutes(app, {
|
|
|
6027
6039
|
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
6028
6040
|
const CODE_TTL_MIN = 10;
|
|
6029
6041
|
const MAX_CODE_ATTEMPTS = 5;
|
|
6030
|
-
const IS_DEV = process.env.NODE_ENV !== 'production';
|
|
6031
6042
|
function genCode() {
|
|
6032
6043
|
return String(Math.floor(100000 + Math.random() * 900000));
|
|
6033
6044
|
}
|
|
6034
|
-
function deliverCode(target, code, purpose) {
|
|
6035
|
-
|
|
6036
|
-
console.log(`[verify] ${purpose} → ${target} code=${code} (expires ${CODE_TTL_MIN}min)`);
|
|
6045
|
+
async function deliverCode(target, code, purpose) {
|
|
6046
|
+
return deliverVerificationCode({ target, code, purpose, ttlMin: CODE_TTL_MIN });
|
|
6037
6047
|
}
|
|
6038
|
-
function issueCode(userId, channel, target, purpose) {
|
|
6048
|
+
async function issueCode(userId, channel, target, purpose) {
|
|
6049
|
+
if (channel === 'email' && !isVerificationEmailReady())
|
|
6050
|
+
return emailDeliveryNotConfigured();
|
|
6039
6051
|
const id = generateId('vcode');
|
|
6040
6052
|
const code = genCode();
|
|
6041
6053
|
const expiresAt = new Date(Date.now() + CODE_TTL_MIN * 60_000).toISOString();
|
|
6042
6054
|
db.prepare(`INSERT INTO verification_codes (id, user_id, channel, target, code, purpose, expires_at)
|
|
6043
6055
|
VALUES (?,?,?,?,?,?,?)`)
|
|
6044
6056
|
.run(id, userId, channel, target, code, purpose, expiresAt);
|
|
6045
|
-
deliverCode(target, code, purpose);
|
|
6046
|
-
|
|
6057
|
+
const delivered = await deliverCode(target, code, purpose);
|
|
6058
|
+
if (!delivered.ok) {
|
|
6059
|
+
try {
|
|
6060
|
+
db.prepare("UPDATE verification_codes SET used_at = datetime('now') WHERE id = ?").run(id);
|
|
6061
|
+
}
|
|
6062
|
+
catch { }
|
|
6063
|
+
return delivered;
|
|
6064
|
+
}
|
|
6065
|
+
return { ok: true, code, expires_at: expiresAt, provider: delivered.provider };
|
|
6047
6066
|
}
|
|
6048
6067
|
function findActiveCode(channel, target, purpose) {
|
|
6049
6068
|
return db.prepare(`
|
|
@@ -6056,12 +6075,13 @@ function findActiveCode(channel, target, purpose) {
|
|
|
6056
6075
|
// #1013 Phase 49: 3 recover-key endpoints 已迁出到 routes/recover-key.ts
|
|
6057
6076
|
registerRecoverKeyRoutes(app, {
|
|
6058
6077
|
db, internalAuditorId: INTERNAL_AUDITOR_ID,
|
|
6059
|
-
issueCode, findActiveCode,
|
|
6078
|
+
issueCode, findActiveCode, canDeliverCodes: isVerificationEmailReady,
|
|
6079
|
+
emailDeliveryNotConfigured, hashPassword, CODE_TTL_MIN, MAX_CODE_ATTEMPTS,
|
|
6060
6080
|
});
|
|
6061
6081
|
// #1013 Phase 55: 5 profile-credentials endpoints 已迁出到 routes/profile-credentials.ts
|
|
6062
6082
|
registerProfileCredentialsRoutes(app, {
|
|
6063
6083
|
db, auth, verifyPassword, hashPassword,
|
|
6064
|
-
issueCode, findActiveCode,
|
|
6084
|
+
issueCode, findActiveCode, MAX_CODE_ATTEMPTS,
|
|
6065
6085
|
});
|
|
6066
6086
|
// 搜索商品(声誉权重排序)
|
|
6067
6087
|
// 构建 agent_summary:一句话决策摘要
|
|
@@ -6292,6 +6312,7 @@ registerFlashSalesRoutes(app, { db, generateId, auth, broadcastSystemEvent });
|
|
|
6292
6312
|
registerTrialRoutes(app, {
|
|
6293
6313
|
db, generateId, auth, clientIpHash, clientUaHash,
|
|
6294
6314
|
requireProtocolAdmin: (req, res) => requireAdminPermission(req, res, 'protocol'),
|
|
6315
|
+
logAdminAction,
|
|
6295
6316
|
});
|
|
6296
6317
|
// ─── Wave B-2: 预售 / waitlist ─────────────────────────────
|
|
6297
6318
|
// #1013 Phase 24: 5 endpoints 已迁出到 routes/waitlist.ts
|
|
@@ -6713,6 +6734,7 @@ registerAdminOpsRoutes(app, {
|
|
|
6713
6734
|
hasAdminPermission,
|
|
6714
6735
|
INTERNAL_AUDITOR_ID, ADMIN_EXPORT_LIMIT, csvEscapeAdmin,
|
|
6715
6736
|
anthropic, applyDecayIfDue, computeValueBadges,
|
|
6737
|
+
logAdminAction,
|
|
6716
6738
|
});
|
|
6717
6739
|
// AI 2 endpoints — Phase 100 已迁出
|
|
6718
6740
|
registerAiRoutes(app, { db, auth, anthropic });
|
|
@@ -6742,6 +6764,7 @@ registerAdminModerationRoutes(app, {
|
|
|
6742
6764
|
db, generateId,
|
|
6743
6765
|
requireUsersAdmin: (req, res) => requireAdminPermission(req, res, 'users'),
|
|
6744
6766
|
authFailures, INTERNAL_AUDITOR_ID, broadcastSystemEvent,
|
|
6767
|
+
logAdminAction,
|
|
6745
6768
|
});
|
|
6746
6769
|
// 邀请码 3 endpoints — Phase 98 已迁出
|
|
6747
6770
|
registerReferralRoutes(app, {
|
|
@@ -7239,6 +7262,7 @@ setInterval(() => {
|
|
|
7239
7262
|
registerAdminAtomicRoutes(app, {
|
|
7240
7263
|
requireProtocolAdmin: (req, res) => requireAdminPermission(req, res, 'protocol'),
|
|
7241
7264
|
processPvLedger, runBinarySettlement, executeSafeSettlementCron,
|
|
7265
|
+
logAdminAction,
|
|
7242
7266
|
});
|
|
7243
7267
|
// #1013 Phase 110: tokenomics/status + shares/dashboard 已迁出
|
|
7244
7268
|
registerDashboardsRoutes(app, { db, auth });
|
|
@@ -7274,6 +7298,7 @@ registerAdminUsersQueryRoutes(app, {
|
|
|
7274
7298
|
adminCanOperateOn, isRootAdmin, isAllowedSponsor,
|
|
7275
7299
|
maskApiKey, computeLightTags, getAdminScope, getSellerDailyLimit, todayStartISO,
|
|
7276
7300
|
broadcastSystemEvent, INTERNAL_AUDITOR_ID,
|
|
7301
|
+
logAdminAction,
|
|
7277
7302
|
});
|
|
7278
7303
|
function getSellerDailyLimit(user) {
|
|
7279
7304
|
const id = String(user.id ?? '');
|
|
@@ -8130,6 +8155,7 @@ registerAuctionRoutes(app, {
|
|
|
8130
8155
|
RFQ_MAX_QTY, RFQ_MAX_PRICE,
|
|
8131
8156
|
LISTING_CATEGORIES, isListingCategoryKey,
|
|
8132
8157
|
requireProtocolAdmin: (req, res) => requireAdminPermission(req, res, 'protocol'),
|
|
8158
|
+
logAdminAction,
|
|
8133
8159
|
});
|
|
8134
8160
|
// AUC 结算 helper:到期 → 最高 active bid → 建单(或流拍)
|
|
8135
8161
|
// P0 audit fix #2:顶层 try/catch 兜底,意外抛错时 status='error' 防 cron 死循环
|
|
@@ -9851,6 +9877,23 @@ registerAdminWalletOpsRoutes(app, {
|
|
|
9851
9877
|
getIsMainnet: () => IS_MAINNET,
|
|
9852
9878
|
getNetwork: () => NETWORK,
|
|
9853
9879
|
executeWithdrawal: (id) => executeWithdrawal(id),
|
|
9880
|
+
logAdminAction,
|
|
9881
|
+
// dual-accept transition for attribution(非最终安全收紧):只读、不响应地解析登录的 protocol-admin。
|
|
9882
|
+
// 用 resolveBearerProtocolAdmin(钱路强校验):仅认 Authorization: Bearer(不认 req.body.api_key)、
|
|
9883
|
+
// 拒暂停用户、拒已吊销会话;角色+protocol 权限用中央 hasAdminPermission(防漂移)。null → 回落共享 ADMIN_KEY。
|
|
9884
|
+
// 最终弃用 x-admin-key 留后续 PR。
|
|
9885
|
+
resolveProtocolAdminSoft: (req) => resolveBearerProtocolAdmin(db, req, (u) => {
|
|
9886
|
+
let rolesList = [];
|
|
9887
|
+
try {
|
|
9888
|
+
rolesList = JSON.parse(u.roles || '[]');
|
|
9889
|
+
}
|
|
9890
|
+
catch {
|
|
9891
|
+
rolesList = [];
|
|
9892
|
+
}
|
|
9893
|
+
if (u.role !== 'admin' && !rolesList.includes('admin'))
|
|
9894
|
+
return false;
|
|
9895
|
+
return hasAdminPermission(u, 'protocol');
|
|
9896
|
+
}),
|
|
9854
9897
|
});
|
|
9855
9898
|
// #1013 Phase 80: 10 wallet read endpoints 已迁出
|
|
9856
9899
|
registerWalletReadRoutes(app, {
|
|
@@ -10336,6 +10379,21 @@ db.exec(`
|
|
|
10336
10379
|
VALUES (?, ?, 'major', ?, ?, ?, ?)`)
|
|
10337
10380
|
.run('1.0', hash, Date.now(), textZh, textEn, 'Initial v1.0 lock — placeholder canonical text pointing to RFC-002');
|
|
10338
10381
|
})();
|
|
10382
|
+
(function seedConsentV11() {
|
|
10383
|
+
const existing = db.prepare("SELECT version FROM rewards_consent_texts WHERE version = '1.1'").get();
|
|
10384
|
+
if (existing)
|
|
10385
|
+
return;
|
|
10386
|
+
const textZh = 'WebAZ 分享分润开通(rewards opt-in) v1.1 — 由 RFC-002 §3.3 / §3.10 定义。本同意仅用于记录分享分润相关的经济关系:Passkey 真人签名、推荐关系/左右区位置、佣金/PV/escrow 结算规则。本流程不是购物流程,也不是共建贡献资格;不影响贡献任务、GitHub 贡献认领或普通下单。佣金层级按地区合规配置生效;当前预发布期全局上限为 1 级,“三级”仅为协议最大设计。你可以随时退出,退出不影响已下单或未来订单;已发生的订单和结算按当时有效规则处理。';
|
|
10387
|
+
const textEn = 'WebAZ share-commission opt-in (rewards opt-in) v1.1 — defined by RFC-002 §3.3 / §3.10. This consent only records the economic relationship for share commission: Passkey-signed proof of personhood, referral relationship / left-right placement, and commission / PV / escrow settlement rules. This is not a shopping flow and not contribution eligibility; it does not affect contribution tasks, GitHub contribution claims, or normal orders. Commission levels follow per-region compliance configuration; during pre-launch the global cap is 1 level, and “three tiers” is only the protocol maximum design. You may leave at any time without affecting past or future orders; already-created orders and settlements follow the rules effective at that time.';
|
|
10388
|
+
const hash = createHash('sha256').update(textZh + '\n---\n' + textEn).digest('hex');
|
|
10389
|
+
// effective_at must be strictly later than v1.0's so "latest major" deterministically resolves to v1.1
|
|
10390
|
+
// even on a fresh DB that seeds both rows in the same boot (avoids a same-ms ORDER BY tie).
|
|
10391
|
+
const v10 = db.prepare("SELECT effective_at FROM rewards_consent_texts WHERE version = '1.0'").get();
|
|
10392
|
+
const effectiveAt = Math.max(Date.now(), (v10?.effective_at ?? 0) + 1);
|
|
10393
|
+
db.prepare(`INSERT INTO rewards_consent_texts (version, hash, change_class, effective_at, text_zh, text_en, changelog)
|
|
10394
|
+
VALUES (?, ?, 'major', ?, ?, ?, ?)`)
|
|
10395
|
+
.run('1.1', hash, effectiveAt, textZh, textEn, 'v1.1 clarification — share-commission opt-in framing (not 共建身份/Builder Identity, not contribution eligibility) + current commission-level reality boundary (pre-launch cap 1 level); v1.0 left frozen');
|
|
10396
|
+
})();
|
|
10339
10397
|
// 4. rewards_applications:申请留痕表(append-only audit;action='activate'|'deactivate'|'auto_downgrade'|'reconfirm')
|
|
10340
10398
|
db.exec(`
|
|
10341
10399
|
CREATE TABLE IF NOT EXISTS rewards_applications (
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seasonkoh/webaz",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
4
4
|
"description": "[PRE-LAUNCH] Agent-native decentralized commerce protocol. Humans and AI agents trade on the same protocol via MCP tools. ⚠️ Repository currently private until W8 public launch — GitHub links may return 404. See https://webaz.xyz for status.",
|
|
5
5
|
"main": "dist/mcp.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
},
|
|
9
9
|
"mcpName": "io.github.seasonsagents-art/webaz",
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "tsc && cp -r src/pwa/public dist/pwa/",
|
|
11
|
+
"build": "npm run build:whitepaper && tsc && cp -r src/pwa/public dist/pwa/",
|
|
12
|
+
"build:whitepaper": "tsx scripts/build-whitepaper-html.ts",
|
|
13
|
+
"check:whitepaper-fresh": "tsx scripts/build-whitepaper-html.ts && git diff --exit-code -- src/pwa/public/whitepaper",
|
|
12
14
|
"prepare": "npm run build",
|
|
13
15
|
"dev": "tsx src/index.ts",
|
|
14
16
|
"mcp": "tsx src/mcp.ts",
|
|
@@ -66,7 +68,33 @@
|
|
|
66
68
|
"check:pwa-syntax": "node --check src/pwa/public/app.js && node --check src/pwa/public/i18n.js && node --check src/pwa/public/sw.js",
|
|
67
69
|
"test:rewards-vs-contribution-copy": "tsx scripts/test-rewards-vs-contribution-copy.ts",
|
|
68
70
|
"test:invite-code-narrowing": "tsx scripts/test-invite-code-narrowing.ts",
|
|
69
|
-
"test:shop-referral-attribution": "tsx scripts/test-shop-referral-attribution.ts"
|
|
71
|
+
"test:shop-referral-attribution": "tsx scripts/test-shop-referral-attribution.ts",
|
|
72
|
+
"test:money-units": "tsx scripts/test-money-units.ts",
|
|
73
|
+
"test:identity-claim-ui": "tsx scripts/test-identity-claim-ui.ts",
|
|
74
|
+
"test:identity-claim-discovery": "tsx scripts/test-identity-claim-discovery.ts",
|
|
75
|
+
"test:operator-ingest-guards": "tsx scripts/test-operator-ingest-guards.ts",
|
|
76
|
+
"test:rewards-consent-text": "tsx scripts/test-rewards-consent-text.ts",
|
|
77
|
+
"test:mcp-apikey-fallback": "tsx scripts/test-mcp-apikey-fallback.ts",
|
|
78
|
+
"test:email-delivery": "tsx scripts/test-email-delivery-resend.ts",
|
|
79
|
+
"test:seller-order-actions": "tsx scripts/test-seller-order-actions.ts",
|
|
80
|
+
"test:decline-action": "tsx tests/test-decline-action.ts",
|
|
81
|
+
"test:recover-key-password-reset": "tsx scripts/test-recover-key-password-reset.ts",
|
|
82
|
+
"test:register-email-verify": "tsx scripts/test-register-email-verify.ts",
|
|
83
|
+
"test:recovery-onboarding-ui": "tsx scripts/test-recovery-onboarding-ui.ts",
|
|
84
|
+
"test:welcome-signup-button": "tsx scripts/test-welcome-signup-button.ts",
|
|
85
|
+
"test:auto-accept-risk-copy": "tsx scripts/test-auto-accept-risk-copy.ts",
|
|
86
|
+
"test:order-detail-return-inline": "tsx scripts/test-order-detail-return-inline.ts",
|
|
87
|
+
"test:seller-store-reviews": "tsx scripts/test-seller-store-reviews.ts",
|
|
88
|
+
"test:seller-ratings-double-blind": "tsx scripts/test-seller-ratings-double-blind.ts",
|
|
89
|
+
"test:seller-self-fulfill-ship": "tsx scripts/test-seller-self-fulfill-ship.ts",
|
|
90
|
+
"test:public-ratings-double-blind": "tsx scripts/test-public-ratings-double-blind.ts",
|
|
91
|
+
"test:admin-security-panel": "tsx scripts/test-admin-security-panel.ts",
|
|
92
|
+
"test:admin-economic-trigger-audit": "tsx scripts/test-admin-economic-trigger-audit.ts",
|
|
93
|
+
"test:admin-audit-slice1": "tsx scripts/test-admin-audit-slice1.ts",
|
|
94
|
+
"test:admin-withdrawal-approve-attribution": "tsx scripts/test-admin-withdrawal-approve-attribution.ts",
|
|
95
|
+
"test:admin-bearer-auth": "tsx scripts/test-admin-bearer-auth.ts",
|
|
96
|
+
"test:admin-reputation-decay-audit": "tsx scripts/test-admin-reputation-decay-audit.ts",
|
|
97
|
+
"test:task-proposal-draft-flow": "tsx scripts/test-task-proposal-draft-flow.ts"
|
|
70
98
|
},
|
|
71
99
|
"keywords": [
|
|
72
100
|
"mcp",
|