@seasonkoh/webaz 0.1.26 → 0.1.28

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.
Files changed (99) hide show
  1. package/LICENSE +2 -2
  2. package/NOTICE +24 -3
  3. package/README.md +74 -330
  4. package/README.zh-CN.md +419 -0
  5. package/dist/layer0-foundation/L0-2-state-machine/genuine-sale.js +21 -0
  6. package/dist/layer0-foundation/L0-5-manifest/manifest.js +8 -3
  7. package/dist/layer1-agent/L1-1-mcp-server/auth.js +13 -1
  8. package/dist/layer1-agent/L1-1-mcp-server/network-mode.js +69 -0
  9. package/dist/layer1-agent/L1-1-mcp-server/server.js +270 -82
  10. package/dist/layer2-business/L2-9-contribution/admin-coordination-ingestion-engine.js +181 -0
  11. package/dist/layer2-business/L2-9-contribution/admin-coordination-resolver.js +114 -0
  12. package/dist/layer2-business/L2-9-contribution/admin-coordination-store.js +251 -0
  13. package/dist/layer2-business/L2-9-contribution/admin-operator-claim-workflow.js +390 -0
  14. package/dist/layer2-business/L2-9-contribution/build-task-agent-metadata-store.js +24 -0
  15. package/dist/layer2-business/L2-9-contribution/build-task-participation.js +6 -2
  16. package/dist/layer2-business/L2-9-contribution/build-task-quota.js +337 -0
  17. package/dist/layer2-business/L2-9-contribution/build-task-read.js +25 -2
  18. package/dist/layer2-business/L2-9-contribution/build-tasks-engine.js +57 -7
  19. package/dist/layer2-business/L2-9-contribution/canonical-contribution-target.js +1 -1
  20. package/dist/layer2-business/L2-9-contribution/contribution-facts-read.js +66 -0
  21. package/dist/layer2-business/L2-9-contribution/task-proposal-draft.js +187 -18
  22. package/dist/layer2-business/L2-9-contribution/task-proposal-store.js +29 -4
  23. package/dist/ledger.js +1 -1
  24. package/dist/pwa/admin-audit.js +38 -0
  25. package/dist/pwa/anti-abuse-thresholds.js +135 -0
  26. package/dist/pwa/cf-origin-guard.js +33 -0
  27. package/dist/pwa/contract-fingerprint.js +1 -0
  28. package/dist/pwa/data/onboarding-cases.js +2 -2
  29. package/dist/pwa/data/onboarding-quiz.js +1 -1
  30. package/dist/pwa/economic-participation.js +2 -2
  31. package/dist/pwa/integration-contract.js +46 -4
  32. package/dist/pwa/internal/pv-settlement.js +12 -0
  33. package/dist/pwa/internal/wallet-signer.js +26 -0
  34. package/dist/pwa/public/app-account.js +977 -0
  35. package/dist/pwa/public/app-admin.js +608 -0
  36. package/dist/pwa/public/app-agents.js +63 -0
  37. package/dist/pwa/public/app-ai.js +2162 -0
  38. package/dist/pwa/public/app-contribution.js +836 -0
  39. package/dist/pwa/public/app-discover.js +1296 -0
  40. package/dist/pwa/public/app-listings.js +226 -0
  41. package/dist/pwa/public/app-profile.js +1692 -0
  42. package/dist/pwa/public/app-seller.js +199 -0
  43. package/dist/pwa/public/app-shop.js +1145 -0
  44. package/dist/pwa/public/app.js +15075 -23960
  45. package/dist/pwa/public/i18n.js +31 -28
  46. package/dist/pwa/public/index.html +11 -1
  47. package/dist/pwa/public/openapi.json +4851 -2776
  48. package/dist/pwa/pv-kill-switch.js +31 -0
  49. package/dist/pwa/routes/admin-admins.js +48 -1
  50. package/dist/pwa/routes/admin-analytics.js +1 -10
  51. package/dist/pwa/routes/admin-atomic.js +4 -17
  52. package/dist/pwa/routes/admin-operator-claims.js +280 -0
  53. package/dist/pwa/routes/admin-reports.js +4 -26
  54. package/dist/pwa/routes/admin-tokenomics.js +2 -76
  55. package/dist/pwa/routes/admin-users-lifecycle.js +1 -14
  56. package/dist/pwa/routes/admin-users-query.js +23 -1
  57. package/dist/pwa/routes/admin-wallet-ops.js +1 -1
  58. package/dist/pwa/routes/agent-grants.js +255 -0
  59. package/dist/pwa/routes/auth-read.js +1 -5
  60. package/dist/pwa/routes/auth-register.js +3 -13
  61. package/dist/pwa/routes/build-task-quota.js +113 -0
  62. package/dist/pwa/routes/claim-verify.js +15 -11
  63. package/dist/pwa/routes/contribution-facts.js +18 -0
  64. package/dist/pwa/routes/dispute-cases.js +5 -4
  65. package/dist/pwa/routes/growth.js +3 -3
  66. package/dist/pwa/routes/orders-action.js +27 -10
  67. package/dist/pwa/routes/orders-create.js +1 -1
  68. package/dist/pwa/routes/products-meta.js +19 -6
  69. package/dist/pwa/routes/profile-placement.js +1 -1
  70. package/dist/pwa/routes/promoter.js +10 -29
  71. package/dist/pwa/routes/public-build-tasks.js +5 -1
  72. package/dist/pwa/routes/public-utils.js +9 -12
  73. package/dist/pwa/routes/referral.js +5 -26
  74. package/dist/pwa/routes/rewards-apply.js +3 -2
  75. package/dist/pwa/routes/share-redirects.js +1 -1
  76. package/dist/pwa/routes/shareables-interactions.js +2 -1
  77. package/dist/pwa/routes/task-proposals.js +85 -9
  78. package/dist/pwa/routes/users-public.js +1 -4
  79. package/dist/pwa/routes/wallet-read.js +2 -14
  80. package/dist/pwa/routes/webauthn.js +7 -2
  81. package/dist/pwa/server-schema.js +9 -0
  82. package/dist/pwa/server.js +319 -2034
  83. package/dist/runtime/agent-grant-scopes.js +128 -0
  84. package/dist/runtime/agent-grant-verifier.js +67 -0
  85. package/dist/runtime/agent-pairing.js +60 -0
  86. package/dist/runtime/apply-webaz-runtime-schema.js +15 -0
  87. package/dist/runtime/webaz-schema-helpers.js +1848 -0
  88. package/dist/settlement-math.js +3 -3
  89. package/dist/version.js +6 -4
  90. package/package.json +43 -8
  91. package/dist/index.js +0 -182
  92. package/dist/pwa/public/docs/ECONOMIC-MODEL.md +0 -287
  93. package/dist/pwa/public/docs/INTEGRATOR.md +0 -67
  94. package/dist/pwa/public/docs/META-RULES-FULL.md +0 -543
  95. package/dist/test-dispute.js +0 -153
  96. package/dist/test-manifest.js +0 -61
  97. package/dist/test-mcp-tools.js +0 -135
  98. package/dist/test-reputation.js +0 -116
  99. package/dist/test-skill-market.js +0 -101
