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.
- package/LICENSE +21 -0
- package/README.md +105 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +9 -0
- package/dist/mod.d.ts +4 -0
- package/dist/mod.js +2 -0
- package/dist/package.json +54 -0
- package/dist/src/ai/batch.d.ts +16 -0
- package/dist/src/ai/batch.js +117 -0
- package/dist/src/ai/fake.d.ts +7 -0
- package/dist/src/ai/fake.js +40 -0
- package/dist/src/ai/hetzner.d.ts +16 -0
- package/dist/src/ai/hetzner.js +69 -0
- package/dist/src/ai/mermaid.d.ts +27 -0
- package/dist/src/ai/mermaid.js +457 -0
- package/dist/src/ai/mermaid_loop.d.ts +12 -0
- package/dist/src/ai/mermaid_loop.js +100 -0
- package/dist/src/ai/openrouter.d.ts +9 -0
- package/dist/src/ai/openrouter.js +29 -0
- package/dist/src/ai/provider.d.ts +4 -0
- package/dist/src/ai/provider.js +63 -0
- package/dist/src/cli/args.d.ts +5 -0
- package/dist/src/cli/args.js +252 -0
- package/dist/src/cli/commands/probe.d.ts +2 -0
- package/dist/src/cli/commands/probe.js +122 -0
- package/dist/src/cli/commands/review.d.ts +3 -0
- package/dist/src/cli/commands/review.js +84 -0
- package/dist/src/cli/commands/serve.d.ts +18 -0
- package/dist/src/cli/commands/serve.js +183 -0
- package/dist/src/cli/commands/set.d.ts +7 -0
- package/dist/src/cli/commands/set.js +171 -0
- package/dist/src/cli/main.d.ts +1 -0
- package/dist/src/cli/main.js +27 -0
- package/dist/src/cli/prompt.d.ts +4 -0
- package/dist/src/cli/prompt.js +26 -0
- package/dist/src/cli/review_args.d.ts +28 -0
- package/dist/src/cli/review_args.js +84 -0
- package/dist/src/cli/review_output.d.ts +2 -0
- package/dist/src/cli/review_output.js +10 -0
- package/dist/src/cli/review_result.d.ts +81 -0
- package/dist/src/cli/review_result.js +281 -0
- package/dist/src/config.d.ts +108 -0
- package/dist/src/config.js +138 -0
- package/dist/src/git/default_branch.d.ts +5 -0
- package/dist/src/git/default_branch.js +22 -0
- package/dist/src/github/app.d.ts +43 -0
- package/dist/src/github/app.js +120 -0
- package/dist/src/github/client.d.ts +17 -0
- package/dist/src/github/client.js +66 -0
- package/dist/src/github/collect.d.ts +7 -0
- package/dist/src/github/collect.js +368 -0
- package/dist/src/github/gh.d.ts +8 -0
- package/dist/src/github/gh.js +63 -0
- package/dist/src/github/jwt.d.ts +12 -0
- package/dist/src/github/jwt.js +84 -0
- package/dist/src/github/oauth.d.ts +9 -0
- package/dist/src/github/oauth.js +51 -0
- package/dist/src/github/pat.d.ts +10 -0
- package/dist/src/github/pat.js +60 -0
- package/dist/src/knowledge/facts.d.ts +3 -0
- package/dist/src/knowledge/facts.js +325 -0
- package/dist/src/knowledge/guide.d.ts +6 -0
- package/dist/src/knowledge/guide.js +82 -0
- package/dist/src/knowledge/probe.d.ts +12 -0
- package/dist/src/knowledge/probe.js +144 -0
- package/dist/src/knowledge/sections.d.ts +2 -0
- package/dist/src/knowledge/sections.js +13 -0
- package/dist/src/knowledge/skill.d.ts +10 -0
- package/dist/src/knowledge/skill.js +106 -0
- package/dist/src/knowledge/synthesis.d.ts +7 -0
- package/dist/src/knowledge/synthesis.js +315 -0
- package/dist/src/knowledge/types.d.ts +49 -0
- package/dist/src/knowledge/types.js +1 -0
- package/dist/src/knowledge/validate.d.ts +6 -0
- package/dist/src/knowledge/validate.js +134 -0
- package/dist/src/local/carry_over_store.d.ts +18 -0
- package/dist/src/local/carry_over_store.js +40 -0
- package/dist/src/local/codegraph_exclude.d.ts +3 -0
- package/dist/src/local/codegraph_exclude.js +26 -0
- package/dist/src/local/codegraph_prepare.d.ts +17 -0
- package/dist/src/local/codegraph_prepare.js +51 -0
- package/dist/src/local/git_ops.d.ts +12 -0
- package/dist/src/local/git_ops.js +98 -0
- package/dist/src/local/git_parse.d.ts +27 -0
- package/dist/src/local/git_parse.js +131 -0
- package/dist/src/local/git_revision.d.ts +15 -0
- package/dist/src/local/git_revision.js +141 -0
- package/dist/src/local/git_untracked.d.ts +21 -0
- package/dist/src/local/git_untracked.js +79 -0
- package/dist/src/local/review_local.d.ts +4 -0
- package/dist/src/local/review_local.js +273 -0
- package/dist/src/local/review_lock.d.ts +7 -0
- package/dist/src/local/review_lock.js +45 -0
- package/dist/src/pr/checkout.d.ts +25 -0
- package/dist/src/pr/checkout.js +136 -0
- package/dist/src/pr/codegraph_tool_args.d.ts +8 -0
- package/dist/src/pr/codegraph_tool_args.js +34 -0
- package/dist/src/pr/codegraph_tools.d.ts +15 -0
- package/dist/src/pr/codegraph_tools.js +408 -0
- package/dist/src/pr/diff_summary.d.ts +6 -0
- package/dist/src/pr/diff_summary.js +52 -0
- package/dist/src/pr/findings.d.ts +14 -0
- package/dist/src/pr/findings.js +94 -0
- package/dist/src/pr/github_revision.d.ts +4 -0
- package/dist/src/pr/github_revision.js +39 -0
- package/dist/src/pr/hunks.d.ts +18 -0
- package/dist/src/pr/hunks.js +73 -0
- package/dist/src/pr/reviewer.d.ts +40 -0
- package/dist/src/pr/reviewer.js +582 -0
- package/dist/src/pr/rounds.d.ts +17 -0
- package/dist/src/pr/rounds.js +43 -0
- package/dist/src/pr/scope.d.ts +35 -0
- package/dist/src/pr/scope.js +84 -0
- package/dist/src/pr/snapshot.d.ts +11 -0
- package/dist/src/pr/snapshot.js +1 -0
- package/dist/src/pr/suggestion.d.ts +17 -0
- package/dist/src/pr/suggestion.js +70 -0
- package/dist/src/remote/client.d.ts +4 -0
- package/dist/src/remote/client.js +199 -0
- package/dist/src/remote/client_tools.d.ts +13 -0
- package/dist/src/remote/client_tools.js +30 -0
- package/dist/src/remote/fixtures.d.ts +1 -0
- package/dist/src/remote/fixtures.js +30 -0
- package/dist/src/remote/revision_from_submit.d.ts +2 -0
- package/dist/src/remote/revision_from_submit.js +26 -0
- package/dist/src/remote/schema.d.ts +14 -0
- package/dist/src/remote/schema.js +30 -0
- package/dist/src/remote/server/body.d.ts +18 -0
- package/dist/src/remote/server/body.js +73 -0
- package/dist/src/remote/server/routes.d.ts +1 -0
- package/dist/src/remote/server/routes.js +150 -0
- package/dist/src/remote/server/sessions.d.ts +16 -0
- package/dist/src/remote/server/sessions.js +48 -0
- package/dist/src/remote/server/sync.d.ts +3 -0
- package/dist/src/remote/server/sync.js +90 -0
- package/dist/src/remote/tool_bridge.d.ts +15 -0
- package/dist/src/remote/tool_bridge.js +84 -0
- package/dist/src/remote/validate.d.ts +8 -0
- package/dist/src/remote/validate.js +248 -0
- package/dist/src/review/carry_over.d.ts +63 -0
- package/dist/src/review/carry_over.js +372 -0
- package/dist/src/review/engine.d.ts +13 -0
- package/dist/src/review/engine.js +4 -0
- package/dist/src/review/guides.d.ts +12 -0
- package/dist/src/review/guides.js +44 -0
- package/dist/src/review/revision.d.ts +26 -0
- package/dist/src/review/revision.js +57 -0
- package/dist/src/server/api/activity.d.ts +1 -0
- package/dist/src/server/api/activity.js +12 -0
- package/dist/src/server/api/analytics.d.ts +1 -0
- package/dist/src/server/api/analytics.js +9 -0
- package/dist/src/server/api/installations.d.ts +4 -0
- package/dist/src/server/api/installations.js +24 -0
- package/dist/src/server/api/jobs.d.ts +1 -0
- package/dist/src/server/api/jobs.js +86 -0
- package/dist/src/server/api/json_body.d.ts +1 -0
- package/dist/src/server/api/json_body.js +14 -0
- package/dist/src/server/api/remote_tokens.d.ts +1 -0
- package/dist/src/server/api/remote_tokens.js +71 -0
- package/dist/src/server/api/repos.d.ts +4 -0
- package/dist/src/server/api/repos.js +136 -0
- package/dist/src/server/api/settings.d.ts +1 -0
- package/dist/src/server/api/settings.js +210 -0
- package/dist/src/server/app.d.ts +27 -0
- package/dist/src/server/app.js +147 -0
- package/dist/src/server/assets.d.ts +3 -0
- package/dist/src/server/assets.js +21 -0
- package/dist/src/server/auth.d.ts +44 -0
- package/dist/src/server/auth.js +188 -0
- package/dist/src/server/errors.d.ts +8 -0
- package/dist/src/server/errors.js +10 -0
- package/dist/src/server/http.d.ts +8 -0
- package/dist/src/server/http.js +117 -0
- package/dist/src/server/logo.d.ts +1 -0
- package/dist/src/server/logo.js +2 -0
- package/dist/src/server/pages/activity.d.ts +4 -0
- package/dist/src/server/pages/activity.js +200 -0
- package/dist/src/server/pages/add_repo.d.ts +9 -0
- package/dist/src/server/pages/add_repo.js +56 -0
- package/dist/src/server/pages/analytics.d.ts +2 -0
- package/dist/src/server/pages/analytics.js +193 -0
- package/dist/src/server/pages/client.d.ts +2 -0
- package/dist/src/server/pages/client.js +2 -0
- package/dist/src/server/pages/home.d.ts +16 -0
- package/dist/src/server/pages/home.js +87 -0
- package/dist/src/server/pages/knowledge.d.ts +2 -0
- package/dist/src/server/pages/knowledge.js +64 -0
- package/dist/src/server/pages/layout.d.ts +32 -0
- package/dist/src/server/pages/layout.js +230 -0
- package/dist/src/server/pages/pr.d.ts +2 -0
- package/dist/src/server/pages/pr.js +109 -0
- package/dist/src/server/pages/repo.d.ts +2 -0
- package/dist/src/server/pages/repo.js +92 -0
- package/dist/src/server/pages/repo_prs.d.ts +2 -0
- package/dist/src/server/pages/repo_prs.js +75 -0
- package/dist/src/server/pages/repo_remote.d.ts +2 -0
- package/dist/src/server/pages/repo_remote.js +38 -0
- package/dist/src/server/pages/repo_settings.d.ts +3 -0
- package/dist/src/server/pages/repo_settings.js +127 -0
- package/dist/src/server/pages/router.d.ts +17 -0
- package/dist/src/server/pages/router.js +312 -0
- package/dist/src/server/pages/settings.d.ts +2 -0
- package/dist/src/server/pages/settings.js +337 -0
- package/dist/src/server/pages/setup.d.ts +5 -0
- package/dist/src/server/pages/setup.js +38 -0
- package/dist/src/server/pages/styles.d.ts +2 -0
- package/dist/src/server/pages/styles.js +2 -0
- package/dist/src/server/webhook/index.d.ts +1 -0
- package/dist/src/server/webhook/index.js +64 -0
- package/dist/src/server/webhook/signature.d.ts +7 -0
- package/dist/src/server/webhook/signature.js +41 -0
- package/dist/src/services/credentials.d.ts +19 -0
- package/dist/src/services/credentials.js +146 -0
- package/dist/src/services/dashboard.d.ts +165 -0
- package/dist/src/services/dashboard.js +200 -0
- package/dist/src/services/drift.d.ts +13 -0
- package/dist/src/services/drift.js +76 -0
- package/dist/src/services/jobs.d.ts +34 -0
- package/dist/src/services/jobs.js +222 -0
- package/dist/src/services/remote_review.d.ts +10 -0
- package/dist/src/services/remote_review.js +274 -0
- package/dist/src/services/remote_review_abort.d.ts +3 -0
- package/dist/src/services/remote_review_abort.js +11 -0
- package/dist/src/services/remote_token_jobs.d.ts +1 -0
- package/dist/src/services/remote_token_jobs.js +20 -0
- package/dist/src/services/remote_tokens.d.ts +13 -0
- package/dist/src/services/remote_tokens.js +57 -0
- package/dist/src/services/replies.d.ts +7 -0
- package/dist/src/services/replies.js +248 -0
- package/dist/src/services/review.d.ts +35 -0
- package/dist/src/services/review.js +671 -0
- package/dist/src/services/setup.d.ts +29 -0
- package/dist/src/services/setup.js +364 -0
- package/dist/src/services/webhook.d.ts +22 -0
- package/dist/src/services/webhook.js +401 -0
- package/dist/src/store/app_db.d.ts +10 -0
- package/dist/src/store/app_db.js +85 -0
- package/dist/src/store/cache_db.d.ts +4 -0
- package/dist/src/store/cache_db.js +63 -0
- package/dist/src/store/deliveries.d.ts +12 -0
- package/dist/src/store/deliveries.js +23 -0
- package/dist/src/store/drift.d.ts +4 -0
- package/dist/src/store/drift.js +22 -0
- package/dist/src/store/findings.d.ts +31 -0
- package/dist/src/store/findings.js +64 -0
- package/dist/src/store/installations.d.ts +9 -0
- package/dist/src/store/installations.js +35 -0
- package/dist/src/store/job_logs.d.ts +5 -0
- package/dist/src/store/job_logs.js +17 -0
- package/dist/src/store/jobs.d.ts +27 -0
- package/dist/src/store/jobs.js +86 -0
- package/dist/src/store/migrations.d.ts +5 -0
- package/dist/src/store/migrations.js +258 -0
- package/dist/src/store/remote_review_inputs.d.ts +11 -0
- package/dist/src/store/remote_review_inputs.js +25 -0
- package/dist/src/store/remote_tokens.d.ts +10 -0
- package/dist/src/store/remote_tokens.js +45 -0
- package/dist/src/store/replies.d.ts +24 -0
- package/dist/src/store/replies.js +79 -0
- package/dist/src/store/repos.d.ts +11 -0
- package/dist/src/store/repos.js +59 -0
- package/dist/src/store/reviews.d.ts +86 -0
- package/dist/src/store/reviews.js +220 -0
- package/dist/src/store/rows.d.ts +179 -0
- package/dist/src/store/rows.js +4 -0
- package/dist/src/store/sessions.d.ts +8 -0
- package/dist/src/store/sessions.js +34 -0
- package/dist/src/store/skill_state.d.ts +3 -0
- package/dist/src/store/skill_state.js +24 -0
- package/dist/src/store/sqlite.d.ts +39 -0
- package/dist/src/store/sqlite.js +77 -0
- package/dist/src/store/subjects.d.ts +15 -0
- package/dist/src/store/subjects.js +78 -0
- package/dist/src/tools/codegraph.d.ts +58 -0
- package/dist/src/tools/codegraph.js +165 -0
- package/dist/src/tools/codegraph_exec.d.ts +13 -0
- package/dist/src/tools/codegraph_exec.js +66 -0
- package/dist/src/types.d.ts +89 -0
- package/dist/src/types.js +1 -0
- package/dist/src/util/atomic.d.ts +4 -0
- package/dist/src/util/atomic.js +78 -0
- package/dist/src/util/keyed_lock.d.ts +3 -0
- package/dist/src/util/keyed_lock.js +33 -0
- package/dist/src/util/log.d.ts +11 -0
- package/dist/src/util/log.js +42 -0
- package/dist/src/util/redact.d.ts +5 -0
- package/dist/src/util/redact.js +30 -0
- package/dist/src/util/runtime.d.ts +90 -0
- package/dist/src/util/runtime.js +206 -0
- package/dist/src/util/time.d.ts +9 -0
- package/dist/src/util/time.js +36 -0
- package/dist/src/version.d.ts +1 -0
- package/dist/src/version.js +5 -0
- 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,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
|
+
}
|