@codraoss/api 0.9.4 → 0.9.6

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,13 +577,18 @@ 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
  }
532
584
  function jobEtag(input) {
533
585
  return `"job-${input.id}-${input.status}-${input.fileCount}-${input.commentCount}-${new Date(input.updatedAt).getTime()}"`;
534
586
  }
587
+ function etagMatches(header, etag) {
588
+ if (!header) return false;
589
+ const bare = etag.replace(/^W\//, "");
590
+ return header.split(",").some((candidate) => candidate.trim().replace(/^W\//, "") === bare);
591
+ }
535
592
  function getExecutionContext(c) {
536
593
  try {
537
594
  return c.executionCtx;
@@ -542,6 +599,8 @@ function getExecutionContext(c) {
542
599
  function createJobsRouter() {
543
600
  const app = new Hono4();
544
601
  app.get("/", async (c) => {
602
+ const denied = await requirePermission(c, "jobs.read");
603
+ if (denied) return denied;
545
604
  c.env.deps.platform.scheduleBestEffortJobMaintenance(getExecutionContext(c));
546
605
  const rawQuery = c.req.query();
547
606
  const query = jobsQuerySchema.parse(rawQuery);
@@ -549,6 +608,8 @@ function createJobsRouter() {
549
608
  return c.json(result);
550
609
  });
551
610
  app.get("/:id", async (c) => {
611
+ const denied = await requirePermission(c, "jobs.read", { type: "job", id: c.req.param("id") });
612
+ if (denied) return denied;
552
613
  c.env.deps.platform.scheduleBestEffortJobMaintenance(getExecutionContext(c));
553
614
  const job = await c.env.deps.repositories.jobs.getJobDetail(c.env, c.req.param("id"));
554
615
  if (!job) {
@@ -556,7 +617,7 @@ function createJobsRouter() {
556
617
  }
557
618
  const etag = jobEtag(job);
558
619
  const lastModified = new Date(job.updatedAt).toUTCString();
559
- if (c.req.header("if-none-match") === etag) {
620
+ if (etagMatches(c.req.header("if-none-match"), etag)) {
560
621
  return new Response(null, {
561
622
  status: 304,
562
623
  headers: {
@@ -572,6 +633,8 @@ function createJobsRouter() {
572
633
  return response;
573
634
  });
574
635
  app.get("/:id/diffs", async (c) => {
636
+ const denied = await requirePermission(c, "jobs.read", { type: "job", id: c.req.param("id") });
637
+ if (denied) return denied;
575
638
  const job = await c.env.deps.repositories.jobs.getJobDetail(c.env, c.req.param("id"));
576
639
  if (!job) {
577
640
  return jsonError("Job not found.", 404);
@@ -599,14 +662,21 @@ function createJobsRouter() {
599
662
  );
600
663
  }
601
664
  const diffs = {};
602
- for (const file of parseUnifiedDiff(rawDiff, config.review)) {
665
+ const parsedFiles = parseUnifiedDiff(rawDiff, config.review);
666
+ const changelogExcerpt = changelogExcerptFromDiff(parsedFiles);
667
+ for (const file of parsedFiles) {
603
668
  if (file.isDeleted || file.isBinary || !file.path) continue;
604
- diffs[file.path] = buildFileReviewPrompts({
669
+ const { userPrompt } = buildFileReviewPrompts({
605
670
  file,
606
671
  prTitle: job.prTitle,
607
672
  prDescription,
673
+ changelogExcerpt,
608
674
  config: config.review
609
- }).userPrompt;
675
+ });
676
+ const hadContext = wantsFileContext(file, config.review.full_file_context);
677
+ diffs[file.path] = hadContext ? `${userPrompt}
678
+
679
+ [The full file at the reviewed commit was included here at review time; it is omitted from this preview.]` : userPrompt;
610
680
  }
611
681
  const response = c.json({ diffs });
612
682
  response.headers.set("Cache-Control", "private, max-age=60");
@@ -658,6 +728,10 @@ function createJobsRouter() {
658
728
  return job;
659
729
  }
660
730
  app.post("/:id/retry", async (c) => {
731
+ const denied = await requirePermission(c, "jobs.retry", { type: "job", id: c.req.param("id") });
732
+ if (denied) return denied;
733
+ const throttled = await requireQuota(c, { action: "jobs.retry" });
734
+ if (throttled) return throttled;
661
735
  const jobs = c.env.deps.repositories.jobs;
662
736
  const rawSource = await jobs.getJobForProcessing(c.env, c.req.param("id"));
663
737
  if (!rawSource) {
@@ -667,6 +741,10 @@ function createJobsRouter() {
667
741
  return c.json({ job }, 202);
668
742
  });
669
743
  app.post("/:id/rerun", async (c) => {
744
+ const denied = await requirePermission(c, "jobs.rerun", { type: "job", id: c.req.param("id") });
745
+ if (denied) return denied;
746
+ const throttled = await requireQuota(c, { action: "jobs.rerun" });
747
+ if (throttled) return throttled;
670
748
  const jobs = c.env.deps.repositories.jobs;
671
749
  const rawSource = await jobs.getJobForProcessing(c.env, c.req.param("id"));
672
750
  if (!rawSource) {
@@ -680,6 +758,8 @@ function createJobsRouter() {
680
758
  return c.json({ job }, 202);
681
759
  });
682
760
  app.post("/:id/stop", async (c) => {
761
+ const denied = await requirePermission(c, "jobs.stop", { type: "job", id: c.req.param("id") });
762
+ if (denied) return denied;
683
763
  const jobs = c.env.deps.repositories.jobs;
684
764
  const id = c.req.param("id");
685
765
  const raw = await jobs.getJobForProcessing(c.env, id);
@@ -696,6 +776,8 @@ function createJobsRouter() {
696
776
  return c.json({ job: updated ? jobs.mapJob(updated) : job }, 200);
697
777
  });
698
778
  app.put("/:id/findings/:fingerprint/label", async (c) => {
779
+ const denied = await requirePermission(c, "jobs.label", { type: "job", id: c.req.param("id") });
780
+ if (denied) return denied;
699
781
  const jobId = c.req.param("id");
700
782
  const fingerprint = c.req.param("fingerprint");
701
783
  const parsed = findingLabelSchema.safeParse(await c.req.json().catch(() => null));
@@ -707,7 +789,7 @@ function createJobsRouter() {
707
789
  prNumber: target.pr_number,
708
790
  fingerprint,
709
791
  anchorHash: target.anchor_hash,
710
- // Carried so a rejection survives the model rewording its title.
792
+ // Keeps rejection valid even if the model rewords the title.
711
793
  fingerprintV2: target.fingerprint_v2,
712
794
  jobId,
713
795
  labelledBy: c.get("sessionUser")?.providerUserId ? Number(c.get("sessionUser")?.providerUserId) : null,
@@ -716,6 +798,8 @@ function createJobsRouter() {
716
798
  return c.json({ label: parsed.data.label }, 200);
717
799
  });
718
800
  app.delete("/:id/findings/:fingerprint/label", async (c) => {
801
+ const denied = await requirePermission(c, "jobs.label", { type: "job", id: c.req.param("id") });
802
+ if (denied) return denied;
719
803
  const jobId = c.req.param("id");
720
804
  const fingerprint = c.req.param("fingerprint");
721
805
  const target = await c.env.deps.repositories.fileReviews.getFindingLabelTarget(c.env, jobId, fingerprint);
@@ -724,6 +808,8 @@ function createJobsRouter() {
724
808
  return c.body(null, 204);
725
809
  });
726
810
  app.delete("/:id", async (c) => {
811
+ const denied = await requirePermission(c, "jobs.delete", { type: "job", id: c.req.param("id") });
812
+ if (denied) return denied;
727
813
  const jobs = c.env.deps.repositories.jobs;
728
814
  const id = c.req.param("id");
729
815
  const raw = await jobs.getJobForProcessing(c.env, id);
@@ -768,10 +854,14 @@ async function mapWithConcurrency(items, limit, mapper) {
768
854
  function createReposRouter() {
769
855
  const app = new Hono5();
770
856
  app.get("/", async (c) => {
857
+ const denied = await requirePermission(c, "repos.read");
858
+ if (denied) return denied;
771
859
  const repos = await c.env.deps.repositories.repoConfigs.listRepoConfigs(c.env);
772
860
  return c.json({ repos });
773
861
  });
774
862
  app.get("/install", async (c) => {
863
+ const denied = await requirePermission(c, "repos.install");
864
+ if (denied) return denied;
775
865
  try {
776
866
  return c.redirect(await c.env.deps.gitProvider.getAppInstallationUrl(), 302);
777
867
  } catch (error) {
@@ -780,6 +870,10 @@ function createReposRouter() {
780
870
  }
781
871
  });
782
872
  app.post("/sync", async (c) => {
873
+ const denied = await requirePermission(c, "repos.sync");
874
+ if (denied) return denied;
875
+ const throttled = await requireQuota(c, { action: "repos.sync" });
876
+ if (throttled) return throttled;
783
877
  try {
784
878
  const installations = await c.env.deps.gitProvider.listInstallations();
785
879
  const synced = [];
@@ -823,6 +917,8 @@ function createReposRouter() {
823
917
  }
824
918
  });
825
919
  app.get("/:owner/:repo/config", async (c) => {
920
+ const denied = await requirePermission(c, "repos.read", { type: "repo", id: `${c.req.param("owner")}/${c.req.param("repo")}` });
921
+ if (denied) return denied;
826
922
  const repo = await c.env.deps.repositories.repoConfigs.getRepoConfigRecord(c.env, c.req.param("owner"), c.req.param("repo"));
827
923
  if (!repo) {
828
924
  return jsonError("Repository config not found.", 404);
@@ -830,6 +926,8 @@ function createReposRouter() {
830
926
  return c.json({ repo });
831
927
  });
832
928
  app.patch("/:owner/:repo/config", async (c) => {
929
+ const denied = await requirePermission(c, "repos.config.write", { type: "repo", id: `${c.req.param("owner")}/${c.req.param("repo")}` });
930
+ if (denied) return denied;
833
931
  const { owner, repo } = c.req.param();
834
932
  const body = await c.req.json();
835
933
  const parsedPatch = repoConfigPatchSchema.safeParse(body);
@@ -885,6 +983,8 @@ import { Hono as Hono6 } from "hono";
885
983
  function createStatsRouter() {
886
984
  const app = new Hono6();
887
985
  app.get("/", async (c) => {
986
+ const denied = await requirePermission(c, "stats.read");
987
+ if (denied) return denied;
888
988
  const daysParam = c.req.query("days");
889
989
  const days = daysParam ? parseInt(daysParam, 10) : 30;
890
990
  const timeZone = c.req.query("tz") ?? "UTC";
@@ -941,6 +1041,8 @@ function readModelIdParam(value) {
941
1041
  function createModelsRouter() {
942
1042
  const app = new Hono7();
943
1043
  app.get("/", async (c) => {
1044
+ const denied = await requirePermission(c, "models.read");
1045
+ if (denied) return denied;
944
1046
  const modelConfigsRepo = c.env.deps.repositories.modelConfigs;
945
1047
  const [providers, configs] = await Promise.all([
946
1048
  modelConfigsRepo.listLlmProviders(c.env),
@@ -949,6 +1051,8 @@ function createModelsRouter() {
949
1051
  return c.json({ providers, configs });
950
1052
  });
951
1053
  app.post("/sync", async (c) => {
1054
+ const denied = await requirePermission(c, "models.sync");
1055
+ if (denied) return denied;
952
1056
  const modelConfigsRepo = c.env.deps.repositories.modelConfigs;
953
1057
  const syncErrors = await c.env.deps.modelRunner.syncProviderModelCatalog();
954
1058
  const [providers, configs] = await Promise.all([
@@ -958,10 +1062,14 @@ function createModelsRouter() {
958
1062
  return c.json({ providers, configs, syncErrors });
959
1063
  });
960
1064
  app.get("/global", async (c) => {
1065
+ const denied = await requirePermission(c, "models.read");
1066
+ if (denied) return denied;
961
1067
  const config = await c.env.deps.config.getGlobalConfig();
962
1068
  return c.json({ config });
963
1069
  });
964
1070
  app.patch("/global", async (c) => {
1071
+ const denied = await requirePermission(c, "models.global.write");
1072
+ if (denied) return denied;
965
1073
  const body = await c.req.json();
966
1074
  const parsed = globalModelConfigSchema.safeParse(body);
967
1075
  if (!parsed.success) {
@@ -971,6 +1079,8 @@ function createModelsRouter() {
971
1079
  return c.json({ ok: true });
972
1080
  });
973
1081
  app.post("/providers", async (c) => {
1082
+ const denied = await requirePermission(c, "models.provider.create");
1083
+ if (denied) return denied;
974
1084
  const parsed = providerCreateSchema.safeParse(await c.req.json());
975
1085
  if (!parsed.success) {
976
1086
  return jsonError("Invalid provider config.", 400);
@@ -997,6 +1107,8 @@ function createModelsRouter() {
997
1107
  }
998
1108
  });
999
1109
  app.patch("/providers/:id", async (c) => {
1110
+ const denied = await requirePermission(c, "models.provider.update", { type: "llmProvider", id: c.req.param("id") });
1111
+ if (denied) return denied;
1000
1112
  const id = c.req.param("id");
1001
1113
  if (!providerIdSchema.safeParse(id).success) {
1002
1114
  return jsonError("Invalid provider id.", 400);
@@ -1028,6 +1140,8 @@ function createModelsRouter() {
1028
1140
  }
1029
1141
  });
1030
1142
  app.delete("/providers/:id", async (c) => {
1143
+ const denied = await requirePermission(c, "models.provider.delete", { type: "llmProvider", id: c.req.param("id") });
1144
+ if (denied) return denied;
1031
1145
  const id = c.req.param("id");
1032
1146
  if (!providerIdSchema.safeParse(id).success) {
1033
1147
  return jsonError("Invalid provider id.", 400);
@@ -1039,6 +1153,10 @@ function createModelsRouter() {
1039
1153
  return c.json({ ok: true });
1040
1154
  });
1041
1155
  app.post("/:id/test", async (c) => {
1156
+ const denied = await requirePermission(c, "models.test", { type: "modelConfig", id: c.req.param("id") });
1157
+ if (denied) return denied;
1158
+ const throttled = await requireQuota(c, { action: "models.test" });
1159
+ if (throttled) return throttled;
1042
1160
  const modelId = readModelIdParam(c.req.param("id"));
1043
1161
  const parsedModelId = modelIdSchema.safeParse(modelId);
1044
1162
  if (!parsedModelId.success) {
@@ -1058,6 +1176,8 @@ function createModelsRouter() {
1058
1176
  }
1059
1177
  });
1060
1178
  app.post("/:id", async (c) => {
1179
+ const denied = await requirePermission(c, "models.mapping.write", { type: "modelConfig", id: c.req.param("id") });
1180
+ if (denied) return denied;
1061
1181
  const modelId = readModelIdParam(c.req.param("id"));
1062
1182
  const parsedModelId = modelIdSchema.safeParse(modelId);
1063
1183
  if (!parsedModelId.success) {
@@ -1077,6 +1197,8 @@ function createModelsRouter() {
1077
1197
  return c.json({ ok: true, config: saved });
1078
1198
  });
1079
1199
  app.delete("/:id", async (c) => {
1200
+ const denied = await requirePermission(c, "models.mapping.write", { type: "modelConfig", id: c.req.param("id") });
1201
+ if (denied) return denied;
1080
1202
  const modelId = readModelIdParam(c.req.param("id"));
1081
1203
  const parsedModelId = modelIdSchema.safeParse(modelId);
1082
1204
  if (!parsedModelId.success) {
@@ -1107,10 +1229,14 @@ var reviewSettingsPatchSchema = z4.strictObject({
1107
1229
  function createSettingsRouter() {
1108
1230
  const app = new Hono8();
1109
1231
  app.get("/", async (c) => {
1232
+ const denied = await requirePermission(c, "settings.read");
1233
+ if (denied) return denied;
1110
1234
  const settings = await c.env.deps.repositories.appSettings.getReviewSettings(c.env);
1111
1235
  return c.json({ settings });
1112
1236
  });
1113
1237
  app.patch("/", async (c) => {
1238
+ const denied = await requirePermission(c, "settings.write");
1239
+ if (denied) return denied;
1114
1240
  const body = await c.req.json().catch(() => null);
1115
1241
  const parsed = reviewSettingsPatchSchema.safeParse(body);
1116
1242
  if (!parsed.success) {
@@ -1126,21 +1252,27 @@ function createSettingsRouter() {
1126
1252
 
1127
1253
  // src/router.ts
1128
1254
  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));
1255
+ const assets = c.env.ASSETS;
1256
+ if (assets && typeof assets.fetch === "function") {
1257
+ return assets.fetch(new Request(new URL("/", c.req.url), c.req.raw));
1132
1258
  }
1133
1259
  return c.text("Not Found: Please mount UI static assets handler here.", 404);
1134
1260
  }
1135
- function createApiRouter() {
1261
+ function createApiRouter(options = {}) {
1136
1262
  const app = new Hono9();
1137
1263
  app.use("*", observability);
1264
+ for (const middleware of options.beforeAuth ?? []) {
1265
+ app.use("*", middleware);
1266
+ }
1138
1267
  app.use("/auth/logout", requireSession);
1139
1268
  app.use("/auth/logout", requireCsrfHeader);
1140
1269
  app.route("/auth", createAuthRouter());
1141
1270
  app.route("/webhook", createWebhookRouter());
1142
1271
  app.use("/api/*", requireSession);
1143
1272
  app.use("/api/*", requireCsrfHeader);
1273
+ for (const middleware of options.afterAuth ?? []) {
1274
+ app.use("/api/*", middleware);
1275
+ }
1144
1276
  app.route("/api/auth", createAuthApiRouter());
1145
1277
  app.route("/api/jobs", createJobsRouter());
1146
1278
  app.route("/api/repos", createReposRouter());
@@ -1157,9 +1289,20 @@ function createApiRouter() {
1157
1289
  app.get("/health", requireSession, serveIndex);
1158
1290
  app.get("/settings", requireSession, serveIndex);
1159
1291
  app.get("/account", requireSession, serveIndex);
1292
+ for (const path of options.publicPages ?? []) {
1293
+ app.get(path, serveIndex);
1294
+ }
1295
+ for (const path of options.pages ?? []) {
1296
+ app.get(path, requireSession, serveIndex);
1297
+ }
1298
+ options.routes?.(app);
1160
1299
  return app;
1161
1300
  }
1162
1301
  export {
1163
- createApiRouter
1302
+ createApiRouter,
1303
+ requireCsrfHeader,
1304
+ requirePermission,
1305
+ requireQuota,
1306
+ requireSession
1164
1307
  };
1165
1308
  //# 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\n// Cloudflare's edge rewrites strong ETags to weak (W/\"...\") when it compresses the response, so the\n// client echoes back a weak validator; compare weakly or the 304 path never fires in production.\nfunction etagMatches(header: string | undefined, etag: string) {\n if (!header) return false;\n const bare = etag.replace(/^W\\//, '');\n return header.split(',').some((candidate) => candidate.trim().replace(/^W\\//, '') === bare);\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 (etagMatches(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":";AAAA,SAAS,QAAAA,aAAY;;;ACArB,SAAS,wBAAwB;;;ACA1B,SAAS,UAAU,SAAiB,SAAS,KAAK;AACvD,SAAO,SAAS,KAAK,EAAE,OAAO,QAAQ,GAAG,EAAE,OAAO,CAAC;AACrD;AAEO,SAAS,UAAU,SAAkB;AAC1C,QAAM,SAAS,QAAQ,QAAQ,IAAI,QAAQ,KAAK;AAChD,SAAO,OAAO,SAAS,WAAW;AACpC;;;ACPA,SAAS,cAAc,WAAW,iBAAiB;AAKnD,IAAM,sBAAsB;AAC5B,IAAM,sBAAsB,KAAK,KAAK,KAAK;AAI3C,eAAsB,cAAc,GAAoB,SAAwB;AAC9E,QAAM,QAAQ,MAAM,EAAE,IAAI,KAAK,aAAa,cAAc,OAAO;AAEjE,YAAU,GAAG,qBAAqB,OAAO;AAAA,IACvC,UAAU;AAAA,IACV,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,QAAQ;AAAA,EACV,CAAC;AAED,IAAE,IAAI,gBAAgB,KAAK;AAC3B,IAAE,IAAI,eAAe,OAAO;AAE5B,SAAO;AACT;AAEA,eAAsB,eAAe,GAAoB;AACvD,QAAM,QAAQ,UAAU,GAAG,mBAAmB;AAC9C,MAAI,OAAO;AACT,UAAM,EAAE,IAAI,KAAK,aAAa,eAAe,KAAK;AAAA,EACpD;AAEA,IAAE,IAAI,gBAAgB,IAAI;AAC1B,IAAE,IAAI,eAAe,IAAI;AAEzB,eAAa,GAAG,qBAAqB;AAAA,IACnC,MAAM;AAAA,EACR,CAAC;AACH;AAEA,eAAsB,YAAY,GAAoB;AACpD,QAAM,QAAQ,UAAU,GAAG,mBAAmB,KAAK;AACnD,IAAE,IAAI,gBAAgB,KAAK;AAE3B,MAAI,CAAC,OAAO;AACV,MAAE,IAAI,eAAe,IAAI;AACzB,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,MAAM,EAAE,IAAI,KAAK,aAAa,YAAY,KAAK;AAC/D,IAAE,IAAI,eAAe,OAAO;AAC5B,SAAO;AACT;;;AFhDO,IAAM,iBAAiB,iBAAyB,OAAO,GAAG,SAAS;AACxE,QAAM,UAAU,MAAM,YAAY,CAAC;AACnC,MAAI,CAAC,SAAS;AACZ,QAAI,UAAU,EAAE,IAAI,GAAG,GAAG;AACxB,aAAO,EAAE,SAAS,QAAQ;AAAA,IAC5B;AAEA,WAAO,SAAS,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACjE;AAEA,QAAM,KAAK;AACb,CAAC;;;AGhBD,SAAS,oBAAAC,yBAAwB;AAGjC,IAAM,eAAe,oBAAI,IAAI,CAAC,OAAO,QAAQ,SAAS,CAAC;AACvD,IAAM,iBAAiB;AAEhB,IAAM,oBAAoBA,kBAAyB,OAAO,GAAG,SAAS;AAC3E,MAAI,aAAa,IAAI,EAAE,IAAI,OAAO,YAAY,CAAC,GAAG;AAChD,UAAM,KAAK;AACX;AAAA,EACF;AAEA,MAAI,EAAE,IAAI,OAAO,kBAAkB,MAAM,gBAAgB;AACvD,WAAO,EAAE,KAAK,EAAE,OAAO,YAAY,GAAG,GAAG;AAAA,EAC3C;AAEA,QAAM,KAAK;AACb,CAAC;;;ACjBD,SAAS,yBAAyB;AAClC,SAAS,iBAAiB,qBAAqB;AAM/C,IAAM,UAAU,IAAI,kBAAuC;AAE3D,IAAM,SAAN,MAAM,QAAO;AAAA,EACX,YAAoB,UAA+B,CAAC,GAAG;AAAnC;AAAA,EAAoC;AAAA,EAApC;AAAA,EAEpB,YAAY,YAAiC;AAC3C,WAAO,IAAI,QAAO,EAAE,GAAG,KAAK,SAAS,GAAG,WAAW,CAAC;AAAA,EACtD;AAAA,EAEQ,IAAI,OAAe,SAAiB,MAAY;AACtD,UAAM,QAAQ,QAAQ,SAAS,KAAK,CAAC;AACrC,UAAM,SAAS,gBAAgB,OAAO,SAAS,CAAC,OAAO,KAAK,OAAO,GAAG,IAAI;AAE1E,QAAI,UAAU,SAAS;AACrB,cAAQ,MAAM,KAAK,UAAU,MAAM,CAAC;AAAA,IACtC,WAAW,UAAU,QAAQ;AAC3B,cAAQ,KAAK,KAAK,UAAU,MAAM,CAAC;AAAA,IACrC,OAAO;AACL,cAAQ,IAAI,KAAK,UAAU,MAAM,CAAC;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,eAAkB,SAA8B,IAAgB;AAC9D,WAAO,QAAQ,IAAI,EAAE,GAAG,QAAQ,SAAS,GAAG,GAAG,QAAQ,GAAG,EAAE;AAAA,EAC9D;AAAA,EAEA,KAAK,SAAiB,MAAY;AAChC,SAAK,IAAI,QAAQ,SAAS,IAAI;AAAA,EAChC;AAAA,EAEA,MAAM,SAAiB,MAAY;AACjC,QAAI,gBAAgB,OAAO;AACzB,WAAK,IAAI,SAAS,SAAS;AAAA,QACzB,MAAM,KAAK;AAAA,QACX,SAAS,KAAK;AAAA,QACd,OAAO,KAAK;AAAA,MACd,CAAC;AAAA,IACH,OAAO;AACL,WAAK,IAAI,SAAS,SAAS,IAAI;AAAA,IACjC;AAAA,EACF;AAAA,EAEA,KAAK,SAAiB,MAAY;AAChC,SAAK,IAAI,QAAQ,SAAS,IAAI;AAAA,EAChC;AAAA,EAEA,MAAM,SAAiB,MAAY;AACjC,SAAK,IAAI,SAAS,SAAS,IAAI;AAAA,EACjC;AACF;AAEO,IAAM,SAAS,IAAI,OAAO;AAIjC,cAAc,MAAM;;;AC1Db,IAAM,gBAA2C,OAAO,GAAG,SAAS;AACzE,QAAM,YAAY,EAAE,IAAI,OAAO,cAAc,KAAK,OAAO,WAAW;AACpE,IAAE,IAAI,aAAa,SAAS;AAE5B,SAAO,OAAO,eAAe;AAAA,IAC3B;AAAA,IACA,QAAQ,EAAE,IAAI;AAAA,IACd,MAAM,EAAE,IAAI;AAAA,EACd,GAAG,YAAY;AACb,WAAO,KAAK,qBAAqB,EAAE,IAAI,MAAM,IAAI,EAAE,IAAI,IAAI,EAAE;AAE7D,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,KAAK;AACX,UAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,WAAO,KAAK,sBAAsB,EAAE,IAAI,MAAM,IAAI,EAAE,IAAI,IAAI,IAAI;AAAA,MAC9D,QAAQ,EAAE,IAAI;AAAA,MACd,YAAY;AAAA,IACd,CAAC;AAAA,EACH,CAAC;AACH;;;ACxBA,SAAS,YAAY;AAIrB,SAAS,gBAAgB,QAAgB;AACvC,QAAM,SAAS,IAAI,gBAAgB,EAAE,OAAO,OAAO,CAAC;AACpD,SAAO,UAAU,OAAO,SAAS,CAAC;AACpC;AAEO,SAAS,kBAAkB,OAAe;AAC/C,SAAO,IAAI;AAAA,IACT,MACG,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,MAAM,KAAK,EAAE,YAAY,CAAC,EACzC,OAAO,OAAO;AAAA,EACnB;AACF;AAEO,SAAS,mBAAmB;AACjC,QAAM,MAAM,IAAI,KAAa;AAE7B,MAAI,IAAI,WAAW,OAAO,MAAM;AAC9B,UAAM,QAAQ,MAAM,EAAE,IAAI,KAAK,aAAa,iBAAiB;AAC7D,UAAM,SAAS,MAAM,EAAE,IAAI,KAAK,aAAa,mBAAmB,EAAE,IAAI,mBAAmB,KAAK;AAC9F,WAAO,EAAE,SAAS,OAAO,KAAK,GAAG;AAAA,EACnC,CAAC;AAED,MAAI,IAAI,oBAAoB,OAAO,MAAM;AACvC,UAAM,QAAQ,EAAE,IAAI,MAAM,OAAO;AACjC,QAAI,OAAO;AACT,aAAO,EAAE,SAAS,gBAAgB,KAAK,GAAG,GAAG;AAAA,IAC/C;AAEA,UAAM,OAAO,EAAE,IAAI,MAAM,MAAM,GAAG,KAAK;AACvC,UAAM,QAAQ,EAAE,IAAI,MAAM,OAAO,GAAG,KAAK;AACzC,QAAI,CAAC,QAAQ,CAAC,OAAO;AACnB,aAAO,EAAE,SAAS,gBAAgB,kBAAkB,GAAG,GAAG;AAAA,IAC5D;AAEA,UAAM,eAAe,MAAM,EAAE,IAAI,KAAK,aAAa,kBAAkB,KAAK;AAC1E,QAAI,CAAC,cAAc;AACjB,aAAO,EAAE,SAAS,gBAAgB,eAAe,GAAG,GAAG;AAAA,IACzD;AAEA,QAAI;AACF,YAAM,EAAE,SAAS,IAAI,MAAM,EAAE,IAAI,KAAK,aAAa,sBAAsB,MAAM,OAAO,KAAK;AAC3F,YAAM,eAAe,kBAAkB,EAAE,IAAI,uBAAuB;AAEpE,UAAI,CAAC,aAAa,IAAI,SAAS,MAAM,YAAY,CAAC,GAAG;AACnD,eAAO,EAAE,SAAS,gBAAgB,aAAa,GAAG,GAAG;AAAA,MACvD;AAEA,YAAM,eAAe,CAAC;AACtB,YAAM,cAAc,GAAG,QAAQ;AAG/B,UAAI;AACF,cAAM,EAAE,IAAI,KAAK,aAAa,SAAS,sBAAsB,EAAE,KAAY;AAAA,UACzE,cAAc,OAAO,SAAS,cAAc;AAAA,UAC5C,gBAAgB,SAAS;AAAA,UACzB,aAAa,SAAS;AAAA,UACtB,cAAc,SAAS;AAAA,QACzB,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,UAAE,IAAI,KAAK,SAAS,OAAO,KAAK,iDAAiD;AAAA,UAC/E,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QACxD,CAAC;AAAA,MACH;AAEA,aAAO,EAAE,SAAS,cAAc,GAAG;AAAA,IACrC,QAAQ;AACN,aAAO,EAAE,SAAS,gBAAgB,cAAc,GAAG,GAAG;AAAA,IACxD;AAAA,EACF,CAAC;AAED,MAAI,KAAK,WAAW,OAAO,MAAM;AAC/B,UAAM,eAAe,CAAC;AACtB,WAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,EAC5B,CAAC;AAED,SAAO;AACT;;;ACjFA,SAAS,QAAAC,aAAY;AAErB;AAAA,EACE;AAAA,OAIK;AAKP,eAAe,oBACb,GACA,OAKiB;AACjB,QAAM,EAAE,WAAW,SAAS,aAAa,IAAI;AAE7C,MAAI,iBAAiB,KAAM,QAAO;AAElC,QAAM,YAAY,EAAE,IAAI,gBAAgB,IAAI,YAAY;AAExD,QAAM,SAAS,CAAC,YACd,QAAQ,QAAQ,MAAM,QAAQ,MAAM,SAAS,IAAI,YAAY,EAAE,WAAW,QAAQ;AAEpF,QAAM,WAAW,QAAQ,cAAc,UAAU;AACjD,QAAM,UAAiB,CAAC;AACxB,QAAM,WAAqB,CAAC;AAE5B,QAAM,MAAM,CAAC,SAAqC,YAAiB;AACjE,QAAI,CAAC,OAAO,OAAO,KAAK,CAAC,SAAS,KAAM;AAIxC,UAAM,eAAe,QAAQ,QAAQ,IAAI,MAAM,0BAA0B;AACzE,QAAI,CAAC,YAAa;AAElB,QAAI,cAAc,YAAY,CAAC;AAC/B,QAAI,aAAa;AACjB,QAAI,gBAAgB;AACpB,QAAI;AACF,YAAM,QAAQ,KAAK,MAAM,WAAW;AACpC,UAAI,MAAM,QAAQ,KAAK,KAAK,MAAM,UAAU,GAAG;AAC7C,wBAAgB,MAAM,CAAC;AACvB,sBAAc,MAAM,CAAC;AACrB,YAAI,MAAM,UAAU,EAAG,cAAa,MAAM,CAAC;AAAA,MAC7C;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,YAAQ,KAAK;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,iBAAiB,QAAQ;AAAA,MACzB;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,cAAc,+BAA+B;AAC/C,UAAM,EAAE,QAAQ,QAAQ,IAAI;AAE5B,QAAI,WAAW,UAAW,KAAI,SAAS,QAAQ;AAAA,aACtC,WAAW,UAAW,KAAI,SAAS,SAAS;AAAA,EACvD,WAAW,cAAc,8BAA8B;AACrD,UAAM,EAAE,QAAQ,OAAO,IAAI;AAC3B,eAAW,WAAW,QAAQ,YAAY,CAAC,GAAG;AAC5C,UAAI,WAAW,YAAY;AACzB,YAAI,SAAS,UAAU;AAAA,MACzB,WAAW,WAAW,cAAc;AAClC,YAAI,SAAS,YAAY;AACzB,YAAI,OAAO,OAAO,EAAG,UAAS,KAAK,QAAQ,EAAE;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AAEA,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,MAAI;AACF,UAAM,eAAe,EAAE,IAAI,KAAK,aAAa;AAE7C,QAAI,SAAS,SAAS,EAAG,OAAM,aAAa,sBAAsB,EAAE,KAAY,cAAc,QAAQ;AACtG,WAAO,MAAM,aAAa,sBAAsB,EAAE,KAAY,OAAO;AAAA,EACvE,SAAS,OAAO;AACd,MAAE,IAAI,KAAK,SAAS,OAAO,KAAK,qCAAqC;AAAA,MACnE;AAAA,MACA;AAAA,MACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAC9D,CAAC;AACD,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,oBAAoB,GAAoB;AAC1D,QAAM,YAAY,EAAE,IAAI,OAAO,gBAAgB;AAC/C,QAAM,aAAa,EAAE,IAAI,OAAO,mBAAmB;AACnD,QAAM,YAAY,EAAE,IAAI,OAAO,qBAAqB;AACpD,QAAM,UAAU,MAAM,EAAE,IAAI,KAAK;AAEjC,MAAI,CAAC,aAAa,CAAC,YAAY;AAC7B,WAAO,UAAU,mCAAmC,GAAG;AAAA,EACzD;AAEA,QAAM,WAAW,MAAM,EAAE,IAAI,KAAK,QAAQ,gBAAgB,aAAa,MAAM,OAAO;AACpF,MAAI,CAAC,UAAU;AACb,WAAO,UAAU,8BAA8B,GAAG;AAAA,EACpD;AAEA,MAAI;AACJ,MAAI;AACF,cAAU,KAAK,MAAM,OAAO;AAAA,EAC9B,QAAQ;AACN,WAAO,UAAU,iCAAiC,GAAG;AAAA,EACvD;AAGA,QAAM,kBAAkB,6BAA6B,SAAS;AAE9D,QAAM,WAAW,MAAM,EAAE,IAAI,KAAK,aAAa,kBAAkB,sBAAsB,EAAE,KAAY;AAAA,IACnG;AAAA,IACA;AAAA,IACA,OAAO,gBAAgB,UAAW,QAAgB,WAAW,MAAM,QAAQ;AAAA,IAC3E,MAAM,gBAAgB,UAAW,QAAgB,WAAW,OAAO;AAAA,IACnE,SAAS,kBAAkB,OAAO;AAAA,EACpC,CAAC;AAED,MAAI,CAAC,SAAS,UAAU;AACtB,WAAO,EAAE,KAAK,EAAE,IAAI,MAAM,WAAW,KAAK,GAAG,GAAG;AAAA,EAClD;AAEA,QAAM,iBAAiB,OAAQ,QAAgB,cAAc,MAAM,EAAE;AACrE,MAAI,CAAC,kBAAkB,EAAE,gBAAgB,YAAY,CAAC,QAAQ,YAAY;AACxE,WAAO,EAAE,KAAK,EAAE,IAAI,MAAM,SAAS,KAAK,GAAG,GAAG;AAAA,EAChD;AAGA,MAAI,iBAAiB;AACnB,UAAM,WAAW,MAAM,oBAAoB,GAAG;AAAA,MAC5C;AAAA,MACA;AAAA,MACA,cAAc,SAAS;AAAA,IACzB,CAAC;AACD,WAAO,EAAE,KAAK,EAAE,IAAI,MAAM,UAAU,MAAM,SAAS,GAAG,GAAG;AAAA,EAC3D;AAEA,QAAM,aAAa,EAAE,IAAI,KAAK,QAAQ,iBAAiB,WAAW,OAAO;AACzE,MAAI,CAAC,YAAY;AACf,WAAO,EAAE,KAAK,EAAE,IAAI,MAAM,SAAS,MAAM,UAAU,GAAG,GAAG;AAAA,EAC3D;AAEA,QAAM,aAAa,MAAM,EAAE,IAAI,KAAK,OAAO,eAAe;AAAA,IACxD;AAAA,IACA,OAAQ,QAAgB,WAAW,MAAM;AAAA,IACzC,MAAO,QAAgB,WAAW;AAAA,EACpC,CAAC;AAED,MAAI,WAAW,YAAY,OAAO;AAChC,WAAO,EAAE,KAAK,EAAE,IAAI,MAAM,SAAS,MAAM,QAAQ,sBAAsB,GAAG,GAAG;AAAA,EAC/E;AAEA,QAAM,YAAY,EAAE,IAAI,KAAK,QAAQ,qBAAqB;AAAA,IACxD,WAAW,WAAW;AAAA,IACtB,SAAS,WAAW;AAAA,IACpB,aAAa,EAAE,IAAI;AAAA,IACnB,QAAQ,WAAW;AAAA,EACrB,CAAC;AAED,MAAI,WAAW,aAAa,UAAU,SAAS;AAC7C,UAAM,WAAW,EAAE,IAAI,KAAK,aAAa;AACzC,UAAM,cAAc,MAAM,SAAS,uBAAuB,EAAE,KAAY;AAAA,MACtE,OAAO,UAAU;AAAA,MACjB,MAAM,UAAU;AAAA,MAChB,UAAU,UAAU;AAAA,MACpB,WAAW,UAAU;AAAA,MACrB,SAAS,UAAU;AAAA,IACrB,CAAC;AAED,QAAI,aAAa;AACf,aAAO,EAAE,KAAK;AAAA,QACZ,IAAI;AAAA,QACJ,WAAW;AAAA,QACX,SAAS,YAAY,WAAW,WAAW,WAAW;AAAA,QACtD,KAAK;AAAA,MACP,GAAG,GAAG;AAAA,IACR;AAEA,UAAM,YAAY,MAAM,EAAE,IAAI,KAAK,aAAa;AAAA,MAC9C,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB,UAAU;AAAA,QAC1B,OAAO,UAAU;AAAA,QACjB,MAAM,UAAU;AAAA,MAClB;AAAA,IACF,CAAC;AACD,QAAI,aAAa,CAAC,UAAU,SAAS;AAEnC,aAAO,EAAE,KAAK,EAAE,IAAI,MAAM,SAAS,MAAM,QAAQ,iBAAiB,GAAG,GAAG;AAAA,IAC1E;AAEA,UAAM,MAAM,MAAM,SAAS,UAAU,EAAE,KAAY;AAAA,MACjD,gBAAgB,UAAU;AAAA,MAC1B,OAAO,UAAU;AAAA,MACjB,MAAM,UAAU;AAAA,MAChB,UAAU,UAAU;AAAA,MACpB,SAAS,UAAU;AAAA,MACnB,UAAU,UAAU;AAAA,MACpB,WAAW,UAAU;AAAA,MACrB,SAAS,UAAU;AAAA,MACnB,SAAS,UAAU;AAAA,MACnB,SAAS,UAAU;AAAA,MACnB,SAAS,UAAU;AAAA,MACnB,gBAAgB,WAAW;AAAA,IAC7B,CAAC;AAED,UAAM,SAAS,mBAAmB,EAAE,KAAY;AAAA,MAC9C,gBAAgB,UAAU;AAAA,MAC1B,OAAO,UAAU;AAAA,MACjB,MAAM,UAAU;AAAA,MAChB,UAAU,UAAU;AAAA,MACpB,UAAU,IAAI;AAAA,IAChB,CAAC;AAED,UAAM,EAAE,IAAI,KAAK,SAAS,iBAAiB;AAAA,MACzC,OAAO,IAAI;AAAA,MACX;AAAA,MACA,OAAO;AAAA,MACP,WAAW,EAAE,IAAI,WAAW;AAAA,IAC9B,CAAC;AAED,WAAO,EAAE,KAAK,EAAE,IAAI,MAAM,SAAS,UAAU,IAAI,GAAG,GAAG;AAAA,EACzD;AAGA,QAAM,EAAE,IAAI,KAAK,SAAS,iBAAiB;AAAA,IACzC;AAAA,IACA;AAAA,IACA,WAAW,EAAE,IAAI,WAAW;AAAA,EAC9B,CAAQ;AAER,SAAO,EAAE,KAAK,EAAE,IAAI,MAAM,SAAS,SAAS,GAAG,GAAG;AACtD;AAEO,SAAS,sBAAsB;AACpC,QAAM,MAAM,IAAIC,MAAa;AAE7B,MAAI,KAAK,KAAK,mBAAmB;AAEjC,SAAO;AACT;;;AC/PA,SAAS,2BAA2B;AACpC,SAAS,QAAAC,aAAY;AACrB,SAAS,SAAS;;;ACKlB,eAAsB,kBACpB,GACA,QACA,UAC0B;AAC1B,QAAM,QAAQ,EAAE,IAAI,KAAK;AACzB,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,OAAO,EAAE,IAAI,aAAa;AAChC,MAAI,CAAC,MAAM;AACT,WAAO,EAAE,KAAK,EAAE,OAAO,gBAAgB,MAAM,gBAAgB,QAAQ,QAAQ,KAAK,GAAG,GAAG;AAAA,EAC1F;AAEA,QAAM,SAAS,MAAM,MAAM,UAAU,EAAE,MAAM,QAAQ,SAAS,CAAC;AAC/D,MAAI,OAAO,QAAS,QAAO;AAE3B,SAAO,EAAE;AAAA,IACP,EAAE,OAAO,aAAa,MAAM,aAAa,QAAQ,QAAQ,OAAO,UAAU,KAAK;AAAA,IAC/E;AAAA,EACF;AACF;AAEA,eAAsB,aACpB,GACA,OAC0B;AAC1B,QAAM,aAAa,EAAE,IAAI,KAAK;AAC9B,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,OAAO,EAAE,IAAI,aAAa,KAAK;AACrC,QAAM,SAAS,MAAM,WAAW,EAAE,GAAG,OAAO,KAAK,CAAC;AAClD,MAAI,OAAO,QAAS,QAAO;AAE3B,QAAM,UAAU,OAAO,oBACnB,EAAE,eAAe,OAAO,OAAO,iBAAiB,EAAE,IAClD;AAEJ,SAAO,EAAE;AAAA,IACP;AAAA,MACE,OAAO;AAAA,MACP,MAAM;AAAA,MACN,QAAQ,MAAM;AAAA,MACd,QAAQ,OAAO,UAAU;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AD/CA,IAAM,cAAc,EAAE,aAAa;AAAA,EACjC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAG;AAC1C,CAAC;AAGD,IAAM,sBAAsB,EAAE,aAAa;AAAA,EACzC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS;AAAA,EACjD,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,qBAAqB;AAAA,IACrE,SAAS;AAAA,EACX,CAAC,EAAE,SAAS,EAAE,SAAS;AACzB,CAAC,EAAE;AAAA,EACD,CAAC,SAAS,KAAK,SAAS,UAAa,KAAK,aAAa;AAAA,EACvD,EAAE,SAAS,qBAAqB;AAClC;AAEO,SAAS,sBAAsB;AACpC,QAAM,MAAM,IAAIC,MAAa;AAE7B,MAAI,IAAI,YAAY,OAAO,MAAM;AAC/B,UAAM,cAAc,EAAE,IAAI,aAAa;AACvC,QAAI,CAAC,aAAa;AAChB,aAAO,SAAS,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACjE;AAGA,UAAM,cAAc,MAAM,EAAE,IAAI,KAAK,OAAO,kBAAkB,WAAW;AAEzE,WAAO,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,YAAY,IAAI,EAAE,MAAM,YAAY,CAAC;AAAA,EACxF,CAAC;AAED,MAAI,IAAI,YAAY,OAAO,MAAM;AAC/B,UAAM,cAAc,EAAE,IAAI,aAAa;AACvC,QAAI,CAAC,aAAa;AAChB,aAAO,SAAS,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACjE;AAEA,UAAM,WAAW,EAAE,IAAI,KAAK,aAAa;AACzC,UAAM,eAAe,OAAO,YAAY,cAAc;AAGtD,QAAI,UAAU,MAAM,SAAS,mBAAmB,EAAE,KAAY,YAAY;AAC1E,QAAI,CAAC,SAAS;AACZ,gBAAU,MAAM,SAAS,sBAAsB,EAAE,KAAY;AAAA,QAC3D;AAAA,QACA,gBAAgB,YAAY;AAAA,QAC5B,aAAa,YAAY;AAAA,QACzB,cAAc,YAAY;AAAA,MAC5B,CAAC;AAAA,IACH;AAEA,WAAO,EAAE,KAAK,EAAE,QAAQ,CAAC;AAAA,EAC3B,CAAC;AAED,MAAI,MAAM,YAAY,OAAO,MAAM;AACjC,UAAM,SAAS,MAAM,kBAAkB,GAAG,eAAe;AACzD,QAAI,OAAQ,QAAO;AACnB,UAAM,cAAc,EAAE,IAAI,aAAa;AACvC,QAAI,CAAC,aAAa;AAChB,aAAO,SAAS,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACjE;AAEA,UAAM,OAAO,MAAM,EAAE,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AAChD,UAAM,SAAS,oBAAoB,UAAU,IAAI;AACjD,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,QAAQ,OAAO,MAAM,OAAO,CAAC,GAAG;AACtC,aAAO;AAAA,QACL,SAAS,UAAU,kBAAkB,QAAQ;AAAA,QAC7C;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,EAAE,IAAI,KAAK,aAAa;AACzC,UAAM,eAAe,OAAO,YAAY,cAAc;AAGtD,UAAM,WAAW,MAAM,SAAS,mBAAmB,EAAE,KAAY,YAAY;AAC7E,QAAI,CAAC,UAAU;AACb,YAAM,SAAS,sBAAsB,EAAE,KAAY;AAAA,QACjD;AAAA,QACA,gBAAgB,YAAY;AAAA,QAC5B,aAAa,YAAY;AAAA,QACzB,cAAc,YAAY;AAAA,MAC5B,CAAC;AAAA,IACH;AAEA,UAAM,UAAU,MAAM,SAAS,sBAAsB,EAAE,KAAY,cAAc;AAAA,MAC/E,aAAa,OAAO,KAAK;AAAA,MACzB,UAAU,OAAO,KAAK;AAAA,IACxB,CAAC;AACD,WAAO,EAAE,KAAK,EAAE,QAAQ,CAAC;AAAA,EAC3B,CAAC;AAED,MAAI,IAAI,kBAAkB,OAAO,MAAM;AACrC,UAAM,cAAc,EAAE,IAAI,aAAa;AACvC,QAAI,CAAC,aAAa;AAChB,aAAO,SAAS,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACjE;AAEA,UAAM,eAAe,OAAO,YAAY,cAAc;AACtD,UAAM,aAAa,MAAM,EAAE,IAAI,KAAK,SAAS,0BAA0B,YAAY;AACnF,WAAO,EAAE,KAAK;AAAA,MACZ,QAAQ,YAAY,UAAU;AAAA,MAC9B,OAAO,YAAY,SAAS;AAAA,MAC5B,WAAW,YAAY,aAAa;AAAA,IACtC,CAAC;AAAA,EACH,CAAC;AAED,MAAI,KAAK,kBAAkB,OAAO,MAAM;AACtC,UAAM,SAAS,MAAM,kBAAkB,GAAG,4BAA4B;AACtE,QAAI,OAAQ,QAAO;AACnB,UAAM,cAAc,EAAE,IAAI,aAAa;AACvC,QAAI,CAAC,aAAa;AAChB,aAAO,SAAS,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IACjE;AAEA,UAAM,OAAO,MAAM,EAAE,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AAChD,UAAM,SAAS,YAAY,UAAU,IAAI;AACzC,QAAI,CAAC,OAAO,SAAS;AACnB,aAAO,UAAU,gCAAgC,GAAG;AAAA,IACtD;AAEA,UAAM,WAAW,EAAE,IAAI,KAAK;AAC5B,UAAM,eAAe,OAAO,YAAY,cAAc;AAEtD,UAAM,qBAAqB,MAAM,SAAS,0BAA0B,YAAY;AAChF,QAAI,oBAAoB;AACtB,aAAO,EAAE,KAAK;AAAA,QACZ,QAAQ,mBAAmB;AAAA,QAC3B,OAAO,mBAAmB;AAAA,QAC1B,WAAW,mBAAmB;AAAA,MAChC,CAAC;AAAA,IACH;AAEA,UAAM,SAAS,MAAM,SAAS,iBAAiB,cAAc,OAAO,KAAK,KAAK;AAC9E,QAAI,CAAC,QAAQ;AACX,aAAO,UAAU,2CAA2C,GAAG;AAAA,IACjE;AAEA,UAAM,aAAa,MAAM,SAAS,0BAA0B,YAAY;AAExE,WAAO,EAAE,KAAK;AAAA,MACZ,QAAQ,YAAY,UAAU;AAAA,MAC9B,OAAO,YAAY,SAAS;AAAA,MAC5B,WAAW,YAAY,aAAa;AAAA,IACtC,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AACT;;;AE3JA,SAAS,QAAAC,aAAY;AAErB,SAAS,mBAAmB,oBAAoB,uBAAuB;AAEvE,SAAS,wBAAwB;AACjC,SAAS,wBAAwB,0BAA0B,wBAAwB;AAKnF,eAAe,qBAAqB,GAAoB,KAAyD;AAE/G,QAAM,EAAE,IAAI,KAAK,SAAS,qBAAqB,GAAG;AACpD;AAEA,SAAS,QAAQ,OAAmG;AAClH,SAAO,QAAQ,MAAM,EAAE,IAAI,MAAM,MAAM,IAAI,MAAM,SAAS,IAAI,MAAM,YAAY,IAAI,IAAI,KAAK,MAAM,SAAS,EAAE,QAAQ,CAAC;AACzH;AAIA,SAAS,YAAY,QAA4B,MAAc;AAC7D,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,OAAO,KAAK,QAAQ,QAAQ,EAAE;AACpC,SAAO,OAAO,MAAM,GAAG,EAAE,KAAK,CAAC,cAAc,UAAU,KAAK,EAAE,QAAQ,QAAQ,EAAE,MAAM,IAAI;AAC5F;AAEA,SAAS,oBAAoB,GAAoB;AAC/C,MAAI;AACF,WAAO,EAAE;AAAA,EACX,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,mBAAmB;AACjC,QAAM,MAAM,IAAIC,MAAa;AAE7B,MAAI,IAAI,KAAK,OAAO,MAAM;AACxB,UAAM,SAAS,MAAM,kBAAkB,GAAG,WAAW;AACrD,QAAI,OAAQ,QAAO;AACnB,MAAE,IAAI,KAAK,SAAS,iCAAiC,oBAAoB,CAAC,CAAC;AAE3E,UAAM,WAAW,EAAE,IAAI,MAAM;AAC7B,UAAM,QAAQ,gBAAgB,MAAM,QAAQ;AAE5C,UAAM,SAAS,MAAM,EAAE,IAAI,KAAK,aAAa,KAAK,SAAS,EAAE,KAAY,KAAY;AACrF,WAAO,EAAE,KAAK,MAAM;AAAA,EACtB,CAAC;AAED,MAAI,IAAI,QAAQ,OAAO,MAAM;AAC3B,UAAM,SAAS,MAAM,kBAAkB,GAAG,aAAa,EAAE,MAAM,OAAO,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,CAAC;AAC7F,QAAI,OAAQ,QAAO;AACnB,MAAE,IAAI,KAAK,SAAS,iCAAiC,oBAAoB,CAAC,CAAC;AAE3E,UAAM,MAAM,MAAM,EAAE,IAAI,KAAK,aAAa,KAAK,aAAa,EAAE,KAAY,EAAE,IAAI,MAAM,IAAI,CAAC;AAC3F,QAAI,CAAC,KAAK;AACR,aAAO,UAAU,kBAAkB,GAAG;AAAA,IACxC;AAEA,UAAM,OAAO,QAAQ,GAAG;AACxB,UAAM,eAAe,IAAI,KAAK,IAAI,SAAS,EAAE,YAAY;AACzD,QAAI,YAAY,EAAE,IAAI,OAAO,eAAe,GAAG,IAAI,GAAG;AACpD,aAAO,IAAI,SAAS,MAAM;AAAA,QACxB,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,MAAM;AAAA,UACN,iBAAiB;AAAA,QACnB;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC;AAC/B,aAAS,QAAQ,IAAI,QAAQ,IAAI;AACjC,aAAS,QAAQ,IAAI,iBAAiB,YAAY;AAClD,aAAS,QAAQ,IAAI,iBAAiB,mBAAmB;AACzD,WAAO;AAAA,EACT,CAAC;AAGD,MAAI,IAAI,cAAc,OAAO,MAAM;AACjC,UAAM,SAAS,MAAM,kBAAkB,GAAG,aAAa,EAAE,MAAM,OAAO,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,CAAC;AAC7F,QAAI,OAAQ,QAAO;AACnB,UAAM,MAAM,MAAM,EAAE,IAAI,KAAK,aAAa,KAAK,aAAa,EAAE,KAAY,EAAE,IAAI,MAAM,IAAI,CAAC;AAC3F,QAAI,CAAC,KAAK;AACR,aAAO,UAAU,kBAAkB,GAAG;AAAA,IACxC;AAEA,UAAM,SAAS,IAAI,kBAAkB;AACrC,UAAM,SAAS,EAAE,IAAI,KAAK,YAAY,cAAc,IAAI,cAAc;AAEtE,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,EAAE,IAAI,KAAK,SAAS;AAAA,QAClC,EAAE,IAAI,KAAK,SAAS,oBAAoB;AAAA,QACxC,EAAE,IAAI,IAAI,IAAI,OAAO,IAAI,OAAO,MAAM,IAAI,MAAM,SAAS,IAAI,SAAS,WAAW,IAAI,UAAU;AAAA,QAC/F;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,QAAE,IAAI,KAAK,SAAS,OAAO,KAAK,sCAAsC,IAAI,EAAE,IAAI,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AACzI,aAAO,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AAAA,IAC7B;AAGA,QAAI,gBAA+B;AACnC,QAAI;AACF,uBAAiB,MAAM,OAAO,eAAe,IAAI,OAAO,IAAI,MAAM,IAAI,QAAQ,GAAG,QAAQ;AAAA,IAC3F,SAAS,OAAO;AAEd,QAAE,IAAI,KAAK,SAAS,OAAO;AAAA,QAAK,sCAAsC,IAAI,EAAE;AAAA,QAC1E,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,MAAC;AAAA,IAC7D;AAGA,UAAM,QAAgC,CAAC;AACvC,UAAM,cAAc,iBAAiB,SAAS,OAAO,MAAM;AAC3D,UAAM,mBAAmB,yBAAyB,WAAW;AAC7D,eAAW,QAAQ,aAAa;AAC9B,UAAI,KAAK,aAAa,KAAK,YAAY,CAAC,KAAK,KAAM;AACnD,YAAM,EAAE,WAAW,IAAI,uBAAuB;AAAA,QAC5C;AAAA,QACA,SAAS,IAAI;AAAA,QACb;AAAA,QACA;AAAA,QACA,QAAQ,OAAO;AAAA,MACjB,CAAC;AACD,YAAM,aAAa,iBAAiB,MAAM,OAAO,OAAO,iBAAiB;AACzE,YAAM,KAAK,IAAI,IAAI,aACf,GAAG,UAAU;AAAA;AAAA,6GACb;AAAA,IACN;AAEA,UAAM,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC;AACjC,aAAS,QAAQ,IAAI,iBAAiB,qBAAqB;AAC3D,WAAO;AAAA,EACT,CAAC;AAGD,iBAAe,oBAAoB,GAAoB,WAAgB,SAA+B;AACpG,UAAM,OAAO,EAAE,IAAI,KAAK,aAAa;AACrC,UAAM,SAAS,KAAK,OAAO,SAAS;AACpC,QAAI;AACJ,QAAI;AACF,YAAM,gBAAgB,MAAM,EAAE,IAAI,KAAK,OAAO,eAAe;AAAA,QAC3D,gBAAgB,OAAO;AAAA,QACvB,OAAO,OAAO;AAAA,QACd,MAAM,OAAO;AAAA,MACf,CAAC;AACD,uBAAiB,eAAe,cAAc;AAAA,IAChD,SAAS,GAAG;AACV,uBAAiB;AAAA,IACnB;AAEA,UAAM,MAAM,MAAM,KAAK,UAAU,EAAE,KAAY;AAAA,MAC7C,gBAAgB,OAAO;AAAA,MACvB,OAAO,OAAO;AAAA,MACd,MAAM,OAAO;AAAA,MACb,UAAU,OAAO;AAAA,MACjB,SAAS,OAAO;AAAA,MAChB,UAAU,OAAO;AAAA,MACjB,WAAW,OAAO;AAAA,MAClB,SAAS,KAAK,WAAW,UAAU,QAAQ;AAAA;AAAA,MAC3C,SAAS;AAAA,MACT,SAAS,UAAU;AAAA,MACnB,SAAS,UAAU;AAAA,MACnB;AAAA,MACA,GAAI,QAAQ,UAAU,EAAE,cAAc,OAAO,GAAG,IAAI,CAAC;AAAA,IACvD,CAAC;AAED,UAAM,KAAK,mBAAmB,EAAE,KAAY;AAAA,MAC1C,gBAAgB,OAAO;AAAA,MACvB,OAAO,OAAO;AAAA,MACd,MAAM,OAAO;AAAA,MACb,UAAU,OAAO;AAAA,MACjB,UAAU,IAAI;AAAA,IAChB,CAAC;AAED,UAAM,EAAE,IAAI,KAAK,SAAS,iBAAiB;AAAA,MACzC,OAAO,IAAI;AAAA,MACX,YAAY,OAAO,WAAW;AAAA,MAC9B,OAAO;AAAA,MACP,WAAW,EAAE,IAAI,WAAW;AAAA,IAC9B,CAAC;AAED,WAAO;AAAA,EACT;AAEA,MAAI,KAAK,cAAc,OAAO,MAAM;AAClC,UAAM,SAAS,MAAM,kBAAkB,GAAG,cAAc,EAAE,MAAM,OAAO,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,CAAC;AAC9F,QAAI,OAAQ,QAAO;AACnB,UAAM,YAAY,MAAM,aAAa,GAAG,EAAE,QAAQ,aAAa,CAAC;AAChE,QAAI,UAAW,QAAO;AACtB,UAAM,OAAO,EAAE,IAAI,KAAK,aAAa;AACrC,UAAM,YAAY,MAAM,KAAK,oBAAoB,EAAE,KAAY,EAAE,IAAI,MAAM,IAAI,CAAC;AAChF,QAAI,CAAC,WAAW;AACd,aAAO,UAAU,kBAAkB,GAAG;AAAA,IACxC;AACA,UAAM,MAAM,MAAM,oBAAoB,GAAG,WAAW,EAAE,SAAS,KAAK,CAAC;AACrE,WAAO,EAAE,KAAK,EAAE,IAAI,GAAG,GAAG;AAAA,EAC5B,CAAC;AAGD,MAAI,KAAK,cAAc,OAAO,MAAM;AAClC,UAAM,SAAS,MAAM,kBAAkB,GAAG,cAAc,EAAE,MAAM,OAAO,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,CAAC;AAC9F,QAAI,OAAQ,QAAO;AACnB,UAAM,YAAY,MAAM,aAAa,GAAG,EAAE,QAAQ,aAAa,CAAC;AAChE,QAAI,UAAW,QAAO;AACtB,UAAM,OAAO,EAAE,IAAI,KAAK,aAAa;AACrC,UAAM,YAAY,MAAM,KAAK,oBAAoB,EAAE,KAAY,EAAE,IAAI,MAAM,IAAI,CAAC;AAChF,QAAI,CAAC,WAAW;AACd,aAAO,UAAU,kBAAkB,GAAG;AAAA,IACxC;AACA,UAAM,SAAS,KAAK,OAAO,SAAS;AACpC,QAAI,OAAO,WAAW,YAAY,OAAO,WAAW,WAAW;AAC7D,YAAM,qBAAqB,GAAG,MAAM;AAAA,IACtC;AACA,UAAM,MAAM,MAAM,oBAAoB,GAAG,WAAW,EAAE,SAAS,MAAM,CAAC;AACtE,WAAO,EAAE,KAAK,EAAE,IAAI,GAAG,GAAG;AAAA,EAC5B,CAAC;AAED,MAAI,KAAK,aAAa,OAAO,MAAM;AACjC,UAAM,SAAS,MAAM,kBAAkB,GAAG,aAAa,EAAE,MAAM,OAAO,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,CAAC;AAC7F,QAAI,OAAQ,QAAO;AACnB,UAAM,OAAO,EAAE,IAAI,KAAK,aAAa;AACrC,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAM,MAAM,MAAM,KAAK,oBAAoB,EAAE,KAAY,EAAE;AAC3D,QAAI,CAAC,KAAK;AACR,aAAO,UAAU,kBAAkB,GAAG;AAAA,IACxC;AACA,UAAM,MAAM,KAAK,OAAO,GAAG;AAC3B,QAAI,IAAI,WAAW,YAAY,IAAI,WAAW,WAAW;AACvD,aAAO,UAAU,gDAAgD,GAAG;AAAA,IACtE;AACA,UAAM,qBAAqB,GAAG,GAAG;AACjC,UAAM,KAAK,UAAU,EAAE,KAAY,EAAE;AACrC,UAAM,UAAU,MAAM,KAAK,oBAAoB,EAAE,KAAY,EAAE;AAC/D,WAAO,EAAE,KAAK,EAAE,KAAK,UAAU,KAAK,OAAO,OAAO,IAAI,IAAI,GAAG,GAAG;AAAA,EAClE,CAAC;AAGD,MAAI,IAAI,oCAAoC,OAAO,MAAM;AACvD,UAAM,SAAS,MAAM,kBAAkB,GAAG,cAAc,EAAE,MAAM,OAAO,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,CAAC;AAC9F,QAAI,OAAQ,QAAO;AACnB,UAAM,QAAQ,EAAE,IAAI,MAAM,IAAI;AAC9B,UAAM,cAAc,EAAE,IAAI,MAAM,aAAa;AAE7C,UAAM,SAAS,mBAAmB,UAAU,MAAM,EAAE,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,CAAC;AAChF,QAAI,CAAC,OAAO,QAAS,QAAO,UAAU,2CAA2C,GAAG;AAEpF,UAAM,SAAS,MAAM,EAAE,IAAI,KAAK,aAAa,YAAY,sBAAsB,EAAE,KAAY,OAAO,WAAW;AAC/G,QAAI,CAAC,OAAQ,QAAO,UAAU,kCAAkC,GAAG;AAEnE,UAAM,EAAE,IAAI,KAAK,aAAa,gBAAgB,wBAAwB,EAAE,KAAY;AAAA,MAClF,cAAc,OAAO;AAAA,MACrB,UAAU,OAAO;AAAA,MACjB;AAAA,MACA,YAAY,OAAO;AAAA;AAAA,MAEnB,eAAe,OAAO;AAAA,MACtB;AAAA,MACA,YAAY,EAAE,IAAI,aAAa,GAAG,iBAAiB,OAAO,EAAE,IAAI,aAAa,GAAG,cAAc,IAAI;AAAA,MAClG,SAAS,OAAO,KAAK,UAAU,UAAU,iBAAiB;AAAA,IAC5D,CAAC;AAED,WAAO,EAAE,KAAK,EAAE,OAAO,OAAO,KAAK,MAAM,GAAG,GAAG;AAAA,EACjD,CAAC;AAGD,MAAI,OAAO,oCAAoC,OAAO,MAAM;AAC1D,UAAM,SAAS,MAAM,kBAAkB,GAAG,cAAc,EAAE,MAAM,OAAO,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,CAAC;AAC9F,QAAI,OAAQ,QAAO;AACnB,UAAM,QAAQ,EAAE,IAAI,MAAM,IAAI;AAC9B,UAAM,cAAc,EAAE,IAAI,MAAM,aAAa;AAE7C,UAAM,SAAS,MAAM,EAAE,IAAI,KAAK,aAAa,YAAY,sBAAsB,EAAE,KAAY,OAAO,WAAW;AAC/G,QAAI,CAAC,OAAQ,QAAO,UAAU,kCAAkC,GAAG;AAEnE,UAAM,EAAE,IAAI,KAAK,aAAa,gBAAgB,uBAAuB,EAAE,KAAY,OAAO,eAAe,WAAW;AACpH,WAAO,EAAE,KAAK,MAAM,GAAG;AAAA,EACzB,CAAC;AAED,MAAI,OAAO,QAAQ,OAAO,MAAM;AAC9B,UAAM,SAAS,MAAM,kBAAkB,GAAG,eAAe,EAAE,MAAM,OAAO,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,CAAC;AAC/F,QAAI,OAAQ,QAAO;AACnB,UAAM,OAAO,EAAE,IAAI,KAAK,aAAa;AACrC,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,UAAM,MAAM,MAAM,KAAK,oBAAoB,EAAE,KAAY,EAAE;AAC3D,QAAI,CAAC,KAAK;AACR,aAAO,UAAU,kBAAkB,GAAG;AAAA,IACxC;AACA,UAAM,MAAM,KAAK,OAAO,GAAG;AAC3B,QAAI,IAAI,WAAW,YAAY,IAAI,WAAW,WAAW;AACvD,YAAM,qBAAqB,GAAG,GAAG;AAAA,IACnC;AACA,UAAM,KAAK,UAAU,EAAE,KAAY,EAAE;AACrC,WAAO,EAAE,KAAK,MAAM,GAAG;AAAA,EACzB,CAAC;AAED,SAAO;AACT;;;AC3SA,SAAS,QAAAC,aAAY;AACrB,SAAS,KAAAC,UAAS;AAIlB,SAAS,wBAAwB;AAEjC,IAAM,wBAAwBC,GAC3B,aAAa;AAAA,EACZ,SAASA,GAAE,QAAQ,EAAE,SAAS;AAAA,EAC9B,QAAQ,iBAAiB,MAAM,OAAO,SAAS;AAAA,EAC/C,OAAO,iBAAiB,MAAM,MAAM,SAAS;AAC/C,CAAC,EACA;AAAA,EACC,CAAC,UAAU,MAAM,YAAY,UAAa,MAAM,WAAW,UAAa,MAAM,UAAU;AAAA,EACxF;AACF;AAEF,eAAe,mBACb,OACA,OACA,QACA;AACA,QAAM,UAAe,IAAI,MAAM,MAAM,MAAM;AAC3C,MAAI,YAAY;AAEhB,iBAAe,SAAS;AACtB,WAAO,YAAY,MAAM,QAAQ;AAC/B,YAAM,eAAe;AACrB,mBAAa;AACb,cAAQ,YAAY,IAAI,MAAM,OAAO,MAAM,YAAY,CAAC;AAAA,IAC1D;AAAA,EACF;AAEA,QAAM,QAAQ,IAAI,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,OAAO,MAAM,MAAM,EAAE,GAAG,MAAM,CAAC;AAC/E,SAAO;AACT;AAEO,SAAS,oBAAoB;AAClC,QAAM,MAAM,IAAIC,MAAa;AAE7B,MAAI,IAAI,KAAK,OAAO,MAAM;AACxB,UAAM,SAAS,MAAM,kBAAkB,GAAG,YAAY;AACtD,QAAI,OAAQ,QAAO;AACnB,UAAM,QAAQ,MAAM,EAAE,IAAI,KAAK,aAAa,YAAY,gBAAgB,EAAE,GAAU;AACpF,WAAO,EAAE,KAAK,EAAE,MAAM,CAAC;AAAA,EACzB,CAAC;AAED,MAAI,IAAI,YAAY,OAAO,MAAM;AAC/B,UAAM,SAAS,MAAM,kBAAkB,GAAG,eAAe;AACzD,QAAI,OAAQ,QAAO;AACnB,QAAI;AACF,aAAO,EAAE,SAAS,MAAM,EAAE,IAAI,KAAK,YAAY,sBAAsB,GAAG,GAAG;AAAA,IAC7E,SAAS,OAAO;AACd,QAAE,IAAI,KAAK,SAAS,OAAO,MAAM,kDAAkD,KAAK;AACxF,aAAO,UAAU,kDAAkD,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,IAAI,GAAG;AAAA,IAClI;AAAA,EACF,CAAC;AAED,MAAI,KAAK,SAAS,OAAO,MAAM;AAC7B,UAAM,SAAS,MAAM,kBAAkB,GAAG,YAAY;AACtD,QAAI,OAAQ,QAAO;AACnB,UAAM,YAAY,MAAM,aAAa,GAAG,EAAE,QAAQ,aAAa,CAAC;AAChE,QAAI,UAAW,QAAO;AACtB,QAAI;AACF,YAAM,gBAAgB,MAAM,EAAE,IAAI,KAAK,YAAY,kBAAkB;AACrE,YAAM,SAAmB,CAAC;AAC1B,YAAM,cAAc,EAAE,IAAI,KAAK,aAAa;AAE5C,iBAAW,QAAQ,eAAe;AAChC,cAAM,SAAS,EAAE,IAAI,KAAK,YAAY,cAAc,OAAO,KAAK,EAAE,CAAC;AACnE,cAAM,QAAQ,MAAM,OAAO,iBAAiB;AAE5C,cAAM,UAAU,MAAM;AAAA,UACpB;AAAA,UACA;AAAA,UACA,OAAO,SAAc;AACnB,kBAAM,QAAQ,KAAK,MAAM;AACzB,kBAAM,OAAO,KAAK;AAClB,kBAAM,WAAW,GAAG,KAAK,IAAI,IAAI;AACjC,gBAAI;AACF,oBAAM,YAAY,eAAe,EAAE,KAAY;AAAA,gBAC7C,gBAAgB,OAAO,KAAK,EAAE;AAAA,gBAC9B;AAAA,gBACA,MAAM;AAAA,cACR,CAAC;AACD,qBAAO;AAAA,YACT,SAAS,WAAW;AAClB,gBAAE,IAAI,KAAK,SAAS,OAAO,MAAM,wBAAwB,QAAQ,IAAI,SAAS;AAC9E,qBAAO;AAAA,YACT;AAAA,UACF;AAAA,QACF;AAEA,cAAM,qBAA+B,CAAC;AACtC,mBAAW,OAAO,SAAS;AACzB,cAAI,KAAK;AACP,mBAAO,KAAK,GAAG;AACf,+BAAmB,KAAK,GAAG;AAAA,UAC7B;AAAA,QACF;AAEA,cAAM,YAAY,uBAAuB,EAAE,KAAY,OAAO,KAAK,EAAE,GAAG,kBAAkB;AAAA,MAC5F;AAEA,aAAO,EAAE,KAAK,EAAE,IAAI,MAAM,OAAO,CAAC;AAAA,IACpC,SAAS,OAAO;AACd,QAAE,IAAI,KAAK,SAAS,OAAO,MAAM,uBAAuB,KAAK;AAC7D,aAAO,UAAU,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,IAAI,GAAG;AAAA,IAChG;AAAA,EACF,CAAC;AAED,MAAI,IAAI,wBAAwB,OAAO,MAAM;AAC3C,UAAM,SAAS,MAAM,kBAAkB,GAAG,cAAc,EAAE,MAAM,QAAQ,IAAI,GAAG,EAAE,IAAI,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,MAAM,MAAM,CAAC,GAAG,CAAC;AAC9H,QAAI,OAAQ,QAAO;AACnB,UAAM,OAAO,MAAM,EAAE,IAAI,KAAK,aAAa,YAAY,oBAAoB,EAAE,KAAY,EAAE,IAAI,MAAM,OAAO,GAAG,EAAE,IAAI,MAAM,MAAM,CAAC;AAClI,QAAI,CAAC,MAAM;AACT,aAAO,UAAU,gCAAgC,GAAG;AAAA,IACtD;AAEA,WAAO,EAAE,KAAK,EAAE,KAAK,CAAC;AAAA,EACxB,CAAC;AAED,MAAI,MAAM,wBAAwB,OAAO,MAAM;AAC7C,UAAM,SAAS,MAAM,kBAAkB,GAAG,sBAAsB,EAAE,MAAM,QAAQ,IAAI,GAAG,EAAE,IAAI,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,MAAM,MAAM,CAAC,GAAG,CAAC;AACtI,QAAI,OAAQ,QAAO;AACnB,UAAM,EAAE,OAAO,KAAK,IAAI,EAAE,IAAI,MAAM;AACpC,UAAM,OAAO,MAAM,EAAE,IAAI,KAAK;AAC9B,UAAM,cAAc,sBAAsB,UAAU,IAAI;AACxD,QAAI,CAAC,YAAY,SAAS;AACxB,aAAO,UAAU,oCAAoC,GAAG;AAAA,IAC1D;AAEA,UAAM,cAAc,EAAE,IAAI,KAAK,aAAa;AAC5C,UAAM,WAAW,MAAM,YAAY,oBAAoB,EAAE,KAAY,OAAO,IAAI;AAEhF,QAAI,CAAC,UAAU;AACb,aAAO,UAAU,gCAAgC,GAAG;AAAA,IACtD;AAEA,UAAM,QAAQ,YAAY;AAC1B,UAAM,iBAAiB,MAAM,WAAW,UAAa,MAAM,UAAU;AAErE,QAAI,CAAC,kBAAkB,MAAM,YAAY,QAAW;AAClD,YAAM,YAAY,wBAAwB,EAAE,KAAY;AAAA,QACtD;AAAA,QACA;AAAA,QACA,SAAS,MAAM;AAAA,MACjB,CAAC;AACD,YAAM,EAAE,IAAI,KAAK,OAAO,0BAA0B,OAAO,IAAI;AAC7D,aAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,IAC5B;AAEA,UAAM,cAAyD,CAAC;AAChE,QAAI,MAAM,WAAW,QAAW;AAC9B,kBAAY,SAAS,MAAM;AAAA,IAC7B;AACA,QAAI,MAAM,UAAU,QAAW;AAC7B,kBAAY,QAAQ,MAAM;AAAA,IAC5B;AAEA,UAAM,oBAAoB;AAAA,MACxB,GAAG,SAAS;AAAA,MACZ,GAAG;AAAA,IACL;AACA,UAAM,eAAe,iBAAiB,UAAU,iBAAiB;AAEjE,QAAI,CAAC,aAAa,SAAS;AACzB,aAAO,UAAU,8BAA8B,GAAG;AAAA,IACpD;AAEA,UAAM,YAAY,iBAAiB,EAAE,KAAY;AAAA,MAC/C,gBAAgB,SAAS;AAAA,MACzB;AAAA,MACA;AAAA,MACA,YAAY,aAAa;AAAA,MACzB,SAAS,MAAM;AAAA,IACjB,CAAC;AACD,UAAM,EAAE,IAAI,KAAK,OAAO,0BAA0B,OAAO,IAAI;AAE7D,WAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,EAC5B,CAAC;AAED,SAAO;AACT;;;ACxLA,SAAS,QAAAC,aAAY;AAId,SAAS,oBAAoB;AAClC,QAAM,MAAM,IAAIC,MAAa;AAE7B,MAAI,IAAI,KAAK,OAAO,MAAM;AACxB,UAAM,SAAS,MAAM,kBAAkB,GAAG,YAAY;AACtD,QAAI,OAAQ,QAAO;AACnB,UAAM,YAAY,EAAE,IAAI,MAAM,MAAM;AACpC,UAAM,OAAO,YAAY,SAAS,WAAW,EAAE,IAAI;AAEnD,UAAM,WAAW,EAAE,IAAI,MAAM,IAAI,KAAK;AACtC,UAAM,QAAQ,MAAM,EAAE,IAAI,KAAK,aAAa,MAAM,SAAS,EAAE,KAAY,MAAM,QAAQ;AACvF,WAAO,EAAE,KAAK,EAAE,MAAM,CAAC;AAAA,EACzB,CAAC;AAED,SAAO;AACT;;;ACnBA,SAAS,QAAAC,aAAY;AACrB,SAAS,KAAAC,UAAS;AAElB,SAAS,qBAAqB;AAI9B,IAAM,kBAAkBC,GAAE,KAAK,aAAa;AAC5C,IAAM,wBAAwBA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO;AACjE,IAAM,gBAAgBA,GAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC;AAC7C,IAAM,oBAAoBA,GAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AACtE,IAAM,mBAAmBA,GAAE,OAAO,EAAE,KAAK;AAEzC,IAAM,uBAAuBA,GAAE,aAAa;AAAA,EAC1C,MAAMA,GAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC;AAAA,EAC7B,WAAW;AAAA,EACX,SAAS;AAAA,EACT,QAAQA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,SAASA,GAAE,QAAQ,EAAE,QAAQ,IAAI;AACnC,CAAC;AAGD,IAAM,uBAAuB,qBAAqB,OAAO;AAAA,EACvD,aAAaA,GAAE,QAAQ,EAAE,SAAS;AACpC,CAAC;AAED,IAAM,0BAA0BA,GAAE,aAAa;AAAA,EAC7C,YAAY;AAAA,EACZ,WAAWA,GAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC;AACpC,CAAC;AAED,IAAM,0BAA0BA,GAAE,aAAa;AAAA,EAC7C,MAAM,cAAc,SAAS,EAAE,QAAQ,IAAI;AAAA,EAC3C,WAAWA,GAAE,MAAM,aAAa,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AAAA,EACvD,gBAAgBA,GACb;AAAA,IACCA,GAAE,aAAa;AAAA,MACb,WAAW;AAAA,MACX,OAAO;AAAA,MACP,WAAWA,GAAE,MAAM,aAAa,EAAE,SAAS;AAAA,IAC7C,CAAC;AAAA,EACH,EACC,SAAS,EACT,SAAS;AACd,CAAC;AAED,SAAS,wBAAwB,WAA4C,SAAoC;AAC/G,SAAO,cAAc,YAAY,CAAC;AACpC;AAEA,SAAS,iBAAiB,OAAe;AACvC,MAAI;AACF,WAAO,mBAAmB,KAAK;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,qBAAqB;AACnC,QAAM,MAAM,IAAIC,MAAa;AAE7B,MAAI,IAAI,KAAK,OAAO,MAAM;AACxB,UAAM,SAAS,MAAM,kBAAkB,GAAG,aAAa;AACvD,QAAI,OAAQ,QAAO;AACnB,UAAM,mBAAmB,EAAE,IAAI,KAAK,aAAa;AACjD,UAAM,CAAC,WAAW,OAAO,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC7C,iBAAiB,iBAAiB,EAAE,GAAU;AAAA,MAC9C,iBAAiB,iBAAiB,EAAE,GAAU;AAAA,IAChD,CAAC;AACD,WAAO,EAAE,KAAK,EAAE,WAAW,QAAQ,CAAC;AAAA,EACtC,CAAC;AAED,MAAI,KAAK,SAAS,OAAO,MAAM;AAC7B,UAAM,SAAS,MAAM,kBAAkB,GAAG,aAAa;AACvD,QAAI,OAAQ,QAAO;AACnB,UAAM,mBAAmB,EAAE,IAAI,KAAK,aAAa;AACjD,UAAM,aAAa,MAAM,EAAE,IAAI,KAAK,YAAY,yBAAyB;AACzE,UAAM,CAAC,WAAW,OAAO,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC7C,iBAAiB,iBAAiB,EAAE,GAAU;AAAA,MAC9C,iBAAiB,iBAAiB,EAAE,GAAU;AAAA,IAChD,CAAC;AACD,WAAO,EAAE,KAAK,EAAE,WAAW,SAAS,WAAW,CAAC;AAAA,EAClD,CAAC;AAED,MAAI,IAAI,WAAW,OAAO,MAAM;AAC9B,UAAM,SAAS,MAAM,kBAAkB,GAAG,aAAa;AACvD,QAAI,OAAQ,QAAO;AACnB,UAAM,SAAS,MAAM,EAAE,IAAI,KAAK,OAAO,gBAAgB;AACvD,WAAO,EAAE,KAAK,EAAE,OAAO,CAAC;AAAA,EAC1B,CAAC;AAED,MAAI,MAAM,WAAW,OAAO,MAAM;AAChC,UAAM,SAAS,MAAM,kBAAkB,GAAG,qBAAqB;AAC/D,QAAI,OAAQ,QAAO;AACnB,UAAM,OAAO,MAAM,EAAE,IAAI,KAAK;AAC9B,UAAM,SAAS,wBAAwB,UAAU,IAAI;AACrD,QAAI,CAAC,OAAO,SAAS;AACnB,aAAO,UAAU,gCAAgC,GAAG;AAAA,IACtD;AAEA,UAAM,EAAE,IAAI,KAAK,OAAO,mBAAmB,OAAO,IAAI;AACtD,WAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,EAC5B,CAAC;AAED,MAAI,KAAK,cAAc,OAAO,MAAM;AAClC,UAAM,SAAS,MAAM,kBAAkB,GAAG,wBAAwB;AAClE,QAAI,OAAQ,QAAO;AACnB,UAAM,SAAS,qBAAqB,UAAU,MAAM,EAAE,IAAI,KAAK,CAAC;AAChE,QAAI,CAAC,OAAO,SAAS;AACnB,aAAO,UAAU,4BAA4B,GAAG;AAAA,IAClD;AAEA,UAAM,QAAQ,OAAO;AACrB,QAAI,wBAAwB,MAAM,WAAW,MAAM,OAAO,GAAG;AAC3D,aAAO,UAAU,2KAA2K,GAAG;AAAA,IACjM;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,EAAE,IAAI,KAAK,YAAY,yBAAyB,KAAK;AAC5E,aAAO,EAAE,KAAK,EAAE,SAAS,GAAG,GAAG;AAAA,IACjC,SAAS,OAAO;AACd,YAAM,MAAM;AACZ,UAAI,IAAI,mBAAmB;AACzB,eAAO,UAAU,YAAY,MAAM,IAAI,0DAA0D,GAAG;AAAA,MACtG;AACA,UAAI,IAAI,yBAAyB;AAC/B,eAAO,UAAU,IAAI,WAAW,qCAAqC,GAAG;AAAA,MAC1E;AACA,UAAI,IAAI,oBAAoB;AAC1B,eAAO,UAAU,YAAY,MAAM,IAAI,+CAA+C,GAAG;AAAA,MAC3F;AACA,YAAM;AAAA,IACR;AAAA,EACF,CAAC;AAED,MAAI,MAAM,kBAAkB,OAAO,MAAM;AACvC,UAAM,SAAS,MAAM,kBAAkB,GAAG,0BAA0B,EAAE,MAAM,eAAe,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,CAAC;AAClH,QAAI,OAAQ,QAAO;AACnB,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,QAAI,CAAC,iBAAiB,UAAU,EAAE,EAAE,SAAS;AAC3C,aAAO,UAAU,wBAAwB,GAAG;AAAA,IAC9C;AAEA,UAAM,SAAS,qBAAqB,UAAU,MAAM,EAAE,IAAI,KAAK,CAAC;AAChE,QAAI,CAAC,OAAO,SAAS;AACnB,aAAO,UAAU,4BAA4B,GAAG;AAAA,IAClD;AAEA,UAAM,QAAQ,OAAO;AACrB,QAAI,wBAAwB,MAAM,WAAW,MAAM,OAAO,GAAG;AAC3D,aAAO,UAAU,2KAA2K,GAAG;AAAA,IACjM;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,EAAE,IAAI,KAAK,YAAY,yBAAyB,IAAI,KAAK;AAChF,UAAI,CAAC,SAAU,QAAO,UAAU,uBAAuB,GAAG;AAC1D,aAAO,EAAE,KAAK,EAAE,SAAS,CAAC;AAAA,IAC5B,SAAS,OAAO;AACd,YAAM,MAAM;AACZ,UAAI,IAAI,mBAAmB;AACzB,eAAO,UAAU,YAAY,MAAM,IAAI,sDAAsD,GAAG;AAAA,MAClG;AACA,UAAI,IAAI,yBAAyB;AAC/B,eAAO,UAAU,IAAI,WAAW,qCAAqC,GAAG;AAAA,MAC1E;AACA,UAAI,IAAI,oBAAoB;AAC1B,eAAO,UAAU,YAAY,MAAM,IAAI,+CAA+C,GAAG;AAAA,MAC3F;AACA,YAAM;AAAA,IACR;AAAA,EACF,CAAC;AAED,MAAI,OAAO,kBAAkB,OAAO,MAAM;AACxC,UAAM,SAAS,MAAM,kBAAkB,GAAG,0BAA0B,EAAE,MAAM,eAAe,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,CAAC;AAClH,QAAI,OAAQ,QAAO;AACnB,UAAM,KAAK,EAAE,IAAI,MAAM,IAAI;AAC3B,QAAI,CAAC,iBAAiB,UAAU,EAAE,EAAE,SAAS;AAC3C,aAAO,UAAU,wBAAwB,GAAG;AAAA,IAC9C;AAEA,UAAM,SAAS,MAAM,EAAE,IAAI,KAAK,aAAa,aAAa,kBAAkB,EAAE,KAAY,EAAE;AAC5F,QAAI,CAAC,OAAO,SAAS;AACnB,aAAO,UAAU,OAAO,UAAU,uBAAuB,OAAO,SAAS,MAAM,GAAG;AAAA,IACpF;AACA,WAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,EAC5B,CAAC;AAED,MAAI,KAAK,aAAa,OAAO,MAAM;AACjC,UAAM,SAAS,MAAM,kBAAkB,GAAG,eAAe,EAAE,MAAM,eAAe,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,CAAC;AACvG,QAAI,OAAQ,QAAO;AACnB,UAAM,YAAY,MAAM,aAAa,GAAG,EAAE,QAAQ,cAAc,CAAC;AACjE,QAAI,UAAW,QAAO;AACtB,UAAM,UAAU,iBAAiB,EAAE,IAAI,MAAM,IAAI,CAAC;AAClD,UAAM,gBAAgB,cAAc,UAAU,OAAO;AACrD,QAAI,CAAC,cAAc,SAAS;AAC1B,aAAO,UAAU,qBAAqB,GAAG;AAAA,IAC3C;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,EAAE,IAAI,KAAK,YAAY,eAAe,cAAc,IAAI;AAC/E,aAAO,EAAE,KAAK,QAAQ;AAAA,IACxB,SAAS,OAAO;AACd,YAAM,MAAM;AACZ,UAAI,IAAI,gBAAiB,QAAO,UAAU,oBAAoB,GAAG;AACjE,UAAI,IAAI,mBAAmB,IAAI,kBAAmB,QAAO,UAAU,IAAI,SAAS,GAAG;AAEnF,aAAO;AAAA,QACL,IAAI,WAAW;AAAA,QACf,IAAI,UAAU;AAAA,MAChB;AAAA,IACF;AAAA,EACF,CAAC;AAED,MAAI,KAAK,QAAQ,OAAO,MAAM;AAC5B,UAAM,SAAS,MAAM,kBAAkB,GAAG,wBAAwB,EAAE,MAAM,eAAe,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,CAAC;AAChH,QAAI,OAAQ,QAAO;AACnB,UAAM,UAAU,iBAAiB,EAAE,IAAI,MAAM,IAAI,CAAC;AAClD,UAAM,gBAAgB,cAAc,UAAU,OAAO;AACrD,QAAI,CAAC,cAAc,SAAS;AAC1B,aAAO,UAAU,qBAAqB,GAAG;AAAA,IAC3C;AAEA,UAAM,OAAO,MAAM,EAAE,IAAI,KAAK;AAC9B,UAAM,SAAS,wBAAwB,UAAU,IAAI;AACrD,QAAI,CAAC,OAAO,SAAS;AACnB,aAAO,UAAU,yBAAyB,GAAG;AAAA,IAC/C;AAEA,UAAM,QAAQ,MAAM,EAAE,IAAI,KAAK,aAAa,aAAa,kBAAkB,EAAE,KAAY;AAAA,MACvF,SAAS,cAAc;AAAA,MACvB,YAAY,OAAO,KAAK;AAAA,MACxB,WAAW,OAAO,KAAK;AAAA,IACzB,CAAC;AAED,QAAI,CAAC,MAAO,QAAO,UAAU,uBAAuB,GAAG;AACvD,WAAO,EAAE,KAAK,EAAE,IAAI,MAAM,QAAQ,MAAM,CAAC;AAAA,EAC3C,CAAC;AAED,MAAI,OAAO,QAAQ,OAAO,MAAM;AAC9B,UAAM,SAAS,MAAM,kBAAkB,GAAG,wBAAwB,EAAE,MAAM,eAAe,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE,CAAC;AAChH,QAAI,OAAQ,QAAO;AACnB,UAAM,UAAU,iBAAiB,EAAE,IAAI,MAAM,IAAI,CAAC;AAClD,UAAM,gBAAgB,cAAc,UAAU,OAAO;AACrD,QAAI,CAAC,cAAc,SAAS;AAC1B,aAAO,UAAU,qBAAqB,GAAG;AAAA,IAC3C;AAEA,UAAM,UAAU,MAAM,EAAE,IAAI,KAAK,aAAa,aAAa,kBAAkB,EAAE,KAAY,cAAc,IAAI;AAC7G,QAAI,CAAC,QAAS,QAAO,UAAU,oBAAoB,GAAG;AACtD,WAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,EAC5B,CAAC;AAED,SAAO;AACT;;;AC7PA,SAAS,QAAAC,aAAY;AACrB,SAAS,KAAAC,UAAS;AAIlB,SAAS,yBAAyB,0BAA0B,qBAAqB,4BAA4B;AAE7G,IAAM,4BAA4BC,GAAE,aAAa;AAAA,EAC/C,kBAAkBA,GAAE,KAAK,uBAAuB,EAAE,SAAS;AAAA,EAC3D,aAAaA,GAAE,OAAO,EAAE,IAAI,EAAE;AAAA,IAC5B,CAAC,UAAW,yBAA+C,SAAS,KAAK;AAAA,IACzE;AAAA,EACF,EAAE,SAAS;AAAA,EACX,UAAUA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,oBAAoB,GAAG,EAAE,IAAI,oBAAoB,GAAG,EAAE,SAAS;AAChG,CAAC,EAAE;AAAA,EACD,CAAC,aAAa,OAAO,OAAO,QAAQ,EAAE,KAAK,CAAC,UAAU,UAAU,MAAS;AAAA,EACzE;AACF;AAEO,SAAS,uBAAuB;AACrC,QAAM,MAAM,IAAIC,MAAa;AAE7B,MAAI,IAAI,KAAK,OAAO,MAAM;AACxB,UAAM,SAAS,MAAM,kBAAkB,GAAG,eAAe;AACzD,QAAI,OAAQ,QAAO;AACnB,UAAM,WAAW,MAAM,EAAE,IAAI,KAAK,aAAa,YAAY,kBAAkB,EAAE,GAAU;AACzF,WAAO,EAAE,KAAK,EAAE,SAAS,CAAC;AAAA,EAC5B,CAAC;AAED,MAAI,MAAM,KAAK,OAAO,MAAM;AAC1B,UAAM,SAAS,MAAM,kBAAkB,GAAG,gBAAgB;AAC1D,QAAI,OAAQ,QAAO;AACnB,UAAM,OAAO,MAAM,EAAE,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AAChD,UAAM,SAAS,0BAA0B,UAAU,IAAI;AACvD,QAAI,CAAC,OAAO,SAAS;AACnB,aAAO,UAAU,4BAA4B,GAAG;AAAA,IAClD;AAEA,UAAM,UAAU,MAAM,EAAE,IAAI,KAAK,aAAa,YAAY,kBAAkB,EAAE,GAAU;AACxF,UAAM,OAAO,qBAAqB,MAAM,EAAE,GAAG,SAAS,GAAG,OAAO,KAAK,CAAC;AACtE,UAAM,EAAE,IAAI,KAAK,aAAa,YAAY,qBAAqB,EAAE,KAAY,IAAI;AACjF,WAAO,EAAE,KAAK,EAAE,IAAI,MAAM,UAAU,KAAK,CAAC;AAAA,EAC5C,CAAC;AAED,SAAO;AACT;;;Af9BA,eAAe,WAAW,GAAoB;AAE5C,QAAM,SAAU,EAAE,IAAY;AAC9B,MAAI,UAAU,OAAO,OAAO,UAAU,YAAY;AAEhD,WAAO,OAAO,MAAM,IAAI,QAAQ,IAAI,IAAI,KAAK,EAAE,IAAI,GAAG,GAAG,EAAE,IAAI,GAAG,CAAC;AAAA,EACrE;AAEA,SAAO,EAAE,KAAK,0DAA0D,GAAG;AAC7E;AAaO,SAAS,gBAAgB,UAA4B,CAAC,GAAG;AAC9D,QAAM,MAAM,IAAIC,MAAa;AAE7B,MAAI,IAAI,KAAK,aAAa;AAC1B,aAAW,cAAc,QAAQ,cAAc,CAAC,GAAG;AACjD,QAAI,IAAI,KAAK,UAAU;AAAA,EACzB;AACA,MAAI,IAAI,gBAAgB,cAAc;AACtC,MAAI,IAAI,gBAAgB,iBAAiB;AAEzC,MAAI,MAAM,SAAS,iBAAiB,CAAC;AACrC,MAAI,MAAM,YAAY,oBAAoB,CAAC;AAE3C,MAAI,IAAI,UAAU,cAAc;AAChC,MAAI,IAAI,UAAU,iBAAiB;AACnC,aAAW,cAAc,QAAQ,aAAa,CAAC,GAAG;AAChD,QAAI,IAAI,UAAU,UAAU;AAAA,EAC9B;AAEA,MAAI,MAAM,aAAa,oBAAoB,CAAC;AAC5C,MAAI,MAAM,aAAa,iBAAiB,CAAC;AACzC,MAAI,MAAM,cAAc,kBAAkB,CAAC;AAC3C,MAAI,MAAM,cAAc,kBAAkB,CAAC;AAC3C,MAAI,MAAM,eAAe,mBAAmB,CAAC;AAC7C,MAAI,MAAM,iBAAiB,qBAAqB,CAAC;AAEjD,MAAI,IAAI,UAAU,UAAU;AAC5B,MAAI,IAAI,KAAK,UAAU;AACvB,MAAI,IAAI,cAAc,gBAAgB,UAAU;AAChD,MAAI,IAAI,SAAS,gBAAgB,UAAU;AAC3C,MAAI,IAAI,WAAW,gBAAgB,UAAU;AAC7C,MAAI,IAAI,UAAU,gBAAgB,UAAU;AAC5C,MAAI,IAAI,UAAU,gBAAgB,UAAU;AAC5C,MAAI,IAAI,WAAW,gBAAgB,UAAU;AAC7C,MAAI,IAAI,aAAa,gBAAgB,UAAU;AAC/C,MAAI,IAAI,YAAY,gBAAgB,UAAU;AAE9C,aAAW,QAAQ,QAAQ,eAAe,CAAC,GAAG;AAC5C,QAAI,IAAI,MAAM,UAAU;AAAA,EAC1B;AACA,aAAW,QAAQ,QAAQ,SAAS,CAAC,GAAG;AACtC,QAAI,IAAI,MAAM,gBAAgB,UAAU;AAAA,EAC1C;AAEA,UAAQ,SAAS,GAAG;AAEpB,SAAO;AACT;","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.6",
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.6"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/node": "^22.0.0",