@seasonkoh/webaz 0.1.24 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/README.md +5 -1
  2. package/dist/layer0-foundation/L0-1-database/db-backends/pg-backend.js +51 -0
  3. package/dist/layer0-foundation/L0-1-database/db-backends/sql-dialect-datetime.js +437 -0
  4. package/dist/layer0-foundation/L0-1-database/db-backends/sql-placeholders.js +98 -0
  5. package/dist/layer0-foundation/L0-1-database/db.js +65 -0
  6. package/dist/layer0-foundation/L0-2-state-machine/order-chain.js +13 -11
  7. package/dist/layer0-foundation/L0-2-state-machine/transitions.js +1 -1
  8. package/dist/layer0-foundation/L0-5-manifest/manifest.js +13 -11
  9. package/dist/layer1-agent/L1-1-mcp-server/server.js +288 -208
  10. package/dist/layer1-agent/L1-2-external-anchor/anchor-engine.js +14 -12
  11. package/dist/layer2-business/L2-6-notifications/notification-engine.js +8 -5
  12. package/dist/layer2-business/L2-7-snf/snf-engine.js +16 -14
  13. package/dist/layer2-business/L2-8-feedback/build-feedback-engine.js +18 -10
  14. package/dist/layer2-business/L2-9-contribution/build-reputation-engine.js +37 -23
  15. package/dist/layer2-business/L2-9-contribution/build-task-agent-metadata-store.js +182 -0
  16. package/dist/layer2-business/L2-9-contribution/build-task-participation.js +47 -0
  17. package/dist/layer2-business/L2-9-contribution/build-task-read.js +222 -0
  18. package/dist/layer2-business/L2-9-contribution/build-tasks-engine.js +11 -3
  19. package/dist/layer2-business/L2-9-contribution/canonical-contribution-target.js +16 -0
  20. package/dist/layer2-business/L2-9-contribution/contribution-display-envelope.js +40 -0
  21. package/dist/layer2-business/L2-9-contribution/contribution-score-contract.js +36 -0
  22. package/dist/layer2-business/L2-9-contribution/contribution-score-evidence.js +61 -0
  23. package/dist/layer2-business/L2-9-contribution/github-credential/canonical.js +60 -0
  24. package/dist/layer2-business/L2-9-contribution/github-credential/github-credential.schema.js +140 -0
  25. package/dist/layer2-business/L2-9-contribution/github-credential/github-fetch-adapter.js +437 -0
  26. package/dist/layer2-business/L2-9-contribution/github-credential/self-consistency.js +38 -0
  27. package/dist/layer2-business/L2-9-contribution/github-credential/verifier.js +231 -0
  28. package/dist/layer2-business/L2-9-contribution/github-credential-ingestion-engine.js +145 -0
  29. package/dist/layer2-business/L2-9-contribution/github-credential-store.js +115 -0
  30. package/dist/layer2-business/L2-9-contribution/identity-binding-engine.js +134 -0
  31. package/dist/layer2-business/L2-9-contribution/identity-binding-store.js +101 -0
  32. package/dist/layer2-business/L2-9-contribution/identity-claim-challenge-engine.js +126 -0
  33. package/dist/layer2-business/L2-9-contribution/identity-claim-challenge-store.js +30 -0
  34. package/dist/layer2-business/L2-9-contribution/identity-claim-discovery.js +55 -0
  35. package/dist/layer2-business/L2-9-contribution/identity-claim-engine.js +109 -0
  36. package/dist/layer2-business/L2-9-contribution/identity-claim-fact-precondition.js +22 -0
  37. package/dist/layer2-business/L2-9-contribution/identity-claim-proof-verifier.js +97 -0
  38. package/dist/layer2-business/L2-9-contribution/identity-claim-read.js +59 -0
  39. package/dist/layer2-business/L2-9-contribution/task-proposal-ai-store.js +99 -0
  40. package/dist/layer2-business/L2-9-contribution/task-proposal-draft.js +191 -0
  41. package/dist/layer2-business/L2-9-contribution/task-proposal-store.js +129 -0
  42. package/dist/layer2-business/L2-notes/note-photo-storage.js +4 -2
  43. package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +17 -15
  44. package/dist/layer3-trust/L3-1-dispute-engine/evidence-storage.js +11 -8
  45. package/dist/layer4-economics/L4-3-reputation/reputation-engine.js +9 -8
  46. package/dist/layer4-economics/L4-4-skill-market/skill-engine.js +11 -8
  47. package/dist/layer4-economics/L4-4-skill-market/skill-listing-engine.js +22 -16
  48. package/dist/pwa/acp-feed.js +13 -1
  49. package/dist/pwa/admin-bearer-auth.js +21 -0
  50. package/dist/pwa/contract-fingerprint.js +2 -0
  51. package/dist/pwa/email-delivery.js +127 -0
  52. package/dist/pwa/endpoint-actions.js +5 -1
  53. package/dist/pwa/goal-index.js +8 -8
  54. package/dist/pwa/human-presence.js +62 -0
  55. package/dist/pwa/public/app.js +1485 -283
  56. package/dist/pwa/public/i18n.js +297 -59
  57. package/dist/pwa/public/index.html +1 -0
  58. package/dist/pwa/public/openapi.json +5 -5
  59. package/dist/pwa/public/whitepaper/en/index.html +153 -0
  60. package/dist/pwa/public/whitepaper/zh-CN/index.html +153 -0
  61. package/dist/pwa/rate-limit.js +22 -0
  62. package/dist/pwa/routes/account-deletion.js +15 -13
  63. package/dist/pwa/routes/addresses.js +10 -9
  64. package/dist/pwa/routes/admin-admins.js +13 -14
  65. package/dist/pwa/routes/admin-analytics.js +109 -69
  66. package/dist/pwa/routes/admin-atomic.js +10 -4
  67. package/dist/pwa/routes/admin-catalog.js +13 -11
  68. package/dist/pwa/routes/admin-editor-picks.js +15 -10
  69. package/dist/pwa/routes/admin-events.js +5 -3
  70. package/dist/pwa/routes/admin-health.js +2 -1
  71. package/dist/pwa/routes/admin-moderation.js +50 -29
  72. package/dist/pwa/routes/admin-ops.js +35 -23
  73. package/dist/pwa/routes/admin-protocol-params.js +16 -19
  74. package/dist/pwa/routes/admin-reports.js +23 -21
  75. package/dist/pwa/routes/admin-tokenomics.js +26 -25
  76. package/dist/pwa/routes/admin-users-lifecycle.js +37 -40
  77. package/dist/pwa/routes/admin-users-query.js +65 -53
  78. package/dist/pwa/routes/admin-verifier-flow.js +82 -41
  79. package/dist/pwa/routes/admin-verifier-whitelist.js +55 -27
  80. package/dist/pwa/routes/admin-wallet-ops.js +32 -7
  81. package/dist/pwa/routes/agent-buy.js +46 -22
  82. package/dist/pwa/routes/agent-governance.js +52 -56
  83. package/dist/pwa/routes/ai.js +7 -5
  84. package/dist/pwa/routes/analytics.js +43 -41
  85. package/dist/pwa/routes/anchors.js +19 -20
  86. package/dist/pwa/routes/announcements.js +13 -13
  87. package/dist/pwa/routes/arbitrator.js +97 -31
  88. package/dist/pwa/routes/auction.js +157 -116
  89. package/dist/pwa/routes/auth-login.js +6 -4
  90. package/dist/pwa/routes/auth-read.js +21 -10
  91. package/dist/pwa/routes/auth-register.js +111 -26
  92. package/dist/pwa/routes/auth-sessions.js +12 -11
  93. package/dist/pwa/routes/blocklist.js +16 -15
  94. package/dist/pwa/routes/build-feedback.js +10 -9
  95. package/dist/pwa/routes/build-reputation.js +6 -2
  96. package/dist/pwa/routes/build-tasks.js +45 -13
  97. package/dist/pwa/routes/buyer-feeds.js +27 -25
  98. package/dist/pwa/routes/cart.js +16 -15
  99. package/dist/pwa/routes/charity.js +212 -150
  100. package/dist/pwa/routes/chat.js +42 -43
  101. package/dist/pwa/routes/checkin-tasks.js +10 -9
  102. package/dist/pwa/routes/checkout-helpers.js +12 -10
  103. package/dist/pwa/routes/claim-initiators.js +34 -14
  104. package/dist/pwa/routes/claim-verify.js +86 -53
  105. package/dist/pwa/routes/claim-voting.js +43 -18
  106. package/dist/pwa/routes/contribution-identity.js +164 -0
  107. package/dist/pwa/routes/contribution-score.js +19 -0
  108. package/dist/pwa/routes/coupons.js +19 -16
  109. package/dist/pwa/routes/dashboards.js +18 -16
  110. package/dist/pwa/routes/dispute-cases.js +25 -24
  111. package/dist/pwa/routes/disputes-read.js +45 -51
  112. package/dist/pwa/routes/disputes-write.js +124 -61
  113. package/dist/pwa/routes/evidence.js +9 -9
  114. package/dist/pwa/routes/external-anchors.js +13 -12
  115. package/dist/pwa/routes/feedback.js +29 -33
  116. package/dist/pwa/routes/flash-sales.js +18 -16
  117. package/dist/pwa/routes/follows.js +25 -24
  118. package/dist/pwa/routes/governance-auto-deactivate.js +21 -9
  119. package/dist/pwa/routes/governance-onboarding.js +70 -59
  120. package/dist/pwa/routes/group-buys.js +22 -22
  121. package/dist/pwa/routes/growth.js +34 -31
  122. package/dist/pwa/routes/import-product.js +12 -10
  123. package/dist/pwa/routes/kyc.js +9 -8
  124. package/dist/pwa/routes/leaderboard.js +20 -18
  125. package/dist/pwa/routes/listings.js +23 -22
  126. package/dist/pwa/routes/logistics.js +10 -8
  127. package/dist/pwa/routes/manifests.js +27 -27
  128. package/dist/pwa/routes/me-data.js +23 -21
  129. package/dist/pwa/routes/notifications.js +7 -6
  130. package/dist/pwa/routes/offers.js +30 -12
  131. package/dist/pwa/routes/orders-action.js +51 -29
  132. package/dist/pwa/routes/orders-create.js +75 -20
  133. package/dist/pwa/routes/orders-read.js +21 -20
  134. package/dist/pwa/routes/p2p-products.js +30 -18
  135. package/dist/pwa/routes/payments-governance.js +61 -56
  136. package/dist/pwa/routes/peers.js +9 -8
  137. package/dist/pwa/routes/pin-receipts.js +13 -13
  138. package/dist/pwa/routes/products-aliases.js +12 -10
  139. package/dist/pwa/routes/products-claims.js +36 -17
  140. package/dist/pwa/routes/products-create.js +53 -38
  141. package/dist/pwa/routes/products-crud.js +17 -16
  142. package/dist/pwa/routes/products-links.js +49 -26
  143. package/dist/pwa/routes/products-list.js +6 -4
  144. package/dist/pwa/routes/products-meta.js +40 -39
  145. package/dist/pwa/routes/products-update.js +19 -5
  146. package/dist/pwa/routes/profile-credentials.js +20 -19
  147. package/dist/pwa/routes/profile-identity.js +14 -13
  148. package/dist/pwa/routes/profile-location.js +7 -6
  149. package/dist/pwa/routes/profile-placement.js +20 -19
  150. package/dist/pwa/routes/profile-prefs.js +11 -11
  151. package/dist/pwa/routes/promoter.js +58 -66
  152. package/dist/pwa/routes/public-build-tasks.js +19 -0
  153. package/dist/pwa/routes/public-utils.js +108 -46
  154. package/dist/pwa/routes/push.js +16 -15
  155. package/dist/pwa/routes/ratings.js +92 -32
  156. package/dist/pwa/routes/recover-key.js +66 -26
  157. package/dist/pwa/routes/referral.js +37 -52
  158. package/dist/pwa/routes/reputation.js +3 -2
  159. package/dist/pwa/routes/returns.js +76 -73
  160. package/dist/pwa/routes/reviews.js +41 -18
  161. package/dist/pwa/routes/rewards-apply.js +16 -15
  162. package/dist/pwa/routes/rewards-auto-downgrade.js +9 -7
  163. package/dist/pwa/routes/rewards-escrow-expire.js +7 -5
  164. package/dist/pwa/routes/rfqs.js +163 -85
  165. package/dist/pwa/routes/search.js +16 -14
  166. package/dist/pwa/routes/secondhand.js +25 -22
  167. package/dist/pwa/routes/seller-quota.js +24 -26
  168. package/dist/pwa/routes/share-redirects.js +60 -55
  169. package/dist/pwa/routes/shareables-interactions.js +34 -35
  170. package/dist/pwa/routes/shareables.js +55 -51
  171. package/dist/pwa/routes/shop-referral.js +58 -0
  172. package/dist/pwa/routes/shops.js +25 -20
  173. package/dist/pwa/routes/signaling.js +10 -9
  174. package/dist/pwa/routes/skill-market.js +16 -16
  175. package/dist/pwa/routes/skills.js +15 -14
  176. package/dist/pwa/routes/snf.js +14 -13
  177. package/dist/pwa/routes/tags.js +10 -9
  178. package/dist/pwa/routes/task-proposals.js +121 -0
  179. package/dist/pwa/routes/trial.js +72 -52
  180. package/dist/pwa/routes/trusted-kpi.js +20 -18
  181. package/dist/pwa/routes/url-claim.js +67 -28
  182. package/dist/pwa/routes/users-public.js +62 -70
  183. package/dist/pwa/routes/variants.js +12 -13
  184. package/dist/pwa/routes/verifier-user.js +61 -21
  185. package/dist/pwa/routes/verify-tasks.js +49 -25
  186. package/dist/pwa/routes/waitlist.js +16 -15
  187. package/dist/pwa/routes/wallet-read.js +75 -37
  188. package/dist/pwa/routes/wallet-write.js +12 -9
  189. package/dist/pwa/routes/webauthn.js +25 -26
  190. package/dist/pwa/routes/webhooks.js +26 -26
  191. package/dist/pwa/routes/welcome.js +45 -50
  192. package/dist/pwa/routes/wishlist-qa.js +29 -32
  193. package/dist/pwa/server.js +304 -90
  194. package/dist/version.js +1 -1
  195. package/package.json +76 -3
