@codraoss/api 0.9.4 → 0.9.5
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/dist/index.d.ts +59 -3
- package/dist/index.js +149 -11
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as hono_types from 'hono/types';
|
|
2
|
-
import
|
|
2
|
+
import * as hono from 'hono';
|
|
3
|
+
import { MiddlewareHandler, Hono, Context } from 'hono';
|
|
3
4
|
import { SessionStore, ReviewRuntime, DashboardSessionUser } from '@codraoss/core/ports';
|
|
5
|
+
import { ApiAction } from '@codraoss/schema/api';
|
|
4
6
|
import * as dbAccounts from '@codraoss/db/accounts';
|
|
5
7
|
import * as dbJobs from '@codraoss/db/jobs';
|
|
6
8
|
import * as dbFileReviews from '@codraoss/db/file-reviews';
|
|
@@ -101,6 +103,41 @@ interface WebhookPort {
|
|
|
101
103
|
normalizePayload: (eventName: string, payload: any) => any;
|
|
102
104
|
extractReviewRequest: (input: any) => any;
|
|
103
105
|
}
|
|
106
|
+
interface AuthorizeContext {
|
|
107
|
+
user: DashboardSessionUser;
|
|
108
|
+
action: ApiAction;
|
|
109
|
+
resource?: {
|
|
110
|
+
type: string;
|
|
111
|
+
id?: string;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
type AuthorizeResult = {
|
|
115
|
+
allowed: true;
|
|
116
|
+
} | {
|
|
117
|
+
allowed: false;
|
|
118
|
+
reason?: string;
|
|
119
|
+
};
|
|
120
|
+
interface AuthzPort {
|
|
121
|
+
authorize(ctx: AuthorizeContext): Promise<AuthorizeResult>;
|
|
122
|
+
listPermissions?(user: DashboardSessionUser): Promise<string[] | undefined>;
|
|
123
|
+
}
|
|
124
|
+
interface QuotaCheckInput {
|
|
125
|
+
action: ApiAction;
|
|
126
|
+
user?: DashboardSessionUser;
|
|
127
|
+
subject?: {
|
|
128
|
+
installationId?: string;
|
|
129
|
+
owner?: string;
|
|
130
|
+
repo?: string;
|
|
131
|
+
};
|
|
132
|
+
cost?: number;
|
|
133
|
+
}
|
|
134
|
+
type QuotaResult = {
|
|
135
|
+
allowed: true;
|
|
136
|
+
} | {
|
|
137
|
+
allowed: false;
|
|
138
|
+
retryAfterSeconds?: number;
|
|
139
|
+
reason?: string;
|
|
140
|
+
};
|
|
104
141
|
interface ApiRouterDeps {
|
|
105
142
|
repositories: RepositoriesPort;
|
|
106
143
|
gitProvider: {
|
|
@@ -114,6 +151,8 @@ interface ApiRouterDeps {
|
|
|
114
151
|
platform: PlatformPort;
|
|
115
152
|
authProvider: AuthProviderPort;
|
|
116
153
|
webhook: WebhookPort;
|
|
154
|
+
authz?: AuthzPort;
|
|
155
|
+
checkQuota?: (input: QuotaCheckInput) => Promise<QuotaResult>;
|
|
117
156
|
}
|
|
118
157
|
interface ApiEnv {
|
|
119
158
|
Bindings: {
|
|
@@ -133,6 +172,23 @@ interface ApiEnv {
|
|
|
133
172
|
};
|
|
134
173
|
}
|
|
135
174
|
|
|
136
|
-
|
|
175
|
+
interface ApiRouterOptions {
|
|
176
|
+
beforeAuth?: MiddlewareHandler<ApiEnv>[];
|
|
177
|
+
afterAuth?: MiddlewareHandler<ApiEnv>[];
|
|
178
|
+
pages?: string[];
|
|
179
|
+
publicPages?: string[];
|
|
180
|
+
routes?: (app: Hono<ApiEnv>) => void;
|
|
181
|
+
}
|
|
182
|
+
declare function createApiRouter(options?: ApiRouterOptions): Hono<ApiEnv, hono_types.BlankSchema, "/">;
|
|
183
|
+
|
|
184
|
+
declare function requirePermission(c: Context<ApiEnv>, action: ApiAction, resource?: {
|
|
185
|
+
type: string;
|
|
186
|
+
id?: string;
|
|
187
|
+
}): Promise<Response | null>;
|
|
188
|
+
declare function requireQuota(c: Context<ApiEnv>, input: Omit<QuotaCheckInput, 'user'>): Promise<Response | null>;
|
|
189
|
+
|
|
190
|
+
declare const requireSession: hono.MiddlewareHandler<ApiEnv, string, {}, Response>;
|
|
191
|
+
|
|
192
|
+
declare const requireCsrfHeader: hono.MiddlewareHandler<ApiEnv, string, {}, Response>;
|
|
137
193
|
|
|
138
|
-
export { type ApiEnv, type ApiRouterDeps, type ConfigPort, type PlatformPort, type RepositoriesPort, createApiRouter };
|
|
194
|
+
export { type ApiEnv, type ApiRouterDeps, type ApiRouterOptions, type AuthorizeContext, type AuthorizeResult, type AuthzPort, type ConfigPort, type PlatformPort, type QuotaCheckInput, type QuotaResult, type RepositoriesPort, createApiRouter, requireCsrfHeader, requirePermission, requireQuota, requireSession };
|
package/dist/index.js
CHANGED
|
@@ -358,6 +358,17 @@ async function handleGitHubWebhook(c) {
|
|
|
358
358
|
job: existingJob
|
|
359
359
|
}, 202);
|
|
360
360
|
}
|
|
361
|
+
const throttled = await c.env.deps.checkQuota?.({
|
|
362
|
+
action: "reviews.enqueue",
|
|
363
|
+
subject: {
|
|
364
|
+
installationId: extracted.installationId,
|
|
365
|
+
owner: extracted.owner,
|
|
366
|
+
repo: extracted.repo
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
if (throttled && !throttled.allowed) {
|
|
370
|
+
return c.json({ ok: true, ignored: true, reason: "quota_exceeded" }, 202);
|
|
371
|
+
}
|
|
361
372
|
const job = await jobsRepo.insertJob(c.env, {
|
|
362
373
|
installationId: extracted.installationId,
|
|
363
374
|
owner: extracted.owner,
|
|
@@ -404,6 +415,42 @@ function createWebhookRouter() {
|
|
|
404
415
|
import { isSupportedTimeZone } from "@codraoss/schema/timezone";
|
|
405
416
|
import { Hono as Hono3 } from "hono";
|
|
406
417
|
import { z } from "zod";
|
|
418
|
+
|
|
419
|
+
// src/middleware/authorize.ts
|
|
420
|
+
async function requirePermission(c, action, resource) {
|
|
421
|
+
const authz = c.env.deps.authz;
|
|
422
|
+
if (!authz) return null;
|
|
423
|
+
const user = c.get("sessionUser");
|
|
424
|
+
if (!user) {
|
|
425
|
+
return c.json({ error: "Unauthorized", code: "unauthorized", action, reason: null }, 401);
|
|
426
|
+
}
|
|
427
|
+
const result = await authz.authorize({ user, action, resource });
|
|
428
|
+
if (result.allowed) return null;
|
|
429
|
+
return c.json(
|
|
430
|
+
{ error: "Forbidden", code: "forbidden", action, reason: result.reason ?? null },
|
|
431
|
+
403
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
async function requireQuota(c, input) {
|
|
435
|
+
const checkQuota = c.env.deps.checkQuota;
|
|
436
|
+
if (!checkQuota) return null;
|
|
437
|
+
const user = c.get("sessionUser") ?? void 0;
|
|
438
|
+
const result = await checkQuota({ ...input, user });
|
|
439
|
+
if (result.allowed) return null;
|
|
440
|
+
const headers = result.retryAfterSeconds ? { "Retry-After": String(result.retryAfterSeconds) } : void 0;
|
|
441
|
+
return c.json(
|
|
442
|
+
{
|
|
443
|
+
error: "Too many requests",
|
|
444
|
+
code: "quota_exceeded",
|
|
445
|
+
action: input.action,
|
|
446
|
+
reason: result.reason ?? null
|
|
447
|
+
},
|
|
448
|
+
429,
|
|
449
|
+
headers
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// src/routes/api/auth.ts
|
|
407
454
|
var emailSchema = z.strictObject({
|
|
408
455
|
email: z.string().trim().email().max(254)
|
|
409
456
|
});
|
|
@@ -423,7 +470,8 @@ function createAuthApiRouter() {
|
|
|
423
470
|
if (!sessionUser) {
|
|
424
471
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
|
425
472
|
}
|
|
426
|
-
|
|
473
|
+
const permissions = await c.env.deps.authz?.listPermissions?.(sessionUser);
|
|
474
|
+
return c.json(permissions ? { user: sessionUser, permissions } : { user: sessionUser });
|
|
427
475
|
});
|
|
428
476
|
app.get("/account", async (c) => {
|
|
429
477
|
const sessionUser = c.get("sessionUser");
|
|
@@ -444,6 +492,8 @@ function createAuthApiRouter() {
|
|
|
444
492
|
return c.json({ account });
|
|
445
493
|
});
|
|
446
494
|
app.patch("/account", async (c) => {
|
|
495
|
+
const denied = await requirePermission(c, "account.write");
|
|
496
|
+
if (denied) return denied;
|
|
447
497
|
const sessionUser = c.get("sessionUser");
|
|
448
498
|
if (!sessionUser) {
|
|
449
499
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
|
@@ -488,6 +538,8 @@ function createAuthApiRouter() {
|
|
|
488
538
|
});
|
|
489
539
|
});
|
|
490
540
|
app.post("/updates-email", async (c) => {
|
|
541
|
+
const denied = await requirePermission(c, "account.updatesEmail.write");
|
|
542
|
+
if (denied) return denied;
|
|
491
543
|
const sessionUser = c.get("sessionUser");
|
|
492
544
|
if (!sessionUser) {
|
|
493
545
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
|
@@ -525,7 +577,7 @@ function createAuthApiRouter() {
|
|
|
525
577
|
import { Hono as Hono4 } from "hono";
|
|
526
578
|
import { defaultRepoConfig, findingLabelSchema, jobsQuerySchema } from "@codraoss/schema";
|
|
527
579
|
import { parseUnifiedDiff } from "@codraoss/core/diff";
|
|
528
|
-
import { buildFileReviewPrompts } from "@codraoss/core/prompts/file-review";
|
|
580
|
+
import { buildFileReviewPrompts, changelogExcerptFromDiff, wantsFileContext } from "@codraoss/core/prompts/file-review";
|
|
529
581
|
async function terminateJobWorkflow(c, job) {
|
|
530
582
|
await c.env.deps.platform.terminateJobWorkflow(job);
|
|
531
583
|
}
|
|
@@ -542,6 +594,8 @@ function getExecutionContext(c) {
|
|
|
542
594
|
function createJobsRouter() {
|
|
543
595
|
const app = new Hono4();
|
|
544
596
|
app.get("/", async (c) => {
|
|
597
|
+
const denied = await requirePermission(c, "jobs.read");
|
|
598
|
+
if (denied) return denied;
|
|
545
599
|
c.env.deps.platform.scheduleBestEffortJobMaintenance(getExecutionContext(c));
|
|
546
600
|
const rawQuery = c.req.query();
|
|
547
601
|
const query = jobsQuerySchema.parse(rawQuery);
|
|
@@ -549,6 +603,8 @@ function createJobsRouter() {
|
|
|
549
603
|
return c.json(result);
|
|
550
604
|
});
|
|
551
605
|
app.get("/:id", async (c) => {
|
|
606
|
+
const denied = await requirePermission(c, "jobs.read", { type: "job", id: c.req.param("id") });
|
|
607
|
+
if (denied) return denied;
|
|
552
608
|
c.env.deps.platform.scheduleBestEffortJobMaintenance(getExecutionContext(c));
|
|
553
609
|
const job = await c.env.deps.repositories.jobs.getJobDetail(c.env, c.req.param("id"));
|
|
554
610
|
if (!job) {
|
|
@@ -572,6 +628,8 @@ function createJobsRouter() {
|
|
|
572
628
|
return response;
|
|
573
629
|
});
|
|
574
630
|
app.get("/:id/diffs", async (c) => {
|
|
631
|
+
const denied = await requirePermission(c, "jobs.read", { type: "job", id: c.req.param("id") });
|
|
632
|
+
if (denied) return denied;
|
|
575
633
|
const job = await c.env.deps.repositories.jobs.getJobDetail(c.env, c.req.param("id"));
|
|
576
634
|
if (!job) {
|
|
577
635
|
return jsonError("Job not found.", 404);
|
|
@@ -599,14 +657,21 @@ function createJobsRouter() {
|
|
|
599
657
|
);
|
|
600
658
|
}
|
|
601
659
|
const diffs = {};
|
|
602
|
-
|
|
660
|
+
const parsedFiles = parseUnifiedDiff(rawDiff, config.review);
|
|
661
|
+
const changelogExcerpt = changelogExcerptFromDiff(parsedFiles);
|
|
662
|
+
for (const file of parsedFiles) {
|
|
603
663
|
if (file.isDeleted || file.isBinary || !file.path) continue;
|
|
604
|
-
|
|
664
|
+
const { userPrompt } = buildFileReviewPrompts({
|
|
605
665
|
file,
|
|
606
666
|
prTitle: job.prTitle,
|
|
607
667
|
prDescription,
|
|
668
|
+
changelogExcerpt,
|
|
608
669
|
config: config.review
|
|
609
|
-
})
|
|
670
|
+
});
|
|
671
|
+
const hadContext = wantsFileContext(file, config.review.full_file_context);
|
|
672
|
+
diffs[file.path] = hadContext ? `${userPrompt}
|
|
673
|
+
|
|
674
|
+
[The full file at the reviewed commit was included here at review time; it is omitted from this preview.]` : userPrompt;
|
|
610
675
|
}
|
|
611
676
|
const response = c.json({ diffs });
|
|
612
677
|
response.headers.set("Cache-Control", "private, max-age=60");
|
|
@@ -658,6 +723,10 @@ function createJobsRouter() {
|
|
|
658
723
|
return job;
|
|
659
724
|
}
|
|
660
725
|
app.post("/:id/retry", async (c) => {
|
|
726
|
+
const denied = await requirePermission(c, "jobs.retry", { type: "job", id: c.req.param("id") });
|
|
727
|
+
if (denied) return denied;
|
|
728
|
+
const throttled = await requireQuota(c, { action: "jobs.retry" });
|
|
729
|
+
if (throttled) return throttled;
|
|
661
730
|
const jobs = c.env.deps.repositories.jobs;
|
|
662
731
|
const rawSource = await jobs.getJobForProcessing(c.env, c.req.param("id"));
|
|
663
732
|
if (!rawSource) {
|
|
@@ -667,6 +736,10 @@ function createJobsRouter() {
|
|
|
667
736
|
return c.json({ job }, 202);
|
|
668
737
|
});
|
|
669
738
|
app.post("/:id/rerun", async (c) => {
|
|
739
|
+
const denied = await requirePermission(c, "jobs.rerun", { type: "job", id: c.req.param("id") });
|
|
740
|
+
if (denied) return denied;
|
|
741
|
+
const throttled = await requireQuota(c, { action: "jobs.rerun" });
|
|
742
|
+
if (throttled) return throttled;
|
|
670
743
|
const jobs = c.env.deps.repositories.jobs;
|
|
671
744
|
const rawSource = await jobs.getJobForProcessing(c.env, c.req.param("id"));
|
|
672
745
|
if (!rawSource) {
|
|
@@ -680,6 +753,8 @@ function createJobsRouter() {
|
|
|
680
753
|
return c.json({ job }, 202);
|
|
681
754
|
});
|
|
682
755
|
app.post("/:id/stop", async (c) => {
|
|
756
|
+
const denied = await requirePermission(c, "jobs.stop", { type: "job", id: c.req.param("id") });
|
|
757
|
+
if (denied) return denied;
|
|
683
758
|
const jobs = c.env.deps.repositories.jobs;
|
|
684
759
|
const id = c.req.param("id");
|
|
685
760
|
const raw = await jobs.getJobForProcessing(c.env, id);
|
|
@@ -696,6 +771,8 @@ function createJobsRouter() {
|
|
|
696
771
|
return c.json({ job: updated ? jobs.mapJob(updated) : job }, 200);
|
|
697
772
|
});
|
|
698
773
|
app.put("/:id/findings/:fingerprint/label", async (c) => {
|
|
774
|
+
const denied = await requirePermission(c, "jobs.label", { type: "job", id: c.req.param("id") });
|
|
775
|
+
if (denied) return denied;
|
|
699
776
|
const jobId = c.req.param("id");
|
|
700
777
|
const fingerprint = c.req.param("fingerprint");
|
|
701
778
|
const parsed = findingLabelSchema.safeParse(await c.req.json().catch(() => null));
|
|
@@ -707,7 +784,7 @@ function createJobsRouter() {
|
|
|
707
784
|
prNumber: target.pr_number,
|
|
708
785
|
fingerprint,
|
|
709
786
|
anchorHash: target.anchor_hash,
|
|
710
|
-
//
|
|
787
|
+
// Keeps rejection valid even if the model rewords the title.
|
|
711
788
|
fingerprintV2: target.fingerprint_v2,
|
|
712
789
|
jobId,
|
|
713
790
|
labelledBy: c.get("sessionUser")?.providerUserId ? Number(c.get("sessionUser")?.providerUserId) : null,
|
|
@@ -716,6 +793,8 @@ function createJobsRouter() {
|
|
|
716
793
|
return c.json({ label: parsed.data.label }, 200);
|
|
717
794
|
});
|
|
718
795
|
app.delete("/:id/findings/:fingerprint/label", async (c) => {
|
|
796
|
+
const denied = await requirePermission(c, "jobs.label", { type: "job", id: c.req.param("id") });
|
|
797
|
+
if (denied) return denied;
|
|
719
798
|
const jobId = c.req.param("id");
|
|
720
799
|
const fingerprint = c.req.param("fingerprint");
|
|
721
800
|
const target = await c.env.deps.repositories.fileReviews.getFindingLabelTarget(c.env, jobId, fingerprint);
|
|
@@ -724,6 +803,8 @@ function createJobsRouter() {
|
|
|
724
803
|
return c.body(null, 204);
|
|
725
804
|
});
|
|
726
805
|
app.delete("/:id", async (c) => {
|
|
806
|
+
const denied = await requirePermission(c, "jobs.delete", { type: "job", id: c.req.param("id") });
|
|
807
|
+
if (denied) return denied;
|
|
727
808
|
const jobs = c.env.deps.repositories.jobs;
|
|
728
809
|
const id = c.req.param("id");
|
|
729
810
|
const raw = await jobs.getJobForProcessing(c.env, id);
|
|
@@ -768,10 +849,14 @@ async function mapWithConcurrency(items, limit, mapper) {
|
|
|
768
849
|
function createReposRouter() {
|
|
769
850
|
const app = new Hono5();
|
|
770
851
|
app.get("/", async (c) => {
|
|
852
|
+
const denied = await requirePermission(c, "repos.read");
|
|
853
|
+
if (denied) return denied;
|
|
771
854
|
const repos = await c.env.deps.repositories.repoConfigs.listRepoConfigs(c.env);
|
|
772
855
|
return c.json({ repos });
|
|
773
856
|
});
|
|
774
857
|
app.get("/install", async (c) => {
|
|
858
|
+
const denied = await requirePermission(c, "repos.install");
|
|
859
|
+
if (denied) return denied;
|
|
775
860
|
try {
|
|
776
861
|
return c.redirect(await c.env.deps.gitProvider.getAppInstallationUrl(), 302);
|
|
777
862
|
} catch (error) {
|
|
@@ -780,6 +865,10 @@ function createReposRouter() {
|
|
|
780
865
|
}
|
|
781
866
|
});
|
|
782
867
|
app.post("/sync", async (c) => {
|
|
868
|
+
const denied = await requirePermission(c, "repos.sync");
|
|
869
|
+
if (denied) return denied;
|
|
870
|
+
const throttled = await requireQuota(c, { action: "repos.sync" });
|
|
871
|
+
if (throttled) return throttled;
|
|
783
872
|
try {
|
|
784
873
|
const installations = await c.env.deps.gitProvider.listInstallations();
|
|
785
874
|
const synced = [];
|
|
@@ -823,6 +912,8 @@ function createReposRouter() {
|
|
|
823
912
|
}
|
|
824
913
|
});
|
|
825
914
|
app.get("/:owner/:repo/config", async (c) => {
|
|
915
|
+
const denied = await requirePermission(c, "repos.read", { type: "repo", id: `${c.req.param("owner")}/${c.req.param("repo")}` });
|
|
916
|
+
if (denied) return denied;
|
|
826
917
|
const repo = await c.env.deps.repositories.repoConfigs.getRepoConfigRecord(c.env, c.req.param("owner"), c.req.param("repo"));
|
|
827
918
|
if (!repo) {
|
|
828
919
|
return jsonError("Repository config not found.", 404);
|
|
@@ -830,6 +921,8 @@ function createReposRouter() {
|
|
|
830
921
|
return c.json({ repo });
|
|
831
922
|
});
|
|
832
923
|
app.patch("/:owner/:repo/config", async (c) => {
|
|
924
|
+
const denied = await requirePermission(c, "repos.config.write", { type: "repo", id: `${c.req.param("owner")}/${c.req.param("repo")}` });
|
|
925
|
+
if (denied) return denied;
|
|
833
926
|
const { owner, repo } = c.req.param();
|
|
834
927
|
const body = await c.req.json();
|
|
835
928
|
const parsedPatch = repoConfigPatchSchema.safeParse(body);
|
|
@@ -885,6 +978,8 @@ import { Hono as Hono6 } from "hono";
|
|
|
885
978
|
function createStatsRouter() {
|
|
886
979
|
const app = new Hono6();
|
|
887
980
|
app.get("/", async (c) => {
|
|
981
|
+
const denied = await requirePermission(c, "stats.read");
|
|
982
|
+
if (denied) return denied;
|
|
888
983
|
const daysParam = c.req.query("days");
|
|
889
984
|
const days = daysParam ? parseInt(daysParam, 10) : 30;
|
|
890
985
|
const timeZone = c.req.query("tz") ?? "UTC";
|
|
@@ -941,6 +1036,8 @@ function readModelIdParam(value) {
|
|
|
941
1036
|
function createModelsRouter() {
|
|
942
1037
|
const app = new Hono7();
|
|
943
1038
|
app.get("/", async (c) => {
|
|
1039
|
+
const denied = await requirePermission(c, "models.read");
|
|
1040
|
+
if (denied) return denied;
|
|
944
1041
|
const modelConfigsRepo = c.env.deps.repositories.modelConfigs;
|
|
945
1042
|
const [providers, configs] = await Promise.all([
|
|
946
1043
|
modelConfigsRepo.listLlmProviders(c.env),
|
|
@@ -949,6 +1046,8 @@ function createModelsRouter() {
|
|
|
949
1046
|
return c.json({ providers, configs });
|
|
950
1047
|
});
|
|
951
1048
|
app.post("/sync", async (c) => {
|
|
1049
|
+
const denied = await requirePermission(c, "models.sync");
|
|
1050
|
+
if (denied) return denied;
|
|
952
1051
|
const modelConfigsRepo = c.env.deps.repositories.modelConfigs;
|
|
953
1052
|
const syncErrors = await c.env.deps.modelRunner.syncProviderModelCatalog();
|
|
954
1053
|
const [providers, configs] = await Promise.all([
|
|
@@ -958,10 +1057,14 @@ function createModelsRouter() {
|
|
|
958
1057
|
return c.json({ providers, configs, syncErrors });
|
|
959
1058
|
});
|
|
960
1059
|
app.get("/global", async (c) => {
|
|
1060
|
+
const denied = await requirePermission(c, "models.read");
|
|
1061
|
+
if (denied) return denied;
|
|
961
1062
|
const config = await c.env.deps.config.getGlobalConfig();
|
|
962
1063
|
return c.json({ config });
|
|
963
1064
|
});
|
|
964
1065
|
app.patch("/global", async (c) => {
|
|
1066
|
+
const denied = await requirePermission(c, "models.global.write");
|
|
1067
|
+
if (denied) return denied;
|
|
965
1068
|
const body = await c.req.json();
|
|
966
1069
|
const parsed = globalModelConfigSchema.safeParse(body);
|
|
967
1070
|
if (!parsed.success) {
|
|
@@ -971,6 +1074,8 @@ function createModelsRouter() {
|
|
|
971
1074
|
return c.json({ ok: true });
|
|
972
1075
|
});
|
|
973
1076
|
app.post("/providers", async (c) => {
|
|
1077
|
+
const denied = await requirePermission(c, "models.provider.create");
|
|
1078
|
+
if (denied) return denied;
|
|
974
1079
|
const parsed = providerCreateSchema.safeParse(await c.req.json());
|
|
975
1080
|
if (!parsed.success) {
|
|
976
1081
|
return jsonError("Invalid provider config.", 400);
|
|
@@ -997,6 +1102,8 @@ function createModelsRouter() {
|
|
|
997
1102
|
}
|
|
998
1103
|
});
|
|
999
1104
|
app.patch("/providers/:id", async (c) => {
|
|
1105
|
+
const denied = await requirePermission(c, "models.provider.update", { type: "llmProvider", id: c.req.param("id") });
|
|
1106
|
+
if (denied) return denied;
|
|
1000
1107
|
const id = c.req.param("id");
|
|
1001
1108
|
if (!providerIdSchema.safeParse(id).success) {
|
|
1002
1109
|
return jsonError("Invalid provider id.", 400);
|
|
@@ -1028,6 +1135,8 @@ function createModelsRouter() {
|
|
|
1028
1135
|
}
|
|
1029
1136
|
});
|
|
1030
1137
|
app.delete("/providers/:id", async (c) => {
|
|
1138
|
+
const denied = await requirePermission(c, "models.provider.delete", { type: "llmProvider", id: c.req.param("id") });
|
|
1139
|
+
if (denied) return denied;
|
|
1031
1140
|
const id = c.req.param("id");
|
|
1032
1141
|
if (!providerIdSchema.safeParse(id).success) {
|
|
1033
1142
|
return jsonError("Invalid provider id.", 400);
|
|
@@ -1039,6 +1148,10 @@ function createModelsRouter() {
|
|
|
1039
1148
|
return c.json({ ok: true });
|
|
1040
1149
|
});
|
|
1041
1150
|
app.post("/:id/test", async (c) => {
|
|
1151
|
+
const denied = await requirePermission(c, "models.test", { type: "modelConfig", id: c.req.param("id") });
|
|
1152
|
+
if (denied) return denied;
|
|
1153
|
+
const throttled = await requireQuota(c, { action: "models.test" });
|
|
1154
|
+
if (throttled) return throttled;
|
|
1042
1155
|
const modelId = readModelIdParam(c.req.param("id"));
|
|
1043
1156
|
const parsedModelId = modelIdSchema.safeParse(modelId);
|
|
1044
1157
|
if (!parsedModelId.success) {
|
|
@@ -1058,6 +1171,8 @@ function createModelsRouter() {
|
|
|
1058
1171
|
}
|
|
1059
1172
|
});
|
|
1060
1173
|
app.post("/:id", async (c) => {
|
|
1174
|
+
const denied = await requirePermission(c, "models.mapping.write", { type: "modelConfig", id: c.req.param("id") });
|
|
1175
|
+
if (denied) return denied;
|
|
1061
1176
|
const modelId = readModelIdParam(c.req.param("id"));
|
|
1062
1177
|
const parsedModelId = modelIdSchema.safeParse(modelId);
|
|
1063
1178
|
if (!parsedModelId.success) {
|
|
@@ -1077,6 +1192,8 @@ function createModelsRouter() {
|
|
|
1077
1192
|
return c.json({ ok: true, config: saved });
|
|
1078
1193
|
});
|
|
1079
1194
|
app.delete("/:id", async (c) => {
|
|
1195
|
+
const denied = await requirePermission(c, "models.mapping.write", { type: "modelConfig", id: c.req.param("id") });
|
|
1196
|
+
if (denied) return denied;
|
|
1080
1197
|
const modelId = readModelIdParam(c.req.param("id"));
|
|
1081
1198
|
const parsedModelId = modelIdSchema.safeParse(modelId);
|
|
1082
1199
|
if (!parsedModelId.success) {
|
|
@@ -1107,10 +1224,14 @@ var reviewSettingsPatchSchema = z4.strictObject({
|
|
|
1107
1224
|
function createSettingsRouter() {
|
|
1108
1225
|
const app = new Hono8();
|
|
1109
1226
|
app.get("/", async (c) => {
|
|
1227
|
+
const denied = await requirePermission(c, "settings.read");
|
|
1228
|
+
if (denied) return denied;
|
|
1110
1229
|
const settings = await c.env.deps.repositories.appSettings.getReviewSettings(c.env);
|
|
1111
1230
|
return c.json({ settings });
|
|
1112
1231
|
});
|
|
1113
1232
|
app.patch("/", async (c) => {
|
|
1233
|
+
const denied = await requirePermission(c, "settings.write");
|
|
1234
|
+
if (denied) return denied;
|
|
1114
1235
|
const body = await c.req.json().catch(() => null);
|
|
1115
1236
|
const parsed = reviewSettingsPatchSchema.safeParse(body);
|
|
1116
1237
|
if (!parsed.success) {
|
|
@@ -1126,21 +1247,27 @@ function createSettingsRouter() {
|
|
|
1126
1247
|
|
|
1127
1248
|
// src/router.ts
|
|
1128
1249
|
async function serveIndex(c) {
|
|
1129
|
-
const
|
|
1130
|
-
if (typeof
|
|
1131
|
-
return
|
|
1250
|
+
const assets = c.env.ASSETS;
|
|
1251
|
+
if (assets && typeof assets.fetch === "function") {
|
|
1252
|
+
return assets.fetch(new Request(new URL("/", c.req.url), c.req.raw));
|
|
1132
1253
|
}
|
|
1133
1254
|
return c.text("Not Found: Please mount UI static assets handler here.", 404);
|
|
1134
1255
|
}
|
|
1135
|
-
function createApiRouter() {
|
|
1256
|
+
function createApiRouter(options = {}) {
|
|
1136
1257
|
const app = new Hono9();
|
|
1137
1258
|
app.use("*", observability);
|
|
1259
|
+
for (const middleware of options.beforeAuth ?? []) {
|
|
1260
|
+
app.use("*", middleware);
|
|
1261
|
+
}
|
|
1138
1262
|
app.use("/auth/logout", requireSession);
|
|
1139
1263
|
app.use("/auth/logout", requireCsrfHeader);
|
|
1140
1264
|
app.route("/auth", createAuthRouter());
|
|
1141
1265
|
app.route("/webhook", createWebhookRouter());
|
|
1142
1266
|
app.use("/api/*", requireSession);
|
|
1143
1267
|
app.use("/api/*", requireCsrfHeader);
|
|
1268
|
+
for (const middleware of options.afterAuth ?? []) {
|
|
1269
|
+
app.use("/api/*", middleware);
|
|
1270
|
+
}
|
|
1144
1271
|
app.route("/api/auth", createAuthApiRouter());
|
|
1145
1272
|
app.route("/api/jobs", createJobsRouter());
|
|
1146
1273
|
app.route("/api/repos", createReposRouter());
|
|
@@ -1157,9 +1284,20 @@ function createApiRouter() {
|
|
|
1157
1284
|
app.get("/health", requireSession, serveIndex);
|
|
1158
1285
|
app.get("/settings", requireSession, serveIndex);
|
|
1159
1286
|
app.get("/account", requireSession, serveIndex);
|
|
1287
|
+
for (const path of options.publicPages ?? []) {
|
|
1288
|
+
app.get(path, serveIndex);
|
|
1289
|
+
}
|
|
1290
|
+
for (const path of options.pages ?? []) {
|
|
1291
|
+
app.get(path, requireSession, serveIndex);
|
|
1292
|
+
}
|
|
1293
|
+
options.routes?.(app);
|
|
1160
1294
|
return app;
|
|
1161
1295
|
}
|
|
1162
1296
|
export {
|
|
1163
|
-
createApiRouter
|
|
1297
|
+
createApiRouter,
|
|
1298
|
+
requireCsrfHeader,
|
|
1299
|
+
requirePermission,
|
|
1300
|
+
requireQuota,
|
|
1301
|
+
requireSession
|
|
1164
1302
|
};
|
|
1165
1303
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/router.ts","../src/middleware/auth.ts","../src/http.ts","../src/sessions.ts","../src/middleware/csrf.ts","../src/logger.ts","../src/middleware/observability.ts","../src/routes/auth.ts","../src/routes/webhook.ts","../src/routes/api/auth.ts","../src/routes/api/jobs.ts","../src/routes/api/repos.ts","../src/routes/api/stats.ts","../src/routes/api/models.ts","../src/routes/api/settings.ts"],"sourcesContent":["import { Hono } from 'hono';\nimport type { Context } from 'hono';\nimport type { ApiEnv } from './ports';\nimport { requireSession } from './middleware/auth';\nimport { requireCsrfHeader } from './middleware/csrf';\nimport { observability } from './middleware/observability';\nimport { createAuthRouter } from './routes/auth';\nimport { createWebhookRouter } from './routes/webhook';\nimport { createAuthApiRouter } from './routes/api/auth';\nimport { createJobsRouter } from './routes/api/jobs';\nimport { createReposRouter } from './routes/api/repos';\nimport { createStatsRouter } from './routes/api/stats';\nimport { createModelsRouter } from './routes/api/models';\nimport { createSettingsRouter } from './routes/api/settings';\n\nasync function serveIndex(c: Context<ApiEnv>) {\n // If the host platform passes an ASSETS binding via `c.env`, use it (e.g., Cloudflare Workers).\n const assetsFetch = (c.env as any).ASSETS?.fetch;\n if (typeof assetsFetch === 'function') {\n return assetsFetch(new URL('/index.html', c.req.url));\n }\n \n return c.text('Not Found: Please mount UI static assets handler here.', 404);\n}\n\nexport function createApiRouter() {\n const app = new Hono<ApiEnv>();\n\n app.use('*', observability);\n app.use('/auth/logout', requireSession);\n app.use('/auth/logout', requireCsrfHeader);\n\n app.route('/auth', createAuthRouter());\n app.route('/webhook', createWebhookRouter());\n\n app.use('/api/*', requireSession);\n app.use('/api/*', requireCsrfHeader);\n\n app.route('/api/auth', createAuthApiRouter());\n app.route('/api/jobs', createJobsRouter());\n app.route('/api/repos', createReposRouter());\n app.route('/api/stats', createStatsRouter());\n app.route('/api/models', createModelsRouter());\n app.route('/api/settings', createSettingsRouter());\n\n app.get('/login', serveIndex);\n app.get('/', serveIndex); // Unauthenticated landing page\n app.get('/dashboard', requireSession, serveIndex);\n app.get('/jobs', requireSession, serveIndex);\n app.get('/jobs/*', requireSession, serveIndex);\n app.get('/repos', requireSession, serveIndex);\n app.get('/stats', requireSession, serveIndex);\n app.get('/health', requireSession, serveIndex);\n app.get('/settings', requireSession, serveIndex);\n app.get('/account', requireSession, serveIndex);\n\n return app;\n}\n","import { createMiddleware } from 'hono/factory';\nimport type { ApiEnv } from '../ports';\nimport { wantsHtml } from '../http';\nimport { readSession } from '../sessions';\n\nexport const requireSession = createMiddleware<ApiEnv>(async (c, next) => {\n const session = await readSession(c);\n if (!session) {\n if (wantsHtml(c.req.raw)) {\n return c.redirect('/login');\n }\n\n return Response.json({ error: 'Unauthorized' }, { status: 401 });\n }\n\n await next();\n});\n","export function jsonError(message: string, status = 400) {\n return Response.json({ error: message }, { status });\n}\n\nexport function wantsHtml(request: Request) {\n const accept = request.headers.get('accept') ?? '';\n return accept.includes('text/html');\n}\n","import { deleteCookie, getCookie, setCookie } from 'hono/cookie';\nimport type { Context } from 'hono';\nimport type { ApiEnv } from './ports';\nimport type { DashboardSessionUser } from '@codraoss/core/ports';\n\nconst SESSION_COOKIE_NAME = 'codra_session';\nconst SESSION_TTL_SECONDS = 60 * 60 * 24 * 7;\n\ntype SessionRecord = DashboardSessionUser;\n\nexport async function createSession(c: Context<ApiEnv>, session: SessionRecord) {\n const token = await c.env.deps.sessionStore.createSession(session);\n\n setCookie(c, SESSION_COOKIE_NAME, token, {\n httpOnly: true,\n sameSite: 'Lax',\n secure: true,\n path: '/',\n maxAge: SESSION_TTL_SECONDS,\n });\n\n c.set('sessionToken', token);\n c.set('sessionUser', session);\n\n return token;\n}\n\nexport async function destroySession(c: Context<ApiEnv>) {\n const token = getCookie(c, SESSION_COOKIE_NAME);\n if (token) {\n await c.env.deps.sessionStore.destroySession(token);\n }\n\n c.set('sessionToken', null);\n c.set('sessionUser', null);\n\n deleteCookie(c, SESSION_COOKIE_NAME, {\n path: '/',\n });\n}\n\nexport async function readSession(c: Context<ApiEnv>) {\n const token = getCookie(c, SESSION_COOKIE_NAME) ?? null;\n c.set('sessionToken', token);\n\n if (!token) {\n c.set('sessionUser', null);\n return null;\n }\n\n const session = await c.env.deps.sessionStore.readSession(token);\n c.set('sessionUser', session);\n return session;\n}\n","import { createMiddleware } from 'hono/factory';\nimport type { ApiEnv } from '../ports';\n\nconst SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);\nconst REQUESTED_WITH = 'XMLHttpRequest';\n\nexport const requireCsrfHeader = createMiddleware<ApiEnv>(async (c, next) => {\n if (SAFE_METHODS.has(c.req.method.toUpperCase())) {\n await next();\n return;\n }\n\n if (c.req.header('x-requested-with') !== REQUESTED_WITH) {\n return c.json({ error: 'Forbidden' }, 403);\n }\n\n await next();\n});\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport { formatLogRecord, setLoggerSink } from '@codraoss/core/logger';\n\n// The request-context half of the logger. Scrubbing and record shaping live in @codraoss/core/logger;\n// this file owns everything platform-bound -- AsyncLocalStorage and the console sink -- so that\n// node:async_hooks never enters the engine package. Importing this module installs it as the sink\n// that @codraoss/core's `logger` facade delegates to (see the bottom of the file).\nconst storage = new AsyncLocalStorage<Record<string, any>>();\n\nclass Logger {\n constructor(private context: Record<string, any> = {}) {}\n\n withContext(newContext: Record<string, any>) {\n return new Logger({ ...this.context, ...newContext });\n }\n\n private log(level: string, message: string, data?: any) {\n const store = storage.getStore() || {};\n const output = formatLogRecord(level, message, [store, this.context], data);\n\n if (level === 'error') {\n console.error(JSON.stringify(output));\n } else if (level === 'warn') {\n console.warn(JSON.stringify(output));\n } else {\n console.log(JSON.stringify(output));\n }\n }\n\n runWithContext<T>(context: Record<string, any>, fn: () => T): T {\n return storage.run({ ...storage.getStore(), ...context }, fn);\n }\n\n info(message: string, data?: any) {\n this.log('info', message, data);\n }\n\n error(message: string, data?: any) {\n if (data instanceof Error) {\n this.log('error', message, {\n name: data.name,\n message: data.message,\n stack: data.stack,\n });\n } else {\n this.log('error', message, data);\n }\n }\n\n warn(message: string, data?: any) {\n this.log('warn', message, data);\n }\n\n debug(message: string, data?: any) {\n this.log('debug', message, data);\n }\n}\n\nexport const logger = new Logger();\n\n// Wired at import scope so engine code logging through @codraoss/core's facade lands here, with request\n// context attached, rather than in core's bare console fallback.\nsetLoggerSink(logger);\n","import type { MiddlewareHandler } from 'hono';\nimport type { ApiEnv } from '../ports';\nimport { logger } from '../logger';\n\nexport const observability: MiddlewareHandler<ApiEnv> = async (c, next) => {\n const requestId = c.req.header('x-request-id') || crypto.randomUUID();\n c.set('requestId', requestId);\n\n return logger.runWithContext({\n requestId,\n method: c.req.method,\n path: c.req.path,\n }, async () => {\n logger.info(`Incoming request: ${c.req.method} ${c.req.path}`);\n\n const start = Date.now();\n await next();\n const duration = Date.now() - start;\n\n logger.info(`Request completed: ${c.req.method} ${c.req.path}`, {\n status: c.res.status,\n durationMs: duration,\n });\n });\n};\n","import { Hono } from 'hono';\nimport type { ApiEnv } from '../ports';\nimport { createSession, destroySession } from '../sessions';\n\nfunction redirectToLogin(reason: string) {\n const params = new URLSearchParams({ error: reason });\n return `/login?${params.toString()}`;\n}\n\nexport function parseAllowedUsers(input: string) {\n return new Set(\n input\n .split(',')\n .map((value) => value.trim().toLowerCase())\n .filter(Boolean),\n );\n}\n\nexport function createAuthRouter() {\n const app = new Hono<ApiEnv>();\n\n app.get('/github', async (c) => {\n const state = await c.env.deps.authProvider.createOAuthState();\n const result = await c.env.deps.authProvider.beginAuthorization(c.env.AUTH_CALLBACK_URL, state);\n return c.redirect(result.url, 302);\n });\n\n app.get('/github/callback', async (c) => {\n const error = c.req.query('error');\n if (error) {\n return c.redirect(redirectToLogin(error), 302);\n }\n\n const code = c.req.query('code')?.trim();\n const state = c.req.query('state')?.trim();\n if (!code || !state) {\n return c.redirect(redirectToLogin('invalid_callback'), 302);\n }\n\n const stateMatches = await c.env.deps.authProvider.consumeOAuthState(state);\n if (!stateMatches) {\n return c.redirect(redirectToLogin('invalid_state'), 302);\n }\n\n try {\n const { identity } = await c.env.deps.authProvider.completeAuthorization(code, state, state);\n const allowedUsers = parseAllowedUsers(c.env.DASHBOARD_ALLOWED_USERS);\n\n if (!allowedUsers.has(identity.login.toLowerCase())) {\n return c.redirect(redirectToLogin('not_allowed'), 302);\n }\n\n await destroySession(c);\n await createSession(c, identity);\n\n // Best-effort: a DB hiccup must not block sign-in (the account page self-heals on next load).\n try {\n await c.env.deps.repositories.accounts.upsertAccountSettings(c.env as any, {\n githubUserId: Number(identity.providerUserId),\n githubUsername: identity.login,\n accountName: identity.name,\n accountEmail: identity.email,\n });\n } catch (err) {\n c.env.deps.platform.logger.warn('Failed to persist account settings on sign-in', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n\n return c.redirect('/dashboard', 302);\n } catch {\n return c.redirect(redirectToLogin('oauth_failed'), 302);\n }\n });\n\n app.post('/logout', async (c) => {\n await destroySession(c);\n return c.json({ ok: true });\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport type { Context } from 'hono';\nimport {\n isFeedbackGitHubWebhookEvent,\n type FeedbackWebhookPayload,\n type GitHubReviewCommentPayload,\n type GitHubWebhookPayload,\n} from '@codraoss/schema/github';\nimport type { ApiEnv } from '../ports';\nimport { jsonError } from '../http';\n\n// Matches via the invisible `codra-fp` marker GitHub echoes back verbatim; comments without one are ignored. Best-effort: failures here must never surface as a webhook error GitHub retries.\nasync function handleFeedbackEvent(\n c: Context<ApiEnv>,\n input: {\n eventName: string;\n payload: FeedbackWebhookPayload;\n repositoryId: number | null;\n },\n): Promise<number> {\n const { eventName, payload, repositoryId } = input;\n // The repository has to be one we know; the FK would reject anything else.\n if (repositoryId === null) return 0;\n\n const botLogin = (c.env.BOT_USERNAME ?? '').toLowerCase();\n // GitHub fires these events for human threads too; without this filter we'd suppress our own findings based on humans deleting each other's comments.\n const isOurs = (comment: GitHubReviewCommentPayload) =>\n Boolean(botLogin) && (comment.user?.login ?? '').toLowerCase().startsWith(botLogin);\n\n const prNumber = payload.pull_request?.number ?? null;\n const entries: any[] = [];\n const reopened: number[] = [];\n\n const add = (comment: GitHubReviewCommentPayload, outcome: any) => {\n if (!isOurs(comment) || !comment?.body) return;\n \n // We need a port for parsing finding markers since it used to come from @server/services/formatter\n // We'll use a local minimal parser or expect the webhook port to provide it\n const markerMatch = (comment.body || '').match(/<!-- codra-fp: (.*?) -->/);\n if (!markerMatch) return;\n \n let fingerprint = markerMatch[1];\n let anchorHash = '';\n let fingerprintV2 = null;\n try {\n const parts = JSON.parse(fingerprint);\n if (Array.isArray(parts) && parts.length >= 2) {\n fingerprintV2 = parts[0];\n fingerprint = parts[1];\n if (parts.length >= 3) anchorHash = parts[2];\n }\n } catch {\n // old format\n }\n \n entries.push({\n repositoryId,\n prNumber,\n fingerprint,\n anchorHash,\n fingerprintV2,\n githubCommentId: comment.id,\n outcome,\n });\n };\n\n if (eventName === 'pull_request_review_comment') {\n const { action, comment } = payload as Extract<FeedbackWebhookPayload, { comment: unknown }>;\n // 'created' is how we learn the real GitHub comment id for free, with no extra request; 'deleted' is the only negative signal we act on.\n if (action === 'created') add(comment, 'posted');\n else if (action === 'deleted') add(comment, 'deleted');\n } else if (eventName === 'pull_request_review_thread') {\n const { action, thread } = payload as Extract<FeedbackWebhookPayload, { thread: unknown }>;\n for (const comment of thread?.comments ?? []) {\n if (action === 'resolved') {\n add(comment, 'resolved');\n } else if (action === 'unresolved') {\n add(comment, 'unresolved');\n if (isOurs(comment)) reopened.push(comment.id);\n }\n }\n }\n\n if (entries.length === 0) return 0;\n\n try {\n const feedbackRepo = c.env.deps.repositories.commentFeedback;\n // Reopening a thread must retract the earlier 'resolved' row, or the finding stays recorded as accepted forever.\n if (reopened.length > 0) await feedbackRepo.clearResolvedFeedback(c.env as any, repositoryId, reopened);\n return await feedbackRepo.recordCommentFeedback(c.env as any, entries);\n } catch (error) {\n c.env.deps.platform.logger.warn('Could not record comment feedback', {\n eventName,\n repositoryId,\n error: error instanceof Error ? error.message : String(error),\n });\n return 0;\n }\n}\n\nexport async function handleGitHubWebhook(c: Context<ApiEnv>) {\n const eventName = c.req.header('x-github-event');\n const deliveryId = c.req.header('x-github-delivery');\n const signature = c.req.header('x-hub-signature-256');\n const rawBody = await c.req.text();\n\n if (!eventName || !deliveryId) {\n return jsonError('Missing GitHub webhook headers.', 400);\n }\n\n const verified = await c.env.deps.webhook.verifySignature(signature ?? null, rawBody);\n if (!verified) {\n return jsonError('Invalid webhook signature.', 401);\n }\n\n let payload: GitHubWebhookPayload;\n try {\n payload = JSON.parse(rawBody) as GitHubWebhookPayload;\n } catch {\n return jsonError('Invalid webhook JSON payload.', 400);\n }\n\n // Feedback deliveries carry the whole PR object plus diff hunks per comment; skip storing the payload (row is still written, so dedup still works).\n const isFeedbackEvent = isFeedbackGitHubWebhookEvent(eventName);\n\n const delivery = await c.env.deps.repositories.webhookDeliveries.recordWebhookDelivery(c.env as any, {\n deliveryId,\n eventName,\n owner: 'repository' in payload ? (payload as any).repository.owner.login : null,\n repo: 'repository' in payload ? (payload as any).repository.name : null,\n payload: isFeedbackEvent ? null : payload,\n });\n\n if (!delivery.inserted) {\n return c.json({ ok: true, duplicate: true }, 202);\n }\n\n const installationId = String((payload as any).installation?.id ?? '');\n if (!installationId || !('repository' in payload) || !payload.repository) {\n return c.json({ ok: true, ignored: true }, 202);\n }\n\n // Handled inline before loadRepoConfig: recording feedback needs no repo config, queue message, or Workflow, so skip that KV/DB cost entirely.\n if (isFeedbackEvent) {\n const recorded = await handleFeedbackEvent(c, {\n eventName,\n payload: payload as unknown as FeedbackWebhookPayload,\n repositoryId: delivery.repositoryId,\n });\n return c.json({ ok: true, feedback: true, recorded }, 202);\n }\n\n const normalized = c.env.deps.webhook.normalizePayload(eventName, payload);\n if (!normalized) {\n return c.json({ ok: true, ignored: true, eventName }, 202);\n }\n\n const repoConfig = await c.env.deps.config.loadRepoConfig({\n installationId,\n owner: (payload as any).repository.owner.login,\n repo: (payload as any).repository.name,\n });\n\n if (repoConfig.enabled === false) {\n return c.json({ ok: true, ignored: true, reason: 'repository_disabled' }, 202);\n }\n\n const extracted = c.env.deps.webhook.extractReviewRequest({\n eventName: normalized.eventName,\n payload: normalized.payload,\n botUsername: c.env.BOT_USERNAME,\n config: repoConfig.parsedJson,\n });\n\n if (extracted?.commitSha && extracted.baseSha) {\n const jobsRepo = c.env.deps.repositories.jobs;\n const existingJob = await jobsRepo.findExistingJobForHead(c.env as any, {\n owner: extracted.owner,\n repo: extracted.repo,\n prNumber: extracted.prNumber,\n commitSha: extracted.commitSha,\n trigger: extracted.trigger,\n });\n\n if (existingJob) {\n return c.json({\n ok: true,\n duplicate: true,\n message: existingJob.status === 'queued' ? 'queued' : 'duplicate',\n job: existingJob,\n }, 202);\n }\n\n const job = await jobsRepo.insertJob(c.env as any, {\n installationId: extracted.installationId,\n owner: extracted.owner,\n repo: extracted.repo,\n prNumber: extracted.prNumber,\n prTitle: extracted.prTitle,\n prAuthor: extracted.prAuthor,\n commitSha: extracted.commitSha,\n baseSha: extracted.baseSha,\n trigger: extracted.trigger,\n headRef: extracted.headRef,\n baseRef: extracted.baseRef,\n configSnapshot: repoConfig.parsedJson,\n });\n\n await jobsRepo.supersedeOlderJobs(c.env as any, {\n installationId: extracted.installationId,\n owner: extracted.owner,\n repo: extracted.repo,\n prNumber: extracted.prNumber,\n newJobId: job.id,\n });\n\n await c.env.deps.platform.enqueueReviewJob({\n jobId: job.id,\n deliveryId,\n phase: 'prepare',\n requestId: c.get('requestId'),\n });\n\n return c.json({ ok: true, message: 'queued', job }, 202);\n }\n\n // Events without a concrete job (e.g. PR close cleanup, mention lookups) still get handled by the worker.\n await c.env.deps.platform.enqueueReviewJob({\n deliveryId,\n eventName,\n requestId: c.get('requestId'),\n } as any);\n\n return c.json({ ok: true, message: 'queued' }, 202);\n}\n\nexport function createWebhookRouter() {\n const app = new Hono<ApiEnv>();\n\n app.post('/', handleGitHubWebhook);\n\n return app;\n}\n","import { isSupportedTimeZone } from '@codraoss/schema/timezone';\nimport { Hono } from 'hono';\nimport { z } from 'zod';\nimport { jsonError } from '../../http';\nimport type { ApiEnv } from '../../ports';\n\nconst emailSchema = z.strictObject({\n email: z.string().trim().email().max(254),\n});\n\n// Fields are independently optional (at least one required); timezone null means \"follow the browser\", else must be an Intl-known zone.\nconst accountUpdateSchema = z.strictObject({\n name: z.string().trim().min(1).max(120).optional(),\n timezone: z.string().trim().min(1).max(64).refine(isSupportedTimeZone, {\n message: 'Unknown time zone.',\n }).nullable().optional(),\n}).refine(\n (body) => body.name !== undefined || body.timezone !== undefined,\n { message: 'Nothing to update.' },\n);\n\nexport function createAuthApiRouter() {\n const app = new Hono<ApiEnv>();\n\n app.get('/session', async (c) => {\n const sessionUser = c.get('sessionUser');\n if (!sessionUser) {\n return Response.json({ error: 'Unauthorized' }, { status: 401 });\n }\n\n return c.json({ user: sessionUser });\n });\n\n app.get('/account', async (c) => {\n const sessionUser = c.get('sessionUser');\n if (!sessionUser) {\n return Response.json({ error: 'Unauthorized' }, { status: 401 });\n }\n\n const accounts = c.env.deps.repositories.accounts;\n const githubUserId = Number(sessionUser.providerUserId);\n\n // Self-heals for sessions created before the account_settings table existed.\n let account = await accounts.getAccountSettings(c.env as any, githubUserId);\n if (!account) {\n account = await accounts.upsertAccountSettings(c.env as any, {\n githubUserId,\n githubUsername: sessionUser.login,\n accountName: sessionUser.name,\n accountEmail: sessionUser.email,\n });\n }\n\n return c.json({ account });\n });\n\n app.patch('/account', async (c) => {\n const sessionUser = c.get('sessionUser');\n if (!sessionUser) {\n return Response.json({ error: 'Unauthorized' }, { status: 401 });\n }\n\n const body = await c.req.json().catch(() => null);\n const parsed = accountUpdateSchema.safeParse(body);\n if (!parsed.success) {\n const issue = parsed.error.issues[0]?.message;\n return jsonError(\n issue && issue !== 'Invalid input' ? issue : 'Enter a name (1-120 characters).',\n 400,\n );\n }\n\n const accounts = c.env.deps.repositories.accounts;\n const githubUserId = Number(sessionUser.providerUserId);\n\n // Ensure a row exists first (self-heal for pre-existing sessions), then update.\n const existing = await accounts.getAccountSettings(c.env as any, githubUserId);\n if (!existing) {\n await accounts.upsertAccountSettings(c.env as any, {\n githubUserId,\n githubUsername: sessionUser.login,\n accountName: sessionUser.name,\n accountEmail: sessionUser.email,\n });\n }\n\n const account = await accounts.updateAccountSettings(c.env as any, githubUserId, {\n accountName: parsed.data.name,\n timezone: parsed.data.timezone,\n });\n return c.json({ account });\n });\n\n app.get('/updates-email', async (c) => {\n const sessionUser = c.get('sessionUser');\n if (!sessionUser) {\n return Response.json({ error: 'Unauthorized' }, { status: 401 });\n }\n\n const githubUserId = Number(sessionUser.providerUserId);\n const preference = await c.env.deps.platform.getUpdatesEmailPreference(githubUserId);\n return c.json({\n status: preference?.status ?? 'pending',\n email: preference?.email ?? null,\n updatedAt: preference?.updatedAt ?? null,\n });\n });\n\n app.post('/updates-email', async (c) => {\n const sessionUser = c.get('sessionUser');\n if (!sessionUser) {\n return Response.json({ error: 'Unauthorized' }, { status: 401 });\n }\n\n const body = await c.req.json().catch(() => null);\n const parsed = emailSchema.safeParse(body);\n if (!parsed.success) {\n return jsonError('Enter a valid email address.', 400);\n }\n\n const platform = c.env.deps.platform;\n const githubUserId = Number(sessionUser.providerUserId);\n\n const existingPreference = await platform.getUpdatesEmailPreference(githubUserId);\n if (existingPreference) {\n return c.json({\n status: existingPreference.status,\n email: existingPreference.email,\n updatedAt: existingPreference.updatedAt,\n });\n }\n\n const synced = await platform.syncUpdatesEmail(githubUserId, parsed.data.email);\n if (!synced) {\n return jsonError('Could not save updates email right now.', 502);\n }\n\n const preference = await platform.getUpdatesEmailPreference(githubUserId);\n\n return c.json({\n status: preference?.status ?? 'pending',\n email: preference?.email ?? null,\n updatedAt: preference?.updatedAt ?? null,\n });\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport type { Context } from 'hono';\nimport { defaultRepoConfig, findingLabelSchema, jobsQuerySchema } from '@codraoss/schema';\nimport { jsonError } from '../../http';\nimport { parseUnifiedDiff } from '@codraoss/core/diff';\nimport { buildFileReviewPrompts } from '@codraoss/core/prompts/file-review';\nimport type { ApiEnv } from '../../ports';\n\n// Best-effort terminate; .get() throws if the instance is gone and .terminate() if already terminal, both non-fatal.\nasync function terminateJobWorkflow(c: Context<ApiEnv>, job: { id: string; workflowInstanceId?: string | null }) {\n // This interacts with bindings. The plan says \"No binding access in a handler\".\n // So we need to put workflow termination behind a platform port.\n // Wait! The user plan says: \"call a packages/core use case\". \n // Let's add it to `platform` port as well.\n await c.env.deps.platform.terminateJobWorkflow(job);\n}\n\nfunction jobEtag(input: { id: string; status: string; updatedAt: string; fileCount: number; commentCount: number }) {\n return `\"job-${input.id}-${input.status}-${input.fileCount}-${input.commentCount}-${new Date(input.updatedAt).getTime()}\"`;\n}\n\nfunction getExecutionContext(c: Context<ApiEnv>) {\n try {\n return c.executionCtx;\n } catch {\n return undefined;\n }\n}\n\nexport function createJobsRouter() {\n const app = new Hono<ApiEnv>();\n\n app.get('/', async (c) => {\n c.env.deps.platform.scheduleBestEffortJobMaintenance(getExecutionContext(c));\n\n const rawQuery = c.req.query();\n const query = jobsQuerySchema.parse(rawQuery);\n\n const result = await c.env.deps.repositories.jobs.listJobs(c.env as any, query as any);\n return c.json(result);\n });\n\n app.get('/:id', async (c) => {\n c.env.deps.platform.scheduleBestEffortJobMaintenance(getExecutionContext(c));\n\n const job = await c.env.deps.repositories.jobs.getJobDetail(c.env as any, c.req.param('id'));\n if (!job) {\n return jsonError('Job not found.', 404);\n }\n\n const etag = jobEtag(job);\n const lastModified = new Date(job.updatedAt).toUTCString();\n if (c.req.header('if-none-match') === etag) {\n return new Response(null, {\n status: 304,\n headers: {\n ETag: etag,\n 'Last-Modified': lastModified,\n },\n });\n }\n\n const response = c.json({ job });\n response.headers.set('ETag', etag);\n response.headers.set('Last-Modified', lastModified);\n response.headers.set('Cache-Control', 'private, no-cache');\n return response;\n });\n\n // diff_input is not persisted; rebuilt on demand from the job's own base/head commits (not the live PR), using the KV cache while warm.\n app.get('/:id/diffs', async (c) => {\n const job = await c.env.deps.repositories.jobs.getJobDetail(c.env as any, c.req.param('id'));\n if (!job) {\n return jsonError('Job not found.', 404);\n }\n\n const config = job.configSnapshot ?? defaultRepoConfig;\n const github = c.env.deps.gitProvider.createService(job.installationId);\n\n let rawDiff: string;\n try {\n rawDiff = await c.env.deps.platform.getOrFetchRawDiffForCompletedJob(\n c.env.deps.platform.createReviewRuntime(),\n { id: job.id, owner: job.owner, repo: job.repo, baseSha: job.baseSha, commitSha: job.commitSha },\n github,\n );\n } catch (error) {\n // Need logger from deps or imported directly since we moved it\n c.env.deps.platform.logger.warn(`Could not reconstruct diff for job ${job.id}`, error instanceof Error ? error : new Error(String(error)));\n return c.json({ diffs: {} });\n }\n\n // Must include the PR description: this reconstructs the prompt the model actually saw.\n let prDescription: string | null = null;\n try {\n prDescription = (await github.getPullRequest(job.owner, job.repo, job.prNumber)).body ?? null;\n } catch (error) {\n // Best-effort: a missing description degrades fidelity, never fails the view.\n c.env.deps.platform.logger.warn(`Could not load the PR body for job ${job.id}; prompts will omit the description`,\n error instanceof Error ? error : new Error(String(error)));\n }\n\n // The ENTIRE PR diff, not just files with a review row, so Files-changed matches GitHub mid-review.\n const diffs: Record<string, string> = {};\n for (const file of parseUnifiedDiff(rawDiff, config.review)) {\n if (file.isDeleted || file.isBinary || !file.path) continue;\n diffs[file.path] = buildFileReviewPrompts({\n file,\n prTitle: job.prTitle,\n prDescription,\n config: config.review,\n }).userPrompt;\n }\n\n const response = c.json({ diffs });\n response.headers.set('Cache-Control', 'private, max-age=60');\n return response;\n });\n\n // Shared by re-run and rerun-from-start; inherit=true links retryOfJobId and reuses `done` file reviews, false reviews everything.\n async function startReplacementJob(c: Context<ApiEnv>, rawSource: any, options: { inherit: boolean }) {\n const jobs = c.env.deps.repositories.jobs;\n const source = jobs.mapJob(rawSource);\n let configSnapshot;\n try {\n const currentConfig = await c.env.deps.config.loadRepoConfig({\n installationId: source.installationId,\n owner: source.owner,\n repo: source.repo,\n });\n configSnapshot = currentConfig?.parsedJson ?? defaultRepoConfig;\n } catch (e) {\n configSnapshot = defaultRepoConfig;\n }\n\n const job = await jobs.insertJob(c.env as any, {\n installationId: source.installationId,\n owner: source.owner,\n repo: source.repo,\n prNumber: source.prNumber,\n prTitle: source.prTitle,\n prAuthor: source.prAuthor,\n commitSha: source.commitSha,\n baseSha: jobs.bytesToHex(rawSource.base_sha), // base_sha is only in raw row/detail\n trigger: 'retry',\n headRef: rawSource.head_ref,\n baseRef: rawSource.base_ref,\n configSnapshot,\n ...(options.inherit ? { retryOfJobId: source.id } : {}),\n });\n\n await jobs.supersedeOlderJobs(c.env as any, {\n installationId: source.installationId,\n owner: source.owner,\n repo: source.repo,\n prNumber: source.prNumber,\n newJobId: job.id,\n });\n\n await c.env.deps.platform.enqueueReviewJob({\n jobId: job.id,\n deliveryId: crypto.randomUUID(),\n phase: 'prepare',\n requestId: c.get('requestId'),\n });\n\n return job;\n }\n\n // Re-run: reuse the parent's completed reviews where the model strategy still matches.\n app.post('/:id/retry', async (c) => {\n const jobs = c.env.deps.repositories.jobs;\n const rawSource = await jobs.getJobForProcessing(c.env as any, c.req.param('id'));\n if (!rawSource) {\n return jsonError('Job not found.', 404);\n }\n const job = await startReplacementJob(c, rawSource, { inherit: true });\n return c.json({ job }, 202);\n });\n\n // Rerun from start: no inheritance. Stops the current run so two workflows cannot race.\n app.post('/:id/rerun', async (c) => {\n const jobs = c.env.deps.repositories.jobs;\n const rawSource = await jobs.getJobForProcessing(c.env as any, c.req.param('id'));\n if (!rawSource) {\n return jsonError('Job not found.', 404);\n }\n const source = jobs.mapJob(rawSource);\n if (source.status === 'queued' || source.status === 'running') {\n await terminateJobWorkflow(c, source);\n }\n const job = await startReplacementJob(c, rawSource, { inherit: false });\n return c.json({ job }, 202);\n });\n\n app.post('/:id/stop', async (c) => {\n const jobs = c.env.deps.repositories.jobs;\n const id = c.req.param('id');\n const raw = await jobs.getJobForProcessing(c.env as any, id);\n if (!raw) {\n return jsonError('Job not found.', 404);\n }\n const job = jobs.mapJob(raw);\n if (job.status !== 'queued' && job.status !== 'running') {\n return jsonError('Only a queued or running job can be stopped.', 409);\n }\n await terminateJobWorkflow(c, job);\n await jobs.cancelJob(c.env as any, id);\n const updated = await jobs.getJobForProcessing(c.env as any, id);\n return c.json({ job: updated ? jobs.mapJob(updated) : job }, 200);\n });\n\n // Human verdict on one finding: WRONG suppresses repository-wide, RIGHT suppresses nothing and is purely measurement.\n app.put('/:id/findings/:fingerprint/label', async (c) => {\n const jobId = c.req.param('id');\n const fingerprint = c.req.param('fingerprint');\n\n const parsed = findingLabelSchema.safeParse(await c.req.json().catch(() => null));\n if (!parsed.success) return jsonError('Body must be {\"label\":\"right\"|\"wrong\"}.', 400);\n\n const target = await c.env.deps.repositories.fileReviews.getFindingLabelTarget(c.env as any, jobId, fingerprint);\n if (!target) return jsonError('Finding not found on this job.', 404);\n\n await c.env.deps.repositories.commentFeedback.upsertDashboardFeedback(c.env as any, {\n repositoryId: target.repository_id,\n prNumber: target.pr_number,\n fingerprint,\n anchorHash: target.anchor_hash,\n // Carried so a rejection survives the model rewording its title.\n fingerprintV2: target.fingerprint_v2,\n jobId,\n labelledBy: c.get('sessionUser')?.providerUserId ? Number(c.get('sessionUser')?.providerUserId) : null,\n outcome: parsed.data.label === 'wrong' ? 'marked_wrong' : 'marked_right',\n });\n\n return c.json({ label: parsed.data.label }, 200);\n });\n\n // Undo a label, scoped to dashboard rows so a real GitHub deletion stays recorded.\n app.delete('/:id/findings/:fingerprint/label', async (c) => {\n const jobId = c.req.param('id');\n const fingerprint = c.req.param('fingerprint');\n\n const target = await c.env.deps.repositories.fileReviews.getFindingLabelTarget(c.env as any, jobId, fingerprint);\n if (!target) return jsonError('Finding not found on this job.', 404);\n\n await c.env.deps.repositories.commentFeedback.clearDashboardFeedback(c.env as any, target.repository_id, fingerprint);\n return c.body(null, 204);\n });\n\n app.delete('/:id', async (c) => {\n const jobs = c.env.deps.repositories.jobs;\n const id = c.req.param('id');\n const raw = await jobs.getJobForProcessing(c.env as any, id);\n if (!raw) {\n return jsonError('Job not found.', 404);\n }\n const job = jobs.mapJob(raw);\n if (job.status === 'queued' || job.status === 'running') {\n await terminateJobWorkflow(c, job);\n }\n await jobs.deleteJob(c.env as any, id);\n return c.body(null, 204);\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport { z } from 'zod';\nimport type { ApiEnv } from '../../ports';\nimport { jsonError } from '../../http';\nimport { repoConfigSchema } from '@codraoss/schema';\n\nconst repoConfigPatchSchema = z\n .strictObject({\n enabled: z.boolean().optional(),\n review: repoConfigSchema.shape.review.optional(),\n model: repoConfigSchema.shape.model.optional(),\n })\n .refine(\n (patch) => patch.enabled !== undefined || patch.review !== undefined || patch.model !== undefined,\n 'Repository config patch cannot be empty.',\n );\n\nasync function mapWithConcurrency<T, R>(\n items: T[],\n limit: number,\n mapper: (item: T) => Promise<R>,\n) {\n const results: R[] = new Array(items.length);\n let nextIndex = 0;\n\n async function worker() {\n while (nextIndex < items.length) {\n const currentIndex = nextIndex;\n nextIndex += 1;\n results[currentIndex] = await mapper(items[currentIndex]);\n }\n }\n\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));\n return results;\n}\n\nexport function createReposRouter() {\n const app = new Hono<ApiEnv>();\n\n app.get('/', async (c) => {\n const repos = await c.env.deps.repositories.repoConfigs.listRepoConfigs(c.env as any);\n return c.json({ repos });\n });\n\n app.get('/install', async (c) => {\n try {\n return c.redirect(await c.env.deps.gitProvider.getAppInstallationUrl(), 302);\n } catch (error) {\n c.env.deps.platform.logger.error('Failed to resolve GitHub App installation URL:', error);\n return jsonError(`Failed to resolve GitHub App installation URL: ${error instanceof Error ? error.message : String(error)}`, 500);\n }\n });\n\n app.post('/sync', async (c) => {\n try {\n const installations = await c.env.deps.gitProvider.listInstallations();\n const synced: string[] = [];\n const repoConfigs = c.env.deps.repositories.repoConfigs;\n\n for (const inst of installations) {\n const github = c.env.deps.gitProvider.createService(String(inst.id));\n const repos = await github.listRepositories();\n\n const results = await mapWithConcurrency(\n repos,\n 5,\n async (repo: any) => {\n const owner = repo.owner.login;\n const name = repo.name;\n const fullName = `${owner}/${name}`;\n try {\n await repoConfigs.syncRepoConfig(c.env as any, {\n installationId: String(inst.id),\n owner,\n repo: name,\n });\n return fullName;\n } catch (repoError) {\n c.env.deps.platform.logger.error(`Failed to sync repo: ${fullName}`, repoError);\n return null;\n }\n },\n );\n\n const installationSynced: string[] = [];\n for (const res of results) {\n if (res) {\n synced.push(res);\n installationSynced.push(res);\n }\n }\n \n await repoConfigs.deleteStaleRepoConfigs(c.env as any, String(inst.id), installationSynced);\n }\n\n return c.json({ ok: true, synced });\n } catch (error) {\n c.env.deps.platform.logger.error('Manual sync failed:', error);\n return jsonError(`Sync failed: ${error instanceof Error ? error.message : String(error)}`, 500);\n }\n });\n\n app.get('/:owner/:repo/config', async (c) => {\n const repo = await c.env.deps.repositories.repoConfigs.getRepoConfigRecord(c.env as any, c.req.param('owner'), c.req.param('repo'));\n if (!repo) {\n return jsonError('Repository config not found.', 404);\n }\n\n return c.json({ repo });\n });\n \n app.patch('/:owner/:repo/config', async (c) => {\n const { owner, repo } = c.req.param();\n const body = await c.req.json();\n const parsedPatch = repoConfigPatchSchema.safeParse(body);\n if (!parsedPatch.success) {\n return jsonError('Invalid repository config patch.', 400);\n }\n\n const repoConfigs = c.env.deps.repositories.repoConfigs;\n const existing = await repoConfigs.getRepoConfigRecord(c.env as any, owner, repo);\n \n if (!existing) {\n return jsonError('Repository config not found.', 404);\n }\n\n const patch = parsedPatch.data;\n const hasConfigPatch = patch.review !== undefined || patch.model !== undefined;\n\n if (!hasConfigPatch && patch.enabled !== undefined) {\n await repoConfigs.updateRepoConfigEnabled(c.env as any, {\n owner,\n repo,\n enabled: patch.enabled,\n });\n await c.env.deps.config.invalidateRepoConfigCache(owner, repo);\n return c.json({ ok: true });\n }\n\n const configPatch: Partial<z.infer<typeof repoConfigSchema>> = {};\n if (patch.review !== undefined) {\n configPatch.review = patch.review;\n }\n if (patch.model !== undefined) {\n configPatch.model = patch.model;\n }\n \n const updatedParsedJson = {\n ...existing.parsedJson,\n ...configPatch,\n };\n const parsedConfig = repoConfigSchema.safeParse(updatedParsedJson);\n\n if (!parsedConfig.success) {\n return jsonError('Invalid repository config.', 400);\n }\n \n await repoConfigs.upsertRepoConfig(c.env as any, {\n installationId: existing.installationId,\n owner,\n repo,\n parsedJson: parsedConfig.data,\n enabled: patch.enabled,\n });\n await c.env.deps.config.invalidateRepoConfigCache(owner, repo);\n \n return c.json({ ok: true });\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport type { ApiEnv } from '../../ports';\n\nexport function createStatsRouter() {\n const app = new Hono<ApiEnv>();\n\n app.get('/', async (c) => {\n const daysParam = c.req.query('days');\n const days = daysParam ? parseInt(daysParam, 10) : 30;\n // Grouped in the caller's display zone so the trend matches timestamps shown elsewhere; getStats falls back to UTC if invalid.\n const timeZone = c.req.query('tz') ?? 'UTC';\n const stats = await c.env.deps.repositories.stats.getStats(c.env as any, days, timeZone);\n return c.json({ stats });\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport { z } from 'zod';\nimport { jsonError } from '../../http';\nimport { llmApiFormats } from '@codraoss/schema';\nimport type { ApiEnv } from '../../ports';\n\nconst apiFormatSchema = z.enum(llmApiFormats);\nconst positiveIntegerSchema = z.number().int().positive().finite();\nconst modelIdSchema = z.string().trim().min(1);\nconst optionalUrlSchema = z.string().trim().url().nullable().optional();\nconst providerIdSchema = z.string().uuid();\n\nconst providerCreateSchema = z.strictObject({\n name: z.string().trim().min(1),\n apiFormat: apiFormatSchema,\n baseUrl: optionalUrlSchema,\n apiKey: z.string().optional(),\n enabled: z.boolean().default(true),\n});\n\n// `.extend()` carries the parent's strictness through, so no second `.strict()` is needed here.\nconst providerUpdateSchema = providerCreateSchema.extend({\n clearApiKey: z.boolean().optional(),\n});\n\nconst modelConfigUpdateSchema = z.strictObject({\n providerId: providerIdSchema,\n modelName: z.string().trim().min(1),\n});\n\nconst globalModelConfigSchema = z.strictObject({\n main: modelIdSchema.nullable().default(null),\n fallbacks: z.array(modelIdSchema).nullable().default([]),\n size_overrides: z\n .array(\n z.strictObject({\n max_lines: positiveIntegerSchema,\n model: modelIdSchema,\n fallbacks: z.array(modelIdSchema).optional(),\n }),\n )\n .nullable()\n .optional(),\n});\n\nfunction requiresExplicitBaseUrl(apiFormat: z.infer<typeof apiFormatSchema>, baseUrl: string | null | undefined) {\n return apiFormat === 'vertex' && !baseUrl;\n}\n\nfunction readModelIdParam(value: string) {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n\nexport function createModelsRouter() {\n const app = new Hono<ApiEnv>();\n\n app.get('/', async (c) => {\n const modelConfigsRepo = c.env.deps.repositories.modelConfigs;\n const [providers, configs] = await Promise.all([\n modelConfigsRepo.listLlmProviders(c.env as any),\n modelConfigsRepo.listModelConfigs(c.env as any),\n ]);\n return c.json({ providers, configs });\n });\n\n app.post('/sync', async (c) => {\n const modelConfigsRepo = c.env.deps.repositories.modelConfigs;\n const syncErrors = await c.env.deps.modelRunner.syncProviderModelCatalog();\n const [providers, configs] = await Promise.all([\n modelConfigsRepo.listLlmProviders(c.env as any),\n modelConfigsRepo.listModelConfigs(c.env as any),\n ]);\n return c.json({ providers, configs, syncErrors });\n });\n\n app.get('/global', async (c) => {\n const config = await c.env.deps.config.getGlobalConfig();\n return c.json({ config });\n });\n\n app.patch('/global', async (c) => {\n const body = await c.req.json();\n const parsed = globalModelConfigSchema.safeParse(body);\n if (!parsed.success) {\n return jsonError('Invalid global model config.', 400);\n }\n\n await c.env.deps.config.updateGlobalConfig(parsed.data);\n return c.json({ ok: true });\n });\n\n app.post('/providers', async (c) => {\n const parsed = providerCreateSchema.safeParse(await c.req.json());\n if (!parsed.success) {\n return jsonError('Invalid provider config.', 400);\n }\n\n const input = parsed.data;\n if (requiresExplicitBaseUrl(input.apiFormat, input.baseUrl)) {\n return jsonError('Vertex AI requires a base URL with your GCP project ID and region, e.g. https://us-central1-aiplatform.googleapis.com/v1/projects/YOUR_PROJECT_ID/locations/us-central1', 400);\n }\n\n try {\n const provider = await c.env.deps.modelRunner.createProviderWithSecret(input);\n return c.json({ provider }, 201);\n } catch (error) {\n const err = error as any;\n if (err.isUniqueNameError) {\n return jsonError(`Provider ${input.name} already exists. Update the existing provider instead.`, 409);\n }\n if (err.isEncryptionConfigError) {\n return jsonError(err.message || 'LLM encryption is not configured.', 400);\n }\n if (err.isKeyRequiredError) {\n return jsonError(`Provider ${input.name} needs an API key before it can be enabled.`, 400);\n }\n throw error;\n }\n });\n\n app.patch('/providers/:id', async (c) => {\n const id = c.req.param('id');\n if (!providerIdSchema.safeParse(id).success) {\n return jsonError('Invalid provider id.', 400);\n }\n\n const parsed = providerUpdateSchema.safeParse(await c.req.json());\n if (!parsed.success) {\n return jsonError('Invalid provider config.', 400);\n }\n\n const input = parsed.data;\n if (requiresExplicitBaseUrl(input.apiFormat, input.baseUrl)) {\n return jsonError('Vertex AI requires a base URL with your GCP project ID and region, e.g. https://us-central1-aiplatform.googleapis.com/v1/projects/YOUR_PROJECT_ID/locations/us-central1', 400);\n }\n\n try {\n const provider = await c.env.deps.modelRunner.updateProviderWithSecret(id, input);\n if (!provider) return jsonError('Provider not found.', 404);\n return c.json({ provider });\n } catch (error) {\n const err = error as any;\n if (err.isUniqueNameError) {\n return jsonError(`Provider ${input.name} already exists. Choose a different provider name.`, 409);\n }\n if (err.isEncryptionConfigError) {\n return jsonError(err.message || 'LLM encryption is not configured.', 400);\n }\n if (err.isKeyRequiredError) {\n return jsonError(`Provider ${input.name} needs an API key before it can be enabled.`, 400);\n }\n throw error;\n }\n });\n\n app.delete('/providers/:id', async (c) => {\n const id = c.req.param('id');\n if (!providerIdSchema.safeParse(id).success) {\n return jsonError('Invalid provider id.', 400);\n }\n\n const result = await c.env.deps.repositories.modelConfigs.deleteLlmProvider(c.env as any, id);\n if (!result.deleted) {\n return jsonError(result.reason ?? 'Provider not found.', result.reason ? 409 : 404);\n }\n return c.json({ ok: true });\n });\n\n app.post('/:id/test', async (c) => {\n const modelId = readModelIdParam(c.req.param('id'));\n const parsedModelId = modelIdSchema.safeParse(modelId);\n if (!parsedModelId.success) {\n return jsonError('Invalid model id.', 400);\n }\n\n try {\n const response = await c.env.deps.modelRunner.testConnection(parsedModelId.data);\n return c.json(response);\n } catch (error) {\n const err = error as any;\n if (err.isNotFoundError) return jsonError('Model not found.', 404);\n if (err.isDisabledError || err.isMissingKeyError) return jsonError(err.message, 400);\n \n return jsonError(\n err.message || 'Connection test failed.',\n err.status || 502,\n );\n }\n });\n\n app.post('/:id', async (c) => {\n const modelId = readModelIdParam(c.req.param('id'));\n const parsedModelId = modelIdSchema.safeParse(modelId);\n if (!parsedModelId.success) {\n return jsonError('Invalid model id.', 400);\n }\n\n const body = await c.req.json();\n const parsed = modelConfigUpdateSchema.safeParse(body);\n if (!parsed.success) {\n return jsonError('Invalid model config.', 400);\n }\n\n const saved = await c.env.deps.repositories.modelConfigs.updateModelConfig(c.env as any, {\n modelId: parsedModelId.data,\n providerId: parsed.data.providerId,\n modelName: parsed.data.modelName,\n });\n\n if (!saved) return jsonError('Provider not found.', 404);\n return c.json({ ok: true, config: saved });\n });\n\n app.delete('/:id', async (c) => {\n const modelId = readModelIdParam(c.req.param('id'));\n const parsedModelId = modelIdSchema.safeParse(modelId);\n if (!parsedModelId.success) {\n return jsonError('Invalid model id.', 400);\n }\n\n const deleted = await c.env.deps.repositories.modelConfigs.deleteModelConfig(c.env as any, parsedModelId.data);\n if (!deleted) return jsonError('Model not found.', 404);\n return c.json({ ok: true });\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport { z } from 'zod';\nimport type { ApiEnv } from '../../ports';\nimport { jsonError } from '../../http';\nimport { reviewConcurrencyLevels, reviewMaxCommentsOptions, reviewMaxFilesRange, reviewSettingsSchema } from '@codraoss/schema';\n\nconst reviewSettingsPatchSchema = z.strictObject({\n concurrencyLevel: z.enum(reviewConcurrencyLevels).optional(),\n maxComments: z.number().int().refine(\n (value) => (reviewMaxCommentsOptions as readonly number[]).includes(value),\n 'Invalid max comments value.',\n ).optional(),\n maxFiles: z.number().int().min(reviewMaxFilesRange.min).max(reviewMaxFilesRange.max).optional(),\n}).refine(\n (settings) => Object.values(settings).some((value) => value !== undefined),\n 'At least one setting must be provided.',\n);\n\nexport function createSettingsRouter() {\n const app = new Hono<ApiEnv>();\n\n app.get('/', async (c) => {\n const settings = await c.env.deps.repositories.appSettings.getReviewSettings(c.env as any);\n return c.json({ settings });\n });\n\n app.patch('/', async (c) => {\n const body = await c.req.json().catch(() => null);\n const parsed = reviewSettingsPatchSchema.safeParse(body);\n if (!parsed.success) {\n return jsonError('Invalid review settings.', 400);\n }\n\n const current = await c.env.deps.repositories.appSettings.getReviewSettings(c.env as any);\n const next = reviewSettingsSchema.parse({ ...current, ...parsed.data });\n await c.env.deps.repositories.appSettings.updateReviewSettings(c.env as any, next);\n return c.json({ ok: true, settings: next });\n });\n\n return app;\n}\n"],"mappings":"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","names":["Hono","createMiddleware","Hono","Hono","Hono","Hono","Hono","Hono","Hono","z","z","Hono","Hono","Hono","z","z","Hono","Hono","z","z","Hono","Hono"]}
|
|
1
|
+
{"version":3,"sources":["../src/router.ts","../src/middleware/auth.ts","../src/http.ts","../src/sessions.ts","../src/middleware/csrf.ts","../src/logger.ts","../src/middleware/observability.ts","../src/routes/auth.ts","../src/routes/webhook.ts","../src/routes/api/auth.ts","../src/middleware/authorize.ts","../src/routes/api/jobs.ts","../src/routes/api/repos.ts","../src/routes/api/stats.ts","../src/routes/api/models.ts","../src/routes/api/settings.ts"],"sourcesContent":["import { Hono } from 'hono';\nimport type { Context, MiddlewareHandler } from 'hono';\nimport type { ApiEnv } from './ports';\nimport { requireSession } from './middleware/auth';\nimport { requireCsrfHeader } from './middleware/csrf';\nimport { observability } from './middleware/observability';\nimport { createAuthRouter } from './routes/auth';\nimport { createWebhookRouter } from './routes/webhook';\nimport { createAuthApiRouter } from './routes/api/auth';\nimport { createJobsRouter } from './routes/api/jobs';\nimport { createReposRouter } from './routes/api/repos';\nimport { createStatsRouter } from './routes/api/stats';\nimport { createModelsRouter } from './routes/api/models';\nimport { createSettingsRouter } from './routes/api/settings';\n\nasync function serveIndex(c: Context<ApiEnv>) {\n // If the host platform passes an ASSETS binding via `c.env`, use it (e.g., Cloudflare Workers).\n const assets = (c.env as any).ASSETS;\n if (assets && typeof assets.fetch === 'function') {\n // Method call, and `/` not `/index.html`: detaching throws, and `/index.html` 307s into a loop.\n return assets.fetch(new Request(new URL('/', c.req.url), c.req.raw));\n }\n\n return c.text('Not Found: Please mount UI static assets handler here.', 404);\n}\n\nexport interface ApiRouterOptions {\n // Cross-cutting request middleware; also sees /webhook and /auth, which the /api/* guards skip.\n beforeAuth?: MiddlewareHandler<ApiEnv>[];\n // Runs on /api/* after the session and CSRF guards, so `sessionUser` is populated.\n afterAuth?: MiddlewareHandler<ApiEnv>[];\n pages?: string[];\n publicPages?: string[];\n // Called last, so paths added under /api/* still inherit the session, CSRF and afterAuth middleware.\n routes?: (app: Hono<ApiEnv>) => void;\n}\n\nexport function createApiRouter(options: ApiRouterOptions = {}) {\n const app = new Hono<ApiEnv>();\n\n app.use('*', observability);\n for (const middleware of options.beforeAuth ?? []) {\n app.use('*', middleware);\n }\n app.use('/auth/logout', requireSession);\n app.use('/auth/logout', requireCsrfHeader);\n\n app.route('/auth', createAuthRouter());\n app.route('/webhook', createWebhookRouter());\n\n app.use('/api/*', requireSession);\n app.use('/api/*', requireCsrfHeader);\n for (const middleware of options.afterAuth ?? []) {\n app.use('/api/*', middleware);\n }\n\n app.route('/api/auth', createAuthApiRouter());\n app.route('/api/jobs', createJobsRouter());\n app.route('/api/repos', createReposRouter());\n app.route('/api/stats', createStatsRouter());\n app.route('/api/models', createModelsRouter());\n app.route('/api/settings', createSettingsRouter());\n\n app.get('/login', serveIndex);\n app.get('/', serveIndex); // Unauthenticated landing page\n app.get('/dashboard', requireSession, serveIndex);\n app.get('/jobs', requireSession, serveIndex);\n app.get('/jobs/*', requireSession, serveIndex);\n app.get('/repos', requireSession, serveIndex);\n app.get('/stats', requireSession, serveIndex);\n app.get('/health', requireSession, serveIndex);\n app.get('/settings', requireSession, serveIndex);\n app.get('/account', requireSession, serveIndex);\n\n for (const path of options.publicPages ?? []) {\n app.get(path, serveIndex);\n }\n for (const path of options.pages ?? []) {\n app.get(path, requireSession, serveIndex);\n }\n\n options.routes?.(app);\n\n return app;\n}\n","import { createMiddleware } from 'hono/factory';\nimport type { ApiEnv } from '../ports';\nimport { wantsHtml } from '../http';\nimport { readSession } from '../sessions';\n\nexport const requireSession = createMiddleware<ApiEnv>(async (c, next) => {\n const session = await readSession(c);\n if (!session) {\n if (wantsHtml(c.req.raw)) {\n return c.redirect('/login');\n }\n\n return Response.json({ error: 'Unauthorized' }, { status: 401 });\n }\n\n await next();\n});\n","export function jsonError(message: string, status = 400) {\n return Response.json({ error: message }, { status });\n}\n\nexport function wantsHtml(request: Request) {\n const accept = request.headers.get('accept') ?? '';\n return accept.includes('text/html');\n}\n","import { deleteCookie, getCookie, setCookie } from 'hono/cookie';\nimport type { Context } from 'hono';\nimport type { ApiEnv } from './ports';\nimport type { DashboardSessionUser } from '@codraoss/core/ports';\n\nconst SESSION_COOKIE_NAME = 'codra_session';\nconst SESSION_TTL_SECONDS = 60 * 60 * 24 * 7;\n\ntype SessionRecord = DashboardSessionUser;\n\nexport async function createSession(c: Context<ApiEnv>, session: SessionRecord) {\n const token = await c.env.deps.sessionStore.createSession(session);\n\n setCookie(c, SESSION_COOKIE_NAME, token, {\n httpOnly: true,\n sameSite: 'Lax',\n secure: true,\n path: '/',\n maxAge: SESSION_TTL_SECONDS,\n });\n\n c.set('sessionToken', token);\n c.set('sessionUser', session);\n\n return token;\n}\n\nexport async function destroySession(c: Context<ApiEnv>) {\n const token = getCookie(c, SESSION_COOKIE_NAME);\n if (token) {\n await c.env.deps.sessionStore.destroySession(token);\n }\n\n c.set('sessionToken', null);\n c.set('sessionUser', null);\n\n deleteCookie(c, SESSION_COOKIE_NAME, {\n path: '/',\n });\n}\n\nexport async function readSession(c: Context<ApiEnv>) {\n const token = getCookie(c, SESSION_COOKIE_NAME) ?? null;\n c.set('sessionToken', token);\n\n if (!token) {\n c.set('sessionUser', null);\n return null;\n }\n\n const session = await c.env.deps.sessionStore.readSession(token);\n c.set('sessionUser', session);\n return session;\n}\n","import { createMiddleware } from 'hono/factory';\nimport type { ApiEnv } from '../ports';\n\nconst SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);\nconst REQUESTED_WITH = 'XMLHttpRequest';\n\nexport const requireCsrfHeader = createMiddleware<ApiEnv>(async (c, next) => {\n if (SAFE_METHODS.has(c.req.method.toUpperCase())) {\n await next();\n return;\n }\n\n if (c.req.header('x-requested-with') !== REQUESTED_WITH) {\n return c.json({ error: 'Forbidden' }, 403);\n }\n\n await next();\n});\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport { formatLogRecord, setLoggerSink } from '@codraoss/core/logger';\n\n// The request-context half of the logger. Scrubbing and record shaping live in @codraoss/core/logger;\n// this file owns everything platform-bound -- AsyncLocalStorage and the console sink -- so that\n// node:async_hooks never enters the engine package. Importing this module installs it as the sink\n// that @codraoss/core's `logger` facade delegates to (see the bottom of the file).\nconst storage = new AsyncLocalStorage<Record<string, any>>();\n\nclass Logger {\n constructor(private context: Record<string, any> = {}) {}\n\n withContext(newContext: Record<string, any>) {\n return new Logger({ ...this.context, ...newContext });\n }\n\n private log(level: string, message: string, data?: any) {\n const store = storage.getStore() || {};\n const output = formatLogRecord(level, message, [store, this.context], data);\n\n if (level === 'error') {\n console.error(JSON.stringify(output));\n } else if (level === 'warn') {\n console.warn(JSON.stringify(output));\n } else {\n console.log(JSON.stringify(output));\n }\n }\n\n runWithContext<T>(context: Record<string, any>, fn: () => T): T {\n return storage.run({ ...storage.getStore(), ...context }, fn);\n }\n\n info(message: string, data?: any) {\n this.log('info', message, data);\n }\n\n error(message: string, data?: any) {\n if (data instanceof Error) {\n this.log('error', message, {\n name: data.name,\n message: data.message,\n stack: data.stack,\n });\n } else {\n this.log('error', message, data);\n }\n }\n\n warn(message: string, data?: any) {\n this.log('warn', message, data);\n }\n\n debug(message: string, data?: any) {\n this.log('debug', message, data);\n }\n}\n\nexport const logger = new Logger();\n\n// Wired at import scope so engine code logging through @codraoss/core's facade lands here, with request\n// context attached, rather than in core's bare console fallback.\nsetLoggerSink(logger);\n","import type { MiddlewareHandler } from 'hono';\nimport type { ApiEnv } from '../ports';\nimport { logger } from '../logger';\n\nexport const observability: MiddlewareHandler<ApiEnv> = async (c, next) => {\n const requestId = c.req.header('x-request-id') || crypto.randomUUID();\n c.set('requestId', requestId);\n\n return logger.runWithContext({\n requestId,\n method: c.req.method,\n path: c.req.path,\n }, async () => {\n logger.info(`Incoming request: ${c.req.method} ${c.req.path}`);\n\n const start = Date.now();\n await next();\n const duration = Date.now() - start;\n\n logger.info(`Request completed: ${c.req.method} ${c.req.path}`, {\n status: c.res.status,\n durationMs: duration,\n });\n });\n};\n","import { Hono } from 'hono';\nimport type { ApiEnv } from '../ports';\nimport { createSession, destroySession } from '../sessions';\n\nfunction redirectToLogin(reason: string) {\n const params = new URLSearchParams({ error: reason });\n return `/login?${params.toString()}`;\n}\n\nexport function parseAllowedUsers(input: string) {\n return new Set(\n input\n .split(',')\n .map((value) => value.trim().toLowerCase())\n .filter(Boolean),\n );\n}\n\nexport function createAuthRouter() {\n const app = new Hono<ApiEnv>();\n\n app.get('/github', async (c) => {\n const state = await c.env.deps.authProvider.createOAuthState();\n const result = await c.env.deps.authProvider.beginAuthorization(c.env.AUTH_CALLBACK_URL, state);\n return c.redirect(result.url, 302);\n });\n\n app.get('/github/callback', async (c) => {\n const error = c.req.query('error');\n if (error) {\n return c.redirect(redirectToLogin(error), 302);\n }\n\n const code = c.req.query('code')?.trim();\n const state = c.req.query('state')?.trim();\n if (!code || !state) {\n return c.redirect(redirectToLogin('invalid_callback'), 302);\n }\n\n const stateMatches = await c.env.deps.authProvider.consumeOAuthState(state);\n if (!stateMatches) {\n return c.redirect(redirectToLogin('invalid_state'), 302);\n }\n\n try {\n const { identity } = await c.env.deps.authProvider.completeAuthorization(code, state, state);\n const allowedUsers = parseAllowedUsers(c.env.DASHBOARD_ALLOWED_USERS);\n\n if (!allowedUsers.has(identity.login.toLowerCase())) {\n return c.redirect(redirectToLogin('not_allowed'), 302);\n }\n\n await destroySession(c);\n await createSession(c, identity);\n\n // Best-effort: a DB hiccup must not block sign-in (the account page self-heals on next load).\n try {\n await c.env.deps.repositories.accounts.upsertAccountSettings(c.env as any, {\n githubUserId: Number(identity.providerUserId),\n githubUsername: identity.login,\n accountName: identity.name,\n accountEmail: identity.email,\n });\n } catch (err) {\n c.env.deps.platform.logger.warn('Failed to persist account settings on sign-in', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n\n return c.redirect('/dashboard', 302);\n } catch {\n return c.redirect(redirectToLogin('oauth_failed'), 302);\n }\n });\n\n app.post('/logout', async (c) => {\n await destroySession(c);\n return c.json({ ok: true });\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport type { Context } from 'hono';\nimport {\n isFeedbackGitHubWebhookEvent,\n type FeedbackWebhookPayload,\n type GitHubReviewCommentPayload,\n type GitHubWebhookPayload,\n} from '@codraoss/schema/github';\nimport type { ApiEnv } from '../ports';\nimport { jsonError } from '../http';\n\n// Matches via the invisible `codra-fp` marker GitHub echoes back verbatim; comments without one are ignored. Best-effort: failures here must never surface as a webhook error GitHub retries.\nasync function handleFeedbackEvent(\n c: Context<ApiEnv>,\n input: {\n eventName: string;\n payload: FeedbackWebhookPayload;\n repositoryId: number | null;\n },\n): Promise<number> {\n const { eventName, payload, repositoryId } = input;\n // The repository has to be one we know; the FK would reject anything else.\n if (repositoryId === null) return 0;\n\n const botLogin = (c.env.BOT_USERNAME ?? '').toLowerCase();\n // GitHub fires these events for human threads too; without this filter we'd suppress our own findings based on humans deleting each other's comments.\n const isOurs = (comment: GitHubReviewCommentPayload) =>\n Boolean(botLogin) && (comment.user?.login ?? '').toLowerCase().startsWith(botLogin);\n\n const prNumber = payload.pull_request?.number ?? null;\n const entries: any[] = [];\n const reopened: number[] = [];\n\n const add = (comment: GitHubReviewCommentPayload, outcome: any) => {\n if (!isOurs(comment) || !comment?.body) return;\n \n // We need a port for parsing finding markers since it used to come from @server/services/formatter\n // We'll use a local minimal parser or expect the webhook port to provide it\n const markerMatch = (comment.body || '').match(/<!-- codra-fp: (.*?) -->/);\n if (!markerMatch) return;\n \n let fingerprint = markerMatch[1];\n let anchorHash = '';\n let fingerprintV2 = null;\n try {\n const parts = JSON.parse(fingerprint);\n if (Array.isArray(parts) && parts.length >= 2) {\n fingerprintV2 = parts[0];\n fingerprint = parts[1];\n if (parts.length >= 3) anchorHash = parts[2];\n }\n } catch {\n // old format\n }\n \n entries.push({\n repositoryId,\n prNumber,\n fingerprint,\n anchorHash,\n fingerprintV2,\n githubCommentId: comment.id,\n outcome,\n });\n };\n\n if (eventName === 'pull_request_review_comment') {\n const { action, comment } = payload as Extract<FeedbackWebhookPayload, { comment: unknown }>;\n // 'created' is how we learn the real GitHub comment id for free, with no extra request; 'deleted' is the only negative signal we act on.\n if (action === 'created') add(comment, 'posted');\n else if (action === 'deleted') add(comment, 'deleted');\n } else if (eventName === 'pull_request_review_thread') {\n const { action, thread } = payload as Extract<FeedbackWebhookPayload, { thread: unknown }>;\n for (const comment of thread?.comments ?? []) {\n if (action === 'resolved') {\n add(comment, 'resolved');\n } else if (action === 'unresolved') {\n add(comment, 'unresolved');\n if (isOurs(comment)) reopened.push(comment.id);\n }\n }\n }\n\n if (entries.length === 0) return 0;\n\n try {\n const feedbackRepo = c.env.deps.repositories.commentFeedback;\n // Reopening a thread must retract the earlier 'resolved' row, or the finding stays recorded as accepted forever.\n if (reopened.length > 0) await feedbackRepo.clearResolvedFeedback(c.env as any, repositoryId, reopened);\n return await feedbackRepo.recordCommentFeedback(c.env as any, entries);\n } catch (error) {\n c.env.deps.platform.logger.warn('Could not record comment feedback', {\n eventName,\n repositoryId,\n error: error instanceof Error ? error.message : String(error),\n });\n return 0;\n }\n}\n\nexport async function handleGitHubWebhook(c: Context<ApiEnv>) {\n const eventName = c.req.header('x-github-event');\n const deliveryId = c.req.header('x-github-delivery');\n const signature = c.req.header('x-hub-signature-256');\n const rawBody = await c.req.text();\n\n if (!eventName || !deliveryId) {\n return jsonError('Missing GitHub webhook headers.', 400);\n }\n\n const verified = await c.env.deps.webhook.verifySignature(signature ?? null, rawBody);\n if (!verified) {\n return jsonError('Invalid webhook signature.', 401);\n }\n\n let payload: GitHubWebhookPayload;\n try {\n payload = JSON.parse(rawBody) as GitHubWebhookPayload;\n } catch {\n return jsonError('Invalid webhook JSON payload.', 400);\n }\n\n // Feedback deliveries carry the whole PR object plus diff hunks per comment; skip storing the payload (row is still written, so dedup still works).\n const isFeedbackEvent = isFeedbackGitHubWebhookEvent(eventName);\n\n const delivery = await c.env.deps.repositories.webhookDeliveries.recordWebhookDelivery(c.env as any, {\n deliveryId,\n eventName,\n owner: 'repository' in payload ? (payload as any).repository.owner.login : null,\n repo: 'repository' in payload ? (payload as any).repository.name : null,\n payload: isFeedbackEvent ? null : payload,\n });\n\n if (!delivery.inserted) {\n return c.json({ ok: true, duplicate: true }, 202);\n }\n\n const installationId = String((payload as any).installation?.id ?? '');\n if (!installationId || !('repository' in payload) || !payload.repository) {\n return c.json({ ok: true, ignored: true }, 202);\n }\n\n // Handled inline before loadRepoConfig: recording feedback needs no repo config, queue message, or Workflow, so skip that KV/DB cost entirely.\n if (isFeedbackEvent) {\n const recorded = await handleFeedbackEvent(c, {\n eventName,\n payload: payload as unknown as FeedbackWebhookPayload,\n repositoryId: delivery.repositoryId,\n });\n return c.json({ ok: true, feedback: true, recorded }, 202);\n }\n\n const normalized = c.env.deps.webhook.normalizePayload(eventName, payload);\n if (!normalized) {\n return c.json({ ok: true, ignored: true, eventName }, 202);\n }\n\n const repoConfig = await c.env.deps.config.loadRepoConfig({\n installationId,\n owner: (payload as any).repository.owner.login,\n repo: (payload as any).repository.name,\n });\n\n if (repoConfig.enabled === false) {\n return c.json({ ok: true, ignored: true, reason: 'repository_disabled' }, 202);\n }\n\n const extracted = c.env.deps.webhook.extractReviewRequest({\n eventName: normalized.eventName,\n payload: normalized.payload,\n botUsername: c.env.BOT_USERNAME,\n config: repoConfig.parsedJson,\n });\n\n if (extracted?.commitSha && extracted.baseSha) {\n const jobsRepo = c.env.deps.repositories.jobs;\n const existingJob = await jobsRepo.findExistingJobForHead(c.env as any, {\n owner: extracted.owner,\n repo: extracted.repo,\n prNumber: extracted.prNumber,\n commitSha: extracted.commitSha,\n trigger: extracted.trigger,\n });\n\n if (existingJob) {\n return c.json({\n ok: true,\n duplicate: true,\n message: existingJob.status === 'queued' ? 'queued' : 'duplicate',\n job: existingJob,\n }, 202);\n }\n\n const throttled = await c.env.deps.checkQuota?.({\n action: 'reviews.enqueue',\n subject: {\n installationId: extracted.installationId,\n owner: extracted.owner,\n repo: extracted.repo,\n },\n });\n if (throttled && !throttled.allowed) {\n // Deliberately 202, not 429: GitHub redelivers failed webhooks, so 4xx causes retry storms.\n return c.json({ ok: true, ignored: true, reason: 'quota_exceeded' }, 202);\n }\n\n const job = await jobsRepo.insertJob(c.env as any, {\n installationId: extracted.installationId,\n owner: extracted.owner,\n repo: extracted.repo,\n prNumber: extracted.prNumber,\n prTitle: extracted.prTitle,\n prAuthor: extracted.prAuthor,\n commitSha: extracted.commitSha,\n baseSha: extracted.baseSha,\n trigger: extracted.trigger,\n headRef: extracted.headRef,\n baseRef: extracted.baseRef,\n configSnapshot: repoConfig.parsedJson,\n });\n\n await jobsRepo.supersedeOlderJobs(c.env as any, {\n installationId: extracted.installationId,\n owner: extracted.owner,\n repo: extracted.repo,\n prNumber: extracted.prNumber,\n newJobId: job.id,\n });\n\n await c.env.deps.platform.enqueueReviewJob({\n jobId: job.id,\n deliveryId,\n phase: 'prepare',\n requestId: c.get('requestId'),\n });\n\n return c.json({ ok: true, message: 'queued', job }, 202);\n }\n\n // Events without a concrete job (e.g. PR close cleanup, mention lookups) still get handled by the worker.\n await c.env.deps.platform.enqueueReviewJob({\n deliveryId,\n eventName,\n requestId: c.get('requestId'),\n } as any);\n\n return c.json({ ok: true, message: 'queued' }, 202);\n}\n\nexport function createWebhookRouter() {\n const app = new Hono<ApiEnv>();\n\n app.post('/', handleGitHubWebhook);\n\n return app;\n}\n","import { isSupportedTimeZone } from '@codraoss/schema/timezone';\nimport { Hono } from 'hono';\nimport { z } from 'zod';\nimport { jsonError } from '../../http';\nimport type { ApiEnv } from '../../ports';\nimport { requirePermission } from '../../middleware/authorize';\n\nconst emailSchema = z.strictObject({\n email: z.string().trim().email().max(254),\n});\n\n// Fields are independently optional (at least one required); timezone null means \"follow the browser\", else must be an Intl-known zone.\nconst accountUpdateSchema = z.strictObject({\n name: z.string().trim().min(1).max(120).optional(),\n timezone: z.string().trim().min(1).max(64).refine(isSupportedTimeZone, {\n message: 'Unknown time zone.',\n }).nullable().optional(),\n}).refine(\n (body) => body.name !== undefined || body.timezone !== undefined,\n { message: 'Nothing to update.' },\n);\n\nexport function createAuthApiRouter() {\n const app = new Hono<ApiEnv>();\n\n app.get('/session', async (c) => {\n const sessionUser = c.get('sessionUser');\n if (!sessionUser) {\n return Response.json({ error: 'Unauthorized' }, { status: 401 });\n }\n\n // Omitted when nothing is restricted, which the dashboard reads as \"every action allowed\".\n const permissions = await c.env.deps.authz?.listPermissions?.(sessionUser);\n\n return c.json(permissions ? { user: sessionUser, permissions } : { user: sessionUser });\n });\n\n app.get('/account', async (c) => {\n const sessionUser = c.get('sessionUser');\n if (!sessionUser) {\n return Response.json({ error: 'Unauthorized' }, { status: 401 });\n }\n\n const accounts = c.env.deps.repositories.accounts;\n const githubUserId = Number(sessionUser.providerUserId);\n\n // Self-heals for sessions created before the account_settings table existed.\n let account = await accounts.getAccountSettings(c.env as any, githubUserId);\n if (!account) {\n account = await accounts.upsertAccountSettings(c.env as any, {\n githubUserId,\n githubUsername: sessionUser.login,\n accountName: sessionUser.name,\n accountEmail: sessionUser.email,\n });\n }\n\n return c.json({ account });\n });\n\n app.patch('/account', async (c) => {\n const denied = await requirePermission(c, 'account.write');\n if (denied) return denied;\n const sessionUser = c.get('sessionUser');\n if (!sessionUser) {\n return Response.json({ error: 'Unauthorized' }, { status: 401 });\n }\n\n const body = await c.req.json().catch(() => null);\n const parsed = accountUpdateSchema.safeParse(body);\n if (!parsed.success) {\n const issue = parsed.error.issues[0]?.message;\n return jsonError(\n issue && issue !== 'Invalid input' ? issue : 'Enter a name (1-120 characters).',\n 400,\n );\n }\n\n const accounts = c.env.deps.repositories.accounts;\n const githubUserId = Number(sessionUser.providerUserId);\n\n // Ensure a row exists first (self-heal for pre-existing sessions), then update.\n const existing = await accounts.getAccountSettings(c.env as any, githubUserId);\n if (!existing) {\n await accounts.upsertAccountSettings(c.env as any, {\n githubUserId,\n githubUsername: sessionUser.login,\n accountName: sessionUser.name,\n accountEmail: sessionUser.email,\n });\n }\n\n const account = await accounts.updateAccountSettings(c.env as any, githubUserId, {\n accountName: parsed.data.name,\n timezone: parsed.data.timezone,\n });\n return c.json({ account });\n });\n\n app.get('/updates-email', async (c) => {\n const sessionUser = c.get('sessionUser');\n if (!sessionUser) {\n return Response.json({ error: 'Unauthorized' }, { status: 401 });\n }\n\n const githubUserId = Number(sessionUser.providerUserId);\n const preference = await c.env.deps.platform.getUpdatesEmailPreference(githubUserId);\n return c.json({\n status: preference?.status ?? 'pending',\n email: preference?.email ?? null,\n updatedAt: preference?.updatedAt ?? null,\n });\n });\n\n app.post('/updates-email', async (c) => {\n const denied = await requirePermission(c, 'account.updatesEmail.write');\n if (denied) return denied;\n const sessionUser = c.get('sessionUser');\n if (!sessionUser) {\n return Response.json({ error: 'Unauthorized' }, { status: 401 });\n }\n\n const body = await c.req.json().catch(() => null);\n const parsed = emailSchema.safeParse(body);\n if (!parsed.success) {\n return jsonError('Enter a valid email address.', 400);\n }\n\n const platform = c.env.deps.platform;\n const githubUserId = Number(sessionUser.providerUserId);\n\n const existingPreference = await platform.getUpdatesEmailPreference(githubUserId);\n if (existingPreference) {\n return c.json({\n status: existingPreference.status,\n email: existingPreference.email,\n updatedAt: existingPreference.updatedAt,\n });\n }\n\n const synced = await platform.syncUpdatesEmail(githubUserId, parsed.data.email);\n if (!synced) {\n return jsonError('Could not save updates email right now.', 502);\n }\n\n const preference = await platform.getUpdatesEmailPreference(githubUserId);\n\n return c.json({\n status: preference?.status ?? 'pending',\n email: preference?.email ?? null,\n updatedAt: preference?.updatedAt ?? null,\n });\n });\n\n return app;\n}\n","import type { Context } from 'hono';\nimport type { ApiAction } from '@codraoss/schema/api';\nimport type { ApiEnv, QuotaCheckInput } from '../ports';\n\n// Guards return a Response to short-circuit with, or null to continue.\n// Both are inert when the corresponding port is not provided.\n\nexport async function requirePermission(\n c: Context<ApiEnv>,\n action: ApiAction,\n resource?: { type: string; id?: string },\n): Promise<Response | null> {\n const authz = c.env.deps.authz;\n if (!authz) return null;\n\n const user = c.get('sessionUser');\n if (!user) {\n return c.json({ error: 'Unauthorized', code: 'unauthorized', action, reason: null }, 401);\n }\n\n const result = await authz.authorize({ user, action, resource });\n if (result.allowed) return null;\n\n return c.json(\n { error: 'Forbidden', code: 'forbidden', action, reason: result.reason ?? null },\n 403,\n );\n}\n\nexport async function requireQuota(\n c: Context<ApiEnv>,\n input: Omit<QuotaCheckInput, 'user'>,\n): Promise<Response | null> {\n const checkQuota = c.env.deps.checkQuota;\n if (!checkQuota) return null;\n\n const user = c.get('sessionUser') ?? undefined;\n const result = await checkQuota({ ...input, user });\n if (result.allowed) return null;\n\n const headers = result.retryAfterSeconds\n ? { 'Retry-After': String(result.retryAfterSeconds) }\n : undefined;\n\n return c.json(\n {\n error: 'Too many requests',\n code: 'quota_exceeded',\n action: input.action,\n reason: result.reason ?? null,\n },\n 429,\n headers,\n );\n}\n","import { Hono } from 'hono';\nimport type { Context } from 'hono';\nimport { defaultRepoConfig, findingLabelSchema, jobsQuerySchema } from '@codraoss/schema';\nimport { jsonError } from '../../http';\nimport { parseUnifiedDiff } from '@codraoss/core/diff';\nimport { buildFileReviewPrompts, changelogExcerptFromDiff, wantsFileContext } from '@codraoss/core/prompts/file-review';\nimport type { ApiEnv } from '../../ports';\nimport { requirePermission, requireQuota } from '../../middleware/authorize';\n\n// Best-effort: .get()/.terminate() throw if instance is gone/already terminal; both non-fatal.\nasync function terminateJobWorkflow(c: Context<ApiEnv>, job: { id: string; workflowInstanceId?: string | null }) {\n // Goes through platform port; handlers must not touch bindings directly.\n await c.env.deps.platform.terminateJobWorkflow(job);\n}\n\nfunction jobEtag(input: { id: string; status: string; updatedAt: string; fileCount: number; commentCount: number }) {\n return `\"job-${input.id}-${input.status}-${input.fileCount}-${input.commentCount}-${new Date(input.updatedAt).getTime()}\"`;\n}\n\nfunction getExecutionContext(c: Context<ApiEnv>) {\n try {\n return c.executionCtx;\n } catch {\n return undefined;\n }\n}\n\nexport function createJobsRouter() {\n const app = new Hono<ApiEnv>();\n\n app.get('/', async (c) => {\n const denied = await requirePermission(c, 'jobs.read');\n if (denied) return denied;\n c.env.deps.platform.scheduleBestEffortJobMaintenance(getExecutionContext(c));\n\n const rawQuery = c.req.query();\n const query = jobsQuerySchema.parse(rawQuery);\n\n const result = await c.env.deps.repositories.jobs.listJobs(c.env as any, query as any);\n return c.json(result);\n });\n\n app.get('/:id', async (c) => {\n const denied = await requirePermission(c, 'jobs.read', { type: 'job', id: c.req.param('id') });\n if (denied) return denied;\n c.env.deps.platform.scheduleBestEffortJobMaintenance(getExecutionContext(c));\n\n const job = await c.env.deps.repositories.jobs.getJobDetail(c.env as any, c.req.param('id'));\n if (!job) {\n return jsonError('Job not found.', 404);\n }\n\n const etag = jobEtag(job);\n const lastModified = new Date(job.updatedAt).toUTCString();\n if (c.req.header('if-none-match') === etag) {\n return new Response(null, {\n status: 304,\n headers: {\n ETag: etag,\n 'Last-Modified': lastModified,\n },\n });\n }\n\n const response = c.json({ job });\n response.headers.set('ETag', etag);\n response.headers.set('Last-Modified', lastModified);\n response.headers.set('Cache-Control', 'private, no-cache');\n return response;\n });\n\n // diff_input isn't persisted; rebuilt from the job's own base/head commits (not the live PR), via KV cache.\n app.get('/:id/diffs', async (c) => {\n const denied = await requirePermission(c, 'jobs.read', { type: 'job', id: c.req.param('id') });\n if (denied) return denied;\n const job = await c.env.deps.repositories.jobs.getJobDetail(c.env as any, c.req.param('id'));\n if (!job) {\n return jsonError('Job not found.', 404);\n }\n\n const config = job.configSnapshot ?? defaultRepoConfig;\n const github = c.env.deps.gitProvider.createService(job.installationId);\n\n let rawDiff: string;\n try {\n rawDiff = await c.env.deps.platform.getOrFetchRawDiffForCompletedJob(\n c.env.deps.platform.createReviewRuntime(),\n { id: job.id, owner: job.owner, repo: job.repo, baseSha: job.baseSha, commitSha: job.commitSha },\n github,\n );\n } catch (error) {\n c.env.deps.platform.logger.warn(`Could not reconstruct diff for job ${job.id}`, error instanceof Error ? error : new Error(String(error)));\n return c.json({ diffs: {} });\n }\n\n // Reconstructs the prompt the model actually saw, so it needs the PR description too.\n let prDescription: string | null = null;\n try {\n prDescription = (await github.getPullRequest(job.owner, job.repo, job.prNumber)).body ?? null;\n } catch (error) {\n // Best-effort: missing description degrades fidelity, doesn't fail the view.\n c.env.deps.platform.logger.warn(`Could not load the PR body for job ${job.id}; prompts will omit the description`,\n error instanceof Error ? error : new Error(String(error)));\n }\n\n // Full PR diff, not just reviewed files, so file count matches GitHub mid-review.\n const diffs: Record<string, string> = {};\n const parsedFiles = parseUnifiedDiff(rawDiff, config.review);\n const changelogExcerpt = changelogExcerptFromDiff(parsedFiles);\n for (const file of parsedFiles) {\n if (file.isDeleted || file.isBinary || !file.path) continue;\n const { userPrompt } = buildFileReviewPrompts({\n file,\n prTitle: job.prTitle,\n prDescription,\n changelogExcerpt,\n config: config.review,\n });\n const hadContext = wantsFileContext(file, config.review.full_file_context);\n diffs[file.path] = hadContext\n ? `${userPrompt}\\n\\n[The full file at the reviewed commit was included here at review time; it is omitted from this preview.]`\n : userPrompt;\n }\n\n const response = c.json({ diffs });\n response.headers.set('Cache-Control', 'private, max-age=60');\n return response;\n });\n\n // inherit=true links retryOfJobId and reuses done file reviews; false reviews everything.\n async function startReplacementJob(c: Context<ApiEnv>, rawSource: any, options: { inherit: boolean }) {\n const jobs = c.env.deps.repositories.jobs;\n const source = jobs.mapJob(rawSource);\n let configSnapshot;\n try {\n const currentConfig = await c.env.deps.config.loadRepoConfig({\n installationId: source.installationId,\n owner: source.owner,\n repo: source.repo,\n });\n configSnapshot = currentConfig?.parsedJson ?? defaultRepoConfig;\n } catch (e) {\n configSnapshot = defaultRepoConfig;\n }\n\n const job = await jobs.insertJob(c.env as any, {\n installationId: source.installationId,\n owner: source.owner,\n repo: source.repo,\n prNumber: source.prNumber,\n prTitle: source.prTitle,\n prAuthor: source.prAuthor,\n commitSha: source.commitSha,\n baseSha: jobs.bytesToHex(rawSource.base_sha), // base_sha is only in raw row/detail\n trigger: 'retry',\n headRef: rawSource.head_ref,\n baseRef: rawSource.base_ref,\n configSnapshot,\n ...(options.inherit ? { retryOfJobId: source.id } : {}),\n });\n\n await jobs.supersedeOlderJobs(c.env as any, {\n installationId: source.installationId,\n owner: source.owner,\n repo: source.repo,\n prNumber: source.prNumber,\n newJobId: job.id,\n });\n\n await c.env.deps.platform.enqueueReviewJob({\n jobId: job.id,\n deliveryId: crypto.randomUUID(),\n phase: 'prepare',\n requestId: c.get('requestId'),\n });\n\n return job;\n }\n\n app.post('/:id/retry', async (c) => {\n const denied = await requirePermission(c, 'jobs.retry', { type: 'job', id: c.req.param('id') });\n if (denied) return denied;\n const throttled = await requireQuota(c, { action: 'jobs.retry' });\n if (throttled) return throttled;\n const jobs = c.env.deps.repositories.jobs;\n const rawSource = await jobs.getJobForProcessing(c.env as any, c.req.param('id'));\n if (!rawSource) {\n return jsonError('Job not found.', 404);\n }\n const job = await startReplacementJob(c, rawSource, { inherit: true });\n return c.json({ job }, 202);\n });\n\n // No inheritance; stops the current run first so two workflows can't race.\n app.post('/:id/rerun', async (c) => {\n const denied = await requirePermission(c, 'jobs.rerun', { type: 'job', id: c.req.param('id') });\n if (denied) return denied;\n const throttled = await requireQuota(c, { action: 'jobs.rerun' });\n if (throttled) return throttled;\n const jobs = c.env.deps.repositories.jobs;\n const rawSource = await jobs.getJobForProcessing(c.env as any, c.req.param('id'));\n if (!rawSource) {\n return jsonError('Job not found.', 404);\n }\n const source = jobs.mapJob(rawSource);\n if (source.status === 'queued' || source.status === 'running') {\n await terminateJobWorkflow(c, source);\n }\n const job = await startReplacementJob(c, rawSource, { inherit: false });\n return c.json({ job }, 202);\n });\n\n app.post('/:id/stop', async (c) => {\n const denied = await requirePermission(c, 'jobs.stop', { type: 'job', id: c.req.param('id') });\n if (denied) return denied;\n const jobs = c.env.deps.repositories.jobs;\n const id = c.req.param('id');\n const raw = await jobs.getJobForProcessing(c.env as any, id);\n if (!raw) {\n return jsonError('Job not found.', 404);\n }\n const job = jobs.mapJob(raw);\n if (job.status !== 'queued' && job.status !== 'running') {\n return jsonError('Only a queued or running job can be stopped.', 409);\n }\n await terminateJobWorkflow(c, job);\n await jobs.cancelJob(c.env as any, id);\n const updated = await jobs.getJobForProcessing(c.env as any, id);\n return c.json({ job: updated ? jobs.mapJob(updated) : job }, 200);\n });\n\n // \"wrong\" suppresses this finding repo-wide; \"right\" is measurement only.\n app.put('/:id/findings/:fingerprint/label', async (c) => {\n const denied = await requirePermission(c, 'jobs.label', { type: 'job', id: c.req.param('id') });\n if (denied) return denied;\n const jobId = c.req.param('id');\n const fingerprint = c.req.param('fingerprint');\n\n const parsed = findingLabelSchema.safeParse(await c.req.json().catch(() => null));\n if (!parsed.success) return jsonError('Body must be {\"label\":\"right\"|\"wrong\"}.', 400);\n\n const target = await c.env.deps.repositories.fileReviews.getFindingLabelTarget(c.env as any, jobId, fingerprint);\n if (!target) return jsonError('Finding not found on this job.', 404);\n\n await c.env.deps.repositories.commentFeedback.upsertDashboardFeedback(c.env as any, {\n repositoryId: target.repository_id,\n prNumber: target.pr_number,\n fingerprint,\n anchorHash: target.anchor_hash,\n // Keeps rejection valid even if the model rewords the title.\n fingerprintV2: target.fingerprint_v2,\n jobId,\n labelledBy: c.get('sessionUser')?.providerUserId ? Number(c.get('sessionUser')?.providerUserId) : null,\n outcome: parsed.data.label === 'wrong' ? 'marked_wrong' : 'marked_right',\n });\n\n return c.json({ label: parsed.data.label }, 200);\n });\n\n // Scoped to dashboard rows; a real GitHub deletion still stays recorded.\n app.delete('/:id/findings/:fingerprint/label', async (c) => {\n const denied = await requirePermission(c, 'jobs.label', { type: 'job', id: c.req.param('id') });\n if (denied) return denied;\n const jobId = c.req.param('id');\n const fingerprint = c.req.param('fingerprint');\n\n const target = await c.env.deps.repositories.fileReviews.getFindingLabelTarget(c.env as any, jobId, fingerprint);\n if (!target) return jsonError('Finding not found on this job.', 404);\n\n await c.env.deps.repositories.commentFeedback.clearDashboardFeedback(c.env as any, target.repository_id, fingerprint);\n return c.body(null, 204);\n });\n\n app.delete('/:id', async (c) => {\n const denied = await requirePermission(c, 'jobs.delete', { type: 'job', id: c.req.param('id') });\n if (denied) return denied;\n const jobs = c.env.deps.repositories.jobs;\n const id = c.req.param('id');\n const raw = await jobs.getJobForProcessing(c.env as any, id);\n if (!raw) {\n return jsonError('Job not found.', 404);\n }\n const job = jobs.mapJob(raw);\n if (job.status === 'queued' || job.status === 'running') {\n await terminateJobWorkflow(c, job);\n }\n await jobs.deleteJob(c.env as any, id);\n return c.body(null, 204);\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport { z } from 'zod';\nimport type { ApiEnv } from '../../ports';\nimport { requirePermission, requireQuota } from '../../middleware/authorize';\nimport { jsonError } from '../../http';\nimport { repoConfigSchema } from '@codraoss/schema';\n\nconst repoConfigPatchSchema = z\n .strictObject({\n enabled: z.boolean().optional(),\n review: repoConfigSchema.shape.review.optional(),\n model: repoConfigSchema.shape.model.optional(),\n })\n .refine(\n (patch) => patch.enabled !== undefined || patch.review !== undefined || patch.model !== undefined,\n 'Repository config patch cannot be empty.',\n );\n\nasync function mapWithConcurrency<T, R>(\n items: T[],\n limit: number,\n mapper: (item: T) => Promise<R>,\n) {\n const results: R[] = new Array(items.length);\n let nextIndex = 0;\n\n async function worker() {\n while (nextIndex < items.length) {\n const currentIndex = nextIndex;\n nextIndex += 1;\n results[currentIndex] = await mapper(items[currentIndex]);\n }\n }\n\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));\n return results;\n}\n\nexport function createReposRouter() {\n const app = new Hono<ApiEnv>();\n\n app.get('/', async (c) => {\n const denied = await requirePermission(c, 'repos.read');\n if (denied) return denied;\n const repos = await c.env.deps.repositories.repoConfigs.listRepoConfigs(c.env as any);\n return c.json({ repos });\n });\n\n app.get('/install', async (c) => {\n const denied = await requirePermission(c, 'repos.install');\n if (denied) return denied;\n try {\n return c.redirect(await c.env.deps.gitProvider.getAppInstallationUrl(), 302);\n } catch (error) {\n c.env.deps.platform.logger.error('Failed to resolve GitHub App installation URL:', error);\n return jsonError(`Failed to resolve GitHub App installation URL: ${error instanceof Error ? error.message : String(error)}`, 500);\n }\n });\n\n app.post('/sync', async (c) => {\n const denied = await requirePermission(c, 'repos.sync');\n if (denied) return denied;\n const throttled = await requireQuota(c, { action: 'repos.sync' });\n if (throttled) return throttled;\n try {\n const installations = await c.env.deps.gitProvider.listInstallations();\n const synced: string[] = [];\n const repoConfigs = c.env.deps.repositories.repoConfigs;\n\n for (const inst of installations) {\n const github = c.env.deps.gitProvider.createService(String(inst.id));\n const repos = await github.listRepositories();\n\n const results = await mapWithConcurrency(\n repos,\n 5,\n async (repo: any) => {\n const owner = repo.owner.login;\n const name = repo.name;\n const fullName = `${owner}/${name}`;\n try {\n await repoConfigs.syncRepoConfig(c.env as any, {\n installationId: String(inst.id),\n owner,\n repo: name,\n });\n return fullName;\n } catch (repoError) {\n c.env.deps.platform.logger.error(`Failed to sync repo: ${fullName}`, repoError);\n return null;\n }\n },\n );\n\n const installationSynced: string[] = [];\n for (const res of results) {\n if (res) {\n synced.push(res);\n installationSynced.push(res);\n }\n }\n \n await repoConfigs.deleteStaleRepoConfigs(c.env as any, String(inst.id), installationSynced);\n }\n\n return c.json({ ok: true, synced });\n } catch (error) {\n c.env.deps.platform.logger.error('Manual sync failed:', error);\n return jsonError(`Sync failed: ${error instanceof Error ? error.message : String(error)}`, 500);\n }\n });\n\n app.get('/:owner/:repo/config', async (c) => {\n const denied = await requirePermission(c, 'repos.read', { type: 'repo', id: `${c.req.param('owner')}/${c.req.param('repo')}` });\n if (denied) return denied;\n const repo = await c.env.deps.repositories.repoConfigs.getRepoConfigRecord(c.env as any, c.req.param('owner'), c.req.param('repo'));\n if (!repo) {\n return jsonError('Repository config not found.', 404);\n }\n\n return c.json({ repo });\n });\n \n app.patch('/:owner/:repo/config', async (c) => {\n const denied = await requirePermission(c, 'repos.config.write', { type: 'repo', id: `${c.req.param('owner')}/${c.req.param('repo')}` });\n if (denied) return denied;\n const { owner, repo } = c.req.param();\n const body = await c.req.json();\n const parsedPatch = repoConfigPatchSchema.safeParse(body);\n if (!parsedPatch.success) {\n return jsonError('Invalid repository config patch.', 400);\n }\n\n const repoConfigs = c.env.deps.repositories.repoConfigs;\n const existing = await repoConfigs.getRepoConfigRecord(c.env as any, owner, repo);\n \n if (!existing) {\n return jsonError('Repository config not found.', 404);\n }\n\n const patch = parsedPatch.data;\n const hasConfigPatch = patch.review !== undefined || patch.model !== undefined;\n\n if (!hasConfigPatch && patch.enabled !== undefined) {\n await repoConfigs.updateRepoConfigEnabled(c.env as any, {\n owner,\n repo,\n enabled: patch.enabled,\n });\n await c.env.deps.config.invalidateRepoConfigCache(owner, repo);\n return c.json({ ok: true });\n }\n\n const configPatch: Partial<z.infer<typeof repoConfigSchema>> = {};\n if (patch.review !== undefined) {\n configPatch.review = patch.review;\n }\n if (patch.model !== undefined) {\n configPatch.model = patch.model;\n }\n \n const updatedParsedJson = {\n ...existing.parsedJson,\n ...configPatch,\n };\n const parsedConfig = repoConfigSchema.safeParse(updatedParsedJson);\n\n if (!parsedConfig.success) {\n return jsonError('Invalid repository config.', 400);\n }\n \n await repoConfigs.upsertRepoConfig(c.env as any, {\n installationId: existing.installationId,\n owner,\n repo,\n parsedJson: parsedConfig.data,\n enabled: patch.enabled,\n });\n await c.env.deps.config.invalidateRepoConfigCache(owner, repo);\n \n return c.json({ ok: true });\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport type { ApiEnv } from '../../ports';\nimport { requirePermission } from '../../middleware/authorize';\n\nexport function createStatsRouter() {\n const app = new Hono<ApiEnv>();\n\n app.get('/', async (c) => {\n const denied = await requirePermission(c, 'stats.read');\n if (denied) return denied;\n const daysParam = c.req.query('days');\n const days = daysParam ? parseInt(daysParam, 10) : 30;\n // Grouped in the caller's display zone so the trend matches timestamps shown elsewhere; getStats falls back to UTC if invalid.\n const timeZone = c.req.query('tz') ?? 'UTC';\n const stats = await c.env.deps.repositories.stats.getStats(c.env as any, days, timeZone);\n return c.json({ stats });\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport { z } from 'zod';\nimport { jsonError } from '../../http';\nimport { llmApiFormats } from '@codraoss/schema';\nimport type { ApiEnv } from '../../ports';\nimport { requirePermission, requireQuota } from '../../middleware/authorize';\n\nconst apiFormatSchema = z.enum(llmApiFormats);\nconst positiveIntegerSchema = z.number().int().positive().finite();\nconst modelIdSchema = z.string().trim().min(1);\nconst optionalUrlSchema = z.string().trim().url().nullable().optional();\nconst providerIdSchema = z.string().uuid();\n\nconst providerCreateSchema = z.strictObject({\n name: z.string().trim().min(1),\n apiFormat: apiFormatSchema,\n baseUrl: optionalUrlSchema,\n apiKey: z.string().optional(),\n enabled: z.boolean().default(true),\n});\n\n// `.extend()` carries the parent's strictness through, so no second `.strict()` is needed here.\nconst providerUpdateSchema = providerCreateSchema.extend({\n clearApiKey: z.boolean().optional(),\n});\n\nconst modelConfigUpdateSchema = z.strictObject({\n providerId: providerIdSchema,\n modelName: z.string().trim().min(1),\n});\n\nconst globalModelConfigSchema = z.strictObject({\n main: modelIdSchema.nullable().default(null),\n fallbacks: z.array(modelIdSchema).nullable().default([]),\n size_overrides: z\n .array(\n z.strictObject({\n max_lines: positiveIntegerSchema,\n model: modelIdSchema,\n fallbacks: z.array(modelIdSchema).optional(),\n }),\n )\n .nullable()\n .optional(),\n});\n\nfunction requiresExplicitBaseUrl(apiFormat: z.infer<typeof apiFormatSchema>, baseUrl: string | null | undefined) {\n return apiFormat === 'vertex' && !baseUrl;\n}\n\nfunction readModelIdParam(value: string) {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n\nexport function createModelsRouter() {\n const app = new Hono<ApiEnv>();\n\n app.get('/', async (c) => {\n const denied = await requirePermission(c, 'models.read');\n if (denied) return denied;\n const modelConfigsRepo = c.env.deps.repositories.modelConfigs;\n const [providers, configs] = await Promise.all([\n modelConfigsRepo.listLlmProviders(c.env as any),\n modelConfigsRepo.listModelConfigs(c.env as any),\n ]);\n return c.json({ providers, configs });\n });\n\n app.post('/sync', async (c) => {\n const denied = await requirePermission(c, 'models.sync');\n if (denied) return denied;\n const modelConfigsRepo = c.env.deps.repositories.modelConfigs;\n const syncErrors = await c.env.deps.modelRunner.syncProviderModelCatalog();\n const [providers, configs] = await Promise.all([\n modelConfigsRepo.listLlmProviders(c.env as any),\n modelConfigsRepo.listModelConfigs(c.env as any),\n ]);\n return c.json({ providers, configs, syncErrors });\n });\n\n app.get('/global', async (c) => {\n const denied = await requirePermission(c, 'models.read');\n if (denied) return denied;\n const config = await c.env.deps.config.getGlobalConfig();\n return c.json({ config });\n });\n\n app.patch('/global', async (c) => {\n const denied = await requirePermission(c, 'models.global.write');\n if (denied) return denied;\n const body = await c.req.json();\n const parsed = globalModelConfigSchema.safeParse(body);\n if (!parsed.success) {\n return jsonError('Invalid global model config.', 400);\n }\n\n await c.env.deps.config.updateGlobalConfig(parsed.data);\n return c.json({ ok: true });\n });\n\n app.post('/providers', async (c) => {\n const denied = await requirePermission(c, 'models.provider.create');\n if (denied) return denied;\n const parsed = providerCreateSchema.safeParse(await c.req.json());\n if (!parsed.success) {\n return jsonError('Invalid provider config.', 400);\n }\n\n const input = parsed.data;\n if (requiresExplicitBaseUrl(input.apiFormat, input.baseUrl)) {\n return jsonError('Vertex AI requires a base URL with your GCP project ID and region, e.g. https://us-central1-aiplatform.googleapis.com/v1/projects/YOUR_PROJECT_ID/locations/us-central1', 400);\n }\n\n try {\n const provider = await c.env.deps.modelRunner.createProviderWithSecret(input);\n return c.json({ provider }, 201);\n } catch (error) {\n const err = error as any;\n if (err.isUniqueNameError) {\n return jsonError(`Provider ${input.name} already exists. Update the existing provider instead.`, 409);\n }\n if (err.isEncryptionConfigError) {\n return jsonError(err.message || 'LLM encryption is not configured.', 400);\n }\n if (err.isKeyRequiredError) {\n return jsonError(`Provider ${input.name} needs an API key before it can be enabled.`, 400);\n }\n throw error;\n }\n });\n\n app.patch('/providers/:id', async (c) => {\n const denied = await requirePermission(c, 'models.provider.update', { type: 'llmProvider', id: c.req.param('id') });\n if (denied) return denied;\n const id = c.req.param('id');\n if (!providerIdSchema.safeParse(id).success) {\n return jsonError('Invalid provider id.', 400);\n }\n\n const parsed = providerUpdateSchema.safeParse(await c.req.json());\n if (!parsed.success) {\n return jsonError('Invalid provider config.', 400);\n }\n\n const input = parsed.data;\n if (requiresExplicitBaseUrl(input.apiFormat, input.baseUrl)) {\n return jsonError('Vertex AI requires a base URL with your GCP project ID and region, e.g. https://us-central1-aiplatform.googleapis.com/v1/projects/YOUR_PROJECT_ID/locations/us-central1', 400);\n }\n\n try {\n const provider = await c.env.deps.modelRunner.updateProviderWithSecret(id, input);\n if (!provider) return jsonError('Provider not found.', 404);\n return c.json({ provider });\n } catch (error) {\n const err = error as any;\n if (err.isUniqueNameError) {\n return jsonError(`Provider ${input.name} already exists. Choose a different provider name.`, 409);\n }\n if (err.isEncryptionConfigError) {\n return jsonError(err.message || 'LLM encryption is not configured.', 400);\n }\n if (err.isKeyRequiredError) {\n return jsonError(`Provider ${input.name} needs an API key before it can be enabled.`, 400);\n }\n throw error;\n }\n });\n\n app.delete('/providers/:id', async (c) => {\n const denied = await requirePermission(c, 'models.provider.delete', { type: 'llmProvider', id: c.req.param('id') });\n if (denied) return denied;\n const id = c.req.param('id');\n if (!providerIdSchema.safeParse(id).success) {\n return jsonError('Invalid provider id.', 400);\n }\n\n const result = await c.env.deps.repositories.modelConfigs.deleteLlmProvider(c.env as any, id);\n if (!result.deleted) {\n return jsonError(result.reason ?? 'Provider not found.', result.reason ? 409 : 404);\n }\n return c.json({ ok: true });\n });\n\n app.post('/:id/test', async (c) => {\n const denied = await requirePermission(c, 'models.test', { type: 'modelConfig', id: c.req.param('id') });\n if (denied) return denied;\n const throttled = await requireQuota(c, { action: 'models.test' });\n if (throttled) return throttled;\n const modelId = readModelIdParam(c.req.param('id'));\n const parsedModelId = modelIdSchema.safeParse(modelId);\n if (!parsedModelId.success) {\n return jsonError('Invalid model id.', 400);\n }\n\n try {\n const response = await c.env.deps.modelRunner.testConnection(parsedModelId.data);\n return c.json(response);\n } catch (error) {\n const err = error as any;\n if (err.isNotFoundError) return jsonError('Model not found.', 404);\n if (err.isDisabledError || err.isMissingKeyError) return jsonError(err.message, 400);\n \n return jsonError(\n err.message || 'Connection test failed.',\n err.status || 502,\n );\n }\n });\n\n app.post('/:id', async (c) => {\n const denied = await requirePermission(c, 'models.mapping.write', { type: 'modelConfig', id: c.req.param('id') });\n if (denied) return denied;\n const modelId = readModelIdParam(c.req.param('id'));\n const parsedModelId = modelIdSchema.safeParse(modelId);\n if (!parsedModelId.success) {\n return jsonError('Invalid model id.', 400);\n }\n\n const body = await c.req.json();\n const parsed = modelConfigUpdateSchema.safeParse(body);\n if (!parsed.success) {\n return jsonError('Invalid model config.', 400);\n }\n\n const saved = await c.env.deps.repositories.modelConfigs.updateModelConfig(c.env as any, {\n modelId: parsedModelId.data,\n providerId: parsed.data.providerId,\n modelName: parsed.data.modelName,\n });\n\n if (!saved) return jsonError('Provider not found.', 404);\n return c.json({ ok: true, config: saved });\n });\n\n app.delete('/:id', async (c) => {\n const denied = await requirePermission(c, 'models.mapping.write', { type: 'modelConfig', id: c.req.param('id') });\n if (denied) return denied;\n const modelId = readModelIdParam(c.req.param('id'));\n const parsedModelId = modelIdSchema.safeParse(modelId);\n if (!parsedModelId.success) {\n return jsonError('Invalid model id.', 400);\n }\n\n const deleted = await c.env.deps.repositories.modelConfigs.deleteModelConfig(c.env as any, parsedModelId.data);\n if (!deleted) return jsonError('Model not found.', 404);\n return c.json({ ok: true });\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport { z } from 'zod';\nimport type { ApiEnv } from '../../ports';\nimport { requirePermission } from '../../middleware/authorize';\nimport { jsonError } from '../../http';\nimport { reviewConcurrencyLevels, reviewMaxCommentsOptions, reviewMaxFilesRange, reviewSettingsSchema } from '@codraoss/schema';\n\nconst reviewSettingsPatchSchema = z.strictObject({\n concurrencyLevel: z.enum(reviewConcurrencyLevels).optional(),\n maxComments: z.number().int().refine(\n (value) => (reviewMaxCommentsOptions as readonly number[]).includes(value),\n 'Invalid max comments value.',\n ).optional(),\n maxFiles: z.number().int().min(reviewMaxFilesRange.min).max(reviewMaxFilesRange.max).optional(),\n}).refine(\n (settings) => Object.values(settings).some((value) => value !== undefined),\n 'At least one setting must be provided.',\n);\n\nexport function createSettingsRouter() {\n const app = new Hono<ApiEnv>();\n\n app.get('/', async (c) => {\n const denied = await requirePermission(c, 'settings.read');\n if (denied) return denied;\n const settings = await c.env.deps.repositories.appSettings.getReviewSettings(c.env as any);\n return c.json({ settings });\n });\n\n app.patch('/', async (c) => {\n const denied = await requirePermission(c, 'settings.write');\n if (denied) return denied;\n const body = await c.req.json().catch(() => null);\n const parsed = reviewSettingsPatchSchema.safeParse(body);\n if (!parsed.success) {\n return jsonError('Invalid review settings.', 400);\n }\n\n const current = await c.env.deps.repositories.appSettings.getReviewSettings(c.env as any);\n const next = reviewSettingsSchema.parse({ ...current, ...parsed.data });\n await c.env.deps.repositories.appSettings.updateReviewSettings(c.env as any, next);\n return c.json({ ok: true, settings: next });\n });\n\n return app;\n}\n"],"mappings":"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","names":["Hono","createMiddleware","Hono","Hono","Hono","Hono","Hono","Hono","Hono","z","z","Hono","Hono","Hono","Hono","z","z","Hono","Hono","z","z","Hono","Hono"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codraoss/api",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5",
|
|
4
4
|
"description": "Codra's HTTP surface as a mountable Hono router, wired through ports.",
|
|
5
5
|
"author": "Devarshi Shimpi",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -47,11 +47,11 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"hono": "^4.12.25",
|
|
49
49
|
"zod": "^4.3.6",
|
|
50
|
-
"@codraoss/schema": "^0.9.
|
|
51
|
-
"@codraoss/core": "^0.9.
|
|
52
|
-
"@codraoss/db": "^0.9.
|
|
53
|
-
"@codraoss/provider-github": "^0.9.
|
|
54
|
-
"@codraoss/models": "^0.9.
|
|
50
|
+
"@codraoss/schema": "^0.9.5",
|
|
51
|
+
"@codraoss/core": "^0.9.5",
|
|
52
|
+
"@codraoss/db": "^0.9.5",
|
|
53
|
+
"@codraoss/provider-github": "^0.9.5",
|
|
54
|
+
"@codraoss/models": "^0.9.5"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/node": "^22.0.0",
|