@@ -0,0 +1,128 @@
1
+ /**
2
+ * RFC-020 PR-B — agent delegation grant scope taxonomy (pure, no I/O).
3
+ *
4
+ * Source of truth: docs/rfcs/RFC-020-agent-delegation-grants.md §3.1/§3.2 and the
5
+ * companion docs/rfcs/RFC-020-implementation-plan.md §3. Importable by the PWA
6
+ * grant routes, the (future) MCP `webaz_pair` consumer, and tests — no DB, no env.
7
+ *
8
+ * THREE tiers. In PR-B a grant may carry ONLY safe scopes:
9
+ * - SAFE: grantable now (server-enforced constraints, no per-action Passkey).
10
+ * - RISK: **default hard-reject** in PR-B. They are NOT yet Passkey-gated at the
11
+ * route level (place_order / order actions / refunds / wallet ops are plain
12
+ * auth() paths today), so a grant must never carry them until each money/state
13
+ * route adds a real Passkey gate in its own dedicated, money-path-aware PR.
14
+ * - NEVER_DELEGABLE: server hard-reject forever — no grant may EVER carry these
15
+ * (funds movement, key/Passkey changes, grant self-escalation, admin/governance,
16
+ * account creation/deletion, access-control). "Do it at webaz.xyz with a live
17
+ * Passkey."
18
+ *
19
+ * PR-B touches NO payment/wallet/order/refund/escrow/commission/fund/tokenomics
20
+ * code; it only classifies scope strings and gates what a grant may be issued for.
21
+ */
22
+ /** Grantable now — read/draft surfaces; benign or constraint-bounded. */
23
+ export const SAFE_SCOPES = [
24
+ 'read_public',
25
+ 'profile_read',
26
+ 'search',
27
+ 'list_product_draft',
28
+ 'product_publish_request',
29
+ 'draft_order',
30
+ ];
31
+ /**
32
+ * Real actions that WILL require a live Passkey each time — but are NOT yet gated
33
+ * at the route level. Default hard-reject in PR-B (see module header).
34
+ */
35
+ export const RISK_SCOPES = [
36
+ 'place_order',
37
+ 'order_accept',
38
+ 'order_ship',
39
+ 'order_status',
40
+ 'wallet',
41
+ 'payout',
42
+ 'refund',
43
+ 'arbitrate',
44
+ 'vote',
45
+ 'claim_verify',
46
+ ];
47
+ /** Never carried by any grant — server hard-reject, independent of UI. */
48
+ export const NEVER_DELEGABLE_SCOPES = [
49
+ 'withdraw',
50
+ 'transfer',
51
+ 'convert',
52
+ 'deposit',
53
+ 'fund_move',
54
+ 'api_key_create',
55
+ 'api_key_rotate',
56
+ 'api_key_reveal',
57
+ 'passkey_change',
58
+ 'grant_escalate',
59
+ 'account_delete',
60
+ 'access_control_change',
61
+ 'admin',
62
+ 'protocol_param',
63
+ 'create_live_account',
64
+ ];
65
+ const SAFE = new Set(SAFE_SCOPES);
66
+ const RISK = new Set(RISK_SCOPES);
67
+ const NEVER = new Set(NEVER_DELEGABLE_SCOPES);
68
+ export function classifyScope(capability) {
69
+ if (NEVER.has(capability))
70
+ return 'never_delegable';
71
+ if (RISK.has(capability))
72
+ return 'risk';
73
+ if (SAFE.has(capability))
74
+ return 'safe';
75
+ return 'unknown';
76
+ }
77
+ /**
78
+ * Validate the capabilities requested for a grant. PR-B policy: a grant is issuable
79
+ * ONLY if every requested capability is SAFE. Any risk / never-delegable / unknown
80
+ * scope rejects the whole request (fail-closed). The error_code distinguishes the
81
+ * permanent never-delegable wall from the "not enabled yet" risk wall.
82
+ */
83
+ export function validateRequestedCapabilities(caps) {
84
+ const safe = [];
85
+ const rejected = [];
86
+ if (!Array.isArray(caps) || caps.length === 0) {
87
+ return { ok: false, safe, rejected: [{ capability: '(none)', tier: 'unknown', error_code: 'NO_CAPABILITIES', reason: 'at least one capability is required' }] };
88
+ }
89
+ for (const c of caps) {
90
+ const cap = c?.capability;
91
+ const tier = typeof cap === 'string' ? classifyScope(cap) : 'unknown';
92
+ switch (tier) {
93
+ case 'safe':
94
+ safe.push(cap);
95
+ break;
96
+ case 'risk':
97
+ rejected.push({ capability: String(cap), tier, error_code: 'RISK_SCOPE_NOT_ENABLED', reason: 'risk scope is not delegable yet — the owning money/state route must add a live-Passkey gate first (RFC-020 §3.1)' });
98
+ break;
99
+ case 'never_delegable':
100
+ rejected.push({ capability: String(cap), tier, error_code: 'NEVER_DELEGABLE', reason: 'never-delegable iron-rule action — must be done by the human at webaz.xyz with a live Passkey (RFC-020 §3.2)' });
101
+ break;
102
+ default:
103
+ rejected.push({ capability: String(cap ?? '(missing)'), tier: 'unknown', error_code: 'UNKNOWN_SCOPE', reason: 'unknown capability — not in the safe scope set' });
104
+ }
105
+ }
106
+ return { ok: rejected.length === 0, safe, rejected };
107
+ }
108
+ /** A grant authorizes nothing unless it is active and unexpired. `nowIso` for testability. */
109
+ export function grantIsActive(grant, nowIso) {
110
+ if (!grant)
111
+ return false;
112
+ if (grant.status !== 'active')
113
+ return false;
114
+ if (grant.revoked_at)
115
+ return false;
116
+ if (!grant.expires_at)
117
+ return false;
118
+ return grant.expires_at > nowIso;
119
+ }
120
+ /** Short-lived bearer policy for SAFE scopes (RFC-020 bearer-first; PoP before risk/longer). */
121
+ export const GRANT_TTL_DEFAULT_SEC = 3600; // 1h
122
+ export const GRANT_TTL_MAX_SEC = 24 * 3600; // 24h cap — short-lived only
123
+ export function clampTtlSeconds(requested) {
124
+ const n = Number(requested);
125
+ if (!Number.isFinite(n) || n <= 0)
126
+ return GRANT_TTL_DEFAULT_SEC;
127
+ return Math.min(Math.floor(n), GRANT_TTL_MAX_SEC);
128
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * RFC-020 PR-C2a — delegation grant verifier (server-side consumption foundation).
3
+ *
4
+ * This is the ONLY sanctioned path that turns a `gtk_*` bearer into an authorization,
5
+ * and it is **opt-in per route + per required SAFE scope** — it is NOT global auth.
6
+ * A grant token is never equivalent to a human session or a permanent api_key:
7
+ * - global auth()/api_key resolution is untouched and never accepts gtk_* tokens;
8
+ * - this verifier returns a NARROW principal (grant_id, human_id, agent_label,
9
+ * capability) — not a user/session — and only for an explicitly-required SAFE scope.
10
+ *
11
+ * PR-C2a does NOT enable any risk scope. The required scope MUST be SAFE; the verifier
12
+ * refuses to authorize anything else (defense in depth), and a grant only passes if it
13
+ * actually carries that exact safe capability and is active/unexpired/unrevoked.
14
+ *
15
+ * Reads via the RFC-016 async seam (dbOne) so PWA and (later, PR-C2b) MCP share it.
16
+ * NO money/order/wallet/refund logic.
17
+ */
18
+ import { createHash } from 'node:crypto';
19
+ import { dbOne } from '../layer0-foundation/L0-1-database/db.js';
20
+ import { classifyScope, grantIsActive } from './agent-grant-scopes.js';
21
+ function parseCaps(json) {
22
+ try {
23
+ const v = JSON.parse(String(json));
24
+ return Array.isArray(v) ? v : [];
25
+ }
26
+ catch {
27
+ return [];
28
+ }
29
+ }
30
+ /**
31
+ * Verify a `gtk_*` bearer for an EXPLICIT required SAFE scope. Returns a narrow grant
32
+ * principal on success, or a typed failure. `nowIso` injectable for tests.
33
+ */
34
+ export async function verifyGrantToken(bearer, requiredScope, nowIso = new Date().toISOString()) {
35
+ // Programming guard: an opt-in route must require a SAFE scope. Risk / never-delegable /
36
+ // unknown can NEVER be satisfied by a grant — fail closed regardless of the token.
37
+ if (classifyScope(requiredScope) !== 'safe') {
38
+ return { ok: false, status: 500, error_code: 'SCOPE_NOT_SAFE', error: `requiredScope "${requiredScope}" is not a safe scope; grants can only ever authorize safe scopes` };
39
+ }
40
+ if (!bearer || !bearer.startsWith('gtk_')) {
41
+ return { ok: false, status: 401, error_code: 'GRANT_TOKEN_REQUIRED', error: 'a gtk_* delegation grant bearer is required for this scope' };
42
+ }
43
+ const tokenHash = createHash('sha256').update(bearer).digest('hex');
44
+ const row = await dbOne('SELECT grant_id, human_id, agent_label, capabilities, status, expires_at, revoked_at FROM agent_delegation_grants WHERE token_hash = ?', [tokenHash]);
45
+ if (!row) {
46
+ return { ok: false, status: 401, error_code: 'GRANT_NOT_FOUND', error: 'delegation grant not found for this token' };
47
+ }
48
+ if (!grantIsActive(row, nowIso)) {
49
+ return { ok: false, status: 403, error_code: 'GRANT_INACTIVE', error: 'delegation grant is revoked, expired, or inactive', grant_id: row.grant_id, human_id: row.human_id };
50
+ }
51
+ // The grant is only as valid as its accountable human. Mirror auth(): the subject must still
52
+ // exist and not be suspended (user_moderation.suspended) — else a grant minted before an admin
53
+ // suspension would outlive it. Fail closed.
54
+ const subject = await dbOne('SELECT u.id AS id, m.suspended AS suspended FROM users u LEFT JOIN user_moderation m ON m.user_id = u.id WHERE u.id = ?', [row.human_id]);
55
+ if (!subject) {
56
+ return { ok: false, status: 403, error_code: 'GRANT_SUBJECT_INACTIVE', error: 'grant subject (human) no longer exists', grant_id: row.grant_id, human_id: row.human_id };
57
+ }
58
+ if (subject.suspended) {
59
+ return { ok: false, status: 403, error_code: 'GRANT_SUBJECT_INACTIVE', error: 'grant subject (human) is suspended', grant_id: row.grant_id, human_id: row.human_id };
60
+ }
61
+ const caps = parseCaps(row.capabilities);
62
+ const holds = caps.some(c => c?.capability === requiredScope && classifyScope(String(c.capability)) === 'safe');
63
+ if (!holds) {
64
+ return { ok: false, status: 403, error_code: 'SCOPE_NOT_GRANTED', error: `grant does not carry the required safe scope "${requiredScope}"`, grant_id: row.grant_id, human_id: row.human_id };
65
+ }
66
+ return { ok: true, principal: { grant_id: row.grant_id, human_id: row.human_id, agent_label: row.agent_label, capability: requiredScope } };
67
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * RFC-020 PR-C1 — pairing primitives (pure, no DB/network).
3
+ *
4
+ * PKCE (RFC 7636, S256) + short one-time code / id generation + TTL. Shared by the
5
+ * PWA pairing routes and the MCP `webaz_pair` tool so both compute the challenge the
6
+ * same way. PR-C1 is pairing + credential delivery ONLY — no scope enforcement
7
+ * (that is PR-C2), no money/order/wallet code.
8
+ */
9
+ import { createHash, randomBytes } from 'node:crypto';
10
+ /** base64url (no padding). */
11
+ function b64url(buf) {
12
+ return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
13
+ }
14
+ /** PKCE code_verifier — high-entropy URL-safe string (RFC 7636 §4.1). */
15
+ export function generateCodeVerifier() {
16
+ return b64url(randomBytes(48)); // ~64 chars
17
+ }
18
+ /** PKCE S256 challenge = base64url(sha256(verifier)). */
19
+ export function pkceChallengeS256(verifier) {
20
+ return b64url(createHash('sha256').update(verifier).digest());
21
+ }
22
+ /** Constant-shape PKCE check: the verifier must hash to the stored challenge. */
23
+ export function verifyPkceS256(verifier, challenge) {
24
+ if (typeof verifier !== 'string' || typeof challenge !== 'string' || !verifier || !challenge)
25
+ return false;
26
+ const computed = pkceChallengeS256(verifier);
27
+ // length-guarded compare (challenges are fixed-length base64url of a sha256)
28
+ if (computed.length !== challenge.length)
29
+ return false;
30
+ let diff = 0;
31
+ for (let i = 0; i < computed.length; i++)
32
+ diff |= computed.charCodeAt(i) ^ challenge.charCodeAt(i);
33
+ return diff === 0;
34
+ }
35
+ const CODE_ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; // Crockford base32 (no I/L/O/U)
36
+ /** Short, human-approvable one-time pairing code. */
37
+ export function generateUserCode(len = 10) {
38
+ const bytes = randomBytes(len);
39
+ let out = '';
40
+ for (let i = 0; i < len; i++)
41
+ out += CODE_ALPHABET[bytes[i] % CODE_ALPHABET.length];
42
+ return out;
43
+ }
44
+ /** Pairing session TTL — short by design (RFC-020 §3.6). */
45
+ export const PAIRING_TTL_DEFAULT_SEC = 600; // 10 min to approve + retrieve
46
+ export const PAIRING_TTL_MAX_SEC = 1800; // 30 min cap
47
+ export function clampPairingTtlSeconds(requested) {
48
+ const n = Number(requested);
49
+ if (!Number.isFinite(n) || n <= 0)
50
+ return PAIRING_TTL_DEFAULT_SEC;
51
+ return Math.min(Math.floor(n), PAIRING_TTL_MAX_SEC);
52
+ }
53
+ /** A pairing can be approved only while pending + unexpired. */
54
+ export function pairingApprovable(p, nowIso) {
55
+ return !!p && p.status === 'pending' && !!p.expires_at && p.expires_at > nowIso;
56
+ }
57
+ /** A pairing can be retrieved only while approved, unexpired, and not yet consumed. */
58
+ export function pairingRetrievable(p, nowIso) {
59
+ return !!p && p.status === 'approved' && !p.consumed_at && !!p.expires_at && p.expires_at > nowIso;
60
+ }
@@ -0,0 +1,15 @@
1
+ import * as helpers from './webaz-schema-helpers.js';
2
+ export function applyWebazRuntimeSchema(db) {
3
+ // Call every pure idempotent DDL initializer exported by the shared helpers
4
+ // module (convention: each is named init*Schema / init*Columns and is a
5
+ // side-effect-free `(db) => void`). Order-independent: all statements are
6
+ // CREATE ... IF NOT EXISTS / guarded ALTER, so re-running or reordering is a
7
+ // no-op. New helpers are picked up automatically — no risk of forgetting one
8
+ // (which is exactly how the MCP/PWA schemas drifted apart in the first place).
9
+ for (const [name, fn] of Object.entries(helpers)) {
10
+ if (name.startsWith('init') && typeof fn === 'function') {
11
+ ;
12
+ fn(db);
13
+ }
14
+ }
15
+ }