@@ -1,40 +1,43 @@
1
+ import { dbOne, dbAll, dbRun } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
1
2
  export function registerAdminVerifierWhitelistRoutes(app, deps) {
3
+ // 单写白名单管理站点走 RFC-016 异步 seam;db 保留:revoke 是没收/退质押 + 白名单重写的
4
+ // 多写资金路径,必须原子(db.transaction + 防重复没收 guard),Phase 3 迁 pg 行锁。
2
5
  const { db, requireVerifierMgmtAdmin, adminCanOperateOn, logAdminAction, INTERNAL_AUDITOR_ID, TIER_QUOTAS, REVOKE_COOLDOWN_DAYS } = deps;
3
- app.get('/api/admin/verifier-whitelist', (req, res) => {
6
+ app.get('/api/admin/verifier-whitelist', async (req, res) => {
4
7
  const user = requireVerifierMgmtAdmin(req, res);
5
8
  if (!user)
6
9
  return;
7
- const list = db.prepare(`
10
+ const list = await dbAll(`
8
11
  SELECT vw.user_id, vw.added_at, vw.note, u.name, u.role
9
12
  FROM verifier_whitelist vw
10
13
  JOIN users u ON u.id = vw.user_id
11
14
  ORDER BY vw.added_at ASC
12
- `).all();
15
+ `);
13
16
  res.json(list);
14
17
  });
15
- app.post('/api/admin/verifier-whitelist', (req, res) => {
18
+ app.post('/api/admin/verifier-whitelist', async (req, res) => {
16
19
  const admin = requireVerifierMgmtAdmin(req, res);
17
20
  if (!admin)
18
21
  return;
19
22
  const { user_id, name, note } = req.body;
20
23
  let targetId = user_id;
21
24
  if (!targetId && name) {
22
- const found = db.prepare('SELECT id FROM users WHERE name = ?').get(name);
25
+ const found = await dbOne('SELECT id FROM users WHERE name = ?', [name]);
23
26
  if (!found)
24
27
  return void res.json({ error: `用户「${name}」不存在` });
25
28
  targetId = found.id;
26
29
  }
27
30
  if (!targetId)
28
31
  return void res.json({ error: '请提供 user_id 或 name' });
29
- const target = db.prepare('SELECT id, name FROM users WHERE id = ?').get(targetId);
32
+ const target = await dbOne('SELECT id, name FROM users WHERE id = ?', [targetId]);
30
33
  if (!target)
31
34
  return void res.json({ error: '用户不存在' });
32
35
  if (!adminCanOperateOn(admin, targetId, res))
33
36
  return;
34
- db.prepare('INSERT OR IGNORE INTO verifier_whitelist (user_id, note) VALUES (?, ?)').run(targetId, note ?? null);
37
+ await dbRun('INSERT OR IGNORE INTO verifier_whitelist (user_id, note) VALUES (?, ?)', [targetId, note ?? null]);
35
38
  res.json({ success: true, user_id: targetId, name: target.name });
36
39
  });
37
- app.delete('/api/admin/verifier-whitelist/:userId', (req, res) => {
40
+ app.delete('/api/admin/verifier-whitelist/:userId', async (req, res) => {
38
41
  const admin = requireVerifierMgmtAdmin(req, res);
39
42
  if (!admin)
40
43
  return;
@@ -43,10 +46,10 @@ export function registerAdminVerifierWhitelistRoutes(app, deps) {
43
46
  return void res.json({ error: '内部审核员不可移除' });
44
47
  if (!adminCanOperateOn(admin, targetId, res))
45
48
  return;
46
- db.prepare('DELETE FROM verifier_whitelist WHERE user_id = ?').run(targetId);
49
+ await dbRun('DELETE FROM verifier_whitelist WHERE user_id = ?', [targetId]);
47
50
  res.json({ success: true });
48
51
  });
49
- app.post('/api/admin/verifier-whitelist/:userId/promote', (req, res) => {
52
+ app.post('/api/admin/verifier-whitelist/:userId/promote', async (req, res) => {
50
53
  const admin = requireVerifierMgmtAdmin(req, res);
51
54
  if (!admin)
52
55
  return;
@@ -56,17 +59,16 @@ export function registerAdminVerifierWhitelistRoutes(app, deps) {
56
59
  if (!TIER_QUOTAS[tier])
57
60
  return void res.json({ error: 'tier 无效' });
58
61
  const targetId = req.params.userId;
59
- const wl = db.prepare("SELECT is_system FROM verifier_whitelist WHERE user_id = ?").get(targetId);
62
+ const wl = await dbOne("SELECT is_system FROM verifier_whitelist WHERE user_id = ?", [targetId]);
60
63
  if (!wl)
61
64
  return void res.json({ error: '该用户不在白名单' });
62
65
  if (wl.is_system)
63
66
  return void res.json({ error: '系统兜底账户不可手动 promote' });
64
- db.prepare("UPDATE verifier_whitelist SET tier = ?, daily_quota = ? WHERE user_id = ?")
65
- .run(tier, TIER_QUOTAS[tier], targetId);
67
+ await dbRun("UPDATE verifier_whitelist SET tier = ?, daily_quota = ? WHERE user_id = ?", [tier, TIER_QUOTAS[tier], targetId]);
66
68
  logAdminAction(admin.id, 'promote_verifier', 'user', targetId, { tier });
67
69
  res.json({ success: true });
68
70
  });
69
- app.post('/api/admin/verifier-whitelist/:userId/suspend', (req, res) => {
71
+ app.post('/api/admin/verifier-whitelist/:userId/suspend', async (req, res) => {
70
72
  const admin = requireVerifierMgmtAdmin(req, res);
71
73
  if (!admin)
72
74
  return;
@@ -76,12 +78,12 @@ export function registerAdminVerifierWhitelistRoutes(app, deps) {
76
78
  const targetId = req.params.userId;
77
79
  const n = Number(days) > 0 ? Number(days) : 7;
78
80
  const until = new Date(Date.now() + n * 86400_000).toISOString();
79
- db.prepare("INSERT OR IGNORE INTO verifier_stats (user_id) VALUES (?)").run(targetId);
80
- db.prepare("UPDATE verifier_stats SET suspended_until = ? WHERE user_id = ?").run(until, targetId);
81
+ await dbRun("INSERT OR IGNORE INTO verifier_stats (user_id) VALUES (?)", [targetId]);
82
+ await dbRun("UPDATE verifier_stats SET suspended_until = ? WHERE user_id = ?", [until, targetId]);
81
83
  logAdminAction(admin.id, 'suspend_verifier', 'user', targetId, { days: n, reason: reason || null, until });
82
84
  res.json({ success: true, suspended_until: until });
83
85
  });
84
- app.post('/api/admin/verifier-whitelist/:userId/revoke', (req, res) => {
86
+ app.post('/api/admin/verifier-whitelist/:userId/revoke', async (req, res) => {
85
87
  const admin = requireVerifierMgmtAdmin(req, res);
86
88
  if (!admin)
87
89
  return;
@@ -89,22 +91,48 @@ export function registerAdminVerifierWhitelistRoutes(app, deps) {
89
91
  return;
90
92
  const { reason } = req.body;
91
93
  const targetId = req.params.userId;
92
- const wl = db.prepare("SELECT is_system, stake_amount FROM verifier_whitelist WHERE user_id = ?").get(targetId);
94
+ // 友好预检查(读);真正的守恒 + 防重复没收门在事务内(重读 active + cooldown guard)
95
+ const wl = await dbOne("SELECT is_system, stake_amount FROM verifier_whitelist WHERE user_id = ?", [targetId]);
93
96
  if (!wl)
94
97
  return void res.json({ error: '该用户不在白名单' });
95
98
  if (wl.is_system)
96
99
  return void res.json({ error: '系统兜底账户不可撤销' });
97
100
  const cooldownUntil = new Date(Date.now() + REVOKE_COOLDOWN_DAYS * 86400_000).toISOString();
98
- // 没收 50% 质押
99
- const forfeit = (wl.stake_amount || 0) * 0.5;
100
- db.prepare("UPDATE wallets SET staked = staked - ? WHERE user_id = ?").run(wl.stake_amount || 0, targetId);
101
- if ((wl.stake_amount || 0) > forfeit) {
102
- db.prepare("UPDATE wallets SET balance = balance + ? WHERE user_id = ?").run((wl.stake_amount || 0) - forfeit, targetId);
101
+ // 原子段:重读 active 行 → 没收 50% + 退还另一半 + DELETE active + INSERT cooldown 一起落。
102
+ // cooldown guard 防并发两次 revoke 重复没收/退款。
103
+ let forfeit = 0;
104
+ try {
105
+ forfeit = db.transaction(() => {
106
+ const cur = db.prepare("SELECT is_system, stake_amount, cooldown_until FROM verifier_whitelist WHERE user_id = ?")
107
+ .get(targetId);
108
+ if (!cur)
109
+ throw new Error('REVOKE_GONE');
110
+ if (cur.is_system)
111
+ throw new Error('REVOKE_SYSTEM');
112
+ if (cur.cooldown_until)
113
+ throw new Error('REVOKE_ALREADY'); // 已在撤销冷却,不再没收一次
114
+ const stakeAmt = cur.stake_amount || 0;
115
+ const f = Math.round(stakeAmt * 0.5 * 100) / 100;
116
+ db.prepare("UPDATE wallets SET staked = staked - ? WHERE user_id = ?").run(stakeAmt, targetId);
117
+ if (stakeAmt > f)
118
+ db.prepare("UPDATE wallets SET balance = balance + ? WHERE user_id = ?").run(stakeAmt - f, targetId);
119
+ db.prepare("DELETE FROM verifier_whitelist WHERE user_id = ?").run(targetId);
120
+ db.prepare(`INSERT INTO verifier_whitelist (user_id, note, tier, daily_quota, cooldown_until, is_system) VALUES (?,?,?,?,?,0)`)
121
+ .run(targetId, `撤销冷却中: ${reason || ''}`, 'trial-1', 0, cooldownUntil);
122
+ return f;
123
+ })();
124
+ }
125
+ catch (e) {
126
+ const msg = e.message;
127
+ if (msg === 'REVOKE_GONE')
128
+ return void res.json({ error: '该用户不在白名单' });
129
+ if (msg === 'REVOKE_SYSTEM')
130
+ return void res.json({ error: '系统兜底账户不可撤销' });
131
+ if (msg === 'REVOKE_ALREADY')
132
+ return void res.json({ error: '该用户已在撤销冷却中' });
133
+ console.error('[verifier revoke tx]', msg);
134
+ return void res.status(500).json({ error: '撤销失败,请重试' });
103
135
  }
104
- db.prepare("DELETE FROM verifier_whitelist WHERE user_id = ?").run(targetId);
105
- // 用 cooldown 记录在 verifier_stats 上(应用层兼容)
106
- db.prepare(`INSERT INTO verifier_whitelist (user_id, note, tier, daily_quota, cooldown_until, is_system) VALUES (?,?,?,?,?,0)`)
107
- .run(targetId, `撤销冷却中: ${reason || ''}`, 'trial-1', 0, cooldownUntil);
108
136
  logAdminAction(admin.id, 'revoke_verifier', 'user', targetId, { reason: reason || null, forfeit, cooldown_until: cooldownUntil });
109
137
  res.json({ success: true, cooldown_until: cooldownUntil, forfeit });
110
138
  });
@@ -1,5 +1,7 @@
1
+ import { dbOne, dbAll } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
1
2
  export function registerAdminWalletOpsRoutes(app, deps) {
2
- const { db, requireProtocolAdmin, adminAuth, getPublicClient, getUsdcAddr, getUsdcAbi, getHotWalletAddr, wazToUsdc, getIsMainnet, getNetwork, executeWithdrawal } = deps;
3
+ // db 已走 RFC-016 异步 seam(dbOne/dbAll),不再直接用 deps.db
4
+ const { requireProtocolAdmin, adminAuth, getPublicClient, getUsdcAddr, getUsdcAbi, getHotWalletAddr, wazToUsdc, getIsMainnet, getNetwork, executeWithdrawal, logAdminAction, resolveProtocolAdminSoft } = deps;
3
5
  // P2-5: protocol 权限(区域 admin 看不到全局热钱包)
4
6
  app.get('/api/admin/hot-wallet/status', async (req, res) => {
5
7
  const admin = requireProtocolAdmin(req, res);
@@ -12,7 +14,7 @@ export function registerAdminWalletOpsRoutes(app, deps) {
12
14
  address: getUsdcAddr(), abi: getUsdcAbi(), functionName: 'balanceOf', args: [hw],
13
15
  });
14
16
  const ethBal = await pc.getBalance({ address: hw });
15
- const pending = db.prepare("SELECT COALESCE(SUM(amount), 0) as t FROM withdrawal_requests WHERE status = 'pending'").get();
17
+ const pending = (await dbOne("SELECT COALESCE(SUM(amount), 0) as t FROM withdrawal_requests WHERE status = 'pending'"));
16
18
  const pendingUsdc = wazToUsdc(Number(pending.t));
17
19
  res.json({
18
20
  address: hw,
@@ -45,22 +47,45 @@ export function registerAdminWalletOpsRoutes(app, deps) {
45
47
  res.json({ address: hw, usdc_balance: null, error: e.message });
46
48
  }
47
49
  });
48
- app.get('/api/admin/withdrawals', (req, res) => {
50
+ app.get('/api/admin/withdrawals', async (req, res) => {
49
51
  if (!adminAuth(req, res))
50
52
  return;
51
- const list = db.prepare(`
53
+ const list = await dbAll(`
52
54
  SELECT wr.*, u.name as user_name
53
55
  FROM withdrawal_requests wr JOIN users u ON wr.user_id = u.id
54
56
  WHERE wr.status = 'pending' ORDER BY wr.created_at ASC
55
- `).all();
57
+ `);
56
58
  res.json(list);
57
59
  });
58
60
  app.post('/api/admin/withdrawals/:id/approve', async (req, res) => {
59
- if (!adminAuth(req, res))
60
- return;
61
+ // 双轨过渡鉴权:优先认登录的 protocol-admin(Bearer)→ 记其真实 admin id;
62
+ // 否则回落到共享 ADMIN_KEY(adminAuth,既有运维路径,行为不变)→ actor 记中性标记 'admin_key'。
63
+ // 仅认 protocol 权限的 admin;非 protocol 的 Bearer 不放行(soft 解析返回 null),不扩大访问面,只精确归属。
64
+ let actorId = 'admin_key';
65
+ let authMethod = 'admin_key';
66
+ const bearerAdmin = resolveProtocolAdminSoft(req);
67
+ if (bearerAdmin) {
68
+ actorId = String(bearerAdmin.id);
69
+ authMethod = 'bearer_admin';
70
+ }
71
+ else {
72
+ if (!adminAuth(req, res))
73
+ return;
74
+ }
75
+ // 出金前读取目标(user + amount),便于审计;执行后用真实 txHash 记一条 admin_audit_log。
76
+ const wr = await dbOne('SELECT user_id, amount FROM withdrawal_requests WHERE id = ?', [req.params.id]);
61
77
  const result = await executeWithdrawal(req.params.id).catch(e => ({ success: false, error: e.message, txHash: undefined }));
62
78
  if (!result.success)
63
79
  return void res.json({ error: result.error });
80
+ // 审计时机:仅在出金成功后写。actor = 真实 admin id(bearer_admin)或中性标记 'admin_key';不写任何密钥。
81
+ try {
82
+ logAdminAction(actorId, 'withdrawal_approve', 'withdrawal', req.params.id, {
83
+ user_id: wr?.user_id ?? null, amount: wr?.amount ?? null, tx_hash: result.txHash, network: getNetwork(), auth_method: authMethod,
84
+ });
85
+ }
86
+ catch (e) {
87
+ console.error('[withdrawal_approve audit]', e);
88
+ }
64
89
  res.json({ success: true, tx_hash: result.txHash });
65
90
  });
66
91
  }
@@ -163,33 +163,57 @@ ${webazFormatted.length > 0 ? JSON.stringify(webazFormatted.map(p => ({
163
163
  const now = new Date();
164
164
  const expiresAt = new Date(now.getTime() + 10 * 60_000);
165
165
  sessionToken = generateId('pst');
166
- db.prepare(`INSERT INTO price_sessions (token, product_id, user_id, price, quantity, created_at, expires_at) VALUES (?,?,?,?,1,?,?)`)
167
- .run(sessionToken, product.id, user.id, product.price, now.toISOString(), expiresAt.toISOString());
168
- verifiedPrice = product.price;
169
166
  const oId = generateId('ord');
170
167
  const totalAmount = product.price;
171
168
  const seller = db.prepare('SELECT id FROM users WHERE id = ?').get(product.seller_id);
172
- db.prepare(`INSERT INTO orders (
173
- id, product_id, buyer_id, seller_id, quantity, unit_price, total_amount, escrow_amount,
174
- status, shipping_address, notes, pay_deadline, accept_deadline, ship_deadline,
175
- pickup_deadline, delivery_deadline, confirm_deadline
176
- ) VALUES (?,?,?,?,1,?,?,?,'created',?,?,?,?,?,?,?,?)`).run(oId, product.id, user.id, seller.id, totalAmount, totalAmount, totalAmount, shipping_address, `[智能下单] ${decision.reason}`, addHours(now, 24), addHours(now, 48), addHours(now, 120), addHours(now, 168), addHours(now, 336), addHours(now, 408));
177
- db.prepare('UPDATE wallets SET balance = balance - ?, escrowed = escrowed + ? WHERE user_id = ?')
178
- .run(totalAmount, totalAmount, user.id);
179
- db.prepare('UPDATE products SET stock = stock - 1 WHERE id = ?').run(product.id);
180
- checkStockAndMaybeDelist(String(product.id));
181
- db.prepare(`UPDATE price_sessions SET used_at = datetime('now') WHERE token = ?`).run(sessionToken);
182
- transition(db, oId, 'paid', user.id, [], '智能下单:模拟支付完成');
183
- notifyTransition(db, oId, 'created', 'paid');
184
- if (shouldAutoAccept(db, oId)) {
185
- const sys = db.prepare("SELECT id FROM users WHERE id = 'sys_protocol'").get();
186
- if (sys) {
187
- const ar = transition(db, oId, 'accepted', sys.id, [], '⚡ auto_accept Skill 自动接单');
188
- if (ar.success)
189
- notifyTransition(db, oId, 'paid', 'accepted');
169
+ // 原子核心:余额扣款(balance>=amount 守卫)+ 库存 CAS(stock>=1)+ 建单 + 价格锁,任一 changes!==1 抛回滚整笔。
170
+ // 注:transition() 自带 db.transaction,不能嵌套进来(better-sqlite3 禁套娃);故状态推进 + 通知放 tx 提交后,
171
+ // 与原顺序一致(原本就是 insert(created) 扣款 transition(paid))。守卫杜绝并发超卖/超扣 + 半写(Phase 3 pg 安全)。
172
+ let committed = false;
173
+ try {
174
+ db.transaction(() => {
175
+ const deb = db.prepare('UPDATE wallets SET balance = balance - ?, escrowed = escrowed + ? WHERE user_id = ? AND balance >= ?')
176
+ .run(totalAmount, totalAmount, user.id, totalAmount);
177
+ if (deb.changes !== 1)
178
+ throw new Error('AGENTBUY_INSUFFICIENT_BALANCE');
179
+ const dec = db.prepare('UPDATE products SET stock = stock - 1 WHERE id = ? AND stock >= 1').run(product.id);
180
+ if (dec.changes !== 1)
181
+ throw new Error('AGENTBUY_OUT_OF_STOCK');
182
+ db.prepare(`INSERT INTO price_sessions (token, product_id, user_id, price, quantity, created_at, expires_at) VALUES (?,?,?,?,1,?,?)`)
183
+ .run(sessionToken, product.id, user.id, product.price, now.toISOString(), expiresAt.toISOString());
184
+ db.prepare(`INSERT INTO orders (
185
+ id, product_id, buyer_id, seller_id, quantity, unit_price, total_amount, escrow_amount,
186
+ status, shipping_address, notes, pay_deadline, accept_deadline, ship_deadline,
187
+ pickup_deadline, delivery_deadline, confirm_deadline
188
+ ) VALUES (?,?,?,?,1,?,?,?,'created',?,?,?,?,?,?,?,?)`).run(oId, product.id, user.id, seller.id, totalAmount, totalAmount, totalAmount, shipping_address, `[智能下单] ${decision.reason}`, addHours(now, 24), addHours(now, 48), addHours(now, 120), addHours(now, 168), addHours(now, 336), addHours(now, 408));
189
+ db.prepare(`UPDATE price_sessions SET used_at = datetime('now') WHERE token = ?`).run(sessionToken);
190
+ committed = true;
191
+ })();
192
+ }
193
+ catch (e) {
194
+ const m = e.message;
195
+ if (m !== 'AGENTBUY_INSUFFICIENT_BALANCE' && m !== 'AGENTBUY_OUT_OF_STOCK')
196
+ throw e;
197
+ // 并发售罄 / 余额已变 → 不下单(auto_bought=false),在 reason 里说明
198
+ sessionToken = null;
199
+ decision.reason = (decision.reason || '') + (m === 'AGENTBUY_OUT_OF_STOCK' ? ' · auto_buy 跳过:商品已售罄' : ' · auto_buy 跳过:余额不足');
200
+ }
201
+ if (committed) {
202
+ // tx 提交后:状态推进 + 通知(transition 自带事务,故置于此)
203
+ checkStockAndMaybeDelist(String(product.id));
204
+ transition(db, oId, 'paid', user.id, [], '智能下单:模拟支付完成');
205
+ notifyTransition(db, oId, 'created', 'paid');
206
+ if (shouldAutoAccept(db, oId)) {
207
+ const sys = db.prepare("SELECT id FROM users WHERE id = 'sys_protocol'").get();
208
+ if (sys) {
209
+ const ar = transition(db, oId, 'accepted', sys.id, [], '⚡ auto_accept Skill 自动接单');
210
+ if (ar.success)
211
+ notifyTransition(db, oId, 'paid', 'accepted');
212
+ }
190
213
  }
214
+ verifiedPrice = product.price;
215
+ orderId = oId;
191
216
  }
192
- orderId = oId;
193
217
  }
194
218
  }
195
219
  }
@@ -1,16 +1,17 @@
1
1
  import { computeAgentPassport } from '../../layer1-agent/L1-2-identity/agent-passport.js';
2
+ import { dbOne, dbAll, dbRun } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
2
3
  export function registerAgentGovernanceRoutes(app, deps) {
3
4
  const { db, generateId, auth, requireRootAdmin, invalidateAgentBlockedCache, requireHumanPresence, issueAgentStrike, custodianFingerprint, signPassport, issuerAddress } = deps;
4
5
  // /api/me/agents — 列出本账号所有 agent + declaration / strikes
5
- app.get('/api/me/agents', (req, res) => {
6
+ app.get('/api/me/agents', async (req, res) => {
6
7
  const user = auth(req, res);
7
8
  if (!user)
8
9
  return;
9
- const keys = db.prepare(`SELECT api_key FROM users WHERE id = ? UNION SELECT api_key FROM agent_reputation WHERE user_id = ?`).all(user.id, user.id);
10
- const items = keys.map(k => {
11
- const decl = db.prepare(`SELECT operator_name, operator_contact, purpose, declared_scope, attestations, repo_url, revoked_at FROM agent_declarations WHERE api_key = ?`).get(k.api_key);
10
+ const keys = await dbAll(`SELECT api_key FROM users WHERE id = ? UNION SELECT api_key FROM agent_reputation WHERE user_id = ?`, [user.id, user.id]);
11
+ const items = await Promise.all(keys.map(async (k) => {
12
+ const decl = await dbOne(`SELECT operator_name, operator_contact, purpose, declared_scope, attestations, repo_url, revoked_at FROM agent_declarations WHERE api_key = ?`, [k.api_key]);
12
13
  // P1 fix 4.4:附 signals JSON
13
- const rep = db.prepare(`SELECT trust_score, level, signals, last_calculated_at FROM agent_reputation WHERE api_key = ?`).get(k.api_key);
14
+ const rep = await dbOne(`SELECT trust_score, level, signals, last_calculated_at FROM agent_reputation WHERE api_key = ?`, [k.api_key]);
14
15
  if (rep && rep.signals) {
15
16
  try {
16
17
  rep.signals = JSON.parse(rep.signals);
@@ -19,9 +20,9 @@ export function registerAgentGovernanceRoutes(app, deps) {
19
20
  rep.signals = null;
20
21
  }
21
22
  }
22
- const calls30d = db.prepare(`SELECT COUNT(*) as n FROM agent_call_log WHERE api_key = ? AND created_at > datetime('now', '-30 days')`).get(k.api_key).n;
23
- const last = db.prepare(`SELECT endpoint, method, status_code, created_at FROM agent_call_log WHERE api_key = ? ORDER BY created_at DESC LIMIT 1`).get(k.api_key);
24
- const strikes = db.prepare(`SELECT severity, reason_code, issued_at, expires_at, appeal_status FROM agent_strikes WHERE api_key = ? ORDER BY issued_at DESC LIMIT 5`).all(k.api_key);
23
+ const calls30d = (await dbOne(`SELECT COUNT(*) as n FROM agent_call_log WHERE api_key = ? AND created_at > datetime('now', '-30 days')`, [k.api_key])).n;
24
+ const last = await dbOne(`SELECT endpoint, method, status_code, created_at FROM agent_call_log WHERE api_key = ? ORDER BY created_at DESC LIMIT 1`, [k.api_key]);
25
+ const strikes = await dbAll(`SELECT severity, reason_code, issued_at, expires_at, appeal_status FROM agent_strikes WHERE api_key = ? ORDER BY issued_at DESC LIMIT 5`, [k.api_key]);
25
26
  let passport = null;
26
27
  try {
27
28
  passport = computeAgentPassport(db, k.api_key, user.id, custodianFingerprint);
@@ -37,9 +38,9 @@ export function registerAgentGovernanceRoutes(app, deps) {
37
38
  recent_strikes: strikes,
38
39
  passport,
39
40
  };
40
- });
41
+ }));
41
42
  // Phase 2 监护人总览(只读/软绑定):真人态 + 旗下 agent 聚合 + 连带
42
- const hasPasskey = (db.prepare('SELECT COUNT(*) AS n FROM webauthn_credentials WHERE user_id = ?').get(user.id)?.n || 0) > 0;
43
+ const hasPasskey = ((await dbOne('SELECT COUNT(*) AS n FROM webauthn_credentials WHERE user_id = ?', [user.id]))?.n || 0) > 0;
43
44
  const pps = items.map(i => i.passport).filter(Boolean);
44
45
  const depthRank = { shallow: 0, medium: 1, deep: 2, profound: 3 };
45
46
  const deepest = pps.reduce((d, p) => (depthRank[p.engagement_depth] ?? 0) > (depthRank[d] ?? 0) ? p.engagement_depth : d, 'shallow');
@@ -65,7 +66,7 @@ export function registerAgentGovernanceRoutes(app, deps) {
65
66
  const prefix = String(req.params.apiKeyPrefix || '').replace('...', '');
66
67
  if (prefix.length < 6)
67
68
  return void res.status(400).json({ error: 'apiKeyPrefix 太短' });
68
- const keys = db.prepare(`SELECT api_key FROM users WHERE id = ? UNION SELECT api_key FROM agent_reputation WHERE user_id = ?`).all(user.id, user.id);
69
+ const keys = await dbAll(`SELECT api_key FROM users WHERE id = ? UNION SELECT api_key FROM agent_reputation WHERE user_id = ?`, [user.id, user.id]);
69
70
  const match = keys.find(k => k.api_key.startsWith(prefix));
70
71
  if (!match)
71
72
  return void res.status(404).json({ error: '未找到该 agent(或不属于你)' });
@@ -135,32 +136,30 @@ export function registerAgentGovernanceRoutes(app, deps) {
135
136
  webaz_format, // 显式重复让消费者明确选哪个
136
137
  });
137
138
  });
138
- app.get('/api/me/agents/:apiKeyPrefix/log', (req, res) => {
139
+ app.get('/api/me/agents/:apiKeyPrefix/log', async (req, res) => {
139
140
  const user = auth(req, res);
140
141
  if (!user)
141
142
  return;
142
143
  const prefix = String(req.params.apiKeyPrefix || '').replace(/[^A-Za-z0-9_]/g, '').slice(0, 32);
143
144
  if (prefix.length < 8)
144
145
  return void res.status(400).json({ error: 'apiKeyPrefix 至少 8 字符' });
145
- const targetKey = db.prepare(`SELECT api_key FROM users WHERE id = ? AND api_key LIKE ? || '%'
146
- UNION SELECT api_key FROM agent_reputation WHERE user_id = ? AND api_key LIKE ? || '%'`)
147
- .get(user.id, prefix, user.id, prefix);
146
+ const targetKey = await dbOne(`SELECT api_key FROM users WHERE id = ? AND api_key LIKE ? || '%'
147
+ UNION SELECT api_key FROM agent_reputation WHERE user_id = ? AND api_key LIKE ? || '%'`, [user.id, prefix, user.id, prefix]);
148
148
  if (!targetKey)
149
149
  return void res.status(404).json({ error: '未找到匹配的 agent api_key(仅可查本人的)' });
150
150
  const limit = Math.min(500, Math.max(10, Number(req.query.limit) || 100));
151
- const rows = db.prepare(`SELECT endpoint, method, status_code, created_at FROM agent_call_log
152
- WHERE api_key = ? AND created_at > datetime('now', '-30 days') ORDER BY id DESC LIMIT ?`).all(targetKey.api_key, limit);
151
+ const rows = await dbAll(`SELECT endpoint, method, status_code, created_at FROM agent_call_log
152
+ WHERE api_key = ? AND created_at > datetime('now', '-30 days') ORDER BY id DESC LIMIT ?`, [targetKey.api_key, limit]);
153
153
  res.json({ items: rows });
154
154
  });
155
- app.post('/api/me/agents/declarations', (req, res) => {
155
+ app.post('/api/me/agents/declarations', async (req, res) => {
156
156
  const user = auth(req, res);
157
157
  if (!user)
158
158
  return;
159
159
  const b = req.body;
160
160
  const targetApiKey = b.api_key ? String(b.api_key) : user.api_key;
161
- const ownership = db.prepare(`SELECT id FROM users WHERE id = ? AND api_key = ?
162
- UNION SELECT user_id as id FROM agent_reputation WHERE user_id = ? AND api_key = ?`)
163
- .get(user.id, targetApiKey, user.id, targetApiKey);
161
+ const ownership = await dbOne(`SELECT id FROM users WHERE id = ? AND api_key = ?
162
+ UNION SELECT user_id as id FROM agent_reputation WHERE user_id = ? AND api_key = ?`, [user.id, targetApiKey, user.id, targetApiKey]);
164
163
  if (!ownership)
165
164
  return void res.status(403).json({ error: 'api_key 不属于本账号' });
166
165
  const operator_name = String(b.operator_name || '').trim().slice(0, 60);
@@ -185,7 +184,7 @@ export function registerAgentGovernanceRoutes(app, deps) {
185
184
  const attestationsJson = b.attestations && typeof b.attestations === 'object' ? JSON.stringify(b.attestations).slice(0, 2000) : null;
186
185
  const repo_url = b.repo_url ? String(b.repo_url).slice(0, 200) : null;
187
186
  const homepage = b.homepage ? String(b.homepage).slice(0, 200) : null;
188
- db.prepare(`INSERT INTO agent_declarations (
187
+ await dbRun(`INSERT INTO agent_declarations (
189
188
  api_key, user_id, operator_name, operator_contact, purpose, declared_scope, attestations, repo_url, homepage
190
189
  ) VALUES (?,?,?,?,?,?,?,?,?)
191
190
  ON CONFLICT(api_key) DO UPDATE SET
@@ -197,36 +196,35 @@ export function registerAgentGovernanceRoutes(app, deps) {
197
196
  repo_url = excluded.repo_url,
198
197
  homepage = excluded.homepage,
199
198
  revoked_at = NULL,
200
- updated_at = datetime('now')`).run(targetApiKey, user.id, operator_name, operator_contact, purpose, scopeJson, attestationsJson, repo_url, homepage);
199
+ updated_at = datetime('now')`, [targetApiKey, user.id, operator_name, operator_contact, purpose, scopeJson, attestationsJson, repo_url, homepage]);
201
200
  invalidateAgentBlockedCache(targetApiKey);
202
201
  res.json({ ok: true });
203
202
  });
204
203
  // 用户撤销 agent(铁律 §4 human presence)
205
- app.post('/api/me/agents/:apiKeyPrefix/revoke', (req, res) => {
204
+ app.post('/api/me/agents/:apiKeyPrefix/revoke', async (req, res) => {
206
205
  const user = auth(req, res);
207
206
  if (!user)
208
207
  return;
209
208
  const prefix = String(req.params.apiKeyPrefix || '').replace(/[^A-Za-z0-9_]/g, '').slice(0, 32);
210
209
  if (prefix.length < 8)
211
210
  return void res.status(400).json({ error: 'apiKeyPrefix 至少 8 字符' });
212
- const targetKey = db.prepare(`SELECT api_key FROM users WHERE id = ? AND api_key LIKE ? || '%'
213
- UNION SELECT api_key FROM agent_reputation WHERE user_id = ? AND api_key LIKE ? || '%'`)
214
- .get(user.id, prefix, user.id, prefix);
211
+ const targetKey = await dbOne(`SELECT api_key FROM users WHERE id = ? AND api_key LIKE ? || '%'
212
+ UNION SELECT api_key FROM agent_reputation WHERE user_id = ? AND api_key LIKE ? || '%'`, [user.id, prefix, user.id, prefix]);
215
213
  if (!targetKey)
216
214
  return void res.status(404).json({ error: '未找到匹配的 agent api_key' });
217
215
  const hpCheck = requireHumanPresence(user.id, 'agent_revoke', (req.body || {}).webauthn_token, 'require_human_presence_for_agent_revoke', () => true);
218
216
  if (!hpCheck.ok)
219
217
  return void res.status(412).json({ error: hpCheck.reason, error_code: hpCheck.error_code });
220
218
  const reason = String((req.body || {}).reason || '').slice(0, 300);
221
- db.prepare(`INSERT INTO agent_revocations (target_kind, target_value, revoked_by, revoked_by_role, reason)
219
+ await dbRun(`INSERT INTO agent_revocations (target_kind, target_value, revoked_by, revoked_by_role, reason)
222
220
  VALUES ('api_key', ?, ?, 'self', ?)
223
- ON CONFLICT(target_kind, target_value, revoked_by) DO NOTHING`).run(targetKey.api_key, user.id, reason);
224
- db.prepare(`UPDATE agent_declarations SET revoked_at = datetime('now'), revoked_reason = ? WHERE api_key = ?`).run(reason, targetKey.api_key);
221
+ ON CONFLICT(target_kind, target_value, revoked_by) DO NOTHING`, [targetKey.api_key, user.id, reason]);
222
+ await dbRun(`UPDATE agent_declarations SET revoked_at = datetime('now'), revoked_reason = ? WHERE api_key = ?`, [reason, targetKey.api_key]);
225
223
  invalidateAgentBlockedCache(targetKey.api_key);
226
224
  res.json({ ok: true });
227
225
  });
228
226
  // 撤销同 operator 名下所有 agent(仅撤销本用户给 operator 旗下 agent 的 attestation)
229
- app.post('/api/me/agents/operators/:operator_name/revoke', (req, res) => {
227
+ app.post('/api/me/agents/operators/:operator_name/revoke', async (req, res) => {
230
228
  const user = auth(req, res);
231
229
  if (!user)
232
230
  return;
@@ -237,27 +235,26 @@ export function registerAgentGovernanceRoutes(app, deps) {
237
235
  if (!hpCheck.ok)
238
236
  return void res.status(412).json({ error: hpCheck.reason, error_code: hpCheck.error_code });
239
237
  const reason = String((req.body || {}).reason || '').slice(0, 300);
240
- const affected = db.prepare(`UPDATE agent_attestations SET revoked_at = datetime('now')
238
+ const affected = await dbRun(`UPDATE agent_attestations SET revoked_at = datetime('now')
241
239
  WHERE user_id = ? AND revoked_at IS NULL
242
- AND api_key IN (SELECT api_key FROM agent_declarations WHERE operator_name = ?)`)
243
- .run(user.id, opName);
244
- db.prepare(`INSERT INTO agent_revocations (target_kind, target_value, revoked_by, revoked_by_role, reason)
240
+ AND api_key IN (SELECT api_key FROM agent_declarations WHERE operator_name = ?)`, [user.id, opName]);
241
+ await dbRun(`INSERT INTO agent_revocations (target_kind, target_value, revoked_by, revoked_by_role, reason)
245
242
  VALUES ('operator_name', ?, ?, 'self', ?)
246
- ON CONFLICT(target_kind, target_value, revoked_by) DO NOTHING`).run(opName, user.id, reason);
247
- const keys = db.prepare(`SELECT api_key FROM agent_declarations WHERE operator_name = ?`).all(opName);
243
+ ON CONFLICT(target_kind, target_value, revoked_by) DO NOTHING`, [opName, user.id, reason]);
244
+ const keys = await dbAll(`SELECT api_key FROM agent_declarations WHERE operator_name = ?`, [opName]);
248
245
  for (const k of keys)
249
246
  invalidateAgentBlockedCache(k.api_key);
250
247
  res.json({ ok: true, attestations_revoked: affected.changes });
251
248
  });
252
249
  // P0 audit fix 4.2: 申诉 strike
253
- app.post('/api/me/agents/strikes/:strikeId/appeal', (req, res) => {
250
+ app.post('/api/me/agents/strikes/:strikeId/appeal', async (req, res) => {
254
251
  const user = auth(req, res);
255
252
  if (!user)
256
253
  return;
257
254
  const strikeId = Number(req.params.strikeId);
258
255
  if (!Number.isInteger(strikeId) || strikeId <= 0)
259
256
  return void res.status(400).json({ error: 'strikeId 必须是正整数' });
260
- const strike = db.prepare(`SELECT id, api_key, user_id, severity, issued_at, appeal_status FROM agent_strikes WHERE id = ?`).get(strikeId);
257
+ const strike = await dbOne(`SELECT id, api_key, user_id, severity, issued_at, appeal_status FROM agent_strikes WHERE id = ?`, [strikeId]);
261
258
  if (!strike)
262
259
  return void res.status(404).json({ error: 'strike 不存在' });
263
260
  if (strike.user_id !== user.id)
@@ -270,11 +267,11 @@ export function registerAgentGovernanceRoutes(app, deps) {
270
267
  const reason = String((req.body || {}).reason || '').trim().slice(0, 500);
271
268
  if (reason.length < 10)
272
269
  return void res.status(400).json({ error: '申诉理由 ≥10 字' });
273
- db.prepare(`UPDATE agent_strikes SET appeal_status = 'pending', appeal_reason = ? WHERE id = ?`).run(reason, strikeId);
270
+ await dbRun(`UPDATE agent_strikes SET appeal_status = 'pending', appeal_reason = ? WHERE id = ?`, [reason, strikeId]);
274
271
  res.json({ ok: true, message: '申诉已提交,等待 root admin 审核' });
275
272
  });
276
273
  // Admin: 审核 strike 申诉
277
- app.post('/api/admin/agent-strikes/:strikeId/decide', (req, res) => {
274
+ app.post('/api/admin/agent-strikes/:strikeId/decide', async (req, res) => {
278
275
  const user = requireRootAdmin(req, res);
279
276
  if (!user)
280
277
  return;
@@ -284,21 +281,20 @@ export function registerAgentGovernanceRoutes(app, deps) {
284
281
  const decision = String((req.body || {}).decision || '');
285
282
  if (!['approved', 'denied'].includes(decision))
286
283
  return void res.status(400).json({ error: 'decision 必须是 approved / denied' });
287
- const strike = db.prepare(`SELECT id, api_key, appeal_status FROM agent_strikes WHERE id = ?`).get(strikeId);
284
+ const strike = await dbOne(`SELECT id, api_key, appeal_status FROM agent_strikes WHERE id = ?`, [strikeId]);
288
285
  if (!strike)
289
286
  return void res.status(404).json({ error: 'strike 不存在' });
290
287
  if (strike.appeal_status !== 'pending')
291
288
  return void res.status(409).json({ error: `当前状态 ${strike.appeal_status} 不可裁决` });
292
- db.prepare(`UPDATE agent_strikes SET appeal_status = ?, appeal_decided_by = ?, appeal_decided_at = datetime('now') WHERE id = ?`)
293
- .run(decision, user.id, strikeId);
289
+ await dbRun(`UPDATE agent_strikes SET appeal_status = ?, appeal_decided_by = ?, appeal_decided_at = datetime('now') WHERE id = ?`, [decision, user.id, strikeId]);
294
290
  invalidateAgentBlockedCache(strike.api_key);
295
291
  // P1 fix 5.3: appeal approved → 恢复因 strike 自动停用的 skills
296
292
  if (decision === 'approved') {
297
293
  try {
298
- const uRow = db.prepare(`SELECT id FROM users WHERE api_key = ?`).get(strike.api_key);
294
+ const uRow = await dbOne(`SELECT id FROM users WHERE api_key = ?`, [strike.api_key]);
299
295
  if (uRow) {
300
- const r = db.prepare(`UPDATE skills SET active = 1, disabled_by_strike_at = NULL
301
- WHERE seller_id = ? AND disabled_by_strike_at IS NOT NULL AND active = 0`).run(uRow.id);
296
+ const r = await dbRun(`UPDATE skills SET active = 1, disabled_by_strike_at = NULL
297
+ WHERE seller_id = ? AND disabled_by_strike_at IS NOT NULL AND active = 0`, [uRow.id]);
302
298
  if (r.changes > 0)
303
299
  console.log(`[appeal approved→skill] restored ${r.changes} skills for ${uRow.id}`);
304
300
  }
@@ -310,17 +306,17 @@ export function registerAgentGovernanceRoutes(app, deps) {
310
306
  res.json({ ok: true, decision });
311
307
  });
312
308
  // Admin: 列出待审 strike 申诉
313
- app.get('/api/admin/agent-strikes/pending', (req, res) => {
309
+ app.get('/api/admin/agent-strikes/pending', async (req, res) => {
314
310
  const user = requireRootAdmin(req, res);
315
311
  if (!user)
316
312
  return;
317
- const rows = db.prepare(`SELECT s.id, s.api_key, s.user_id, u.handle, s.severity, s.reason_code, s.reason_detail, s.issued_at, s.appeal_reason
313
+ const rows = await dbAll(`SELECT s.id, s.api_key, s.user_id, u.handle, s.severity, s.reason_code, s.reason_detail, s.issued_at, s.appeal_reason
318
314
  FROM agent_strikes s JOIN users u ON u.id = s.user_id
319
- WHERE s.appeal_status = 'pending' ORDER BY s.id DESC LIMIT 100`).all();
315
+ WHERE s.appeal_status = 'pending' ORDER BY s.id DESC LIMIT 100`);
320
316
  res.json({ items: rows });
321
317
  });
322
318
  // P1 fix 4.3: admin 主动 issue strike
323
- app.post('/api/admin/agent-strikes/issue', (req, res) => {
319
+ app.post('/api/admin/agent-strikes/issue', async (req, res) => {
324
320
  const adminUser = requireRootAdmin(req, res);
325
321
  if (!adminUser)
326
322
  return;
@@ -328,7 +324,7 @@ export function registerAgentGovernanceRoutes(app, deps) {
328
324
  const apiKey = String(b.api_key || '').trim();
329
325
  if (apiKey.length < 8)
330
326
  return void res.status(400).json({ error: 'api_key 必填' });
331
- const targetUser = db.prepare(`SELECT id, handle FROM users WHERE api_key = ?`).get(apiKey);
327
+ const targetUser = await dbOne(`SELECT id, handle FROM users WHERE api_key = ?`, [apiKey]);
332
328
  if (!targetUser)
333
329
  return void res.status(404).json({ error: '未找到该 api_key' });
334
330
  const reasonCode = String(b.reason_code || '').trim().slice(0, 40);
@@ -349,7 +345,7 @@ export function registerAgentGovernanceRoutes(app, deps) {
349
345
  res.json({ ok: true, target_handle: targetUser.handle, ...result });
350
346
  });
351
347
  // bilateral attestation(用户批准某 agent 的 scope)
352
- app.post('/api/me/agents/attestations', (req, res) => {
348
+ app.post('/api/me/agents/attestations', async (req, res) => {
353
349
  const user = auth(req, res);
354
350
  if (!user)
355
351
  return;
@@ -357,7 +353,7 @@ export function registerAgentGovernanceRoutes(app, deps) {
357
353
  const apiKey = String(b.api_key || '');
358
354
  if (!apiKey)
359
355
  return void res.status(400).json({ error: 'api_key 必填' });
360
- const decl = db.prepare(`SELECT declared_scope, operator_name, purpose FROM agent_declarations WHERE api_key = ? AND revoked_at IS NULL`).get(apiKey);
356
+ const decl = await dbOne(`SELECT declared_scope, operator_name, purpose FROM agent_declarations WHERE api_key = ? AND revoked_at IS NULL`, [apiKey]);
361
357
  if (!decl)
362
358
  return void res.status(404).json({ error: '该 agent 未声明 / 已撤销,无法授权' });
363
359
  let approvedScopeJson;
@@ -373,14 +369,14 @@ export function registerAgentGovernanceRoutes(app, deps) {
373
369
  const spendCapPerOrder = b.spend_cap_per_order != null ? Math.max(0, Number(b.spend_cap_per_order)) : null;
374
370
  const spendCapDaily = b.spend_cap_daily != null ? Math.max(0, Number(b.spend_cap_daily)) : null;
375
371
  const id = generateId('aat');
376
- db.prepare(`INSERT INTO agent_attestations (id, api_key, user_id, approved_scope, spend_cap_per_order, spend_cap_daily)
372
+ await dbRun(`INSERT INTO agent_attestations (id, api_key, user_id, approved_scope, spend_cap_per_order, spend_cap_daily)
377
373
  VALUES (?,?,?,?,?,?)
378
374
  ON CONFLICT(api_key, user_id) DO UPDATE SET
379
375
  approved_scope = excluded.approved_scope,
380
376
  spend_cap_per_order = excluded.spend_cap_per_order,
381
377
  spend_cap_daily = excluded.spend_cap_daily,
382
378
  revoked_at = NULL,
383
- granted_at = datetime('now')`).run(id, apiKey, user.id, approvedScopeJson, spendCapPerOrder, spendCapDaily);
379
+ granted_at = datetime('now')`, [id, apiKey, user.id, approvedScopeJson, spendCapPerOrder, spendCapDaily]);
384
380
  res.json({ ok: true });
385
381
  });
386
382
  }