@seasonkoh/webaz 0.1.25 → 0.1.27
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/LICENSE +2 -2
- package/NOTICE +24 -3
- package/README.md +74 -328
- package/README.zh-CN.md +419 -0
- package/dist/layer0-foundation/L0-2-state-machine/genuine-sale.js +21 -0
- package/dist/layer0-foundation/L0-5-manifest/manifest.js +8 -3
- package/dist/layer1-agent/L1-1-mcp-server/auth.js +13 -1
- package/dist/layer1-agent/L1-1-mcp-server/server.js +164 -177
- package/dist/layer2-business/L2-9-contribution/admin-coordination-ingestion-engine.js +181 -0
- package/dist/layer2-business/L2-9-contribution/admin-coordination-resolver.js +114 -0
- package/dist/layer2-business/L2-9-contribution/admin-coordination-store.js +251 -0
- package/dist/layer2-business/L2-9-contribution/admin-operator-claim-workflow.js +390 -0
- package/dist/layer2-business/L2-9-contribution/build-task-agent-metadata-store.js +33 -0
- package/dist/layer2-business/L2-9-contribution/build-task-participation.js +6 -2
- package/dist/layer2-business/L2-9-contribution/build-task-quota.js +337 -0
- package/dist/layer2-business/L2-9-contribution/build-task-read.js +25 -2
- package/dist/layer2-business/L2-9-contribution/build-tasks-engine.js +58 -8
- package/dist/layer2-business/L2-9-contribution/canonical-contribution-target.js +1 -1
- package/dist/layer2-business/L2-9-contribution/contribution-facts-read.js +66 -0
- 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 +360 -0
- package/dist/layer2-business/L2-9-contribution/task-proposal-store.js +29 -4
- package/dist/ledger.js +1 -1
- package/dist/pwa/admin-audit.js +38 -0
- package/dist/pwa/admin-bearer-auth.js +21 -0
- package/dist/pwa/anti-abuse-thresholds.js +135 -0
- package/dist/pwa/cf-origin-guard.js +33 -0
- package/dist/pwa/contract-fingerprint.js +1 -0
- package/dist/pwa/data/onboarding-cases.js +2 -2
- package/dist/pwa/data/onboarding-quiz.js +1 -1
- package/dist/pwa/economic-participation.js +2 -2
- package/dist/pwa/email-delivery.js +127 -0
- package/dist/pwa/integration-contract.js +46 -4
- package/dist/pwa/internal/pv-settlement.js +12 -0
- package/dist/pwa/internal/wallet-signer.js +26 -0
- package/dist/pwa/public/app.js +1607 -912
- package/dist/pwa/public/i18n.js +284 -68
- package/dist/pwa/public/index.html +1 -1
- package/dist/pwa/public/openapi.json +4760 -2769
- package/dist/pwa/public/whitepaper/en/index.html +153 -0
- package/dist/pwa/public/whitepaper/zh-CN/index.html +153 -0
- package/dist/pwa/pv-kill-switch.js +31 -0
- package/dist/pwa/routes/admin-admins.js +48 -1
- package/dist/pwa/routes/admin-analytics.js +1 -10
- package/dist/pwa/routes/admin-atomic.js +7 -14
- package/dist/pwa/routes/admin-moderation.js +25 -1
- package/dist/pwa/routes/admin-operator-claims.js +280 -0
- package/dist/pwa/routes/admin-ops.js +13 -2
- package/dist/pwa/routes/admin-reports.js +4 -26
- package/dist/pwa/routes/admin-tokenomics.js +2 -76
- package/dist/pwa/routes/admin-users-lifecycle.js +1 -14
- package/dist/pwa/routes/admin-users-query.js +35 -2
- 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 +11 -6
- package/dist/pwa/routes/auth-register.js +84 -24
- package/dist/pwa/routes/build-task-quota.js +113 -0
- package/dist/pwa/routes/claim-verify.js +15 -11
- package/dist/pwa/routes/contribution-facts.js +18 -0
- package/dist/pwa/routes/contribution-identity.js +17 -0
- package/dist/pwa/routes/dispute-cases.js +5 -4
- package/dist/pwa/routes/growth.js +4 -4
- package/dist/pwa/routes/orders-action.js +46 -23
- package/dist/pwa/routes/orders-create.js +1 -1
- package/dist/pwa/routes/products-meta.js +19 -6
- package/dist/pwa/routes/profile-credentials.js +7 -4
- package/dist/pwa/routes/profile-placement.js +8 -9
- package/dist/pwa/routes/promoter.js +11 -44
- package/dist/pwa/routes/public-build-tasks.js +5 -1
- package/dist/pwa/routes/public-utils.js +9 -12
- package/dist/pwa/routes/ratings.js +64 -4
- package/dist/pwa/routes/recover-key.js +58 -19
- package/dist/pwa/routes/referral.js +9 -50
- package/dist/pwa/routes/rewards-apply.js +3 -2
- package/dist/pwa/routes/share-redirects.js +5 -4
- package/dist/pwa/routes/shareables-interactions.js +2 -1
- package/dist/pwa/routes/shop-referral.js +6 -5
- package/dist/pwa/routes/shops.js +5 -2
- package/dist/pwa/routes/task-proposals.js +159 -7
- package/dist/pwa/routes/trial.js +4 -2
- package/dist/pwa/routes/users-public.js +1 -14
- package/dist/pwa/routes/wallet-read.js +3 -15
- package/dist/pwa/routes/webauthn.js +1 -1
- package/dist/pwa/server.js +223 -478
- package/dist/settlement-math.js +3 -3
- package/dist/version.js +6 -4
- package/package.json +62 -8
- package/dist/index.js +0 -182
- package/dist/pwa/public/docs/ECONOMIC-MODEL.md +0 -287
- package/dist/pwa/public/docs/INTEGRATOR.md +0 -67
- package/dist/pwa/public/docs/META-RULES-FULL.md +0 -543
- package/dist/test-dispute.js +0 -153
- package/dist/test-manifest.js +0 -61
- package/dist/test-mcp-tools.js +0 -135
- package/dist/test-reputation.js +0 -116
- package/dist/test-skill-market.js +0 -101
|
@@ -43,6 +43,15 @@ const TELEMETRY_ENABLED = (process.env.WEBAZ_TELEMETRY ?? 'off').toLowerCase() =
|
|
|
43
43
|
// P0 不迁移任何工具(NETWORK_TOOLS 为空)→ 一切仍走本地 = 零行为变化;P1/P2 逐个把工具名加入集合切到网络。
|
|
44
44
|
const WEBAZ_API_URL = (process.env.WEBAZ_API_URL ?? 'https://webaz.xyz').replace(/\/+$/, '');
|
|
45
45
|
const WEBAZ_API_KEY = process.env.WEBAZ_API_KEY ?? '';
|
|
46
|
+
// F6 (dogfood R2): keyed MCP handlers resolve api_key as explicit args.api_key > env WEBAZ_API_KEY >
|
|
47
|
+
// '' (→ the existing typed API_KEY_REQUIRED guards). Explicit ALWAYS wins; env never overrides an explicit
|
|
48
|
+
// key. Keyless actions (list/discover/detail/suggest/browse/get_campaign…) gate their public branches
|
|
49
|
+
// separately and never call this, so a configured env key does NOT change the public read boundary.
|
|
50
|
+
// The key is never printed/returned/logged.
|
|
51
|
+
export function resolveMcpApiKey(args, envKey = WEBAZ_API_KEY) {
|
|
52
|
+
const explicit = typeof args?.api_key === 'string' ? args.api_key.trim() : '';
|
|
53
|
+
return explicit || envKey;
|
|
54
|
+
}
|
|
46
55
|
const WEBAZ_MODE_ENV = (process.env.WEBAZ_MODE ?? '').toLowerCase();
|
|
47
56
|
// 模式:显式 WEBAZ_MODE 优先;否则有 api_key → network,无 key → network_readonly(装完即见真网络)。
|
|
48
57
|
// network_readonly(L1 onboarding,2026-06-08):无 key 默认。公共读匿名打 webaz.xyz(真 catalog/协议),
|
|
@@ -403,11 +412,11 @@ Skipping is allowed but agent then carries price/stock-race risk itself.`,
|
|
|
403
412
|
inputSchema: {
|
|
404
413
|
type: 'object',
|
|
405
414
|
properties: {
|
|
406
|
-
api_key: { type: 'string', description: "Buyer's api_key" },
|
|
415
|
+
api_key: { type: 'string', description: "Buyer's api_key (or omit and set the WEBAZ_API_KEY env var)" },
|
|
407
416
|
product_id: { type: 'string', description: 'Product ID (from webaz_search)' },
|
|
408
417
|
quantity: { type: 'number', description: 'Quantity, default 1' },
|
|
409
418
|
},
|
|
410
|
-
required: ['
|
|
419
|
+
required: ['product_id'],
|
|
411
420
|
},
|
|
412
421
|
},
|
|
413
422
|
{
|
|
@@ -423,7 +432,7 @@ Actions: create (title/description/price) | mine | update (product_id + changed
|
|
|
423
432
|
inputSchema: {
|
|
424
433
|
type: 'object',
|
|
425
434
|
properties: {
|
|
426
|
-
api_key: { type: 'string', description: "Seller's api_key" },
|
|
435
|
+
api_key: { type: 'string', description: "Seller's api_key (or omit and set the WEBAZ_API_KEY env var)" },
|
|
427
436
|
action: {
|
|
428
437
|
type: 'string',
|
|
429
438
|
enum: ['create', 'mine', 'update', 'delist', 'relist', 'trash', 'delete'],
|
|
@@ -479,7 +488,7 @@ Actions: create (title/description/price) | mine | update (product_id + changed
|
|
|
479
488
|
description: '[S4] Product-origin claims (challengeable). E.g. {"made_in":"Kyoto JP","material":"100% cotton GOTS-cert","certs":[{"name":"GOTS","sha256":"<64-hex>"}]}. Total JSON ≤4KB; any cert sha256 must be 64-hex. Any buyer can challenge.',
|
|
480
489
|
},
|
|
481
490
|
},
|
|
482
|
-
required: [
|
|
491
|
+
required: [],
|
|
483
492
|
},
|
|
484
493
|
},
|
|
485
494
|
{
|
|
@@ -501,7 +510,7 @@ Options:
|
|
|
501
510
|
inputSchema: {
|
|
502
511
|
type: 'object',
|
|
503
512
|
properties: {
|
|
504
|
-
api_key: { type: 'string', description: "Buyer's api_key" },
|
|
513
|
+
api_key: { type: 'string', description: "Buyer's api_key (or omit and set the WEBAZ_API_KEY env var)" },
|
|
505
514
|
product_id: { type: 'string', description: 'Product ID to buy (from webaz_search)' },
|
|
506
515
|
quantity: { type: 'number', description: 'Quantity, default 1' },
|
|
507
516
|
shipping_address: { type: 'string', description: 'Shipping address' },
|
|
@@ -512,7 +521,7 @@ Options:
|
|
|
512
521
|
},
|
|
513
522
|
promoter_api_key: {
|
|
514
523
|
type: 'string',
|
|
515
|
-
description: "Referrer api_key (optional). ⚠️ Only L1 recorded (direct referrer, 70% commission); L2/L3 can't be inferred via MCP,
|
|
524
|
+
description: "Referrer api_key (optional). ⚠️ Only L1 recorded (direct referrer, 70% commission); L2/L3 can't be inferred via MCP, so the undelivered L2/L3 portions go to commission_reserve (protocol reserve, in-only). Full 7:2:1 three-tier chain requires buyer clicking ?ref= URL from webaz_share_link (creates product_share_attribution).",
|
|
516
525
|
},
|
|
517
526
|
// B2 隐私购物
|
|
518
527
|
anonymous_recipient: {
|
|
@@ -526,7 +535,7 @@ Options:
|
|
|
526
535
|
description: '[B5] Per-order donation pct (0 / 0.5 / 1 / 2 / 5). Computed separately + into charity_fund, posted on order complete.',
|
|
527
536
|
},
|
|
528
537
|
},
|
|
529
|
-
required: ['
|
|
538
|
+
required: ['product_id', 'shipping_address'],
|
|
530
539
|
},
|
|
531
540
|
},
|
|
532
541
|
{
|
|
@@ -534,15 +543,15 @@ Options:
|
|
|
534
543
|
// was ~927 chars, now ~430 chars
|
|
535
544
|
description: `STATUS TRANSITIONS on an order — NOT for editing order content (price/qty/address immutable after creation). Each role can only perform their own actions.
|
|
536
545
|
|
|
537
|
-
- **Seller**: accept (24h after payment) | ship (needs tracking, within handling time)
|
|
538
|
-
- **Logistics**: pickup (48h after ship) | transit | deliver (needs proof description)
|
|
546
|
+
- **Seller**: accept (24h after payment) | ship (needs tracking/notes, within handling time) | pickup/transit/deliver ONLY when order.logistics_id is empty (Phase-1 self-fulfill; seller carries logistics responsibility) | decline (actively refuse a PAID order instead of silent timeout; requires decline_reason_code — objective codes [stock_consumed_concurrent/stale_price_snapshot/force_majeure] go to a PROVISIONAL fault you must then contest within the window, NOT auto-cleared; subjective codes [price_regret/cherry_pick/other] settle immediately as seller-fault + buyer refund) | contest_decline (open human arbitration on an objective-claimed provisional fault, within the contest window, to be cleared to no-fault; pass evidence_description — window expiry finalizes as fault)
|
|
547
|
+
- **Logistics**: pickup (48h after ship) | transit | deliver (needs proof description) when assigned or claiming an unassigned shipped order
|
|
539
548
|
- **Buyer**: confirm (→ fund settlement) | dispute (needs reason; freezes funds → arbitration)
|
|
540
549
|
|
|
541
550
|
Missing deadline → protocol auto-marks party in default.`,
|
|
542
551
|
inputSchema: {
|
|
543
552
|
type: 'object',
|
|
544
553
|
properties: {
|
|
545
|
-
api_key: { type: 'string', description: "Operator's api_key" },
|
|
554
|
+
api_key: { type: 'string', description: "Operator's api_key (or omit and set the WEBAZ_API_KEY env var)" },
|
|
546
555
|
order_id: { type: 'string', description: 'Order ID' },
|
|
547
556
|
action: {
|
|
548
557
|
type: 'string',
|
|
@@ -560,7 +569,7 @@ Missing deadline → protocol auto-marks party in default.`,
|
|
|
560
569
|
description: 'Evidence description (recommended for ship/pickup/deliver; required for dispute)',
|
|
561
570
|
},
|
|
562
571
|
},
|
|
563
|
-
required: ['
|
|
572
|
+
required: ['order_id', 'action'],
|
|
564
573
|
},
|
|
565
574
|
},
|
|
566
575
|
{
|
|
@@ -570,10 +579,10 @@ Missing deadline → protocol auto-marks party in default.`,
|
|
|
570
579
|
inputSchema: {
|
|
571
580
|
type: 'object',
|
|
572
581
|
properties: {
|
|
573
|
-
api_key: { type: 'string', description: "Querier's api_key" },
|
|
582
|
+
api_key: { type: 'string', description: "Querier's api_key (or omit and set the WEBAZ_API_KEY env var)" },
|
|
574
583
|
order_id: { type: 'string', description: 'Order ID' },
|
|
575
584
|
},
|
|
576
|
-
required: ['
|
|
585
|
+
required: ['order_id'],
|
|
577
586
|
},
|
|
578
587
|
},
|
|
579
588
|
{
|
|
@@ -591,14 +600,14 @@ Actions:
|
|
|
591
600
|
inputSchema: {
|
|
592
601
|
type: 'object',
|
|
593
602
|
properties: {
|
|
594
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
603
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
595
604
|
action: {
|
|
596
605
|
type: 'string',
|
|
597
606
|
enum: ['view', 'deposits', 'withdrawals', 'income'],
|
|
598
607
|
description: 'Action type (default: view)',
|
|
599
608
|
},
|
|
600
609
|
},
|
|
601
|
-
required: [
|
|
610
|
+
required: [],
|
|
602
611
|
},
|
|
603
612
|
},
|
|
604
613
|
{
|
|
@@ -610,11 +619,11 @@ Actions:
|
|
|
610
619
|
inputSchema: {
|
|
611
620
|
type: 'object',
|
|
612
621
|
properties: {
|
|
613
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
622
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
614
623
|
unread: { type: 'boolean', description: 'Return only unread (default false)' },
|
|
615
624
|
mark_read: { type: 'boolean', description: 'Auto-mark read after call (default false)' },
|
|
616
625
|
},
|
|
617
|
-
required: [
|
|
626
|
+
required: [],
|
|
618
627
|
},
|
|
619
628
|
},
|
|
620
629
|
{
|
|
@@ -637,7 +646,7 @@ Protocol auto-judges (no human): respondent silent 48h → favor initiator; arbi
|
|
|
637
646
|
inputSchema: {
|
|
638
647
|
type: 'object',
|
|
639
648
|
properties: {
|
|
640
|
-
api_key: { type: 'string', description: "Operator's api_key" },
|
|
649
|
+
api_key: { type: 'string', description: "Operator's api_key (or omit and set the WEBAZ_API_KEY env var)" },
|
|
641
650
|
action: {
|
|
642
651
|
type: 'string',
|
|
643
652
|
enum: ['view', 'list_open', 'respond', 'add_evidence', 'arbitrate'],
|
|
@@ -668,7 +677,7 @@ Protocol auto-judges (no human): respondent silent 48h → favor initiator; arbi
|
|
|
668
677
|
},
|
|
669
678
|
ruling_reason: { type: 'string', description: 'Ruling reason (required for arbitrate; permanently recorded on-chain)' },
|
|
670
679
|
},
|
|
671
|
-
required: ['
|
|
680
|
+
required: ['action'],
|
|
672
681
|
},
|
|
673
682
|
},
|
|
674
683
|
{
|
|
@@ -695,7 +704,7 @@ Actions:
|
|
|
695
704
|
inputSchema: {
|
|
696
705
|
type: 'object',
|
|
697
706
|
properties: {
|
|
698
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
707
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
699
708
|
action: {
|
|
700
709
|
type: 'string',
|
|
701
710
|
enum: ['create', 'view', 'mine', 'submit_seller_evidence', 'available', 'vote', 'eligibility', 'verifier_status', 'apply', 'withdraw_application', 'appeal'],
|
|
@@ -718,7 +727,7 @@ Actions:
|
|
|
718
727
|
// appeal
|
|
719
728
|
reason: { type: 'string', description: 'Appeal reason (required for appeal, ≤500 chars)' },
|
|
720
729
|
},
|
|
721
|
-
required: ['
|
|
730
|
+
required: ['action'],
|
|
722
731
|
},
|
|
723
732
|
},
|
|
724
733
|
{
|
|
@@ -743,7 +752,7 @@ Actions: list (no auth) | publish (seller) | subscribe / unsubscribe (buyer) | m
|
|
|
743
752
|
inputSchema: {
|
|
744
753
|
type: 'object',
|
|
745
754
|
properties: {
|
|
746
|
-
api_key: { type: 'string', description: 'Your api_key (omit for list)' },
|
|
755
|
+
api_key: { type: 'string', description: 'Your api_key (omit for list) (or set the WEBAZ_API_KEY env var)' },
|
|
747
756
|
action: {
|
|
748
757
|
type: 'string',
|
|
749
758
|
enum: ['list', 'publish', 'subscribe', 'unsubscribe', 'my_skills', 'my_subs'],
|
|
@@ -807,7 +816,7 @@ Public-profile actions:
|
|
|
807
816
|
inputSchema: {
|
|
808
817
|
type: 'object',
|
|
809
818
|
properties: {
|
|
810
|
-
api_key: { type: 'string', description: 'Your api_key (required for view/add_role/switch_role/view_user; optional for public feed)' },
|
|
819
|
+
api_key: { type: 'string', description: 'Your api_key (required for view/add_role/switch_role/view_user; optional for public feed) (or set the WEBAZ_API_KEY env var)' },
|
|
811
820
|
action: {
|
|
812
821
|
type: 'string',
|
|
813
822
|
enum: ['view', 'add_role', 'switch_role', 'view_user', 'feed'],
|
|
@@ -841,10 +850,10 @@ Use revoke when: PERMANENT decommission of agent/device, OR want access death NO
|
|
|
841
850
|
inputSchema: {
|
|
842
851
|
type: 'object',
|
|
843
852
|
properties: {
|
|
844
|
-
api_key: { type: 'string', description: 'Your current api_key (the one to revoke)' },
|
|
853
|
+
api_key: { type: 'string', description: 'Your current api_key (the one to revoke) (or set the WEBAZ_API_KEY env var)' },
|
|
845
854
|
reason: { type: 'string', description: 'Optional: leaked / lost_device / rotation / unspecified' },
|
|
846
855
|
},
|
|
847
|
-
required: [
|
|
856
|
+
required: [],
|
|
848
857
|
},
|
|
849
858
|
},
|
|
850
859
|
{
|
|
@@ -856,10 +865,10 @@ Safer than \`webaz_revoke_key\` — atomic swap, no access gap.`,
|
|
|
856
865
|
inputSchema: {
|
|
857
866
|
type: 'object',
|
|
858
867
|
properties: {
|
|
859
|
-
api_key: { type: 'string', description: 'Your current api_key (will be invalidated after PWA confirm)' },
|
|
868
|
+
api_key: { type: 'string', description: 'Your current api_key (will be invalidated after PWA confirm) (or set the WEBAZ_API_KEY env var)' },
|
|
860
869
|
reason: { type: 'string', description: 'Optional: rotation / leaked / scheduled' },
|
|
861
870
|
},
|
|
862
|
-
required: [
|
|
871
|
+
required: [],
|
|
863
872
|
},
|
|
864
873
|
},
|
|
865
874
|
{
|
|
@@ -872,9 +881,9 @@ Safer than \`webaz_revoke_key\` — atomic swap, no access gap.`,
|
|
|
872
881
|
inputSchema: {
|
|
873
882
|
type: 'object',
|
|
874
883
|
properties: {
|
|
875
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
884
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
876
885
|
},
|
|
877
|
-
required: [
|
|
886
|
+
required: [],
|
|
878
887
|
},
|
|
879
888
|
},
|
|
880
889
|
{
|
|
@@ -887,15 +896,15 @@ Safer than \`webaz_revoke_key\` — atomic swap, no access gap.`,
|
|
|
887
896
|
inputSchema: {
|
|
888
897
|
type: 'object',
|
|
889
898
|
properties: {
|
|
890
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
899
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
891
900
|
product_id: { type: 'string', description: 'Product to promote (from webaz_search)' },
|
|
892
901
|
side: {
|
|
893
902
|
type: 'string',
|
|
894
|
-
enum: ['
|
|
895
|
-
description: '
|
|
903
|
+
enum: ['auto'],
|
|
904
|
+
description: 'Deprecated / no-op — placement is always automatic (system-decided). Left/right选择已下线。',
|
|
896
905
|
},
|
|
897
906
|
},
|
|
898
|
-
required: ['
|
|
907
|
+
required: ['product_id'],
|
|
899
908
|
},
|
|
900
909
|
},
|
|
901
910
|
{
|
|
@@ -909,12 +918,12 @@ Actions: list | block | unblock.`,
|
|
|
909
918
|
inputSchema: {
|
|
910
919
|
type: 'object',
|
|
911
920
|
properties: {
|
|
912
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
921
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
913
922
|
action: { type: 'string', enum: ['list', 'block', 'unblock'], description: 'list: my blocked users | block: add | unblock: remove' },
|
|
914
923
|
user_id: { type: 'string', description: 'Target user id (required for block/unblock)' },
|
|
915
924
|
reason: { type: 'string', description: 'Optional reason for block (e.g. "fake product", "abuse")' },
|
|
916
925
|
},
|
|
917
|
-
required: ['
|
|
926
|
+
required: ['action'],
|
|
918
927
|
},
|
|
919
928
|
},
|
|
920
929
|
{
|
|
@@ -923,11 +932,11 @@ Actions: list | block | unblock.`,
|
|
|
923
932
|
inputSchema: {
|
|
924
933
|
type: 'object',
|
|
925
934
|
properties: {
|
|
926
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
935
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
927
936
|
action: { type: 'string', enum: ['list', 'follow', 'unfollow', 'status'], description: 'list: my follows + followers | follow/unfollow: change relation | status: check if I follow a user' },
|
|
928
937
|
user_id: { type: 'string', description: 'Target user (required for follow/unfollow/status)' },
|
|
929
938
|
},
|
|
930
|
-
required: ['
|
|
939
|
+
required: ['action'],
|
|
931
940
|
},
|
|
932
941
|
},
|
|
933
942
|
{
|
|
@@ -941,12 +950,12 @@ USE THIS for "what's popular near me / 我附近 / 同城" — geo-aggregated, n
|
|
|
941
950
|
inputSchema: {
|
|
942
951
|
type: 'object',
|
|
943
952
|
properties: {
|
|
944
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
953
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
945
954
|
action: { type: 'string', enum: ['query', 'set_location', 'clear_location'], description: 'query: get aggregated nearby activity | set_location: set your geo cell | clear_location: remove' },
|
|
946
955
|
lat: { type: 'number', description: 'Latitude -90..90 (for set_location, auto-truncated to 0.1°)' },
|
|
947
956
|
lng: { type: 'number', description: 'Longitude -180..180 (for set_location, auto-truncated to 0.1°)' },
|
|
948
957
|
},
|
|
949
|
-
required: ['
|
|
958
|
+
required: ['action'],
|
|
950
959
|
},
|
|
951
960
|
},
|
|
952
961
|
{
|
|
@@ -958,12 +967,12 @@ USE THIS for "what's popular near me / 我附近 / 同城" — geo-aggregated, n
|
|
|
958
967
|
inputSchema: {
|
|
959
968
|
type: 'object',
|
|
960
969
|
properties: {
|
|
961
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
970
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
962
971
|
action: { type: 'string', enum: ['read', 'set'], description: 'read: get current default | set: update' },
|
|
963
972
|
text: { type: 'string', description: 'Full address as free-text string (e.g. "John Doe / 1 Test St / Singapore SG / +65 12345678"). Required for set. ≤ 200 chars.' },
|
|
964
973
|
region: { type: 'string', description: 'Region tag for shipping match (e.g. "global", "china", "SG"). Optional for set. ≤ 40 chars.' },
|
|
965
974
|
},
|
|
966
|
-
required: ['
|
|
975
|
+
required: ['action'],
|
|
967
976
|
},
|
|
968
977
|
},
|
|
969
978
|
{
|
|
@@ -979,7 +988,7 @@ Actions: list_mine | add (external_url + product/anchor) | delete | by_product |
|
|
|
979
988
|
inputSchema: {
|
|
980
989
|
type: 'object',
|
|
981
990
|
properties: {
|
|
982
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
991
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
983
992
|
action: { type: 'string', enum: ['list_mine', 'add', 'delete', 'by_product', 'by_anchor'], description: 'list_mine | add (need external_url + product/anchor) | delete | by_product | by_anchor' },
|
|
984
993
|
external_url: { type: 'string', description: 'For action=add' },
|
|
985
994
|
title: { type: 'string', description: 'For action=add (optional)' },
|
|
@@ -988,7 +997,7 @@ Actions: list_mine | add (external_url + product/anchor) | delete | by_product |
|
|
|
988
997
|
related_anchor: { type: 'string', description: 'For action=add or by_anchor' },
|
|
989
998
|
shareable_id: { type: 'string', description: 'For action=delete' },
|
|
990
999
|
},
|
|
991
|
-
required: ['
|
|
1000
|
+
required: ['action'],
|
|
992
1001
|
},
|
|
993
1002
|
},
|
|
994
1003
|
// ── P3 RFQ / bid / chat / auto_bid(MCP 通过 HTTP 调 PWA,复用所有校验+状态机)────
|
|
@@ -1017,7 +1026,7 @@ Shipping address falls back to webaz_default_address if omitted.`,
|
|
|
1017
1026
|
inputSchema: {
|
|
1018
1027
|
type: 'object',
|
|
1019
1028
|
properties: {
|
|
1020
|
-
api_key: { type: 'string', description: 'Your api_key' },
|
|
1029
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1021
1030
|
action: { type: 'string', enum: ['create', 'mine', 'browse', 'detail', 'award', 'cancel'] },
|
|
1022
1031
|
// create
|
|
1023
1032
|
title: { type: 'string' },
|
|
@@ -1036,7 +1045,7 @@ Shipping address falls back to webaz_default_address if omitted.`,
|
|
|
1036
1045
|
rfq_id: { type: 'string' },
|
|
1037
1046
|
bid_id: { type: 'string', description: 'Optional for award — if omitted, auto-pick current lowest bid' },
|
|
1038
1047
|
},
|
|
1039
|
-
required: ['
|
|
1048
|
+
required: ['action'],
|
|
1040
1049
|
},
|
|
1041
1050
|
},
|
|
1042
1051
|
{
|
|
@@ -1050,7 +1059,7 @@ Actions: submit (rfq_id + price + qty_offered + fulfillment_type; optional eta/n
|
|
|
1050
1059
|
inputSchema: {
|
|
1051
1060
|
type: 'object',
|
|
1052
1061
|
properties: {
|
|
1053
|
-
api_key: { type: 'string', description: 'Seller api_key' },
|
|
1062
|
+
api_key: { type: 'string', description: 'Seller api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1054
1063
|
action: { type: 'string', enum: ['submit', 'patch', 'cancel', 'list_mine'] },
|
|
1055
1064
|
rfq_id: { type: 'string' },
|
|
1056
1065
|
bid_id: { type: 'string' },
|
|
@@ -1061,7 +1070,7 @@ Actions: submit (rfq_id + price + qty_offered + fulfillment_type; optional eta/n
|
|
|
1061
1070
|
note: { type: 'string' },
|
|
1062
1071
|
offer_id: { type: 'string', description: 'Optional; reference existing offer' },
|
|
1063
1072
|
},
|
|
1064
|
-
required: ['
|
|
1073
|
+
required: ['action'],
|
|
1065
1074
|
},
|
|
1066
1075
|
},
|
|
1067
1076
|
{
|
|
@@ -1081,7 +1090,7 @@ webaz_blocklist hides from search but does NOT auto-silence existing convs (busi
|
|
|
1081
1090
|
inputSchema: {
|
|
1082
1091
|
type: 'object',
|
|
1083
1092
|
properties: {
|
|
1084
|
-
api_key: { type: 'string' },
|
|
1093
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1085
1094
|
action: { type: 'string', enum: ['start', 'list', 'read', 'send', 'mark_read', 'block'] },
|
|
1086
1095
|
kind: { type: 'string', enum: ['order', 'rfq', 'listing_qa'] },
|
|
1087
1096
|
context_id: { type: 'string' },
|
|
@@ -1089,7 +1098,7 @@ webaz_blocklist hides from search but does NOT auto-silence existing convs (busi
|
|
|
1089
1098
|
conversation_id: { type: 'string' },
|
|
1090
1099
|
body: { type: 'string', description: 'Message body for send action (≤2000 chars)' },
|
|
1091
1100
|
},
|
|
1092
|
-
required: ['
|
|
1101
|
+
required: ['action'],
|
|
1093
1102
|
},
|
|
1094
1103
|
},
|
|
1095
1104
|
{
|
|
@@ -1129,7 +1138,7 @@ Actions (15):
|
|
|
1129
1138
|
inputSchema: {
|
|
1130
1139
|
type: 'object',
|
|
1131
1140
|
properties: {
|
|
1132
|
-
api_key: { type: 'string' },
|
|
1141
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1133
1142
|
action: { type: 'string', enum: ['list', 'detail', 'create', 'claim', 'proof', 'confirm', 'disclose', 'cancel', 'me', 'stories', 'leaderboard', 'repay', 'repay_respond', 'donate', 'fund'] },
|
|
1134
1143
|
wish_id: { type: 'string' },
|
|
1135
1144
|
fulfillment_id: { type: 'string' },
|
|
@@ -1165,7 +1174,7 @@ Actions: create (seller, needs title/price/stock + content_hash sha256 + content
|
|
|
1165
1174
|
inputSchema: {
|
|
1166
1175
|
type: 'object',
|
|
1167
1176
|
properties: {
|
|
1168
|
-
api_key: { type: 'string' },
|
|
1177
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1169
1178
|
action: { type: 'string', enum: ['create', 'list', 'detail', 'patch'] },
|
|
1170
1179
|
product_id: { type: 'string' },
|
|
1171
1180
|
title: { type: 'string' }, price: { type: 'number' }, stock: { type: 'number' },
|
|
@@ -1190,11 +1199,11 @@ Actions: toggle (same endpoint; 2nd call auto-unlikes) | status (my like status
|
|
|
1190
1199
|
inputSchema: {
|
|
1191
1200
|
type: 'object',
|
|
1192
1201
|
properties: {
|
|
1193
|
-
api_key: { type: 'string' },
|
|
1202
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1194
1203
|
action: { type: 'string', enum: ['toggle', 'status'] },
|
|
1195
1204
|
shareable_id: { type: 'string' },
|
|
1196
1205
|
},
|
|
1197
|
-
required: ['
|
|
1206
|
+
required: ['action', 'shareable_id'],
|
|
1198
1207
|
},
|
|
1199
1208
|
},
|
|
1200
1209
|
{
|
|
@@ -1239,7 +1248,7 @@ Actions:
|
|
|
1239
1248
|
inputSchema: {
|
|
1240
1249
|
type: 'object',
|
|
1241
1250
|
properties: {
|
|
1242
|
-
api_key: { type: 'string' },
|
|
1251
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1243
1252
|
action: { type: 'string', enum: ['create', 'browse', 'mine', 'detail', 'bid', 'cancel'] },
|
|
1244
1253
|
title: { type: 'string' },
|
|
1245
1254
|
qty: { type: 'number' },
|
|
@@ -1253,7 +1262,7 @@ Actions:
|
|
|
1253
1262
|
auction_id: { type: 'string' },
|
|
1254
1263
|
price: { type: 'number' },
|
|
1255
1264
|
},
|
|
1256
|
-
required: ['
|
|
1265
|
+
required: ['action'],
|
|
1257
1266
|
},
|
|
1258
1267
|
},
|
|
1259
1268
|
{
|
|
@@ -1267,7 +1276,7 @@ Actions: get | set (categories[] / regions[] / max_eta_h / bid_strategy) | disab
|
|
|
1267
1276
|
inputSchema: {
|
|
1268
1277
|
type: 'object',
|
|
1269
1278
|
properties: {
|
|
1270
|
-
api_key: { type: 'string' },
|
|
1279
|
+
api_key: { type: 'string', description: 'Your api_key (or set the WEBAZ_API_KEY env var)' },
|
|
1271
1280
|
action: { type: 'string', enum: ['get', 'set', 'disable'] },
|
|
1272
1281
|
categories: { type: 'array', items: { type: 'string' } },
|
|
1273
1282
|
regions: { type: 'array', items: { type: 'string' } },
|
|
@@ -1280,7 +1289,7 @@ Actions: get | set (categories[] / regions[] / max_eta_h / bid_strategy) | disab
|
|
|
1280
1289
|
cooldown_min: { type: 'number' },
|
|
1281
1290
|
enabled: { type: 'boolean' },
|
|
1282
1291
|
},
|
|
1283
|
-
required: ['
|
|
1292
|
+
required: ['action'],
|
|
1284
1293
|
},
|
|
1285
1294
|
},
|
|
1286
1295
|
{
|
|
@@ -1300,7 +1309,7 @@ Actions: list (no auth, filters: kind/billing/query) | detail (public, no conten
|
|
|
1300
1309
|
inputSchema: {
|
|
1301
1310
|
type: 'object',
|
|
1302
1311
|
properties: {
|
|
1303
|
-
api_key: { type: 'string', description: 'Your api_key (omit for list/detail)' },
|
|
1312
|
+
api_key: { type: 'string', description: 'Your api_key (omit for list/detail) (or set the WEBAZ_API_KEY env var)' },
|
|
1304
1313
|
action: {
|
|
1305
1314
|
type: 'string',
|
|
1306
1315
|
enum: ['list', 'detail', 'publish', 'update', 'delist', 'resubmit', 'purchase', 'read', 'my_skills', 'library'],
|
|
@@ -1337,7 +1346,7 @@ Enums: **category** phone/computer/appliance/furniture/clothing/book/toy/sports/
|
|
|
1337
1346
|
inputSchema: {
|
|
1338
1347
|
type: 'object',
|
|
1339
1348
|
properties: {
|
|
1340
|
-
api_key: { type: 'string', description: 'Your api_key (omit for browse/detail)' },
|
|
1349
|
+
api_key: { type: 'string', description: 'Your api_key (omit for browse/detail) (or set the WEBAZ_API_KEY env var)' },
|
|
1341
1350
|
action: { type: 'string', enum: ['browse', 'detail', 'publish', 'update', 'mine', 'buy'], description: 'Action to execute' },
|
|
1342
1351
|
item_id: { type: 'string', description: 'Item ID (required for detail/update/buy)' },
|
|
1343
1352
|
// publish / update
|
|
@@ -1386,7 +1395,7 @@ Seller actions:
|
|
|
1386
1395
|
inputSchema: {
|
|
1387
1396
|
type: 'object',
|
|
1388
1397
|
properties: {
|
|
1389
|
-
api_key: { type: 'string', description: 'Your api_key (omit for get_campaign)' },
|
|
1398
|
+
api_key: { type: 'string', description: 'Your api_key (omit for get_campaign) (or set the WEBAZ_API_KEY env var)' },
|
|
1390
1399
|
action: { type: 'string', enum: ['get_campaign', 'apply', 'link_note', 'my_claims', 'create_campaign', 'cancel_campaign', 'my_campaigns', 'campaign_claims'], description: 'Action to execute' },
|
|
1391
1400
|
product_id: { type: 'string', description: 'Product ID (required for get_campaign/apply/create_campaign/cancel_campaign)' },
|
|
1392
1401
|
claim_id: { type: 'string', description: 'Claim ID (required for link_note)' },
|
|
@@ -1415,7 +1424,7 @@ Gate by type: ux_issue/bug (reporting = using) → login only, NO Passkey, anyon
|
|
|
1415
1424
|
type: 'object',
|
|
1416
1425
|
properties: {
|
|
1417
1426
|
action: { type: 'string', enum: ['submit', 'my', 'get'], description: 'submit (default) | my | get' },
|
|
1418
|
-
api_key: { type: 'string', description: "User's api_key (real person required)" },
|
|
1427
|
+
api_key: { type: 'string', description: "User's api_key (real person required; or set the WEBAZ_API_KEY env var)" },
|
|
1419
1428
|
type: { type: 'string', enum: ['ux_issue', 'bug', 'proposal'], description: 'submit: kind of feedback' },
|
|
1420
1429
|
area: { type: 'string', description: 'submit: which feature, e.g. search / order / dispute' },
|
|
1421
1430
|
severity: { type: 'string', enum: ['low', 'annoying', 'blocking'], description: 'submit: for ux_issue/bug' },
|
|
@@ -1423,7 +1432,7 @@ Gate by type: ux_issue/bug (reporting = using) → login only, NO Passkey, anyon
|
|
|
1423
1432
|
text: { type: 'string', description: 'submit: the feedback / idea (≥5 chars)' },
|
|
1424
1433
|
feedback_id: { type: 'string', description: 'get: the feedback id' },
|
|
1425
1434
|
},
|
|
1426
|
-
required: [
|
|
1435
|
+
required: [],
|
|
1427
1436
|
},
|
|
1428
1437
|
},
|
|
1429
1438
|
{
|
|
@@ -1435,7 +1444,8 @@ Discovery + suggesting need NO api_key (anyone / any agent can browse and propos
|
|
|
1435
1444
|
Actions:
|
|
1436
1445
|
- list_open (default): open public tasks (opt. filters: area / risk_level / auto_claimable / required_capabilities / agent_capabilities / max_duration_minutes / estimated_context_size / estimated_agent_budget — estimated_agent_budget is a resource/effort estimate, NOT a payment). Each task carries its execution boundary + the trusted canonical contribution target. NO api_key needed.
|
|
1437
1446
|
- detail: one task's full execution boundary (allowed/forbidden paths, prohibited actions, acceptance criteria, verification commands, deliverables, definition_of_done) + the canonical repo to PR to + a copy-ready agent_handoff. NO api_key needed.
|
|
1438
|
-
- suggest: propose a NEW task (title + summary/reason; opt. area/expected_outcome/source_ref/github_login). It enters the maintainer inbox — it is a suggestion, NOT a contribution fact / reward / participation, and never auto-becomes a task. NO api_key needed.
|
|
1447
|
+
- suggest: propose a NEW task (title + summary/reason; opt. area/expected_outcome/source_ref/github_login). It enters the maintainer inbox — it is a suggestion, NOT a contribution fact / reward / participation, and never auto-becomes a task. NO api_key needed (but pass your key to LINK it to your account so you can track it via my_suggestions).
|
|
1448
|
+
- my_suggestions: your OWN past proposals + their review status / public_reply / next_action (api_key). Agent-readable 回执 so a proposer-agent can act on the maintainer's decision (needs_info → resubmit; converted → see converted_ref).
|
|
1439
1449
|
- claim: take an open task (api_key); provenance=human|ai_assisted|ai_authored (self-declared, not detected); auto-expires ~7d if not submitted. Returns a handoff — point a coding agent at it; the human needn't know git but stays accountable (Passkey).
|
|
1440
1450
|
- submit: mark in_review with pr_ref + verification_summary (api_key). The PR's base repo MUST be the canonical WebAZ repo, and a verification_summary (what you ran/verified) is REQUIRED — both server-enforced. A human maintainer reviews next; done ≠ merge.
|
|
1441
1451
|
- status: tasks you hold (api_key).
|
|
@@ -1445,12 +1455,12 @@ Coordinates + records only — NO merge/reward; acceptance (done) = human mainta
|
|
|
1445
1455
|
inputSchema: {
|
|
1446
1456
|
type: 'object',
|
|
1447
1457
|
properties: {
|
|
1448
|
-
action: { type: 'string', enum: ['list_open', 'detail', 'suggest', 'claim', 'submit', 'status', 'profile'], description: 'list_open (default) | detail | suggest | claim | submit | status | profile' },
|
|
1449
|
-
api_key: { type: 'string', description: 'claim/submit/status/profile: your api_key (accountable identity). NOT needed for list_open/detail/suggest.' },
|
|
1458
|
+
action: { type: 'string', enum: ['list_open', 'detail', 'suggest', 'my_suggestions', 'claim', 'submit', 'status', 'profile'], description: 'list_open (default) | detail | suggest | my_suggestions | claim | submit | status | profile' },
|
|
1459
|
+
api_key: { type: 'string', description: 'claim/submit/status/profile: your api_key (accountable identity). NOT needed for list_open/detail/suggest. (or set the WEBAZ_API_KEY env var)' },
|
|
1450
1460
|
task_id: { type: 'string', description: 'detail / claim / submit: the task id' },
|
|
1451
1461
|
area: { type: 'string', description: 'list_open: area filter / suggest: suggested area (e.g. search / docs / mcp)' },
|
|
1452
1462
|
risk_level: { type: 'string', enum: ['low', 'medium', 'high', 'critical'], description: 'list_open: optional risk filter' },
|
|
1453
|
-
auto_claimable: { type: 'boolean', description: 'list_open: optional filter —
|
|
1463
|
+
auto_claimable: { type: 'boolean', description: 'list_open: optional filter — true returns tasks an agent can just do (auto-claimable AND with a real effort estimate; matches the derived claimability=auto_claimable), false returns manual-claim tasks. A task with a placeholder (unknown) estimate is treated as manual_review even if its raw auto_claimable flag is true, so it is excluded from true.' },
|
|
1454
1464
|
required_capabilities: { type: 'string', description: 'list_open: optional filter — comma-separated; matches tasks that REQUIRE ALL of the listed capabilities (superset/AND match on the task requirement). For "tasks my agent can do", use agent_capabilities instead.' },
|
|
1455
1465
|
agent_capabilities: { type: 'string', description: 'list_open: optional filter — capabilities your agent HAS (comma-separated); matches tasks whose required_capabilities are a SUBSET of these, i.e. tasks your agent can actually do' },
|
|
1456
1466
|
max_duration_minutes: { type: 'number', description: 'list_open: optional filter — only tasks whose estimated max duration fits within this many minutes (your idle time)' },
|
|
@@ -1473,7 +1483,7 @@ Coordinates + records only — NO merge/reward; acceptance (done) = human mainta
|
|
|
1473
1483
|
// RFC-004: webaz_feedback — agent-native "use → build" 反馈(双模;仅 NETWORK 能送达)
|
|
1474
1484
|
async function handleFeedback(args) {
|
|
1475
1485
|
const action = args.action || 'submit';
|
|
1476
|
-
const apiKey = args
|
|
1486
|
+
const apiKey = resolveMcpApiKey(args);
|
|
1477
1487
|
if (!apiKey)
|
|
1478
1488
|
return { error: 'api_key required' };
|
|
1479
1489
|
if (toolBackend('webaz_feedback') !== 'network') {
|
|
@@ -1509,18 +1519,22 @@ async function handleFeedback(args) {
|
|
|
1509
1519
|
}
|
|
1510
1520
|
// RFC-006 断点1(b)交接:从【可信】canonical 目标(API 响应里,绝不硬编码/不取自 task metadata)构造"怎么真正
|
|
1511
1521
|
// 动手"。人的编码 agent 做 git/PR;Passkey 真人担责。sandbox 运行 / 本地草稿不算正式参与。
|
|
1512
|
-
function buildContributeHandoff(cct, taskId) {
|
|
1522
|
+
function buildContributeHandoff(cct, taskId, caseId) {
|
|
1513
1523
|
const c = (cct ?? {});
|
|
1514
|
-
const repoUrl = c.canonical_github_url || 'https://github.com/
|
|
1515
|
-
const baseRepo = c.expected_pr_base_repo || c.canonical_repository_full_name || '
|
|
1524
|
+
const repoUrl = c.canonical_github_url || 'https://github.com/webaz-protocol/webaz';
|
|
1525
|
+
const baseRepo = c.expected_pr_base_repo || c.canonical_repository_full_name || 'webaz-protocol/webaz';
|
|
1516
1526
|
const baseBranch = c.base_branch || 'main';
|
|
1527
|
+
// case_id threads proposal → task → PR. = the source proposal id when this task came from a proposal,
|
|
1528
|
+
// else the task id itself. Quote it in the PR so the whole case stays traceable end to end.
|
|
1529
|
+
const cid = caseId || taskId;
|
|
1517
1530
|
return {
|
|
1531
|
+
case_id: cid,
|
|
1518
1532
|
canonical_repo: baseRepo,
|
|
1519
1533
|
repo: repoUrl,
|
|
1520
1534
|
base_branch: baseBranch,
|
|
1521
1535
|
start_here: 'Read AGENTS.md (project map + before-you-code + PR flow), then CONTRIBUTING.md.',
|
|
1522
1536
|
do_the_work: 'Point a coding agent (e.g. Claude Code) at the repo on a single-topic branch. The buyer/shopping agent is not the coding agent — hand off to one.',
|
|
1523
|
-
submit_pr: `Open a PR whose BASE repo is ${baseRepo} (${repoUrl}), base branch ${baseBranch}. If any target repo differs from this canonical repo, STOP and ask the human — never contribute to a non-canonical repository.`,
|
|
1537
|
+
submit_pr: `Open a PR whose BASE repo is ${baseRepo} (${repoUrl}), base branch ${baseBranch}. Reference case ${cid} in the PR title/body so the proposal → task → PR chain stays traceable. If any target repo differs from this canonical repo, STOP and ask the human — never contribute to a non-canonical repository.`,
|
|
1524
1538
|
pr_flow: 'Commit with DCO sign-off (git commit -s). If AI-authored, mark the PR per the meta-rule. Humans merge — no auto-merge.',
|
|
1525
1539
|
then: `When the PR is open, report it back: webaz_contribute action=submit task_id=${taskId} pr_ref=#<N> verification_summary="<the verification_commands you ran + their results>". Both pr_ref and verification_summary are required.`,
|
|
1526
1540
|
not_participation: 'A sandbox run or a local-only draft is NOT participation and is NOT a contribution; only a merged PR (or recognized issue/task/RFC) on the canonical repo enters the contribution record.',
|
|
@@ -1531,7 +1545,7 @@ function buildContributeHandoff(cct, taskId) {
|
|
|
1531
1545
|
// 端口 #329/#331);claim/submit/status/profile 需 key(真实可问责身份,走受 #330 守卫的 member 端口)。
|
|
1532
1546
|
export async function handleContribute(args) {
|
|
1533
1547
|
const action = args.action || 'list_open';
|
|
1534
|
-
const apiKey = args
|
|
1548
|
+
const apiKey = resolveMcpApiKey(args);
|
|
1535
1549
|
if (toolBackend('webaz_contribute') !== 'network') {
|
|
1536
1550
|
return {
|
|
1537
1551
|
_mode: 'sandbox',
|
|
@@ -1571,7 +1585,7 @@ export async function handleContribute(args) {
|
|
|
1571
1585
|
return { error: 'task_id required for action=detail' };
|
|
1572
1586
|
const r = await apiCall('/api/public/build-tasks/' + encodeURIComponent(tid));
|
|
1573
1587
|
if (!r.error && r.task)
|
|
1574
|
-
r.agent_handoff = buildContributeHandoff(r.canonical_contribution_target, tid);
|
|
1588
|
+
r.agent_handoff = buildContributeHandoff(r.canonical_contribution_target, tid, r.task.case_id);
|
|
1575
1589
|
return r;
|
|
1576
1590
|
}
|
|
1577
1591
|
if (action === 'suggest') {
|
|
@@ -1583,6 +1597,7 @@ export async function handleContribute(args) {
|
|
|
1583
1597
|
return { error: 'summary (the reason) required for action=suggest' };
|
|
1584
1598
|
const r = await apiCall('/api/public/task-proposals', {
|
|
1585
1599
|
method: 'POST',
|
|
1600
|
+
apiKey, // optional — when present, links the proposal to the submitter so it shows up in action=my_suggestions (still works anonymously)
|
|
1586
1601
|
body: {
|
|
1587
1602
|
title, summary,
|
|
1588
1603
|
suggested_area: args.area ?? args.suggested_area,
|
|
@@ -1591,6 +1606,8 @@ export async function handleContribute(args) {
|
|
|
1591
1606
|
proposer_github_login: args.proposer_github_login,
|
|
1592
1607
|
},
|
|
1593
1608
|
});
|
|
1609
|
+
if (!r.error && r.linked_to_account)
|
|
1610
|
+
r._next = 'Track this proposal\'s review status + reply: webaz_contribute action=my_suggestions api_key=<key>.';
|
|
1594
1611
|
// typed errors (RATE_LIMITED / DUPLICATE_PROPOSAL / validation) are already mapped by apiCall; the
|
|
1595
1612
|
// success response already carries the route-level `proposal_notice` (suggestion ≠ contribution/reward).
|
|
1596
1613
|
return r;
|
|
@@ -1603,6 +1620,13 @@ export async function handleContribute(args) {
|
|
|
1603
1620
|
};
|
|
1604
1621
|
if (action === 'status')
|
|
1605
1622
|
return apiCall('/api/build-tasks?mine=1', { apiKey });
|
|
1623
|
+
if (action === 'my_suggestions') {
|
|
1624
|
+
// your OWN past proposals + review status/public_reply/next_action (agent-readable 回执). Own rows only (server-enforced).
|
|
1625
|
+
const r = await apiCall('/api/me/task-proposals', { apiKey });
|
|
1626
|
+
if (!r.error)
|
|
1627
|
+
r._next = 'Each item carries status + public_reply + next_action. needs_info → resubmit via action=suggest referencing the id; converted → see converted_ref.';
|
|
1628
|
+
return r;
|
|
1629
|
+
}
|
|
1606
1630
|
if (action === 'profile')
|
|
1607
1631
|
return apiCall('/api/build-reputation/me', { apiKey });
|
|
1608
1632
|
if (action === 'claim') {
|
|
@@ -1721,8 +1745,11 @@ async function handleInfo() {
|
|
|
1721
1745
|
// 连接两个场景:用协议(本工具) ↔ 改协议(开发协作)。想改 WebAZ 本身的 agent 从这里进。
|
|
1722
1746
|
for_contributors: {
|
|
1723
1747
|
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,但需问责。',
|
|
1724
|
-
repo: 'https://github.com/
|
|
1748
|
+
repo: 'https://github.com/webaz-protocol/webaz',
|
|
1725
1749
|
start_here: 'AGENTS.md (project map + before-you-code + PR flow) → CONTRIBUTING.md (full guide)',
|
|
1750
|
+
// 低门槛路径:无需 api_key、无需 clone 仓库,直接经协议发现任务 / 提建议(对外 well-known 入口也有,见 agent_quickstart)。
|
|
1751
|
+
no_key_path: 'No api_key needed to START contributing: discover open tasks and submit a suggestion via webaz_contribute action=list_open / action=suggest (mirrors GET /api/public/build-tasks + POST /api/public/task-proposals). / 无需 key 即可起步:webaz_contribute action=list_open 发现开放任务、action=suggest 提建议。',
|
|
1752
|
+
contribution_boundary: 'A suggestion is a proposal in the maintainer review inbox — NOT a contribution fact, NOT formal participation, and NOT any economic or redemption right; recorded contribution is facts / evidence / attribution only (RFC-017). / 建议只是进入维护者审阅箱的提议,不是贡献事实、不是正式参与、不构成任何经济或兑现权利;记录的贡献只是事实/证据/归属(RFC-017)。',
|
|
1726
1753
|
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)触发并担责。',
|
|
1727
1754
|
},
|
|
1728
1755
|
// NETWORK 模式:真网络 live 状态(best-effort 拉自 webaz.xyz);SANDBOX 模式为 null。
|
|
@@ -1732,10 +1759,10 @@ async function handleInfo() {
|
|
|
1732
1759
|
// 佣金机制 —— 纯功能性描述(怎么运作),不做"自证清白"式辩护。
|
|
1733
1760
|
commission_model: {
|
|
1734
1761
|
split: '7:2:1 — L1 70% / L2 20% / L3 10% of an order\'s commission_pool',
|
|
1735
|
-
jurisdiction_tiers: 'Tiers are graded by the order region\'s max_levels — NOT a uniform 3 tiers everywhere. e.g. global region max_levels=1 → L1 only; singapore (etc.) max_levels=3 → up to L3. A region may also be 0 (no commission tiers; pool →
|
|
1762
|
+
jurisdiction_tiers: 'Tiers are graded by the order region\'s max_levels — NOT a uniform 3 tiers everywhere. e.g. global region max_levels=1 → L1 only; singapore (etc.) max_levels=3 → up to L3. A region may also be 0 (no commission tiers; pool → commission_reserve / protocol reserve).',
|
|
1736
1763
|
attribution: 'EXPLICIT per-order — commission goes to the promoter attributed at purchase time, not derived from the buyer\'s sponsor chain.',
|
|
1737
1764
|
how_to_attribute: 'L1: webaz_place_order(promoter_api_key) records the direct promoter. Full L2/L3 chain requires the buyer to arrive via a webaz_share_link /i/<permanent_code> (?ref=<permanent_code>) URL clicked in a browser (builds product_share_attribution).',
|
|
1738
|
-
redirect_rules: 'chain_gap
|
|
1765
|
+
redirect_rules: 'all undelivered commission (chain_gap / no L / invalid sponsor / level beyond the region cap / max_levels=0 / opt-out / escrow expiry) → commission_reserve (protocol reserve, in-only; use decided by governance).',
|
|
1739
1766
|
l1_gate: 'the promoter must be a verified buyer (≥1 completed order) to receive commission, otherwise that share redirects.',
|
|
1740
1767
|
opt_in: 'Participation is opt-in (RFC-002): default = off. A user applies (Passkey + ≥1 completed order); attribution is always recorded. Commission destination is state-dependent: never_activated / auto_downgraded → held in pending_commission_escrow (30d grace), recoverable by (re-)activating within the window, else swept to commission_reserve; deactivated (active opt-out) → future commission goes directly to commission_reserve, NOT escrow and NOT recoverable. Never to charity_fund. See docs/rfcs/RFC-002-rewards-opt-in.md.',
|
|
1741
1768
|
},
|
|
@@ -2138,11 +2165,11 @@ async function handleVerifyPrice(args) {
|
|
|
2138
2165
|
if (toolBackend('webaz_verify_price') === 'network') {
|
|
2139
2166
|
return apiCall('/api/verify-price', {
|
|
2140
2167
|
method: 'POST',
|
|
2141
|
-
apiKey: args
|
|
2168
|
+
apiKey: resolveMcpApiKey(args),
|
|
2142
2169
|
body: { product_id: args.product_id, quantity: Number(args.quantity ?? 1) },
|
|
2143
2170
|
});
|
|
2144
2171
|
}
|
|
2145
|
-
const auth = requireAuth(db, args
|
|
2172
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
2146
2173
|
if ('error' in auth)
|
|
2147
2174
|
return auth;
|
|
2148
2175
|
const { user } = auth;
|
|
@@ -2188,7 +2215,7 @@ async function handleVerifyPrice(args) {
|
|
|
2188
2215
|
async function handleListProduct(args) {
|
|
2189
2216
|
// Wave 3 audit P0: 加 action 分发 — agent 卖家能完整管理目录(不止 create)
|
|
2190
2217
|
const action = args.action || 'create';
|
|
2191
|
-
const apiKey = args
|
|
2218
|
+
const apiKey = resolveMcpApiKey(args);
|
|
2192
2219
|
if (!apiKey)
|
|
2193
2220
|
return { error: 'api_key required' };
|
|
2194
2221
|
// RFC-003 P2b: NETWORK 模式 — 卖家目录管理全部转发生产端点(单一真相源)
|
|
@@ -2366,9 +2393,9 @@ async function handlePlaceOrder(args) {
|
|
|
2366
2393
|
body.shipping_address = args.shipping_address;
|
|
2367
2394
|
if (args.donation_pct != null)
|
|
2368
2395
|
body.donation_pct = args.donation_pct;
|
|
2369
|
-
return apiCall('/api/orders', { method: 'POST', apiKey: args
|
|
2396
|
+
return apiCall('/api/orders', { method: 'POST', apiKey: resolveMcpApiKey(args), body });
|
|
2370
2397
|
}
|
|
2371
|
-
const auth = requireAuth(db, args
|
|
2398
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
2372
2399
|
if ('error' in auth)
|
|
2373
2400
|
return auth;
|
|
2374
2401
|
const { user } = auth;
|
|
@@ -2535,7 +2562,7 @@ async function handleUpdateOrder(args) {
|
|
|
2535
2562
|
return { error: 'order_id and action required' };
|
|
2536
2563
|
return apiCall(`/api/orders/${encodeURIComponent(orderId)}/action`, {
|
|
2537
2564
|
method: 'POST',
|
|
2538
|
-
apiKey: args
|
|
2565
|
+
apiKey: resolveMcpApiKey(args),
|
|
2539
2566
|
body: {
|
|
2540
2567
|
action,
|
|
2541
2568
|
notes: args.notes ?? '',
|
|
@@ -2545,7 +2572,7 @@ async function handleUpdateOrder(args) {
|
|
|
2545
2572
|
},
|
|
2546
2573
|
});
|
|
2547
2574
|
}
|
|
2548
|
-
const auth = requireAuth(db, args
|
|
2575
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
2549
2576
|
if ('error' in auth)
|
|
2550
2577
|
return auth;
|
|
2551
2578
|
const { user } = auth;
|
|
@@ -2559,7 +2586,7 @@ async function handleUpdateOrder(args) {
|
|
|
2559
2586
|
// agent-native 协议要求"哪个接口进结果一致"。MCP confirm 不再自己结算,
|
|
2560
2587
|
// 走 PWA /api/orders/:id/action 的 settleOrder + settleCommission(authoritative)。
|
|
2561
2588
|
if (action === 'confirm') {
|
|
2562
|
-
const apiKey = args
|
|
2589
|
+
const apiKey = resolveMcpApiKey(args);
|
|
2563
2590
|
const result = await pwaApi('POST', `/orders/${encodeURIComponent(orderId)}/action`, apiKey, {
|
|
2564
2591
|
action: 'confirm',
|
|
2565
2592
|
notes,
|
|
@@ -2668,9 +2695,9 @@ async function handleGetStatus(args) {
|
|
|
2668
2695
|
const orderId = args.order_id;
|
|
2669
2696
|
if (!orderId)
|
|
2670
2697
|
return { error: 'order_id required' };
|
|
2671
|
-
return apiCall(`/api/orders/${encodeURIComponent(orderId)}`, { apiKey: args
|
|
2698
|
+
return apiCall(`/api/orders/${encodeURIComponent(orderId)}`, { apiKey: resolveMcpApiKey(args) });
|
|
2672
2699
|
}
|
|
2673
|
-
const auth = requireAuth(db, args
|
|
2700
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
2674
2701
|
if ('error' in auth)
|
|
2675
2702
|
return auth;
|
|
2676
2703
|
const statusInfo = getOrderStatus(db, args.order_id);
|
|
@@ -2707,7 +2734,7 @@ async function handleGetStatus(args) {
|
|
|
2707
2734
|
async function handleWallet(args) {
|
|
2708
2735
|
// Wave 3 audit P0: 加 action 分发 — agent 能查充值/提现/收入历史(写操作仍 UI-only 走 2FA)
|
|
2709
2736
|
const action = args.action || 'view';
|
|
2710
|
-
const apiKey = args
|
|
2737
|
+
const apiKey = resolveMcpApiKey(args);
|
|
2711
2738
|
if (!apiKey)
|
|
2712
2739
|
return { error: 'api_key required' };
|
|
2713
2740
|
// RFC-003 Batch 4:NETWORK 模式 → webaz.xyz 真网络【只读】(Bearer api_key)。
|
|
@@ -2775,14 +2802,14 @@ async function handleWallet(args) {
|
|
|
2775
2802
|
async function handleNotifications(args) {
|
|
2776
2803
|
// RFC-003 Batch 1:NETWORK 模式 → 调 webaz.xyz 真网络通知端点(Bearer api_key);SANDBOX 走本地。
|
|
2777
2804
|
if (toolBackend('webaz_notifications') === 'network') {
|
|
2778
|
-
const apiKey =
|
|
2805
|
+
const apiKey = resolveMcpApiKey(args);
|
|
2779
2806
|
if (!apiKey)
|
|
2780
2807
|
return { error: 'api_key required' };
|
|
2781
2808
|
if (args.mark_read)
|
|
2782
2809
|
await apiCall('/api/notifications/read', { method: 'POST', apiKey });
|
|
2783
2810
|
return await apiCall('/api/notifications' + (args.unread === true ? '?unread=1' : ''), { apiKey });
|
|
2784
2811
|
}
|
|
2785
|
-
const auth = requireAuth(db, args
|
|
2812
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
2786
2813
|
if ('error' in auth)
|
|
2787
2814
|
return auth;
|
|
2788
2815
|
const { user } = auth;
|
|
@@ -2806,7 +2833,7 @@ async function handleNotifications(args) {
|
|
|
2806
2833
|
}
|
|
2807
2834
|
// ─── 争议处理 ─────────────────────────────────────────────────
|
|
2808
2835
|
async function handleDispute(args) {
|
|
2809
|
-
const apiKey = args
|
|
2836
|
+
const apiKey = resolveMcpApiKey(args);
|
|
2810
2837
|
if (!apiKey)
|
|
2811
2838
|
return { error: 'api_key required' };
|
|
2812
2839
|
const action = args.action;
|
|
@@ -2969,7 +2996,7 @@ async function handleDispute(args) {
|
|
|
2969
2996
|
}
|
|
2970
2997
|
// ─── 索赔验证(claim-verification)处理 — Wave 6 新增 ────────────
|
|
2971
2998
|
async function handleClaimVerify(args) {
|
|
2972
|
-
const apiKey = args
|
|
2999
|
+
const apiKey = resolveMcpApiKey(args);
|
|
2973
3000
|
if (!apiKey)
|
|
2974
3001
|
return { error: 'api_key required' };
|
|
2975
3002
|
const action = String(args.action || '');
|
|
@@ -3057,7 +3084,7 @@ async function handleSkill(args) {
|
|
|
3057
3084
|
const action = args.action;
|
|
3058
3085
|
// RFC-003 Batch 3:NETWORK 模式 → webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地引擎。
|
|
3059
3086
|
if (toolBackend('webaz_skill') === 'network') {
|
|
3060
|
-
const apiKey =
|
|
3087
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3061
3088
|
if (action === 'list') {
|
|
3062
3089
|
const qs = new URLSearchParams();
|
|
3063
3090
|
if (args.skill_type)
|
|
@@ -3094,8 +3121,8 @@ async function handleSkill(args) {
|
|
|
3094
3121
|
// ── 浏览 Skill 市场 ────────────────────────────────────────
|
|
3095
3122
|
if (action === 'list') {
|
|
3096
3123
|
let userId;
|
|
3097
|
-
if (args
|
|
3098
|
-
const a = requireAuth(db, args
|
|
3124
|
+
if (resolveMcpApiKey(args)) {
|
|
3125
|
+
const a = requireAuth(db, resolveMcpApiKey(args));
|
|
3099
3126
|
if (!('error' in a))
|
|
3100
3127
|
userId = a.user.id;
|
|
3101
3128
|
}
|
|
@@ -3112,7 +3139,7 @@ async function handleSkill(args) {
|
|
|
3112
3139
|
};
|
|
3113
3140
|
}
|
|
3114
3141
|
// 以下操作需要身份验证
|
|
3115
|
-
const auth = requireAuth(db, args
|
|
3142
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3116
3143
|
if ('error' in auth)
|
|
3117
3144
|
return auth;
|
|
3118
3145
|
const { user } = auth;
|
|
@@ -3253,7 +3280,7 @@ function handleMyKey(args) {
|
|
|
3253
3280
|
}
|
|
3254
3281
|
async function handleProfile(args) {
|
|
3255
3282
|
const action = args.action;
|
|
3256
|
-
const apiKey =
|
|
3283
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3257
3284
|
// RFC-003 Batch 1:NETWORK 模式 → 全部 action 调 webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3258
3285
|
if (toolBackend('webaz_profile') === 'network') {
|
|
3259
3286
|
if (action === 'view_user') {
|
|
@@ -3350,7 +3377,7 @@ async function handleProfile(args) {
|
|
|
3350
3377
|
function handleRevokeKey(args) {
|
|
3351
3378
|
// RFC-003 Batch 5:NETWORK 模式 → 不本地校验 key(PWA 会鉴权),直接返回 Passkey 撤销指引。
|
|
3352
3379
|
if (toolBackend('webaz_revoke_key') === 'network') {
|
|
3353
|
-
const apiKey =
|
|
3380
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3354
3381
|
const reason = (args.reason || 'unspecified').trim().slice(0, 100);
|
|
3355
3382
|
return {
|
|
3356
3383
|
_mode: 'network',
|
|
@@ -3368,7 +3395,7 @@ function handleRevokeKey(args) {
|
|
|
3368
3395
|
},
|
|
3369
3396
|
};
|
|
3370
3397
|
}
|
|
3371
|
-
const auth = requireAuth(db, args
|
|
3398
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3372
3399
|
if ('error' in auth)
|
|
3373
3400
|
return auth;
|
|
3374
3401
|
const { user } = auth;
|
|
@@ -3392,7 +3419,7 @@ function handleRevokeKey(args) {
|
|
|
3392
3419
|
function handleRotateKey(args) {
|
|
3393
3420
|
// RFC-003 Batch 5:NETWORK 模式 → 不本地校验 key(PWA 会鉴权),直接返回 Passkey 轮换指引。
|
|
3394
3421
|
if (toolBackend('webaz_rotate_key') === 'network') {
|
|
3395
|
-
const apiKey =
|
|
3422
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3396
3423
|
const reason = (args.reason || 'rotation').trim().slice(0, 100);
|
|
3397
3424
|
return {
|
|
3398
3425
|
_mode: 'network',
|
|
@@ -3409,7 +3436,7 @@ function handleRotateKey(args) {
|
|
|
3409
3436
|
},
|
|
3410
3437
|
};
|
|
3411
3438
|
}
|
|
3412
|
-
const auth = requireAuth(db, args
|
|
3439
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3413
3440
|
if ('error' in auth)
|
|
3414
3441
|
return auth;
|
|
3415
3442
|
const { user } = auth;
|
|
@@ -3429,16 +3456,16 @@ function handleRotateKey(args) {
|
|
|
3429
3456
|
},
|
|
3430
3457
|
};
|
|
3431
3458
|
}
|
|
3432
|
-
// ─── 推广 /
|
|
3459
|
+
// ─── 推广 / 推荐网络 (Tokenomics) ───────────────────────────────────
|
|
3433
3460
|
async function handleReferral(args) {
|
|
3434
3461
|
// RFC-003 Batch 2:NETWORK 模式 → webaz.xyz 真网络聚合(Bearer api_key);SANDBOX 走本地。
|
|
3435
3462
|
if (toolBackend('webaz_referral') === 'network') {
|
|
3436
|
-
const apiKey =
|
|
3463
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3437
3464
|
if (!apiKey)
|
|
3438
3465
|
return { error: 'api_key required' };
|
|
3439
3466
|
return await apiCall('/api/referral/me', { apiKey });
|
|
3440
3467
|
}
|
|
3441
|
-
const auth = requireAuth(db, args
|
|
3468
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3442
3469
|
if ('error' in auth)
|
|
3443
3470
|
return auth;
|
|
3444
3471
|
const { user } = auth;
|
|
@@ -3456,16 +3483,9 @@ async function handleReferral(args) {
|
|
|
3456
3483
|
const completed = db.prepare("SELECT COUNT(*) as n FROM orders WHERE buyer_id = ? AND status = 'completed'").get(userId).n;
|
|
3457
3484
|
const override = db.prepare("SELECT l1_share_override FROM users WHERE id = ?").get(userId)?.l1_share_override ?? 0;
|
|
3458
3485
|
const canL1 = override === 1 || (override === 0 && completed > 0);
|
|
3459
|
-
//
|
|
3486
|
+
// Neutral participation record only — placement position + per-leg PV. Matching-rewards engine excised (#401):
|
|
3487
|
+
// no Score / tier / pair-volume / payout is read or exposed.
|
|
3460
3488
|
const me = db.prepare("SELECT total_left_pv, total_right_pv, left_child_id, right_child_id, placement_id, placement_side FROM users WHERE id = ?").get(userId);
|
|
3461
|
-
const score = db.prepare(`
|
|
3462
|
-
SELECT COALESCE(SUM(CASE WHEN settled_at IS NULL THEN score ELSE 0 END),0) as pending,
|
|
3463
|
-
COALESCE(SUM(CASE WHEN settled_at IS NOT NULL THEN waz_amount ELSE 0 END),0) as settled_waz
|
|
3464
|
-
FROM binary_score_records WHERE user_id = ?
|
|
3465
|
-
`).get(userId);
|
|
3466
|
-
const tiers = db.prepare("SELECT tier, pv_threshold, score_per_hit FROM binary_tier_config WHERE active=1 ORDER BY tier ASC").all();
|
|
3467
|
-
const pair = Math.min(Number(me?.total_left_pv ?? 0), Number(me?.total_right_pv ?? 0));
|
|
3468
|
-
const nextTier = tiers.find(t => t.pv_threshold > pair);
|
|
3469
3489
|
// invite / share links use permanent_code ONLY — never usr_xxx. (sandbox users have one from register.)
|
|
3470
3490
|
const permaCode = db.prepare("SELECT permanent_code FROM users WHERE id = ?").get(userId)?.permanent_code || null;
|
|
3471
3491
|
return {
|
|
@@ -3487,17 +3507,12 @@ async function handleReferral(args) {
|
|
|
3487
3507
|
l1: byLevel[1], l2: byLevel[2], l3: byLevel[3],
|
|
3488
3508
|
grand_total: byLevel[1].total + byLevel[2].total + byLevel[3].total,
|
|
3489
3509
|
},
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
platform_left: permaCode ? `/?placement=${permaCode}&side=left` : null, // 仅 PV 条线
|
|
3494
|
-
platform_right: permaCode ? `/?placement=${permaCode}&side=right` : null,
|
|
3510
|
+
placement: {
|
|
3511
|
+
// Neutral participation/attribution record: a single referral code + per-leg PV. No matching rewards.
|
|
3512
|
+
referral_link: permaCode ? `/i/${permaCode}` : null,
|
|
3495
3513
|
total_left_pv: Number(me?.total_left_pv ?? 0),
|
|
3496
3514
|
total_right_pv: Number(me?.total_right_pv ?? 0),
|
|
3497
|
-
|
|
3498
|
-
next_tier: nextTier ? { tier: nextTier.tier, pv_threshold: nextTier.pv_threshold, score_per_hit: nextTier.score_per_hit, pv_needed: nextTier.pv_threshold - pair } : null,
|
|
3499
|
-
score_pending: score.pending,
|
|
3500
|
-
waz_total_earned: score.settled_waz,
|
|
3515
|
+
note: 'total_left_pv / total_right_pv are a participation / attribution record only — not income, not redeemable, no entitlement.',
|
|
3501
3516
|
},
|
|
3502
3517
|
rewards_status: (() => {
|
|
3503
3518
|
// RFC-002 §3.5 — 4 states + pending escrow visibility (PR-4)
|
|
@@ -3540,23 +3555,21 @@ async function handleReferral(args) {
|
|
|
3540
3555
|
async function handleShareLink(args) {
|
|
3541
3556
|
// RFC-003 #1122:NETWORK 模式 → 调 webaz.xyz 的 /api/share-link(服务端同款计算);SANDBOX 走本地。
|
|
3542
3557
|
if (toolBackend('webaz_share_link') === 'network') {
|
|
3543
|
-
const apiKey =
|
|
3558
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3544
3559
|
if (!apiKey)
|
|
3545
3560
|
return { error: 'api_key required' };
|
|
3546
3561
|
if (!args.product_id)
|
|
3547
3562
|
return { error: 'product_id required' };
|
|
3563
|
+
// pre-public 去左右码:不再向 /api/share-link 转发 side(放置永远自动)
|
|
3548
3564
|
const qs = new URLSearchParams({ product_id: String(args.product_id) });
|
|
3549
|
-
if (args.side)
|
|
3550
|
-
qs.set('side', String(args.side));
|
|
3551
3565
|
return await apiCall('/api/share-link?' + qs.toString(), { apiKey });
|
|
3552
3566
|
}
|
|
3553
|
-
const auth = requireAuth(db, args
|
|
3567
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3554
3568
|
if ('error' in auth)
|
|
3555
3569
|
return auth;
|
|
3556
3570
|
const { user } = auth;
|
|
3557
3571
|
const userId = user.id;
|
|
3558
3572
|
const productId = args.product_id;
|
|
3559
|
-
const sideArg = args.side || 'auto';
|
|
3560
3573
|
// RFC-002 §3.5 valuation-layer gate — share_link generation requires opt-in
|
|
3561
3574
|
const optIn = db.prepare("SELECT rewards_opted_in FROM users WHERE id = ?").get(userId)?.rewards_opted_in ?? 0;
|
|
3562
3575
|
if (optIn !== 1) {
|
|
@@ -3589,32 +3602,7 @@ async function handleShareLink(args) {
|
|
|
3589
3602
|
const product = db.prepare("SELECT id, title, price, commission_rate FROM products WHERE id = ? AND status='active'").get(productId);
|
|
3590
3603
|
if (!product)
|
|
3591
3604
|
return { error: '商品不存在或已下架' };
|
|
3592
|
-
|
|
3593
|
-
if (sideArg === 'left' || sideArg === 'right') {
|
|
3594
|
-
side = sideArg;
|
|
3595
|
-
}
|
|
3596
|
-
else {
|
|
3597
|
-
// auto = 与 PWA pickPreferredSide 对齐:尊重 placement_pref(team_count | pv_count)
|
|
3598
|
-
// 老版只看 total_left_pv vs total_right_pv,team_count 用户被错算
|
|
3599
|
-
const u = db.prepare("SELECT placement_pref, total_left_pv, total_right_pv, left_count, right_count FROM users WHERE id = ?")
|
|
3600
|
-
.get(userId);
|
|
3601
|
-
const pref = u?.placement_pref || 'team_count';
|
|
3602
|
-
if (pref === 'pv_count') {
|
|
3603
|
-
const since = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000).toISOString().slice(0, 19).replace('T', ' ');
|
|
3604
|
-
const w = db.prepare(`SELECT COALESCE(SUM(consumed_left_pv),0) AS l, COALESCE(SUM(consumed_right_pv),0) AS r
|
|
3605
|
-
FROM binary_score_records WHERE user_id = ? AND created_at >= ?`)
|
|
3606
|
-
.get(userId, since);
|
|
3607
|
-
const leftPv = Number(u?.total_left_pv ?? 0) + Number(w.l);
|
|
3608
|
-
const rightPv = Number(u?.total_right_pv ?? 0) + Number(w.r);
|
|
3609
|
-
side = leftPv <= rightPv ? 'left' : 'right';
|
|
3610
|
-
}
|
|
3611
|
-
else {
|
|
3612
|
-
// team_count: 整棵子树人数(增量维护的 left_count/right_count,与 PWA pickPreferredSide 对齐)。
|
|
3613
|
-
// 2026-06-04 修:旧版沿单条脊链数(countLeg)名实不符 → 选边失真。分享链接的 side 会被注册时
|
|
3614
|
-
// 当 placement_side 显式采用,必须与 PWA joinPowerLeg 用同一指标,否则两路径不一致。
|
|
3615
|
-
side = (Number(u?.left_count ?? 0) <= Number(u?.right_count ?? 0)) ? 'left' : 'right';
|
|
3616
|
-
}
|
|
3617
|
-
}
|
|
3605
|
+
// pre-public 去左右码:分享链接不再携带 side(放置侧别由注册时系统自动决定)
|
|
3618
3606
|
const completed = db.prepare("SELECT COUNT(*) as n FROM orders WHERE buyer_id = ? AND status = 'completed'").get(userId).n;
|
|
3619
3607
|
const override = db.prepare("SELECT l1_share_override FROM users WHERE id = ?").get(userId)?.l1_share_override ?? 0;
|
|
3620
3608
|
const canL1 = override === 1 || (override === 0 && completed > 0);
|
|
@@ -3623,13 +3611,12 @@ async function handleShareLink(args) {
|
|
|
3623
3611
|
const permaCode = db.prepare("SELECT permanent_code FROM users WHERE id = ?").get(userId)?.permanent_code || null;
|
|
3624
3612
|
if (!permaCode)
|
|
3625
3613
|
return { error: 'permanent_code_missing — cannot build a share link; re-register or contact support', error_code: 'PERMANENT_CODE_MISSING' };
|
|
3626
|
-
const link = `/?ref=${permaCode}
|
|
3614
|
+
const link = `/?ref=${permaCode}#order-product/${productId}`;
|
|
3627
3615
|
return {
|
|
3628
3616
|
product: { id: product.id, title: product.title, price: product.price, commission_rate: rate },
|
|
3629
3617
|
share_link: link,
|
|
3630
3618
|
full_url_hint: 'Prepend webaz.xyz (production) or http://localhost:3000 (local) to get the absolute URL',
|
|
3631
|
-
|
|
3632
|
-
binary_explanation: `New user via this link → placed in your ${side === 'left' ? '🔵 left' : '🟢 right'} subtree (tail anchor)`,
|
|
3619
|
+
placement_note: 'New user via this link → placement is recorded automatically by the system (no left/right choice).',
|
|
3633
3620
|
commission_eligibility: canL1
|
|
3634
3621
|
? `You will earn 3-tier commission: L1=${(rate * 0.70 * 100).toFixed(1)}% L2=${(rate * 0.20 * 100).toFixed(1)}% L3=${(rate * 0.10 * 100).toFixed(1)}% of sale price`
|
|
3635
3622
|
: 'You are NOT verified yet (need 1 completed purchase). 3-tier commission will be skipped, but points-matching still builds.',
|
|
@@ -3640,7 +3627,7 @@ async function handleShareLink(args) {
|
|
|
3640
3627
|
async function handleBlocklist(args) {
|
|
3641
3628
|
// RFC-003 Batch 2:NETWORK 模式 → webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3642
3629
|
if (toolBackend('webaz_blocklist') === 'network') {
|
|
3643
|
-
const apiKey =
|
|
3630
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3644
3631
|
if (!apiKey)
|
|
3645
3632
|
return { error: 'api_key required' };
|
|
3646
3633
|
const act = String(args.action || '');
|
|
@@ -3655,7 +3642,7 @@ async function handleBlocklist(args) {
|
|
|
3655
3642
|
return await apiCall('/api/blocklist/' + uid, { method: 'DELETE', apiKey });
|
|
3656
3643
|
return { error: `unknown action: ${act}` };
|
|
3657
3644
|
}
|
|
3658
|
-
const auth = requireAuth(db, args
|
|
3645
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3659
3646
|
if ('error' in auth)
|
|
3660
3647
|
return auth;
|
|
3661
3648
|
const { user } = auth;
|
|
@@ -3694,7 +3681,7 @@ async function handleBlocklist(args) {
|
|
|
3694
3681
|
async function handleFollows(args) {
|
|
3695
3682
|
// RFC-003 Batch 2:NETWORK 模式 → webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3696
3683
|
if (toolBackend('webaz_follows') === 'network') {
|
|
3697
|
-
const apiKey =
|
|
3684
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3698
3685
|
if (!apiKey)
|
|
3699
3686
|
return { error: 'api_key required' };
|
|
3700
3687
|
const act = String(args.action || '');
|
|
@@ -3711,7 +3698,7 @@ async function handleFollows(args) {
|
|
|
3711
3698
|
return await apiCall('/api/follows/' + uid + '/status', { apiKey });
|
|
3712
3699
|
return { error: `unknown action: ${act}` };
|
|
3713
3700
|
}
|
|
3714
|
-
const auth = requireAuth(db, args
|
|
3701
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3715
3702
|
if ('error' in auth)
|
|
3716
3703
|
return auth;
|
|
3717
3704
|
const { user } = auth;
|
|
@@ -3760,7 +3747,7 @@ async function handleNearby(args) {
|
|
|
3760
3747
|
const action = String(args.action || '');
|
|
3761
3748
|
// RFC-003 Batch 1:NETWORK 模式 → 调 webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3762
3749
|
if (toolBackend('webaz_nearby') === 'network') {
|
|
3763
|
-
const apiKey =
|
|
3750
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3764
3751
|
if (!apiKey)
|
|
3765
3752
|
return { error: 'api_key required' };
|
|
3766
3753
|
if (action === 'set_location')
|
|
@@ -3778,7 +3765,7 @@ async function handleNearby(args) {
|
|
|
3778
3765
|
}
|
|
3779
3766
|
return { error: `unknown action: ${action}` };
|
|
3780
3767
|
}
|
|
3781
|
-
const auth = requireAuth(db, args
|
|
3768
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3782
3769
|
if ('error' in auth)
|
|
3783
3770
|
return auth;
|
|
3784
3771
|
const { user } = auth;
|
|
@@ -3829,7 +3816,7 @@ async function handleNearby(args) {
|
|
|
3829
3816
|
async function handleDefaultAddress(args) {
|
|
3830
3817
|
// RFC-003 Batch 2:NETWORK 模式 → webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3831
3818
|
if (toolBackend('webaz_default_address') === 'network') {
|
|
3832
|
-
const apiKey =
|
|
3819
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3833
3820
|
if (!apiKey)
|
|
3834
3821
|
return { error: 'api_key required' };
|
|
3835
3822
|
const act = String(args.action || '');
|
|
@@ -3848,7 +3835,7 @@ async function handleDefaultAddress(args) {
|
|
|
3848
3835
|
}
|
|
3849
3836
|
return { error: `unknown action: ${act}` };
|
|
3850
3837
|
}
|
|
3851
|
-
const auth = requireAuth(db, args
|
|
3838
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3852
3839
|
if ('error' in auth)
|
|
3853
3840
|
return auth;
|
|
3854
3841
|
const { user } = auth;
|
|
@@ -3889,7 +3876,7 @@ async function handleShareables(args) {
|
|
|
3889
3876
|
const action = String(args.action || '');
|
|
3890
3877
|
// RFC-003 Batch 1:NETWORK 模式 → 调 webaz.xyz 真网络(Bearer api_key);SANDBOX 走本地。
|
|
3891
3878
|
if (toolBackend('webaz_shareables') === 'network') {
|
|
3892
|
-
const apiKey =
|
|
3879
|
+
const apiKey = resolveMcpApiKey(args);
|
|
3893
3880
|
if (!apiKey)
|
|
3894
3881
|
return { error: 'api_key required' };
|
|
3895
3882
|
if (action === 'list_mine')
|
|
@@ -3917,7 +3904,7 @@ async function handleShareables(args) {
|
|
|
3917
3904
|
}
|
|
3918
3905
|
return { error: `unknown action: ${action}` };
|
|
3919
3906
|
}
|
|
3920
|
-
const auth = requireAuth(db, args
|
|
3907
|
+
const auth = requireAuth(db, resolveMcpApiKey(args));
|
|
3921
3908
|
if ('error' in auth)
|
|
3922
3909
|
return auth;
|
|
3923
3910
|
const { user } = auth;
|
|
@@ -4073,7 +4060,7 @@ async function pwaApi(method, path, apiKey, body) {
|
|
|
4073
4060
|
}
|
|
4074
4061
|
}
|
|
4075
4062
|
async function handleSecondhand(args) {
|
|
4076
|
-
const apiKey =
|
|
4063
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4077
4064
|
const action = String(args.action || '');
|
|
4078
4065
|
const isPublic = action === 'browse' || action === 'detail';
|
|
4079
4066
|
if (!isPublic) {
|
|
@@ -4144,7 +4131,7 @@ async function handleSecondhand(args) {
|
|
|
4144
4131
|
}
|
|
4145
4132
|
}
|
|
4146
4133
|
async function handleTrial(args) {
|
|
4147
|
-
const apiKey =
|
|
4134
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4148
4135
|
const action = String(args.action || '');
|
|
4149
4136
|
const isPublic = action === 'get_campaign';
|
|
4150
4137
|
if (!isPublic) {
|
|
@@ -4197,7 +4184,7 @@ async function handleTrial(args) {
|
|
|
4197
4184
|
}
|
|
4198
4185
|
}
|
|
4199
4186
|
async function handleSkillMarket(args) {
|
|
4200
|
-
const apiKey =
|
|
4187
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4201
4188
|
const action = String(args.action || '');
|
|
4202
4189
|
const isPublic = action === 'list' || action === 'detail';
|
|
4203
4190
|
if (!isPublic) {
|
|
@@ -4270,7 +4257,7 @@ async function handleSkillMarket(args) {
|
|
|
4270
4257
|
}
|
|
4271
4258
|
}
|
|
4272
4259
|
async function handleRfq(args) {
|
|
4273
|
-
const apiKey =
|
|
4260
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4274
4261
|
const action = String(args.action || '');
|
|
4275
4262
|
if (!apiKey)
|
|
4276
4263
|
return { error: 'api_key required' };
|
|
@@ -4319,7 +4306,7 @@ async function handleRfq(args) {
|
|
|
4319
4306
|
}
|
|
4320
4307
|
}
|
|
4321
4308
|
async function handleBid(args) {
|
|
4322
|
-
const apiKey =
|
|
4309
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4323
4310
|
const action = String(args.action || '');
|
|
4324
4311
|
if (!apiKey)
|
|
4325
4312
|
return { error: 'api_key required' };
|
|
@@ -4393,7 +4380,7 @@ async function handleBid(args) {
|
|
|
4393
4380
|
}
|
|
4394
4381
|
}
|
|
4395
4382
|
async function handleChat(args) {
|
|
4396
|
-
const apiKey =
|
|
4383
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4397
4384
|
const action = String(args.action || '');
|
|
4398
4385
|
if (!apiKey)
|
|
4399
4386
|
return { error: 'api_key required' };
|
|
@@ -4435,7 +4422,7 @@ async function handleChat(args) {
|
|
|
4435
4422
|
}
|
|
4436
4423
|
}
|
|
4437
4424
|
async function handleAutoBidSkill(args) {
|
|
4438
|
-
const apiKey =
|
|
4425
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4439
4426
|
const action = String(args.action || '');
|
|
4440
4427
|
if (!apiKey)
|
|
4441
4428
|
return { error: 'api_key required' };
|
|
@@ -4558,7 +4545,7 @@ async function handleCharity(args) {
|
|
|
4558
4545
|
return readEndpoint('webaz_charity', '/charity/leaderboard');
|
|
4559
4546
|
if (action === 'fund')
|
|
4560
4547
|
return readEndpoint('webaz_charity', '/charity/fund');
|
|
4561
|
-
const apiKey =
|
|
4548
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4562
4549
|
if (!apiKey)
|
|
4563
4550
|
return { error: 'api_key required for this action' };
|
|
4564
4551
|
if (toolBackend('webaz_charity') !== 'network') {
|
|
@@ -4621,7 +4608,7 @@ async function handleP2pProduct(args) {
|
|
|
4621
4608
|
return { error: String(e.message) };
|
|
4622
4609
|
}
|
|
4623
4610
|
}
|
|
4624
|
-
const apiKey =
|
|
4611
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4625
4612
|
if (!apiKey)
|
|
4626
4613
|
return { error: 'api_key required for create/patch' };
|
|
4627
4614
|
const auth = requireAuth(db, apiKey);
|
|
@@ -4648,7 +4635,7 @@ async function handleP2pProduct(args) {
|
|
|
4648
4635
|
return { error: `unknown action: ${action}` };
|
|
4649
4636
|
}
|
|
4650
4637
|
async function handleLike(args) {
|
|
4651
|
-
const apiKey =
|
|
4638
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4652
4639
|
const action = String(args.action || '');
|
|
4653
4640
|
const sid = String(args.shareable_id || '');
|
|
4654
4641
|
if (!apiKey || !sid)
|
|
@@ -4674,7 +4661,7 @@ async function handleLeaderboard(args) {
|
|
|
4674
4661
|
return readEndpoint('webaz_leaderboard', '/leaderboard?kind=' + kind + '&limit=' + limit);
|
|
4675
4662
|
}
|
|
4676
4663
|
async function handleAuction(args) {
|
|
4677
|
-
const apiKey =
|
|
4664
|
+
const apiKey = resolveMcpApiKey(args);
|
|
4678
4665
|
const action = String(args.action || '');
|
|
4679
4666
|
if (!apiKey)
|
|
4680
4667
|
return { error: 'api_key required' };
|
|
@@ -5132,7 +5119,7 @@ function addHours(date, hours) {
|
|
|
5132
5119
|
function recordToolCall(tool, args, result, latencyMs) {
|
|
5133
5120
|
let userId = null;
|
|
5134
5121
|
try {
|
|
5135
|
-
const apiKey = args
|
|
5122
|
+
const apiKey = resolveMcpApiKey(args);
|
|
5136
5123
|
if (apiKey) {
|
|
5137
5124
|
const row = db.prepare('SELECT id FROM users WHERE api_key = ?').get(apiKey);
|
|
5138
5125
|
if (row)
|