co-maintainer 0.4.0-beta.1

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 (294) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -0
  3. package/dist/main.d.ts +2 -0
  4. package/dist/main.js +9 -0
  5. package/dist/mod.d.ts +4 -0
  6. package/dist/mod.js +2 -0
  7. package/dist/package.json +54 -0
  8. package/dist/src/ai/batch.d.ts +16 -0
  9. package/dist/src/ai/batch.js +117 -0
  10. package/dist/src/ai/fake.d.ts +7 -0
  11. package/dist/src/ai/fake.js +40 -0
  12. package/dist/src/ai/hetzner.d.ts +16 -0
  13. package/dist/src/ai/hetzner.js +69 -0
  14. package/dist/src/ai/mermaid.d.ts +27 -0
  15. package/dist/src/ai/mermaid.js +457 -0
  16. package/dist/src/ai/mermaid_loop.d.ts +12 -0
  17. package/dist/src/ai/mermaid_loop.js +100 -0
  18. package/dist/src/ai/openrouter.d.ts +9 -0
  19. package/dist/src/ai/openrouter.js +29 -0
  20. package/dist/src/ai/provider.d.ts +4 -0
  21. package/dist/src/ai/provider.js +63 -0
  22. package/dist/src/cli/args.d.ts +5 -0
  23. package/dist/src/cli/args.js +252 -0
  24. package/dist/src/cli/commands/probe.d.ts +2 -0
  25. package/dist/src/cli/commands/probe.js +122 -0
  26. package/dist/src/cli/commands/review.d.ts +3 -0
  27. package/dist/src/cli/commands/review.js +84 -0
  28. package/dist/src/cli/commands/serve.d.ts +18 -0
  29. package/dist/src/cli/commands/serve.js +183 -0
  30. package/dist/src/cli/commands/set.d.ts +7 -0
  31. package/dist/src/cli/commands/set.js +171 -0
  32. package/dist/src/cli/main.d.ts +1 -0
  33. package/dist/src/cli/main.js +27 -0
  34. package/dist/src/cli/prompt.d.ts +4 -0
  35. package/dist/src/cli/prompt.js +26 -0
  36. package/dist/src/cli/review_args.d.ts +28 -0
  37. package/dist/src/cli/review_args.js +84 -0
  38. package/dist/src/cli/review_output.d.ts +2 -0
  39. package/dist/src/cli/review_output.js +10 -0
  40. package/dist/src/cli/review_result.d.ts +81 -0
  41. package/dist/src/cli/review_result.js +281 -0
  42. package/dist/src/config.d.ts +108 -0
  43. package/dist/src/config.js +138 -0
  44. package/dist/src/git/default_branch.d.ts +5 -0
  45. package/dist/src/git/default_branch.js +22 -0
  46. package/dist/src/github/app.d.ts +43 -0
  47. package/dist/src/github/app.js +120 -0
  48. package/dist/src/github/client.d.ts +17 -0
  49. package/dist/src/github/client.js +66 -0
  50. package/dist/src/github/collect.d.ts +7 -0
  51. package/dist/src/github/collect.js +368 -0
  52. package/dist/src/github/gh.d.ts +8 -0
  53. package/dist/src/github/gh.js +63 -0
  54. package/dist/src/github/jwt.d.ts +12 -0
  55. package/dist/src/github/jwt.js +84 -0
  56. package/dist/src/github/oauth.d.ts +9 -0
  57. package/dist/src/github/oauth.js +51 -0
  58. package/dist/src/github/pat.d.ts +10 -0
  59. package/dist/src/github/pat.js +60 -0
  60. package/dist/src/knowledge/facts.d.ts +3 -0
  61. package/dist/src/knowledge/facts.js +325 -0
  62. package/dist/src/knowledge/guide.d.ts +6 -0
  63. package/dist/src/knowledge/guide.js +82 -0
  64. package/dist/src/knowledge/probe.d.ts +12 -0
  65. package/dist/src/knowledge/probe.js +144 -0
  66. package/dist/src/knowledge/sections.d.ts +2 -0
  67. package/dist/src/knowledge/sections.js +13 -0
  68. package/dist/src/knowledge/skill.d.ts +10 -0
  69. package/dist/src/knowledge/skill.js +106 -0
  70. package/dist/src/knowledge/synthesis.d.ts +7 -0
  71. package/dist/src/knowledge/synthesis.js +315 -0
  72. package/dist/src/knowledge/types.d.ts +49 -0
  73. package/dist/src/knowledge/types.js +1 -0
  74. package/dist/src/knowledge/validate.d.ts +6 -0
  75. package/dist/src/knowledge/validate.js +134 -0
  76. package/dist/src/local/carry_over_store.d.ts +18 -0
  77. package/dist/src/local/carry_over_store.js +40 -0
  78. package/dist/src/local/codegraph_exclude.d.ts +3 -0
  79. package/dist/src/local/codegraph_exclude.js +26 -0
  80. package/dist/src/local/codegraph_prepare.d.ts +17 -0
  81. package/dist/src/local/codegraph_prepare.js +51 -0
  82. package/dist/src/local/git_ops.d.ts +12 -0
  83. package/dist/src/local/git_ops.js +98 -0
  84. package/dist/src/local/git_parse.d.ts +27 -0
  85. package/dist/src/local/git_parse.js +131 -0
  86. package/dist/src/local/git_revision.d.ts +15 -0
  87. package/dist/src/local/git_revision.js +141 -0
  88. package/dist/src/local/git_untracked.d.ts +21 -0
  89. package/dist/src/local/git_untracked.js +79 -0
  90. package/dist/src/local/review_local.d.ts +4 -0
  91. package/dist/src/local/review_local.js +273 -0
  92. package/dist/src/local/review_lock.d.ts +7 -0
  93. package/dist/src/local/review_lock.js +45 -0
  94. package/dist/src/pr/checkout.d.ts +25 -0
  95. package/dist/src/pr/checkout.js +136 -0
  96. package/dist/src/pr/codegraph_tool_args.d.ts +8 -0
  97. package/dist/src/pr/codegraph_tool_args.js +34 -0
  98. package/dist/src/pr/codegraph_tools.d.ts +15 -0
  99. package/dist/src/pr/codegraph_tools.js +408 -0
  100. package/dist/src/pr/diff_summary.d.ts +6 -0
  101. package/dist/src/pr/diff_summary.js +52 -0
  102. package/dist/src/pr/findings.d.ts +14 -0
  103. package/dist/src/pr/findings.js +94 -0
  104. package/dist/src/pr/github_revision.d.ts +4 -0
  105. package/dist/src/pr/github_revision.js +39 -0
  106. package/dist/src/pr/hunks.d.ts +18 -0
  107. package/dist/src/pr/hunks.js +73 -0
  108. package/dist/src/pr/reviewer.d.ts +40 -0
  109. package/dist/src/pr/reviewer.js +582 -0
  110. package/dist/src/pr/rounds.d.ts +17 -0
  111. package/dist/src/pr/rounds.js +43 -0
  112. package/dist/src/pr/scope.d.ts +35 -0
  113. package/dist/src/pr/scope.js +84 -0
  114. package/dist/src/pr/snapshot.d.ts +11 -0
  115. package/dist/src/pr/snapshot.js +1 -0
  116. package/dist/src/pr/suggestion.d.ts +17 -0
  117. package/dist/src/pr/suggestion.js +70 -0
  118. package/dist/src/remote/client.d.ts +4 -0
  119. package/dist/src/remote/client.js +199 -0
  120. package/dist/src/remote/client_tools.d.ts +13 -0
  121. package/dist/src/remote/client_tools.js +30 -0
  122. package/dist/src/remote/fixtures.d.ts +1 -0
  123. package/dist/src/remote/fixtures.js +30 -0
  124. package/dist/src/remote/revision_from_submit.d.ts +2 -0
  125. package/dist/src/remote/revision_from_submit.js +26 -0
  126. package/dist/src/remote/schema.d.ts +14 -0
  127. package/dist/src/remote/schema.js +30 -0
  128. package/dist/src/remote/server/body.d.ts +18 -0
  129. package/dist/src/remote/server/body.js +73 -0
  130. package/dist/src/remote/server/routes.d.ts +1 -0
  131. package/dist/src/remote/server/routes.js +150 -0
  132. package/dist/src/remote/server/sessions.d.ts +16 -0
  133. package/dist/src/remote/server/sessions.js +48 -0
  134. package/dist/src/remote/server/sync.d.ts +3 -0
  135. package/dist/src/remote/server/sync.js +90 -0
  136. package/dist/src/remote/tool_bridge.d.ts +15 -0
  137. package/dist/src/remote/tool_bridge.js +84 -0
  138. package/dist/src/remote/validate.d.ts +8 -0
  139. package/dist/src/remote/validate.js +248 -0
  140. package/dist/src/review/carry_over.d.ts +63 -0
  141. package/dist/src/review/carry_over.js +372 -0
  142. package/dist/src/review/engine.d.ts +13 -0
  143. package/dist/src/review/engine.js +4 -0
  144. package/dist/src/review/guides.d.ts +12 -0
  145. package/dist/src/review/guides.js +44 -0
  146. package/dist/src/review/revision.d.ts +26 -0
  147. package/dist/src/review/revision.js +57 -0
  148. package/dist/src/server/api/activity.d.ts +1 -0
  149. package/dist/src/server/api/activity.js +12 -0
  150. package/dist/src/server/api/analytics.d.ts +1 -0
  151. package/dist/src/server/api/analytics.js +9 -0
  152. package/dist/src/server/api/installations.d.ts +4 -0
  153. package/dist/src/server/api/installations.js +24 -0
  154. package/dist/src/server/api/jobs.d.ts +1 -0
  155. package/dist/src/server/api/jobs.js +86 -0
  156. package/dist/src/server/api/json_body.d.ts +1 -0
  157. package/dist/src/server/api/json_body.js +14 -0
  158. package/dist/src/server/api/remote_tokens.d.ts +1 -0
  159. package/dist/src/server/api/remote_tokens.js +71 -0
  160. package/dist/src/server/api/repos.d.ts +4 -0
  161. package/dist/src/server/api/repos.js +136 -0
  162. package/dist/src/server/api/settings.d.ts +1 -0
  163. package/dist/src/server/api/settings.js +210 -0
  164. package/dist/src/server/app.d.ts +27 -0
  165. package/dist/src/server/app.js +147 -0
  166. package/dist/src/server/assets.d.ts +3 -0
  167. package/dist/src/server/assets.js +21 -0
  168. package/dist/src/server/auth.d.ts +44 -0
  169. package/dist/src/server/auth.js +188 -0
  170. package/dist/src/server/errors.d.ts +8 -0
  171. package/dist/src/server/errors.js +10 -0
  172. package/dist/src/server/http.d.ts +8 -0
  173. package/dist/src/server/http.js +117 -0
  174. package/dist/src/server/logo.d.ts +1 -0
  175. package/dist/src/server/logo.js +2 -0
  176. package/dist/src/server/pages/activity.d.ts +4 -0
  177. package/dist/src/server/pages/activity.js +200 -0
  178. package/dist/src/server/pages/add_repo.d.ts +9 -0
  179. package/dist/src/server/pages/add_repo.js +56 -0
  180. package/dist/src/server/pages/analytics.d.ts +2 -0
  181. package/dist/src/server/pages/analytics.js +193 -0
  182. package/dist/src/server/pages/client.d.ts +2 -0
  183. package/dist/src/server/pages/client.js +2 -0
  184. package/dist/src/server/pages/home.d.ts +16 -0
  185. package/dist/src/server/pages/home.js +87 -0
  186. package/dist/src/server/pages/knowledge.d.ts +2 -0
  187. package/dist/src/server/pages/knowledge.js +64 -0
  188. package/dist/src/server/pages/layout.d.ts +32 -0
  189. package/dist/src/server/pages/layout.js +230 -0
  190. package/dist/src/server/pages/pr.d.ts +2 -0
  191. package/dist/src/server/pages/pr.js +109 -0
  192. package/dist/src/server/pages/repo.d.ts +2 -0
  193. package/dist/src/server/pages/repo.js +92 -0
  194. package/dist/src/server/pages/repo_prs.d.ts +2 -0
  195. package/dist/src/server/pages/repo_prs.js +75 -0
  196. package/dist/src/server/pages/repo_remote.d.ts +2 -0
  197. package/dist/src/server/pages/repo_remote.js +38 -0
  198. package/dist/src/server/pages/repo_settings.d.ts +3 -0
  199. package/dist/src/server/pages/repo_settings.js +127 -0
  200. package/dist/src/server/pages/router.d.ts +17 -0
  201. package/dist/src/server/pages/router.js +312 -0
  202. package/dist/src/server/pages/settings.d.ts +2 -0
  203. package/dist/src/server/pages/settings.js +337 -0
  204. package/dist/src/server/pages/setup.d.ts +5 -0
  205. package/dist/src/server/pages/setup.js +38 -0
  206. package/dist/src/server/pages/styles.d.ts +2 -0
  207. package/dist/src/server/pages/styles.js +2 -0
  208. package/dist/src/server/webhook/index.d.ts +1 -0
  209. package/dist/src/server/webhook/index.js +64 -0
  210. package/dist/src/server/webhook/signature.d.ts +7 -0
  211. package/dist/src/server/webhook/signature.js +41 -0
  212. package/dist/src/services/credentials.d.ts +19 -0
  213. package/dist/src/services/credentials.js +146 -0
  214. package/dist/src/services/dashboard.d.ts +165 -0
  215. package/dist/src/services/dashboard.js +200 -0
  216. package/dist/src/services/drift.d.ts +13 -0
  217. package/dist/src/services/drift.js +76 -0
  218. package/dist/src/services/jobs.d.ts +34 -0
  219. package/dist/src/services/jobs.js +222 -0
  220. package/dist/src/services/remote_review.d.ts +10 -0
  221. package/dist/src/services/remote_review.js +274 -0
  222. package/dist/src/services/remote_review_abort.d.ts +3 -0
  223. package/dist/src/services/remote_review_abort.js +11 -0
  224. package/dist/src/services/remote_token_jobs.d.ts +1 -0
  225. package/dist/src/services/remote_token_jobs.js +20 -0
  226. package/dist/src/services/remote_tokens.d.ts +13 -0
  227. package/dist/src/services/remote_tokens.js +57 -0
  228. package/dist/src/services/replies.d.ts +7 -0
  229. package/dist/src/services/replies.js +248 -0
  230. package/dist/src/services/review.d.ts +35 -0
  231. package/dist/src/services/review.js +671 -0
  232. package/dist/src/services/setup.d.ts +29 -0
  233. package/dist/src/services/setup.js +364 -0
  234. package/dist/src/services/webhook.d.ts +22 -0
  235. package/dist/src/services/webhook.js +401 -0
  236. package/dist/src/store/app_db.d.ts +10 -0
  237. package/dist/src/store/app_db.js +85 -0
  238. package/dist/src/store/cache_db.d.ts +4 -0
  239. package/dist/src/store/cache_db.js +63 -0
  240. package/dist/src/store/deliveries.d.ts +12 -0
  241. package/dist/src/store/deliveries.js +23 -0
  242. package/dist/src/store/drift.d.ts +4 -0
  243. package/dist/src/store/drift.js +22 -0
  244. package/dist/src/store/findings.d.ts +31 -0
  245. package/dist/src/store/findings.js +64 -0
  246. package/dist/src/store/installations.d.ts +9 -0
  247. package/dist/src/store/installations.js +35 -0
  248. package/dist/src/store/job_logs.d.ts +5 -0
  249. package/dist/src/store/job_logs.js +17 -0
  250. package/dist/src/store/jobs.d.ts +27 -0
  251. package/dist/src/store/jobs.js +86 -0
  252. package/dist/src/store/migrations.d.ts +5 -0
  253. package/dist/src/store/migrations.js +258 -0
  254. package/dist/src/store/remote_review_inputs.d.ts +11 -0
  255. package/dist/src/store/remote_review_inputs.js +25 -0
  256. package/dist/src/store/remote_tokens.d.ts +10 -0
  257. package/dist/src/store/remote_tokens.js +45 -0
  258. package/dist/src/store/replies.d.ts +24 -0
  259. package/dist/src/store/replies.js +79 -0
  260. package/dist/src/store/repos.d.ts +11 -0
  261. package/dist/src/store/repos.js +59 -0
  262. package/dist/src/store/reviews.d.ts +86 -0
  263. package/dist/src/store/reviews.js +220 -0
  264. package/dist/src/store/rows.d.ts +179 -0
  265. package/dist/src/store/rows.js +4 -0
  266. package/dist/src/store/sessions.d.ts +8 -0
  267. package/dist/src/store/sessions.js +34 -0
  268. package/dist/src/store/skill_state.d.ts +3 -0
  269. package/dist/src/store/skill_state.js +24 -0
  270. package/dist/src/store/sqlite.d.ts +39 -0
  271. package/dist/src/store/sqlite.js +77 -0
  272. package/dist/src/store/subjects.d.ts +15 -0
  273. package/dist/src/store/subjects.js +78 -0
  274. package/dist/src/tools/codegraph.d.ts +58 -0
  275. package/dist/src/tools/codegraph.js +165 -0
  276. package/dist/src/tools/codegraph_exec.d.ts +13 -0
  277. package/dist/src/tools/codegraph_exec.js +66 -0
  278. package/dist/src/types.d.ts +89 -0
  279. package/dist/src/types.js +1 -0
  280. package/dist/src/util/atomic.d.ts +4 -0
  281. package/dist/src/util/atomic.js +78 -0
  282. package/dist/src/util/keyed_lock.d.ts +3 -0
  283. package/dist/src/util/keyed_lock.js +33 -0
  284. package/dist/src/util/log.d.ts +11 -0
  285. package/dist/src/util/log.js +42 -0
  286. package/dist/src/util/redact.d.ts +5 -0
  287. package/dist/src/util/redact.js +30 -0
  288. package/dist/src/util/runtime.d.ts +90 -0
  289. package/dist/src/util/runtime.js +206 -0
  290. package/dist/src/util/time.d.ts +9 -0
  291. package/dist/src/util/time.js +36 -0
  292. package/dist/src/version.d.ts +1 -0
  293. package/dist/src/version.js +5 -0
  294. package/package.json +54 -0
