@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 CHANGED
@@ -1,6 +1,8 @@
1
1
  import * as hono_types from 'hono/types';
2
- import { Hono } from 'hono';
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
- declare function createApiRouter(): Hono<ApiEnv, hono_types.BlankSchema, "/">;
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
- return c.json({ user: sessionUser });
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
- for (const file of parseUnifiedDiff(rawDiff, config.review)) {
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
- diffs[file.path] = buildFileReviewPrompts({
664
+ const { userPrompt } = buildFileReviewPrompts({
605
665
  file,
606
666
  prTitle: job.prTitle,
607
667
  prDescription,
668
+ changelogExcerpt,
608
669
  config: config.review
609
- }).userPrompt;
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
- // Carried so a rejection survives the model rewording its title.
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 assetsFetch = c.env.ASSETS?.fetch;
1130
- if (typeof assetsFetch === "function") {
1131
- return assetsFetch(new URL("/index.html", c.req.url));
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.4",
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.4",
51
- "@codraoss/core": "^0.9.4",
52
- "@codraoss/db": "^0.9.4",
53
- "@codraoss/provider-github": "^0.9.4",
54
- "@codraoss/models": "^0.9.4"
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",