@tequila99/githuman 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/dist/cli/config.js +4 -2
- package/dist/cli/server-runtime.js +1 -0
- package/dist/server/app.js +10 -0
- package/dist/server/config/openapi.js +29 -0
- package/dist/server/repositories/review.repo.js +31 -5
- package/dist/server/routes/agent.js +145 -18
- package/dist/server/routes/app-info.js +8 -1
- package/dist/server/routes/comments.js +73 -27
- package/dist/server/routes/diff.js +58 -31
- package/dist/server/routes/events.js +14 -1
- package/dist/server/routes/export.js +22 -5
- package/dist/server/routes/git.js +52 -21
- package/dist/server/routes/health.js +8 -1
- package/dist/server/routes/openapi.js +4 -0
- package/dist/server/routes/reviews.js +59 -33
- package/dist/server/services/agent/agent-session.js +5 -3
- package/dist/shared/agents/schemas.js +120 -31
- package/dist/shared/app/schemas.js +7 -0
- package/dist/shared/comments/schemas.js +48 -0
- package/dist/shared/diff/schemas.js +77 -0
- package/dist/shared/diff/types.js +0 -3
- package/dist/shared/events/schemas.js +4 -0
- package/dist/shared/git/schemas.js +59 -0
- package/dist/shared/http/schemas.js +23 -0
- package/dist/shared/reviews/schemas.js +96 -0
- package/dist/shared/reviews/summary.js +5 -0
- package/dist/shared/utils/schemas.js +9 -0
- package/dist/web/assets/{AppPage-BNPBe_fi.js → AppPage-BSvF7ik1.js} +1 -1
- package/dist/web/assets/CreateReviewFabButton-M0od1QEZ.js +1 -0
- package/dist/web/assets/{IndexPage-xqMEFqAN.js → IndexPage-Do-EZOBf.js} +1 -1
- package/dist/web/assets/{MainLayout-CMbAu8_d.js → MainLayout-daEN-VtX.js} +1 -1
- package/dist/web/assets/{QMenu-QYPSkHBE.js → QMenu-DXgFwoTJ.js} +1 -1
- package/dist/web/assets/{QSelect-D4U0Of4k.js → QSelect-DzYPZHrW.js} +1 -1
- package/dist/web/assets/ReviewDetailPage-DEtNg2UN.js +1 -0
- package/dist/web/assets/{ReviewsPage-CmHdSBsU.js → ReviewsPage-Dg-iCxsQ.js} +1 -1
- package/dist/web/assets/{StatusSwitcher-CenEW-Lu.js → StatusSwitcher-BpebV47C.js} +3 -3
- package/dist/web/assets/{abnfDiagram-O67JEVCF-C4PFTrGZ.js → abnfDiagram-O67JEVCF-BUX560Qb.js} +1 -1
- package/dist/web/assets/{agent-icon-C1oimBWa.js → agent-icon-BJbyp2DP.js} +3 -3
- package/dist/web/assets/{agent-store-D1WRRYIE.js → agent-store-B9E-aiz3.js} +1 -1
- package/dist/web/assets/{arc-CiNErV2i.js → arc-dElW-85p.js} +1 -1
- package/dist/web/assets/architecture-7GRP2DOG-bgxww3gY.js +1 -0
- package/dist/web/assets/{architectureDiagram-NJMV4G6O-DnAqHYSW.js → architectureDiagram-NJMV4G6O-CUUKpxro.js} +1 -1
- package/dist/web/assets/{blockDiagram-BEXU5L5S-CCN9Ja5L.js → blockDiagram-BEXU5L5S-D4r5wIie.js} +1 -1
- package/dist/web/assets/{c4Diagram-YGBWAQC7-CPzIw79A.js → c4Diagram-YGBWAQC7-ww04LJhe.js} +1 -1
- package/dist/web/assets/channel-CdThk9yc.js +1 -0
- package/dist/web/assets/{chunk-3FUC2YCW-BbeiQKv_.js → chunk-3FUC2YCW-9i3K5FXP.js} +1 -1
- package/dist/web/assets/{chunk-5DYCD2WN-CuzXaWoe.js → chunk-5DYCD2WN-BmtGl0Xn.js} +1 -1
- package/dist/web/assets/{chunk-742MDFTN-DJUCmQfr.js → chunk-742MDFTN-5p0wX5Yc.js} +1 -1
- package/dist/web/assets/{chunk-7INBJB4K-BBd4ghzW.js → chunk-7INBJB4K-CWFVY0jp.js} +1 -1
- package/dist/web/assets/{chunk-7M6MHVWA-B0L68u_O.js → chunk-7M6MHVWA-DUgZIYj4.js} +1 -1
- package/dist/web/assets/{chunk-7PRAP22T-BvEmVPr6.js → chunk-7PRAP22T-BrhA6Tm7.js} +1 -1
- package/dist/web/assets/{chunk-GTNCS2PH-B_vHEKRO.js → chunk-GTNCS2PH-B83nsnuR.js} +1 -1
- package/dist/web/assets/{chunk-GWA4HPMP-DWaW5-VL.js → chunk-GWA4HPMP-cewRmL7f.js} +1 -1
- package/dist/web/assets/{chunk-MBY4JIJT-Dfo1FM_M.js → chunk-MBY4JIJT-D18SeqyB.js} +1 -1
- package/dist/web/assets/{chunk-NETBCI7D-DzjBtXVV.js → chunk-NETBCI7D-5SjiRygT.js} +1 -1
- package/dist/web/assets/{chunk-O7XYJQB3-DY5ZFCJH.js → chunk-O7XYJQB3-H6mLYErS.js} +1 -1
- package/dist/web/assets/{chunk-UA2S7LBM-DX_QjVLS.js → chunk-UA2S7LBM-C5VQVDlC.js} +1 -1
- package/dist/web/assets/{chunk-WEXAMYUT-Cckc2fqg.js → chunk-WEXAMYUT-CrOcvEcH.js} +1 -1
- package/dist/web/assets/{chunk-XXDRQBXY-DjF0kpJr.js → chunk-XXDRQBXY-BuVPxQhc.js} +1 -1
- package/dist/web/assets/{chunk-Z7XXMR3K-Do7oG6KK.js → chunk-Z7XXMR3K-BVP3f1-g.js} +1 -1
- package/dist/web/assets/{chunk-ZIGJFQKS-DTcqsqHq.js → chunk-ZIGJFQKS-CF-OzGXH.js} +1 -1
- package/dist/web/assets/{classDiagram-v2-NBCMYWYE-Z6ZhPv4N.js → classDiagram-v2-NBCMYWYE-Bd3VDNaV.js} +1 -1
- package/dist/web/assets/{core-tP7fpdit.js → core-DiiZIGCg.js} +1 -1
- package/dist/web/assets/{cose-bilkent-JH36ORCC-B5DV641c.js → cose-bilkent-JH36ORCC-ifMLrEkd.js} +1 -1
- package/dist/web/assets/{cynefin-OW5HDTMX-BYrEwheK.js → cynefin-OW5HDTMX-6l9c0yRf.js} +1 -1
- package/dist/web/assets/{cynefinDiagram-VND7K2PF-BXbLnUDP.js → cynefinDiagram-VND7K2PF-u9Gy0Odz.js} +1 -1
- package/dist/web/assets/{dagre-6A5THRUB-DJHHAqZU.js → dagre-6A5THRUB-D1jsEBl4.js} +1 -1
- package/dist/web/assets/{diagram-22UHCM2B-DA9deaTX.js → diagram-22UHCM2B-DpyH5n1y.js} +1 -1
- package/dist/web/assets/{diagram-3UASUU5V-C6bo1gN6.js → diagram-3UASUU5V-3X1RJV9-.js} +1 -1
- package/dist/web/assets/{diagram-ATOU4E4O-TTQR35E_.js → diagram-ATOU4E4O-B_bvvAua.js} +1 -1
- package/dist/web/assets/{diagram-CDSNMT55-ChFXwow0.js → diagram-CDSNMT55-DheqAuOA.js} +1 -1
- package/dist/web/assets/{diagram-MLGK6HIB-D3F8BzhV.js → diagram-MLGK6HIB-CYwe9zYy.js} +1 -1
- package/dist/web/assets/{diagram-MPIPVDR6-NJ6F45a1.js → diagram-MPIPVDR6-CVd9i23e.js} +1 -1
- package/dist/web/assets/{dist-Besl4xjp.js → dist-BzuUT3GV.js} +1 -1
- package/dist/web/assets/{ebnfDiagram-ZINNZB2B-u2e1UzYe.js → ebnfDiagram-ZINNZB2B-B5vtXULf.js} +1 -1
- package/dist/web/assets/{elk-276RUBZZ-tCLm9cCU.js → elk-276RUBZZ-BmMM0tV5.js} +1 -1
- package/dist/web/assets/{engine-oniguruma-DaaPGLR3.js → engine-oniguruma-D6DCTEub.js} +1 -1
- package/dist/web/assets/{erDiagram-OPXOYQCR-ChNVjKDe.js → erDiagram-OPXOYQCR-1ORPVLQR.js} +1 -1
- package/dist/web/assets/eventmodeling-NTZA5JFV-BH97sTDs.js +1 -0
- package/dist/web/assets/flowDiagram-KWPJA3E3-CRwhE8ka.js +1 -0
- package/dist/web/assets/{ganttDiagram-FUAMR5RP-8mcAm2ao.js → ganttDiagram-FUAMR5RP-CAWNdy1S.js} +1 -1
- package/dist/web/assets/{gitGraph-4MIJSDKK-DtM1RZrd.js → gitGraph-4MIJSDKK-CXCCnZ1j.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-X574FWY7-C7KXkLLU.js → gitGraphDiagram-X574FWY7-DPDrqSt6.js} +1 -1
- package/dist/web/assets/{i18n-BmgFD4Gx.js → i18n-D5wmXwHr.js} +1 -1
- package/dist/web/assets/{index-CuqF4_ks.js → index-DWi4DKrl.js} +2 -2
- package/dist/web/assets/{info-A6RAGUB7-C5tVl8cQ.js → info-A6RAGUB7-CGg3Xa4X.js} +1 -1
- package/dist/web/assets/{infoDiagram-VRGFBTTK-DO4D3mI7.js → infoDiagram-VRGFBTTK-vx9EUrdV.js} +1 -1
- package/dist/web/assets/{init-app-C0kkg1Ie.js → init-app-CgDkvlRb.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-DA2H3Eih.js → ishikawaDiagram-OU5B5YK6-DL7T0J63.js} +1 -1
- package/dist/web/assets/{journeyDiagram-ZHPQQLJL-CdPvR3Sr.js → journeyDiagram-ZHPQQLJL-D45T1Pj5.js} +1 -1
- package/dist/web/assets/{kanban-definition-PNTS6WVX-BRIiazVf.js → kanban-definition-PNTS6WVX-C9ZnTE1l.js} +1 -1
- package/dist/web/assets/{line-CmXhYhmh.js → line-CLBEcmiH.js} +1 -1
- package/dist/web/assets/{linear-Cyx-ptw-.js → linear-B5bBEu0i.js} +1 -1
- package/dist/web/assets/{mermaid-parser.core-CZ05RARR.js → mermaid-parser.core-Dj-n65Ou.js} +3 -3
- package/dist/web/assets/{mermaid.core-ZosqiKX-.js → mermaid.core-CmPsESsw.js} +4 -4
- package/dist/web/assets/{mindmap-definition-NLK3R4M7-a7nG9rPn.js → mindmap-definition-NLK3R4M7-ZX638ce_.js} +1 -1
- package/dist/web/assets/{packet-AYTQ26CC-yIJsAali.js → packet-AYTQ26CC-D-XZHUq_.js} +1 -1
- package/dist/web/assets/{pegDiagram-GJSIUBJH-CBdB4i41.js → pegDiagram-GJSIUBJH-sE-EOKNL.js} +1 -1
- package/dist/web/assets/{pie-WAS4IAKB-ybMEHYPX.js → pie-WAS4IAKB-DMkVggJA.js} +1 -1
- package/dist/web/assets/{pieDiagram-5QR66LMP-BfiG4Lho.js → pieDiagram-5QR66LMP-DSdIEQyS.js} +1 -1
- package/dist/web/assets/pinia-BMN9Drn3.js +1 -0
- package/dist/web/assets/{quadrantDiagram-O4NWA36T-7A6x_hoF.js → quadrantDiagram-O4NWA36T-BReXSVss.js} +1 -1
- package/dist/web/assets/{radar-RG4KPBEZ-BbeFIczu.js → radar-RG4KPBEZ-CJzPwlj6.js} +1 -1
- package/dist/web/assets/{railroad-74A4TZTK-CAyDA7Zz.js → railroad-74A4TZTK-CkhRuR3N.js} +1 -1
- package/dist/web/assets/railroad-abnf-HS5TGJTU-CcS8fVgm.js +1 -0
- package/dist/web/assets/railroad-ebnf-LZEXJU2U-BlpYA103.js +1 -0
- package/dist/web/assets/railroad-peg-WCYAUIDC-C2EznudM.js +1 -0
- package/dist/web/assets/{railroadDiagram-XR7U4H2S-BXiOJ_AA.js → railroadDiagram-XR7U4H2S-DCoF4AEN.js} +1 -1
- package/dist/web/assets/{requirementDiagram-PLB6GJNP-bQNGcpuV.js → requirementDiagram-PLB6GJNP-B15aXZXI.js} +1 -1
- package/dist/web/assets/{safe-storage-CqQMHcn7.js → safe-storage-ak36tVRO.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-IPEJSGJF-CgfGV4TY.js → sankeyDiagram-IPEJSGJF-DcWsnYYP.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-PO4LG4MO-BdCuKkDZ.js → sequenceDiagram-PO4LG4MO-CXWUNmSl.js} +1 -1
- package/dist/web/assets/{src-AvcVSqt3.js → src-CB-shhJB.js} +1 -1
- package/dist/web/assets/{stateDiagram-v2-GCMORJYK-aj1Op7xi.js → stateDiagram-v2-GCMORJYK-M9PkKdix.js} +1 -1
- package/dist/web/assets/{swimlanes-2SLR337P-DQ_YYhZu.js → swimlanes-2SLR337P-B13LpAqh.js} +1 -1
- package/dist/web/assets/swimlanesDiagram-TC7HE7FX-BmCQPnJC.js +8 -0
- package/dist/web/assets/theme-BMaTNJdz.js +1 -0
- package/dist/web/assets/{timeline-definition-EJHVYXUP-Bk3Mvy3w.js → timeline-definition-EJHVYXUP-Dli0jMaX.js} +1 -1
- package/dist/web/assets/{treeView-Q6P3EWNA-BzM7oZcT.js → treeView-Q6P3EWNA-BwBFpWIC.js} +1 -1
- package/dist/web/assets/{treemap-WGGIJYW6-DzgzhZei.js → treemap-WGGIJYW6-DYGjPuTv.js} +1 -1
- package/dist/web/assets/{use-app-theme-DEiGbVuj.js → use-app-theme-C9HTPdVu.js} +1 -1
- package/dist/web/assets/{use-panel-width-ujFPWf-l.js → use-panel-width-DGbjs_T8.js} +1 -1
- package/dist/web/assets/{usecaseDiagram-POWQR4AR-kriFnh7L.js → usecaseDiagram-POWQR4AR-Chs4O-o-.js} +1 -1
- package/dist/web/assets/{vennDiagram-UO4OBE2U-CDdCMQs7.js → vennDiagram-UO4OBE2U-D3GVZcIr.js} +1 -1
- package/dist/web/assets/{wardley-WFR3VGLG-BUWTYQyf.js → wardley-WFR3VGLG-B5YUEVo5.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-VNRHLVJA-CW7lMcTj.js → wardleyDiagram-VNRHLVJA-liqH-4lm.js} +1 -1
- package/dist/web/assets/{xychartDiagram-PMCCYNJV-DoIu8J_3.js → xychartDiagram-PMCCYNJV-C63N6ZvA.js} +1 -1
- package/dist/web/index.html +1 -1
- package/package.json +2 -2
- package/dist/web/assets/CreateReviewFabButton-3V9ZYSHd.js +0 -1
- package/dist/web/assets/ReviewDetailPage-DSCeLI9D.js +0 -1
- package/dist/web/assets/architecture-7GRP2DOG-DORh0iBM.js +0 -1
- package/dist/web/assets/channel-WV0H_O-w.js +0 -1
- package/dist/web/assets/eventmodeling-NTZA5JFV-KBTHxbEB.js +0 -1
- package/dist/web/assets/flowDiagram-KWPJA3E3-DM-yXypD.js +0 -1
- package/dist/web/assets/pinia-r4XAEwbB.js +0 -1
- package/dist/web/assets/railroad-abnf-HS5TGJTU-BzS6thC5.js +0 -1
- package/dist/web/assets/railroad-ebnf-LZEXJU2U-MS09sAPW.js +0 -1
- package/dist/web/assets/railroad-peg-WCYAUIDC-DEQwqbcE.js +0 -1
- package/dist/web/assets/swimlanesDiagram-TC7HE7FX-9frEtrG_.js +0 -8
- package/dist/web/assets/theme-BSjPUW3f.js +0 -1
|
@@ -1,40 +1,41 @@
|
|
|
1
1
|
import { BadRequestError } from "../errors/http.js";
|
|
2
2
|
import { Type } from '@sinclair/typebox';
|
|
3
|
-
import {
|
|
3
|
+
import { getBranchDiff, getCommitsDiff, getDiffSummaries, getFileDiff } from "../services/diff.service.js";
|
|
4
4
|
import { resolveWithinRepo } from "../services/git.service.js";
|
|
5
5
|
import { errorMessage } from "../../shared/utils/error-message.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
from: Type.String({ minLength: 1 }),
|
|
11
|
-
to: Type.String({ minLength: 1 })
|
|
12
|
-
});
|
|
13
|
-
const SourceParams = Type.Object({
|
|
14
|
-
source: Type.Union([Type.Literal('staged'), Type.Literal('unstaged')])
|
|
15
|
-
});
|
|
16
|
-
const FileQuery = Type.Object({
|
|
17
|
-
path: Type.String({ minLength: 1 }),
|
|
18
|
-
oldPath: Type.Optional(Type.String({ minLength: 1 })),
|
|
19
|
-
status: Type.Union([
|
|
20
|
-
Type.Literal('added'),
|
|
21
|
-
Type.Literal('modified'),
|
|
22
|
-
Type.Literal('deleted'),
|
|
23
|
-
Type.Literal('renamed')
|
|
24
|
-
])
|
|
25
|
-
});
|
|
6
|
+
import { BranchQuery, CommitsQuery, DiffFileSchema, DiffFileSummarySchema, FileQuery, SourceParams } from "../../shared/diff/schemas.js";
|
|
7
|
+
import { ERROR_RESPONSES } from "../../shared/http/schemas.js";
|
|
8
|
+
// OpenAPI group of these routes.
|
|
9
|
+
const TAGS = ['diff'];
|
|
26
10
|
export async function diffRoutes(app, opts) {
|
|
27
11
|
const { repositoryPath } = opts;
|
|
28
12
|
const typedApp = app.withTypeProvider();
|
|
29
|
-
typedApp.get('/api/diff/staged', async () => {
|
|
30
|
-
return getStagedDiff(repositoryPath);
|
|
31
|
-
});
|
|
32
|
-
typedApp.get('/api/diff/unstaged', async () => {
|
|
33
|
-
return getUnstagedDiff(repositoryPath);
|
|
34
|
-
});
|
|
35
13
|
// Files without hunks, then one file with hunks: the list stays small (ADR 0033).
|
|
36
|
-
typedApp.get('/api/diff/:source/files', {
|
|
37
|
-
|
|
14
|
+
typedApp.get('/api/diff/:source/files', {
|
|
15
|
+
schema: {
|
|
16
|
+
tags: TAGS,
|
|
17
|
+
summary: 'List changed files without hunks',
|
|
18
|
+
params: SourceParams,
|
|
19
|
+
response: {
|
|
20
|
+
200: Type.Array(DiffFileSummarySchema, {
|
|
21
|
+
description: 'Changed files of the side.'
|
|
22
|
+
}),
|
|
23
|
+
...ERROR_RESPONSES
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}, async (request) => getDiffSummaries(repositoryPath, request.params.source));
|
|
27
|
+
typedApp.get('/api/diff/:source/file', {
|
|
28
|
+
schema: {
|
|
29
|
+
tags: TAGS,
|
|
30
|
+
summary: 'Get one changed file with hunks',
|
|
31
|
+
params: SourceParams,
|
|
32
|
+
querystring: FileQuery,
|
|
33
|
+
response: {
|
|
34
|
+
200: { ...DiffFileSchema, description: 'The file with its hunks.' },
|
|
35
|
+
...ERROR_RESPONSES
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}, async (request) => {
|
|
38
39
|
const { path, oldPath, status } = request.query;
|
|
39
40
|
// Only a path outside the repository is a bad request. Other errors stay server errors.
|
|
40
41
|
try {
|
|
@@ -51,7 +52,20 @@ export async function diffRoutes(app, opts) {
|
|
|
51
52
|
status
|
|
52
53
|
});
|
|
53
54
|
});
|
|
54
|
-
typedApp.get('/api/diff/branch', {
|
|
55
|
+
typedApp.get('/api/diff/branch', {
|
|
56
|
+
schema: {
|
|
57
|
+
tags: TAGS,
|
|
58
|
+
summary: 'Diff of HEAD against a ref',
|
|
59
|
+
description: 'No UI calls it yet. It is kept for branch reviews.',
|
|
60
|
+
querystring: BranchQuery,
|
|
61
|
+
response: {
|
|
62
|
+
200: Type.Array(DiffFileSchema, {
|
|
63
|
+
description: 'Changed files with hunks.'
|
|
64
|
+
}),
|
|
65
|
+
...ERROR_RESPONSES
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}, async (request) => {
|
|
55
69
|
try {
|
|
56
70
|
return await getBranchDiff(repositoryPath, request.query.base);
|
|
57
71
|
}
|
|
@@ -60,7 +74,20 @@ export async function diffRoutes(app, opts) {
|
|
|
60
74
|
throw new BadRequestError(message, { cause: err });
|
|
61
75
|
}
|
|
62
76
|
});
|
|
63
|
-
typedApp.get('/api/diff/commits', {
|
|
77
|
+
typedApp.get('/api/diff/commits', {
|
|
78
|
+
schema: {
|
|
79
|
+
tags: TAGS,
|
|
80
|
+
summary: 'Diff between two refs',
|
|
81
|
+
description: 'No UI calls it yet. It is kept for commit reviews.',
|
|
82
|
+
querystring: CommitsQuery,
|
|
83
|
+
response: {
|
|
84
|
+
200: Type.Array(DiffFileSchema, {
|
|
85
|
+
description: 'Changed files with hunks.'
|
|
86
|
+
}),
|
|
87
|
+
...ERROR_RESPONSES
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}, async (request) => {
|
|
64
91
|
try {
|
|
65
92
|
return await getCommitsDiff(repositoryPath, request.query.from, request.query.to);
|
|
66
93
|
}
|
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import { safeSend } from "../utils/sse.js";
|
|
2
|
+
import { EventStreamSchema } from "../../shared/events/schemas.js";
|
|
2
3
|
export async function eventRoutes(app, opts) {
|
|
3
4
|
const { eventBus, instanceId } = opts;
|
|
4
5
|
const hello = { instanceId };
|
|
5
|
-
app.get('/api/events', {
|
|
6
|
+
app.get('/api/events', {
|
|
7
|
+
sse: true,
|
|
8
|
+
schema: {
|
|
9
|
+
tags: ['events'],
|
|
10
|
+
summary: 'Stream of review, comment and file changes',
|
|
11
|
+
response: {
|
|
12
|
+
200: {
|
|
13
|
+
description: 'An SSE stream that stays open.',
|
|
14
|
+
content: { 'text/event-stream': { schema: EventStreamSchema } }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}, async (request, reply) => {
|
|
6
19
|
reply.sse.keepAlive();
|
|
7
20
|
await reply.sse.send({ event: 'connected', data: hello });
|
|
8
21
|
// The client can leave during the await; a later `onClose` would never run.
|
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
import { Type } from '@sinclair/typebox';
|
|
2
2
|
import { exportAsJson, exportAsMarkdown } from "../services/export.service.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
format: Type.Union([Type.Literal('json'), Type.Literal('markdown')])
|
|
6
|
-
});
|
|
3
|
+
import { ExportQuery, ReviewExportSchema, ReviewIdParams } from "../../shared/reviews/schemas.js";
|
|
4
|
+
import { ERROR_RESPONSES } from "../../shared/http/schemas.js";
|
|
7
5
|
export async function exportRoutes(app, opts) {
|
|
8
6
|
const { db, repositoryPath } = opts;
|
|
9
7
|
const typedApp = app.withTypeProvider();
|
|
10
|
-
typedApp.get('/api/reviews/:id/export', {
|
|
8
|
+
typedApp.get('/api/reviews/:id/export', {
|
|
9
|
+
schema: {
|
|
10
|
+
tags: ['reviews'],
|
|
11
|
+
summary: 'Export a review with its comments',
|
|
12
|
+
params: ReviewIdParams,
|
|
13
|
+
querystring: ExportQuery,
|
|
14
|
+
response: {
|
|
15
|
+
200: {
|
|
16
|
+
description: 'The review as JSON or as a markdown report.',
|
|
17
|
+
content: {
|
|
18
|
+
'application/json': { schema: ReviewExportSchema },
|
|
19
|
+
'text/markdown': {
|
|
20
|
+
schema: Type.String({ description: 'Markdown report.' })
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
...ERROR_RESPONSES
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}, async (request, reply) => {
|
|
11
28
|
if (request.query.format === 'json') {
|
|
12
29
|
return exportAsJson(db, request.params.id);
|
|
13
30
|
}
|
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
import { BadRequestError } from "../errors/http.js";
|
|
2
|
-
import { Type } from '@sinclair/typebox';
|
|
3
2
|
import { getRepositoryInfo, stagePaths, unstagePaths, discardPaths, getFilesAtRef, getFileAtRef } from "../services/git.service.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
ref: Type.String({ minLength: 1 })
|
|
9
|
-
});
|
|
10
|
-
/** Body for `/stage` and `/unstage`: an omitted/empty `paths` means "all". */
|
|
11
|
-
const OptionalPathsBody = Type.Object({
|
|
12
|
-
paths: Type.Optional(Type.Array(Type.String()))
|
|
13
|
-
});
|
|
14
|
-
/** Body for `/discard`: `paths` is required — no "discard everything". */
|
|
15
|
-
const DiscardBody = Type.Object({
|
|
16
|
-
paths: Type.Array(Type.String(), { minItems: 1 })
|
|
17
|
-
});
|
|
3
|
+
import { DiscardBody, FileContentQuery, FileContentResponseSchema, FileParams, FileTreeResponseSchema, OkSchema, OptionalPathsBody, RepositoryInfoSchema, TreeParams } from "../../shared/git/schemas.js";
|
|
4
|
+
import { ERROR_RESPONSES } from "../../shared/http/schemas.js";
|
|
5
|
+
// OpenAPI group of these routes.
|
|
6
|
+
const TAGS = ['git'];
|
|
18
7
|
export async function gitRoutes(app, opts) {
|
|
19
8
|
const { repositoryPath } = opts;
|
|
20
9
|
const typedApp = app.withTypeProvider();
|
|
21
|
-
|
|
10
|
+
typedApp.get('/api/git/info', {
|
|
11
|
+
schema: {
|
|
12
|
+
tags: TAGS,
|
|
13
|
+
summary: 'Repository facts',
|
|
14
|
+
response: { 200: RepositoryInfoSchema, ...ERROR_RESPONSES }
|
|
15
|
+
}
|
|
16
|
+
}, async () => {
|
|
22
17
|
return getRepositoryInfo(repositoryPath);
|
|
23
18
|
});
|
|
24
|
-
typedApp.post('/api/git/stage', {
|
|
19
|
+
typedApp.post('/api/git/stage', {
|
|
20
|
+
schema: {
|
|
21
|
+
tags: TAGS,
|
|
22
|
+
summary: 'Stage paths',
|
|
23
|
+
body: OptionalPathsBody,
|
|
24
|
+
response: { 200: OkSchema, ...ERROR_RESPONSES }
|
|
25
|
+
}
|
|
26
|
+
}, async (request) => {
|
|
25
27
|
try {
|
|
26
28
|
await stagePaths(repositoryPath, request.body.paths ?? []);
|
|
27
29
|
return { ok: true };
|
|
@@ -31,7 +33,14 @@ export async function gitRoutes(app, opts) {
|
|
|
31
33
|
throw new BadRequestError(message, { cause: err });
|
|
32
34
|
}
|
|
33
35
|
});
|
|
34
|
-
typedApp.post('/api/git/unstage', {
|
|
36
|
+
typedApp.post('/api/git/unstage', {
|
|
37
|
+
schema: {
|
|
38
|
+
tags: TAGS,
|
|
39
|
+
summary: 'Unstage paths',
|
|
40
|
+
body: OptionalPathsBody,
|
|
41
|
+
response: { 200: OkSchema, ...ERROR_RESPONSES }
|
|
42
|
+
}
|
|
43
|
+
}, async (request) => {
|
|
35
44
|
try {
|
|
36
45
|
await unstagePaths(repositoryPath, request.body.paths ?? []);
|
|
37
46
|
return { ok: true };
|
|
@@ -41,7 +50,14 @@ export async function gitRoutes(app, opts) {
|
|
|
41
50
|
throw new BadRequestError(message, { cause: err });
|
|
42
51
|
}
|
|
43
52
|
});
|
|
44
|
-
typedApp.post('/api/git/discard', {
|
|
53
|
+
typedApp.post('/api/git/discard', {
|
|
54
|
+
schema: {
|
|
55
|
+
tags: TAGS,
|
|
56
|
+
summary: 'Discard working-tree changes of paths',
|
|
57
|
+
body: DiscardBody,
|
|
58
|
+
response: { 200: OkSchema, ...ERROR_RESPONSES }
|
|
59
|
+
}
|
|
60
|
+
}, async (request) => {
|
|
45
61
|
try {
|
|
46
62
|
await discardPaths(repositoryPath, request.body.paths);
|
|
47
63
|
return { ok: true };
|
|
@@ -51,7 +67,14 @@ export async function gitRoutes(app, opts) {
|
|
|
51
67
|
throw new BadRequestError(message, { cause: err });
|
|
52
68
|
}
|
|
53
69
|
});
|
|
54
|
-
typedApp.get('/api/git/tree/:ref', {
|
|
70
|
+
typedApp.get('/api/git/tree/:ref', {
|
|
71
|
+
schema: {
|
|
72
|
+
tags: TAGS,
|
|
73
|
+
summary: 'List files at a ref',
|
|
74
|
+
params: TreeParams,
|
|
75
|
+
response: { 200: FileTreeResponseSchema, ...ERROR_RESPONSES }
|
|
76
|
+
}
|
|
77
|
+
}, async (request) => {
|
|
55
78
|
const { ref } = request.params;
|
|
56
79
|
try {
|
|
57
80
|
const files = await getFilesAtRef(repositoryPath, ref);
|
|
@@ -62,7 +85,15 @@ export async function gitRoutes(app, opts) {
|
|
|
62
85
|
throw new BadRequestError(message, { cause: err });
|
|
63
86
|
}
|
|
64
87
|
});
|
|
65
|
-
typedApp.get('/api/git/file/*', {
|
|
88
|
+
typedApp.get('/api/git/file/*', {
|
|
89
|
+
schema: {
|
|
90
|
+
tags: TAGS,
|
|
91
|
+
summary: 'Read one file at a ref',
|
|
92
|
+
params: FileParams,
|
|
93
|
+
querystring: FileContentQuery,
|
|
94
|
+
response: { 200: FileContentResponseSchema, ...ERROR_RESPONSES }
|
|
95
|
+
}
|
|
96
|
+
}, async (request) => {
|
|
66
97
|
const filePath = request.params['*'];
|
|
67
98
|
const { ref } = request.query;
|
|
68
99
|
if (!filePath) {
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
import { HealthSchema } from "../../shared/app/schemas.js";
|
|
1
2
|
export async function healthRoutes(app) {
|
|
2
|
-
app.get('/health',
|
|
3
|
+
app.withTypeProvider().get('/health', {
|
|
4
|
+
schema: {
|
|
5
|
+
tags: ['app'],
|
|
6
|
+
summary: 'Liveness check',
|
|
7
|
+
response: { 200: HealthSchema }
|
|
8
|
+
}
|
|
9
|
+
}, async () => {
|
|
3
10
|
return { status: 'ok' };
|
|
4
11
|
});
|
|
5
12
|
}
|
|
@@ -2,43 +2,41 @@ import { NotFoundError, BadRequestError } from "../errors/http.js";
|
|
|
2
2
|
import { Type } from '@sinclair/typebox';
|
|
3
3
|
import { createReview, getReview, getReviews, setReviewStatus, removeReview } from "../services/review.service.js";
|
|
4
4
|
import { getRepositoryInfo } from "../services/git.service.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Type.Literal('unstaged'),
|
|
10
|
-
Type.Literal('branch'),
|
|
11
|
-
Type.Literal('commits')
|
|
12
|
-
])),
|
|
13
|
-
sourceRef: Type.Optional(Type.String()),
|
|
14
|
-
baseRef: Type.Optional(Type.String()),
|
|
15
|
-
name: Type.Optional(Type.String())
|
|
16
|
-
});
|
|
17
|
-
const ReviewsQuery = Type.Object({
|
|
18
|
-
branch: Type.Optional(Type.String()),
|
|
19
|
-
search: Type.Optional(Type.String()),
|
|
20
|
-
createdFrom: Type.Optional(Type.String()),
|
|
21
|
-
createdTo: Type.Optional(Type.String()),
|
|
22
|
-
/** Comma-separated file paths — reviews matching any of them are returned. */
|
|
23
|
-
files: Type.Optional(Type.String())
|
|
24
|
-
});
|
|
25
|
-
const UpdateReviewBody = Type.Object({
|
|
26
|
-
status: Type.Optional(Type.Union([
|
|
27
|
-
Type.Literal('in_progress'),
|
|
28
|
-
Type.Literal('approved'),
|
|
29
|
-
Type.Literal('changes_requested')
|
|
30
|
-
]))
|
|
31
|
-
});
|
|
32
|
-
const ReviewIdParams = Type.Object({ id: Type.String() });
|
|
5
|
+
import { CreateReviewBody, ReviewIdParams, ReviewSchema, ReviewSummarySchema, ReviewsQuery, UpdateReviewBody } from "../../shared/reviews/schemas.js";
|
|
6
|
+
import { ERROR_RESPONSES, NoContentSchema } from "../../shared/http/schemas.js";
|
|
7
|
+
// OpenAPI group of these routes.
|
|
8
|
+
const TAGS = ['reviews'];
|
|
33
9
|
export async function reviewRoutes(app, opts) {
|
|
34
10
|
const { repositoryPath, db, eventBus } = opts;
|
|
35
11
|
const typedApp = app.withTypeProvider();
|
|
36
|
-
typedApp.post('/api/reviews', {
|
|
12
|
+
typedApp.post('/api/reviews', {
|
|
13
|
+
schema: {
|
|
14
|
+
tags: TAGS,
|
|
15
|
+
summary: 'Create a review from the current diff',
|
|
16
|
+
body: CreateReviewBody,
|
|
17
|
+
response: {
|
|
18
|
+
201: { ...ReviewSchema, description: 'The new review.' },
|
|
19
|
+
...ERROR_RESPONSES
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}, async (request, reply) => {
|
|
37
23
|
const review = await createReview(db, repositoryPath, request.body, eventBus);
|
|
38
24
|
reply.code(201);
|
|
39
25
|
return review;
|
|
40
26
|
});
|
|
41
|
-
typedApp.get('/api/reviews', {
|
|
27
|
+
typedApp.get('/api/reviews', {
|
|
28
|
+
schema: {
|
|
29
|
+
tags: TAGS,
|
|
30
|
+
summary: 'List reviews, newest first',
|
|
31
|
+
querystring: ReviewsQuery,
|
|
32
|
+
response: {
|
|
33
|
+
200: Type.Array(ReviewSummarySchema, {
|
|
34
|
+
description: 'Reviews without snapshots.'
|
|
35
|
+
}),
|
|
36
|
+
...ERROR_RESPONSES
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}, async (request) => {
|
|
42
40
|
const { branch, search, createdFrom, createdTo, files } = request.query;
|
|
43
41
|
const resolvedBranch = branch ?? (await getRepositoryInfo(repositoryPath)).branch;
|
|
44
42
|
return getReviews(db, {
|
|
@@ -51,14 +49,35 @@ export async function reviewRoutes(app, opts) {
|
|
|
51
49
|
: undefined
|
|
52
50
|
});
|
|
53
51
|
});
|
|
54
|
-
typedApp.get('/api/reviews/:id', {
|
|
52
|
+
typedApp.get('/api/reviews/:id', {
|
|
53
|
+
schema: {
|
|
54
|
+
tags: TAGS,
|
|
55
|
+
summary: 'Get a review with its snapshot',
|
|
56
|
+
params: ReviewIdParams,
|
|
57
|
+
response: { 200: ReviewSchema, ...ERROR_RESPONSES }
|
|
58
|
+
}
|
|
59
|
+
}, async (request) => {
|
|
55
60
|
const review = getReview(db, request.params.id);
|
|
56
61
|
if (!review) {
|
|
57
62
|
throw new NotFoundError(`Review ${request.params.id} not found`);
|
|
58
63
|
}
|
|
59
64
|
return review;
|
|
60
65
|
});
|
|
61
|
-
typedApp.patch('/api/reviews/:id', {
|
|
66
|
+
typedApp.patch('/api/reviews/:id', {
|
|
67
|
+
schema: {
|
|
68
|
+
tags: TAGS,
|
|
69
|
+
summary: 'Change the review status',
|
|
70
|
+
params: ReviewIdParams,
|
|
71
|
+
body: UpdateReviewBody,
|
|
72
|
+
response: {
|
|
73
|
+
200: {
|
|
74
|
+
...ReviewSummarySchema,
|
|
75
|
+
description: 'The changed review without its snapshot.'
|
|
76
|
+
},
|
|
77
|
+
...ERROR_RESPONSES
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}, async (request) => {
|
|
62
81
|
if (!request.body.status) {
|
|
63
82
|
throw new BadRequestError('"status" is required');
|
|
64
83
|
}
|
|
@@ -68,7 +87,14 @@ export async function reviewRoutes(app, opts) {
|
|
|
68
87
|
}
|
|
69
88
|
return updated;
|
|
70
89
|
});
|
|
71
|
-
typedApp.delete('/api/reviews/:id', {
|
|
90
|
+
typedApp.delete('/api/reviews/:id', {
|
|
91
|
+
schema: {
|
|
92
|
+
tags: TAGS,
|
|
93
|
+
summary: 'Delete a review and its comments',
|
|
94
|
+
params: ReviewIdParams,
|
|
95
|
+
response: { 204: NoContentSchema, ...ERROR_RESPONSES }
|
|
96
|
+
}
|
|
97
|
+
}, async (request, reply) => {
|
|
72
98
|
if (!getReview(db, request.params.id)) {
|
|
73
99
|
throw new NotFoundError(`Review ${request.params.id} not found`);
|
|
74
100
|
}
|
|
@@ -195,6 +195,7 @@ export class AgentSession {
|
|
|
195
195
|
* Changes a session setting (model, mode, …) and returns the agent's new
|
|
196
196
|
* full set of options. Validated against what the agent itself offered, so
|
|
197
197
|
* an unknown id or value is rejected here rather than sent on.
|
|
198
|
+
* `value` is `unknown`: the route schema gives it no type (see ConfigBody).
|
|
198
199
|
*/
|
|
199
200
|
async setConfig(configId, value) {
|
|
200
201
|
const connection = this.#connection;
|
|
@@ -208,9 +209,10 @@ export class AgentSession {
|
|
|
208
209
|
if (!option) {
|
|
209
210
|
throw new AgentConfigError(`Unknown setting "${configId}"`);
|
|
210
211
|
}
|
|
211
|
-
if (
|
|
212
|
-
|
|
213
|
-
|
|
212
|
+
if ((typeof value !== 'boolean' && typeof value !== 'string') ||
|
|
213
|
+
(option.type === 'boolean'
|
|
214
|
+
? typeof value !== 'boolean'
|
|
215
|
+
: !option.options?.some(c => c.value === value))) {
|
|
214
216
|
throw new AgentConfigError(`Invalid value for "${option.name}"`);
|
|
215
217
|
}
|
|
216
218
|
try {
|
|
@@ -1,51 +1,140 @@
|
|
|
1
1
|
import { Type } from '@sinclair/typebox';
|
|
2
2
|
import { MAX_ATTACHMENT_BYTES, MAX_FILE_SEARCH_LIMIT } from "./constants.js";
|
|
3
|
-
|
|
3
|
+
import { DiffSourceNameSchema } from "../diff/schemas.js";
|
|
4
|
+
import { Nullable } from "../utils/schemas.js";
|
|
5
|
+
export const SessionParams = Type.Object({
|
|
6
|
+
id: Type.String({ description: 'Chat session id.' })
|
|
7
|
+
});
|
|
4
8
|
export const PermissionParams = Type.Object({
|
|
5
|
-
id: Type.String(),
|
|
6
|
-
requestId: Type.String()
|
|
9
|
+
id: Type.String({ description: 'Chat session id.' }),
|
|
10
|
+
requestId: Type.String({ description: 'Pending permission request id.' })
|
|
7
11
|
});
|
|
8
12
|
export const CreateSessionBody = Type.Object({
|
|
9
|
-
presetId: Type.String({
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
})
|
|
13
|
-
|
|
13
|
+
presetId: Type.String({
|
|
14
|
+
minLength: 1,
|
|
15
|
+
description: 'Preset from GET /api/agent/presets.'
|
|
16
|
+
}),
|
|
17
|
+
name: Type.Optional(Type.String({
|
|
18
|
+
maxLength: 60,
|
|
19
|
+
description: 'Chat name. Omitted: the server makes a unique one.'
|
|
20
|
+
})),
|
|
21
|
+
reviewId: Type.Optional(Type.String({ description: 'Review that the chat belongs to.' }))
|
|
22
|
+
}, { description: 'A new chat session.' });
|
|
23
|
+
export const AutoApproveBody = Type.Object({
|
|
24
|
+
enabled: Type.Boolean({
|
|
25
|
+
description: 'The server answers permission requests itself.'
|
|
26
|
+
})
|
|
27
|
+
}, { description: 'Auto-approve switch of one session.' });
|
|
14
28
|
export const FileSearchQuery = Type.Object({
|
|
15
|
-
q: Type.Optional(Type.String({
|
|
16
|
-
|
|
29
|
+
q: Type.Optional(Type.String({
|
|
30
|
+
maxLength: 200,
|
|
31
|
+
description: 'Fuzzy text to find in paths. Empty lists files.'
|
|
32
|
+
})),
|
|
33
|
+
limit: Type.Optional(Type.Integer({
|
|
34
|
+
minimum: 1,
|
|
35
|
+
maximum: MAX_FILE_SEARCH_LIMIT,
|
|
36
|
+
description: 'Maximum number of paths.'
|
|
37
|
+
}))
|
|
17
38
|
});
|
|
18
39
|
export const ContextItem = Type.Union([
|
|
19
40
|
Type.Object({
|
|
20
|
-
kind: Type.Literal('diff'),
|
|
21
|
-
source:
|
|
22
|
-
path: Type.Optional(Type.String({
|
|
23
|
-
|
|
41
|
+
kind: Type.Literal('diff', { description: 'Live working-tree diff.' }),
|
|
42
|
+
source: DiffSourceNameSchema,
|
|
43
|
+
path: Type.Optional(Type.String({
|
|
44
|
+
minLength: 1,
|
|
45
|
+
description: 'One file of the diff. Omitted: the whole diff.'
|
|
46
|
+
}))
|
|
47
|
+
}, { description: 'The live diff of one side.' }),
|
|
24
48
|
Type.Object({
|
|
25
|
-
kind: Type.Literal('file'),
|
|
26
|
-
path: Type.String({
|
|
27
|
-
|
|
28
|
-
|
|
49
|
+
kind: Type.Literal('file', { description: 'A repository file.' }),
|
|
50
|
+
path: Type.String({
|
|
51
|
+
minLength: 1,
|
|
52
|
+
description: 'Repository-relative path. The agent reads the file.'
|
|
53
|
+
})
|
|
54
|
+
}, { description: 'A link to a repository file.' }),
|
|
29
55
|
Type.Object({
|
|
30
|
-
kind: Type.Literal('
|
|
31
|
-
|
|
32
|
-
|
|
56
|
+
kind: Type.Literal('review', { description: 'A review.' }),
|
|
57
|
+
reviewId: Type.String({ description: 'Review id.' })
|
|
58
|
+
}, { description: 'A review as markdown with its comments.' }),
|
|
59
|
+
Type.Object({
|
|
60
|
+
kind: Type.Literal('attachment', {
|
|
61
|
+
description: 'A file from the user.'
|
|
62
|
+
}),
|
|
63
|
+
name: Type.String({
|
|
64
|
+
minLength: 1,
|
|
65
|
+
maxLength: 255,
|
|
66
|
+
description: 'File name.'
|
|
67
|
+
}),
|
|
68
|
+
mimeType: Type.String({ maxLength: 255, description: 'MIME type.' }),
|
|
33
69
|
data: Type.String({
|
|
34
|
-
maxLength: Math.ceil(MAX_ATTACHMENT_BYTES / 3) * 4
|
|
70
|
+
maxLength: Math.ceil(MAX_ATTACHMENT_BYTES / 3) * 4,
|
|
71
|
+
description: 'File content in base64.'
|
|
35
72
|
})
|
|
36
|
-
})
|
|
37
|
-
]);
|
|
73
|
+
}, { description: 'An uploaded file or a pasted image.' })
|
|
74
|
+
], { description: 'Context of a prompt (docs/agent-context-uris.md).' });
|
|
38
75
|
export const PromptBody = Type.Object({
|
|
39
|
-
text: Type.String({ minLength: 1 }),
|
|
40
|
-
context: Type.Optional(Type.Array(ContextItem, {
|
|
41
|
-
|
|
76
|
+
text: Type.String({ minLength: 1, description: 'Message to the agent.' }),
|
|
77
|
+
context: Type.Optional(Type.Array(ContextItem, {
|
|
78
|
+
maxItems: 20,
|
|
79
|
+
description: 'Context items that go with the message.'
|
|
80
|
+
}))
|
|
81
|
+
}, { description: 'A user message.' });
|
|
42
82
|
export const PermissionBody = Type.Object({
|
|
43
|
-
optionId: Type.Optional(Type.String(
|
|
44
|
-
|
|
83
|
+
optionId: Type.Optional(Type.String({
|
|
84
|
+
description: 'Option from the permission request. Omitted: the request is cancelled.'
|
|
85
|
+
}))
|
|
86
|
+
}, { description: 'Answer to a permission request.' });
|
|
45
87
|
export const ConfigBody = Type.Object({
|
|
46
|
-
configId: Type.String({
|
|
88
|
+
configId: Type.String({
|
|
89
|
+
minLength: 1,
|
|
90
|
+
description: 'Setting id from the config event.'
|
|
91
|
+
}),
|
|
47
92
|
// No schema type on purpose: a Union/anyOf makes Fastify's Ajv coerce `true`
|
|
48
93
|
// into the string "true", and a `type` array trips Ajv's strictTypes. The
|
|
49
94
|
// session checks the value against the option the agent actually offered.
|
|
50
|
-
value: Type.Unsafe({
|
|
95
|
+
value: Type.Unsafe({
|
|
96
|
+
description: 'A select value (string) or a boolean.'
|
|
97
|
+
})
|
|
98
|
+
}, { description: 'A new value of an agent setting.' });
|
|
99
|
+
export const AgentPresetInfoSchema = Type.Object({
|
|
100
|
+
id: Type.String({ description: 'Preset id.' }),
|
|
101
|
+
title: Type.String({ description: 'Name in the UI.' }),
|
|
102
|
+
available: Type.Boolean({
|
|
103
|
+
description: 'The launcher binary is in PATH. Says nothing about login or auth.'
|
|
104
|
+
}),
|
|
105
|
+
autoApprovesEdits: Type.Boolean({
|
|
106
|
+
description: 'The agent applies edits without permission requests.'
|
|
107
|
+
})
|
|
108
|
+
}, { description: 'An ACP agent that can start a chat.' });
|
|
109
|
+
export const AgentSessionStatusSchema = Type.Union([
|
|
110
|
+
Type.Literal('starting'),
|
|
111
|
+
Type.Literal('ready'),
|
|
112
|
+
Type.Literal('busy'),
|
|
113
|
+
Type.Literal('closed')
|
|
114
|
+
], { description: 'Session state.' });
|
|
115
|
+
export const AgentSessionInfoSchema = Type.Object({
|
|
116
|
+
id: Type.String({ description: 'Chat session id.' }),
|
|
117
|
+
presetId: Type.String({ description: 'Preset of the agent.' }),
|
|
118
|
+
name: Type.String({ description: 'Chat name in the UI.' }),
|
|
119
|
+
status: AgentSessionStatusSchema,
|
|
120
|
+
reviewId: Nullable(Type.String({ description: 'Review of the chat. Else null.' })),
|
|
121
|
+
autoApprove: Type.Boolean({
|
|
122
|
+
description: 'The server answers permission requests itself.'
|
|
123
|
+
}),
|
|
124
|
+
error: Nullable(Type.String({
|
|
125
|
+
description: 'Why the session could not start. Else null.'
|
|
126
|
+
}))
|
|
127
|
+
}, { description: 'A chat session.' });
|
|
128
|
+
export const AgentFileSearchResponseSchema = Type.Object({
|
|
129
|
+
files: Type.Array(Type.String(), {
|
|
130
|
+
description: 'Repository-relative paths, best match first.'
|
|
131
|
+
})
|
|
132
|
+
}, { description: 'Files for the context picker.' });
|
|
133
|
+
export const AgentEventsHeaders = Type.Object({
|
|
134
|
+
'last-event-id': Type.Optional(Type.String({
|
|
135
|
+
description: 'Last event id that the client got. Events after it come again.'
|
|
136
|
+
}))
|
|
137
|
+
});
|
|
138
|
+
export const AgentStreamSchema = Type.String({
|
|
139
|
+
description: 'Server-sent events: connected, then agent (AgentStreamEnvelope, SSE id = event id), state (AgentSessionState), gap ({ sessionId }) and sessions (reread the list).'
|
|
51
140
|
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Type } from '@sinclair/typebox';
|
|
2
|
+
export const AppInfoSchema = Type.Object({
|
|
3
|
+
version: Type.String({ description: 'githuman version from package.json.' })
|
|
4
|
+
}, { description: 'Facts about the running server.' });
|
|
5
|
+
export const HealthSchema = Type.Object({
|
|
6
|
+
status: Type.Literal('ok', { description: 'Always "ok".' })
|
|
7
|
+
}, { description: 'The server is up.' });
|