@@ -0,0 +1,79 @@
1
+ import { getAppDb } from "./app_db.js";
2
+ import { insertJob } from "./jobs.js";
3
+ import { nowIso } from "../util/time.js";
4
+ export function createReplyRequestAndJob(input) {
5
+ const existing = findReplyRequest(input.repo, input.sourceKind, input.sourceCommentId);
6
+ if (existing)
7
+ return { request: existing, created: false };
8
+ const db = getAppDb();
9
+ const requestId = crypto.randomUUID();
10
+ const jobId = crypto.randomUUID();
11
+ const now = nowIso();
12
+ const queueKey = `reply:${input.repo}:${input.sourceKind}:${input.sourceCommentId}`;
13
+ db.exec("BEGIN");
14
+ try {
15
+ db.prepare(`INSERT INTO reply_requests
16
+ (id, repo, pr_number, source_kind, source_comment_id,
17
+ target_comment_id, source_body, source_author, source_path,
18
+ source_line, source_commit_id, status, job_id, created_at, updated_at)
19
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'queued', ?, ?, ?)`).run(requestId, input.repo, input.prNumber, input.sourceKind, input.sourceCommentId, input.targetCommentId ?? null, input.sourceBody, input.sourceAuthor ?? null, input.sourcePath ?? null, input.sourceLine ?? null, input.sourceCommitId ?? null, jobId, now, now);
20
+ insertJob({
21
+ id: jobId,
22
+ type: "reply",
23
+ repo: input.repo,
24
+ prNumber: input.prNumber,
25
+ args: { requestId },
26
+ deliveryId: input.deliveryId,
27
+ queueKey,
28
+ });
29
+ db.exec("COMMIT");
30
+ }
31
+ catch (error) {
32
+ db.exec("ROLLBACK");
33
+ throw error;
34
+ }
35
+ return { request: getReplyRequest(requestId), created: true };
36
+ }
37
+ export function getReplyRequest(id) {
38
+ return getAppDb()
39
+ .prepare(`SELECT * FROM reply_requests WHERE id = ?`)
40
+ .get(id);
41
+ }
42
+ export function findReplyRequest(repo, sourceKind, sourceCommentId) {
43
+ return getAppDb()
44
+ .prepare(`SELECT * FROM reply_requests
45
+ WHERE repo = ? AND source_kind = ? AND source_comment_id = ?`)
46
+ .get(repo, sourceKind, sourceCommentId);
47
+ }
48
+ export function findReplyByPostedComment(repo, prNumber, commentId) {
49
+ return getAppDb()
50
+ .prepare(`SELECT * FROM reply_requests
51
+ WHERE repo = ? AND pr_number = ? AND posted_comment_id = ?
52
+ ORDER BY created_at DESC LIMIT 1`)
53
+ .get(repo, prNumber, commentId);
54
+ }
55
+ export function setReplyStatus(id, status, patch = {}) {
56
+ const attempts = status === "generating" || status === "posting" ? 1 : 0;
57
+ getAppDb()
58
+ .prepare(`UPDATE reply_requests SET
59
+ status = ?,
60
+ answer_md = COALESCE(?, answer_md),
61
+ posted_comment_id = COALESCE(?, posted_comment_id),
62
+ error = COALESCE(?, error),
63
+ attempts = attempts + ?,
64
+ updated_at = ?
65
+ WHERE id = ?`)
66
+ .run(status, patch.answer_md ?? null, patch.posted_comment_id ?? null, patch.error ?? null, attempts, nowIso(), id);
67
+ }
68
+ export function setReplyJobId(id, jobId) {
69
+ getAppDb()
70
+ .prepare(`UPDATE reply_requests SET job_id = ?, updated_at = ? WHERE id = ?`)
71
+ .run(jobId, nowIso(), id);
72
+ }
73
+ export function listRecoverableReplies() {
74
+ return getAppDb()
75
+ .prepare(`SELECT * FROM reply_requests
76
+ WHERE status IN ('queued', 'generating', 'ready', 'posting')
77
+ ORDER BY created_at`)
78
+ .all();
79
+ }
@@ -0,0 +1,11 @@
1
+ import type { RepoRow } from "./rows.ts";
2
+ export declare function activateRepo(fullName: string, installationId: number | undefined): void;
3
+ export declare function deactivateRepo(fullName: string): void;
4
+ export declare function deactivateReposForInstallation(installationId: number): void;
5
+ export declare function updateRepoSettings(fullName: string, patch: Partial<Pick<RepoRow, "auto_review" | "review_scope" | "skip_drafts" | "skip_bots" | "use_codegraph">>): void;
6
+ export declare function markKnowledgeBuilt(fullName: string, baseSha: string): void;
7
+ export declare function setInstallationId(fullName: string, installationId: number): void;
8
+ export declare function getRepo(fullName: string): RepoRow | undefined;
9
+ /** Case-insensitive match on `repos.full_name` (plan §14.4). */
10
+ export declare function findRepoByFullName(fullName: string): RepoRow | undefined;
11
+ export declare function listActiveRepos(): RepoRow[];
@@ -0,0 +1,59 @@
1
+ import { getAppDb } from "./app_db.js";
2
+ import { deleteDrift } from "./drift.js";
3
+ import { nowIso } from "../util/time.js";
4
+ export function activateRepo(fullName, installationId) {
5
+ getAppDb()
6
+ .prepare(`INSERT INTO repos (full_name, installation_id, active, auto_review, created_at)
7
+ VALUES (?, ?, 1, 1, ?)
8
+ ON CONFLICT(full_name) DO UPDATE SET
9
+ installation_id = excluded.installation_id, active = 1`)
10
+ .run(fullName, installationId ?? null, nowIso());
11
+ }
12
+ export function deactivateRepo(fullName) {
13
+ getAppDb()
14
+ .prepare(`UPDATE repos SET active = 0 WHERE full_name = ?`)
15
+ .run(fullName);
16
+ }
17
+ export function deactivateReposForInstallation(installationId) {
18
+ getAppDb()
19
+ .prepare(`UPDATE repos SET active = 0 WHERE installation_id = ?`)
20
+ .run(installationId);
21
+ }
22
+ export function updateRepoSettings(fullName, patch) {
23
+ const fields = Object.keys(patch);
24
+ if (fields.length === 0)
25
+ return;
26
+ const set = fields.map((field) => `${field} = ?`).join(", ");
27
+ getAppDb()
28
+ .prepare(`UPDATE repos SET ${set} WHERE full_name = ?`)
29
+ .run(...fields.map((field) => patch[field]), fullName);
30
+ }
31
+ export function markKnowledgeBuilt(fullName, baseSha) {
32
+ getAppDb()
33
+ .prepare(`UPDATE repos SET knowledge_built_at = ?, knowledge_base_sha = ? WHERE full_name = ?`)
34
+ .run(nowIso(), baseSha, fullName);
35
+ // The stored drift counts how far the repository moved past the previous
36
+ // baseline, so a rebuild makes them meaningless rather than merely stale.
37
+ deleteDrift(fullName);
38
+ }
39
+ export function setInstallationId(fullName, installationId) {
40
+ getAppDb()
41
+ .prepare(`UPDATE repos SET installation_id = ? WHERE full_name = ?`)
42
+ .run(installationId, fullName);
43
+ }
44
+ export function getRepo(fullName) {
45
+ return getAppDb()
46
+ .prepare(`SELECT * FROM repos WHERE full_name = ?`)
47
+ .get(fullName);
48
+ }
49
+ /** Case-insensitive match on `repos.full_name` (plan §14.4). */
50
+ export function findRepoByFullName(fullName) {
51
+ return getAppDb()
52
+ .prepare(`SELECT * FROM repos WHERE lower(full_name) = lower(?)`)
53
+ .get(fullName);
54
+ }
55
+ export function listActiveRepos() {
56
+ return getAppDb()
57
+ .prepare(`SELECT * FROM repos WHERE active = 1 ORDER BY full_name`)
58
+ .all();
59
+ }
@@ -0,0 +1,86 @@
1
+ import type { ReviewRow } from "./rows.ts";
2
+ export declare function insertRemoteReview(row: {
3
+ id: string;
4
+ subjectId: string;
5
+ repo: string;
6
+ branch: string;
7
+ tokenId: string;
8
+ tokenName: string;
9
+ jobId: string;
10
+ scope: string;
11
+ model: string;
12
+ }): void;
13
+ export declare function insertReview(row: {
14
+ id: string;
15
+ repo: string;
16
+ prNumber: number;
17
+ jobId: string;
18
+ headSha: string;
19
+ baseSha: string;
20
+ scope: string;
21
+ model: string;
22
+ trigger?: string;
23
+ round?: number;
24
+ subjectId?: string;
25
+ guideBuiltAt?: string | null;
26
+ }): void;
27
+ /** Written immediately before and after the `POST .../reviews` call, with
28
+ * nothing else run between either write and the call. */
29
+ export declare function setReviewStatus(id: string, status: ReviewRow["status"], patch?: Partial<Pick<ReviewRow, "posted_review_id" | "findings_count" | "tokens_in" | "tokens_out" | "cost" | "duration_ms" | "check_run_id" | "posted_fallback" | "open_count" | "closed_count" | "subject_id" | "guide_built_at">>): void;
30
+ export declare function setReviewScope(id: string, scope: string, baseSha: string): void;
31
+ export declare function getReview(id: string): ReviewRow | undefined;
32
+ export declare function getReviewByJobId(jobId: string): ReviewRow | undefined;
33
+ /** Newest round first. The PR detail page reads this directly. */
34
+ export declare function listReviewsForPr(repo: string, prNumber: number): ReviewRow[];
35
+ export declare function latestPostedReview(repo: string, prNumber: number): ReviewRow | undefined;
36
+ export type ReviewStats = {
37
+ reviews: number;
38
+ pullRequests: number;
39
+ findings: number;
40
+ cost: number;
41
+ tokensIn: number;
42
+ tokensOut: number;
43
+ failed: number;
44
+ avgDurationMs: number;
45
+ };
46
+ /** `untilIso` is exclusive, which is what lets the trend row ask for the
47
+ * window before the one on screen without double counting its edge. */
48
+ export declare function reviewStats(sinceIso: string, repo?: string, untilIso?: string): ReviewStats;
49
+ export declare function reviewStatsByRepo(sinceIso: string): (ReviewStats & {
50
+ repo: string;
51
+ })[];
52
+ export declare function reviewStatsByDay(sinceIso: string): {
53
+ day: string;
54
+ reviews: number;
55
+ findings: number;
56
+ cost: number;
57
+ }[];
58
+ export declare function reviewStatsByModel(sinceIso: string): (ReviewStats & {
59
+ model: string;
60
+ })[];
61
+ export declare function listRecentReviews(limit: number, offset?: number): ReviewRow[];
62
+ export declare function countReviews(): number;
63
+ export type PullSummary = {
64
+ pr_number: number;
65
+ last_reviewed: string;
66
+ findings: number;
67
+ cost: number;
68
+ review_count: number;
69
+ };
70
+ export declare function listPullSummaries(repo: string, limit: number, offset: number): {
71
+ items: PullSummary[];
72
+ total: number;
73
+ };
74
+ export declare function listRemoteReviewsForRepo(repo: string, limit: number, offset?: number, sinceIso?: string): ReviewRow[];
75
+ export declare function remoteTokenUsageSince(tokenId: string, sinceIso: string): {
76
+ reviews: number;
77
+ cost: number;
78
+ };
79
+ export declare function reviewStatsByRemoteToken(sinceIso: string): {
80
+ tokenId: string;
81
+ tokenName: string;
82
+ reviews: number;
83
+ cost: number;
84
+ findings: number;
85
+ }[];
86
+ export declare function listLatestReviewsForRepo(repo: string, limit: number): ReviewRow[];
@@ -0,0 +1,220 @@
1
+ import { getAppDb } from "./app_db.js";
2
+ import { nowIso } from "../util/time.js";
3
+ export function insertRemoteReview(row) {
4
+ getAppDb()
5
+ .prepare(`INSERT INTO reviews
6
+ (id, kind, subject_id, repo, pr_number, branch, token_id, token_name,
7
+ job_id, head_sha, base_sha, scope, model, status, created_at, round)
8
+ VALUES (?, 'remote', ?, ?, NULL, ?, ?, ?, ?, NULL, NULL, ?, ?, 'queued', ?, 1)`)
9
+ .run(row.id, row.subjectId, row.repo, row.branch, row.tokenId, row.tokenName, row.jobId, row.scope, row.model, nowIso());
10
+ }
11
+ export function insertReview(row) {
12
+ getAppDb()
13
+ .prepare(`INSERT INTO reviews
14
+ (id, kind, subject_id, repo, pr_number, job_id, head_sha, base_sha, scope, model,
15
+ status, created_at, round, "trigger", guide_built_at)
16
+ VALUES (?, 'pr', ?, ?, ?, ?, ?, ?, ?, ?, 'drafting', ?, ?, ?, ?)`)
17
+ .run(row.id, row.subjectId ?? null, row.repo, row.prNumber, row.jobId, row.headSha, row.baseSha, row.scope, row.model, nowIso(), row.round ?? 1, row.trigger ?? null, row.guideBuiltAt ?? null);
18
+ }
19
+ /** Written immediately before and after the `POST .../reviews` call, with
20
+ * nothing else run between either write and the call. */
21
+ export function setReviewStatus(id, status, patch = {}) {
22
+ getAppDb()
23
+ .prepare(`UPDATE reviews SET status = ?,
24
+ posted_review_id = COALESCE(?, posted_review_id),
25
+ findings_count = COALESCE(?, findings_count),
26
+ open_count = COALESCE(?, open_count),
27
+ closed_count = COALESCE(?, closed_count),
28
+ tokens_in = COALESCE(?, tokens_in),
29
+ tokens_out = COALESCE(?, tokens_out),
30
+ cost = COALESCE(?, cost),
31
+ duration_ms = COALESCE(?, duration_ms),
32
+ check_run_id = COALESCE(?, check_run_id),
33
+ posted_fallback = COALESCE(?, posted_fallback),
34
+ subject_id = COALESCE(?, subject_id),
35
+ guide_built_at = COALESCE(?, guide_built_at)
36
+ WHERE id = ?`)
37
+ .run(status, patch.posted_review_id ?? null, patch.findings_count ?? null, patch.open_count ?? null, patch.closed_count ?? null, patch.tokens_in ?? null, patch.tokens_out ?? null, patch.cost ?? null, patch.duration_ms ?? null, patch.check_run_id ?? null, patch.posted_fallback ?? null, patch.subject_id ?? null, patch.guide_built_at ?? null, id);
38
+ }
39
+ export function setReviewScope(id, scope, baseSha) {
40
+ getAppDb()
41
+ .prepare(`UPDATE reviews SET scope = ?, base_sha = ? WHERE id = ?`)
42
+ .run(scope, baseSha, id);
43
+ }
44
+ export function getReview(id) {
45
+ return getAppDb()
46
+ .prepare(`SELECT * FROM reviews WHERE id = ?`)
47
+ .get(id);
48
+ }
49
+ export function getReviewByJobId(jobId) {
50
+ return getAppDb()
51
+ .prepare(`SELECT * FROM reviews WHERE job_id = ? ORDER BY created_at DESC LIMIT 1`)
52
+ .get(jobId);
53
+ }
54
+ /** Newest round first. The PR detail page reads this directly. */
55
+ export function listReviewsForPr(repo, prNumber) {
56
+ return getAppDb()
57
+ .prepare(`SELECT * FROM reviews WHERE repo = ? AND pr_number = ? ORDER BY round DESC, created_at DESC`)
58
+ .all(repo, prNumber);
59
+ }
60
+ export function latestPostedReview(repo, prNumber) {
61
+ return getAppDb()
62
+ .prepare(`SELECT * FROM reviews
63
+ WHERE repo = ? AND pr_number = ? AND status = 'posted'
64
+ ORDER BY round DESC, created_at DESC LIMIT 1`)
65
+ .get(repo, prNumber);
66
+ }
67
+ /** Every aggregate the dashboard reads comes from this one column list, so a
68
+ * new figure lands on the totals, the per repo table and the trend window at
69
+ * the same time. */
70
+ const STATS_COLUMNS = `COUNT(*) AS reviews,
71
+ COUNT(DISTINCT pr_number) AS pullRequests,
72
+ COALESCE(SUM(findings_count), 0) AS findings,
73
+ COALESCE(SUM(cost), 0) AS cost,
74
+ COALESCE(SUM(tokens_in), 0) AS tokensIn,
75
+ COALESCE(SUM(tokens_out), 0) AS tokensOut,
76
+ COALESCE(SUM(status = 'failed'), 0) AS failed,
77
+ COALESCE(AVG(duration_ms), 0) AS avgDurationMs`;
78
+ function asStats(row) {
79
+ return {
80
+ reviews: Number(row?.reviews ?? 0),
81
+ pullRequests: Number(row?.pullRequests ?? 0),
82
+ findings: Number(row?.findings ?? 0),
83
+ cost: Number(row?.cost ?? 0),
84
+ tokensIn: Number(row?.tokensIn ?? 0),
85
+ tokensOut: Number(row?.tokensOut ?? 0),
86
+ failed: Number(row?.failed ?? 0),
87
+ avgDurationMs: Number(row?.avgDurationMs ?? 0),
88
+ };
89
+ }
90
+ /** `untilIso` is exclusive, which is what lets the trend row ask for the
91
+ * window before the one on screen without double counting its edge. */
92
+ export function reviewStats(sinceIso, repo, untilIso) {
93
+ const where = ["created_at >= ?", "kind = 'pr'"];
94
+ const params = [sinceIso];
95
+ if (untilIso) {
96
+ where.push("created_at < ?");
97
+ params.push(untilIso);
98
+ }
99
+ if (repo) {
100
+ where.push("repo = ?");
101
+ params.push(repo);
102
+ }
103
+ return asStats(getAppDb()
104
+ .prepare(`SELECT ${STATS_COLUMNS} FROM reviews WHERE ${where.join(" AND ")}`)
105
+ .get(...params));
106
+ }
107
+ export function reviewStatsByRepo(sinceIso) {
108
+ return getAppDb()
109
+ .prepare(`SELECT repo, ${STATS_COLUMNS}
110
+ FROM reviews WHERE created_at >= ?
111
+ GROUP BY repo
112
+ ORDER BY cost DESC`)
113
+ .all(sinceIso)
114
+ .map((row) => ({ ...asStats(row), repo: row.repo }));
115
+ }
116
+ export function reviewStatsByDay(sinceIso) {
117
+ return getAppDb()
118
+ .prepare(`SELECT substr(created_at, 1, 10) AS day,
119
+ COUNT(*) AS reviews,
120
+ COALESCE(SUM(findings_count), 0) AS findings,
121
+ COALESCE(SUM(cost), 0) AS cost
122
+ FROM reviews WHERE created_at >= ?
123
+ GROUP BY day
124
+ ORDER BY day`)
125
+ .all(sinceIso);
126
+ }
127
+ export function reviewStatsByModel(sinceIso) {
128
+ return getAppDb()
129
+ .prepare(`SELECT model, ${STATS_COLUMNS}
130
+ FROM reviews WHERE created_at >= ?
131
+ GROUP BY model
132
+ ORDER BY cost DESC`)
133
+ .all(sinceIso)
134
+ .map((row) => ({ ...asStats(row), model: row.model }));
135
+ }
136
+ export function listRecentReviews(limit, offset = 0) {
137
+ return getAppDb()
138
+ .prepare(`SELECT * FROM reviews ORDER BY created_at DESC LIMIT ? OFFSET ?`)
139
+ .all(limit, offset);
140
+ }
141
+ export function countReviews() {
142
+ return Number(getAppDb().prepare(`SELECT COUNT(*) AS n FROM reviews`).get()
143
+ ?.n ?? 0);
144
+ }
145
+ export function listPullSummaries(repo, limit, offset) {
146
+ const total = Number(getAppDb()
147
+ .prepare(`SELECT COUNT(DISTINCT pr_number) AS n FROM reviews WHERE repo = ?`)
148
+ .get(repo)?.n ?? 0);
149
+ const items = getAppDb()
150
+ .prepare(`SELECT pr_number,
151
+ MAX(created_at) AS last_reviewed,
152
+ COALESCE(SUM(findings_count), 0) AS findings,
153
+ COALESCE(SUM(cost), 0) AS cost,
154
+ COUNT(*) AS review_count
155
+ FROM reviews WHERE repo = ?
156
+ GROUP BY pr_number
157
+ ORDER BY last_reviewed DESC
158
+ LIMIT ? OFFSET ?`)
159
+ .all(repo, limit, offset);
160
+ return { items, total };
161
+ }
162
+ export function listRemoteReviewsForRepo(repo, limit, offset = 0, sinceIso) {
163
+ if (sinceIso) {
164
+ return getAppDb()
165
+ .prepare(`SELECT * FROM reviews
166
+ WHERE repo = ? AND kind = 'remote' AND created_at >= ?
167
+ ORDER BY created_at DESC
168
+ LIMIT ? OFFSET ?`)
169
+ .all(repo, sinceIso, limit, offset);
170
+ }
171
+ return getAppDb()
172
+ .prepare(`SELECT * FROM reviews
173
+ WHERE repo = ? AND kind = 'remote'
174
+ ORDER BY created_at DESC
175
+ LIMIT ? OFFSET ?`)
176
+ .all(repo, limit, offset);
177
+ }
178
+ export function remoteTokenUsageSince(tokenId, sinceIso) {
179
+ const row = getAppDb()
180
+ .prepare(`SELECT COUNT(*) AS reviews, COALESCE(SUM(cost), 0) AS cost
181
+ FROM reviews
182
+ WHERE token_id = ? AND kind = 'remote' AND created_at >= ?`)
183
+ .get(tokenId, sinceIso);
184
+ return {
185
+ reviews: Number(row?.reviews ?? 0),
186
+ cost: Number(row?.cost ?? 0),
187
+ };
188
+ }
189
+ export function reviewStatsByRemoteToken(sinceIso) {
190
+ return getAppDb()
191
+ .prepare(`SELECT token_id, token_name,
192
+ COUNT(*) AS reviews,
193
+ COALESCE(SUM(cost), 0) AS cost,
194
+ COALESCE(SUM(findings_count), 0) AS findings
195
+ FROM reviews
196
+ WHERE kind = 'remote' AND created_at >= ? AND token_id IS NOT NULL
197
+ GROUP BY token_id, token_name
198
+ ORDER BY cost DESC`)
199
+ .all(sinceIso)
200
+ .map((row) => ({
201
+ tokenId: row.token_id,
202
+ tokenName: row.token_name,
203
+ reviews: Number(row.reviews),
204
+ cost: Number(row.cost),
205
+ findings: Number(row.findings),
206
+ }));
207
+ }
208
+ export function listLatestReviewsForRepo(repo, limit) {
209
+ return getAppDb()
210
+ .prepare(`SELECT r.* FROM reviews r
211
+ INNER JOIN (
212
+ SELECT pr_number, MAX(created_at) AS t
213
+ FROM reviews WHERE repo = ?
214
+ GROUP BY pr_number
215
+ ) latest
216
+ ON r.repo = ? AND r.pr_number = latest.pr_number AND r.created_at = latest.t
217
+ ORDER BY r.created_at DESC
218
+ LIMIT ?`)
219
+ .all(repo, repo, limit);
220
+ }
@@ -0,0 +1,179 @@
1
+ /** JSON columns (permissions, events, args, settings) are stored as their
2
+ * raw text; the table module that owns each one decides whether callers
3
+ * see the string or a parsed object. */
4
+ export type InstallationRow = {
5
+ id: number;
6
+ account_login: string;
7
+ account_type: string;
8
+ permissions: string;
9
+ events: string;
10
+ created_at: string;
11
+ suspended_at: string | null;
12
+ removed_at: string | null;
13
+ };
14
+ export type RepoRow = {
15
+ full_name: string;
16
+ installation_id: number | null;
17
+ active: number;
18
+ auto_review: number;
19
+ review_scope: string;
20
+ skip_drafts: number;
21
+ skip_bots: number;
22
+ knowledge_built_at: string | null;
23
+ knowledge_base_sha: string | null;
24
+ settings: string;
25
+ created_at: string;
26
+ use_codegraph: number;
27
+ };
28
+ export type JobRow = {
29
+ id: string;
30
+ type: string;
31
+ repo: string;
32
+ pr_number: number | null;
33
+ status: string;
34
+ args: string;
35
+ delivery_id: string | null;
36
+ queue_key: string | null;
37
+ attempt: number;
38
+ created_at: string;
39
+ started_at: string | null;
40
+ finished_at: string | null;
41
+ error: string | null;
42
+ superseded_by: string | null;
43
+ cancel_reason: string | null;
44
+ };
45
+ export type JobLogRow = {
46
+ job_id: string;
47
+ seq: number;
48
+ at: string;
49
+ level: string;
50
+ message: string;
51
+ };
52
+ export type ReviewRow = {
53
+ id: string;
54
+ kind: string;
55
+ subject_id: string | null;
56
+ repo: string;
57
+ pr_number: number | null;
58
+ branch: string | null;
59
+ token_id: string | null;
60
+ token_name: string | null;
61
+ job_id: string;
62
+ head_sha: string | null;
63
+ base_sha: string | null;
64
+ scope: string;
65
+ model: string;
66
+ findings_count: number;
67
+ open_count: number;
68
+ closed_count: number;
69
+ tokens_in: number;
70
+ tokens_out: number;
71
+ cost: number | null;
72
+ duration_ms: number | null;
73
+ posted_review_id: string | null;
74
+ status: string;
75
+ created_at: string;
76
+ round: number;
77
+ trigger: string | null;
78
+ check_run_id: string | null;
79
+ posted_fallback: number;
80
+ guide_built_at: string | null;
81
+ codegraph: string | null;
82
+ };
83
+ export type FindingRow = {
84
+ id: string;
85
+ review_id: string;
86
+ severity: string;
87
+ path: string | null;
88
+ line_from: number | null;
89
+ line_to: number | null;
90
+ title: string;
91
+ body_md: string;
92
+ posted_comment_id: string | null;
93
+ thread_comment_id: string | null;
94
+ first_seen_review_id: string | null;
95
+ anchor_text: string | null;
96
+ state: string;
97
+ carried_from_finding_id: string | null;
98
+ close_reason: string | null;
99
+ };
100
+ export type SubjectRow = {
101
+ id: string;
102
+ kind: string;
103
+ repo: string;
104
+ pr_number: number | null;
105
+ branch: string | null;
106
+ token_id: string | null;
107
+ created_at: string;
108
+ updated_at: string;
109
+ };
110
+ export type SubjectRevisionRow = {
111
+ subject_id: string;
112
+ review_id: string;
113
+ files_json: string;
114
+ visible_paths_json: string;
115
+ guide_built_at: string | null;
116
+ created_at: string;
117
+ };
118
+ export type ReplyRequestRow = {
119
+ id: string;
120
+ repo: string;
121
+ pr_number: number;
122
+ source_kind: string;
123
+ source_comment_id: string;
124
+ target_comment_id: string | null;
125
+ source_body: string;
126
+ source_author: string | null;
127
+ source_path: string | null;
128
+ source_line: number | null;
129
+ source_commit_id: string | null;
130
+ status: string;
131
+ answer_md: string | null;
132
+ posted_comment_id: string | null;
133
+ job_id: string | null;
134
+ error: string | null;
135
+ attempts: number;
136
+ created_at: string;
137
+ updated_at: string;
138
+ };
139
+ export type DeliveryRow = {
140
+ delivery_id: string;
141
+ event: string;
142
+ action: string | null;
143
+ repo: string | null;
144
+ pr_number: number | null;
145
+ received_at: string;
146
+ outcome: string;
147
+ reason: string | null;
148
+ };
149
+ export type SessionRow = {
150
+ token_hash: string;
151
+ username: string;
152
+ created_at: string;
153
+ expires_at: string;
154
+ last_seen_at: string;
155
+ };
156
+ export type DriftRow = {
157
+ repo: string;
158
+ as_of: string;
159
+ prs_since: number;
160
+ prs_updated: number;
161
+ commits_since: number;
162
+ files_changed: number;
163
+ };
164
+ export type RemoteTokenRow = {
165
+ id: string;
166
+ name: string;
167
+ token_hash: string;
168
+ active: number;
169
+ created_at: string;
170
+ last_used_at: string | null;
171
+ };
172
+ export type RemoteReviewInputRow = {
173
+ job_id: string;
174
+ revision_json: string;
175
+ capabilities_json: string;
176
+ request_id: string;
177
+ token_id: string;
178
+ created_at: string;
179
+ };
@@ -0,0 +1,4 @@
1
+ /** JSON columns (permissions, events, args, settings) are stored as their
2
+ * raw text; the table module that owns each one decides whether callers
3
+ * see the string or a parsed object. */
4
+ export {};
@@ -0,0 +1,8 @@
1
+ import type { SessionRow } from "./rows.ts";
2
+ export declare function insertSession(tokenHash: string, username: string, expiresAt: string): void;
3
+ /** Returns the session only if it exists and has not expired, touching
4
+ * `last_seen_at` on the way — an expired row is left for
5
+ * `deleteExpiredSessions` to reap rather than deleted here. */
6
+ export declare function getSession(tokenHash: string): SessionRow | undefined;
7
+ export declare function deleteSession(tokenHash: string): void;
8
+ export declare function deleteExpiredSessions(): void;
@@ -0,0 +1,34 @@
1
+ /** Stores only the token's hash, never the raw token — hashing itself is
2
+ * `server/auth.ts`'s concern, not this module's. */
3
+ import { getAppDb } from "./app_db.js";
4
+ import { nowIso } from "../util/time.js";
5
+ export function insertSession(tokenHash, username, expiresAt) {
6
+ const now = nowIso();
7
+ getAppDb()
8
+ .prepare(`INSERT INTO sessions (token_hash, username, created_at, expires_at, last_seen_at)
9
+ VALUES (?, ?, ?, ?, ?)`)
10
+ .run(tokenHash, username, now, expiresAt, now);
11
+ }
12
+ /** Returns the session only if it exists and has not expired, touching
13
+ * `last_seen_at` on the way — an expired row is left for
14
+ * `deleteExpiredSessions` to reap rather than deleted here. */
15
+ export function getSession(tokenHash) {
16
+ const db = getAppDb();
17
+ const row = db
18
+ .prepare(`SELECT * FROM sessions WHERE token_hash = ?`)
19
+ .get(tokenHash);
20
+ if (!row || row.expires_at <= nowIso())
21
+ return undefined;
22
+ db.prepare(`UPDATE sessions SET last_seen_at = ? WHERE token_hash = ?`).run(nowIso(), tokenHash);
23
+ return row;
24
+ }
25
+ export function deleteSession(tokenHash) {
26
+ getAppDb()
27
+ .prepare(`DELETE FROM sessions WHERE token_hash = ?`)
28
+ .run(tokenHash);
29
+ }
30
+ export function deleteExpiredSessions() {
31
+ getAppDb()
32
+ .prepare(`DELETE FROM sessions WHERE expires_at <= ?`)
33
+ .run(nowIso());
34
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from "../knowledge/types.ts";
2
+ export declare function readState(repo: string): Promise<State | undefined>;
3
+ export declare function writeState(state: State): Promise<void>;