@braidhq/server 0.0.1 → 0.1.0

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.
Files changed (258) hide show
  1. package/README.md +136 -0
  2. package/dist/app.d.ts +19 -2
  3. package/dist/app.d.ts.map +1 -1
  4. package/dist/app.js +121 -3
  5. package/dist/app.js.map +1 -1
  6. package/dist/composeFs.d.ts +41 -6
  7. package/dist/composeFs.d.ts.map +1 -1
  8. package/dist/composeFs.js +234 -52
  9. package/dist/composeFs.js.map +1 -1
  10. package/dist/composition.d.ts +86 -2
  11. package/dist/composition.d.ts.map +1 -1
  12. package/dist/composition.js +71 -1
  13. package/dist/composition.js.map +1 -1
  14. package/dist/infrastructure/agent/SubprocessSkillRunner.d.ts +21 -0
  15. package/dist/infrastructure/agent/SubprocessSkillRunner.d.ts.map +1 -1
  16. package/dist/infrastructure/agent/SubprocessSkillRunner.js +35 -3
  17. package/dist/infrastructure/agent/SubprocessSkillRunner.js.map +1 -1
  18. package/dist/infrastructure/agent/mcpConfig.d.ts +25 -5
  19. package/dist/infrastructure/agent/mcpConfig.d.ts.map +1 -1
  20. package/dist/infrastructure/agent/mcpConfig.js +22 -14
  21. package/dist/infrastructure/agent/mcpConfig.js.map +1 -1
  22. package/dist/infrastructure/auth/AccessPolicy.d.ts +112 -0
  23. package/dist/infrastructure/auth/AccessPolicy.d.ts.map +1 -0
  24. package/dist/infrastructure/auth/AccessPolicy.js +184 -0
  25. package/dist/infrastructure/auth/AccessPolicy.js.map +1 -0
  26. package/dist/infrastructure/auth/SessionStore.d.ts +40 -0
  27. package/dist/infrastructure/auth/SessionStore.d.ts.map +1 -0
  28. package/dist/infrastructure/auth/SessionStore.js +107 -0
  29. package/dist/infrastructure/auth/SessionStore.js.map +1 -0
  30. package/dist/infrastructure/auth/ensureApprovedEmails.d.ts +16 -0
  31. package/dist/infrastructure/auth/ensureApprovedEmails.d.ts.map +1 -0
  32. package/dist/infrastructure/auth/ensureApprovedEmails.js +20 -0
  33. package/dist/infrastructure/auth/ensureApprovedEmails.js.map +1 -0
  34. package/dist/infrastructure/env.d.ts +11 -0
  35. package/dist/infrastructure/env.d.ts.map +1 -0
  36. package/dist/infrastructure/env.js +20 -0
  37. package/dist/infrastructure/env.js.map +1 -0
  38. package/dist/infrastructure/fs/FsBatchPlanRepository.d.ts +300 -0
  39. package/dist/infrastructure/fs/FsBatchPlanRepository.d.ts.map +1 -0
  40. package/dist/infrastructure/fs/FsBatchPlanRepository.js +42 -0
  41. package/dist/infrastructure/fs/FsBatchPlanRepository.js.map +1 -0
  42. package/dist/infrastructure/fs/FsClarifyTicketRepository.d.ts.map +1 -1
  43. package/dist/infrastructure/fs/FsClarifyTicketRepository.js +7 -1
  44. package/dist/infrastructure/fs/FsClarifyTicketRepository.js.map +1 -1
  45. package/dist/infrastructure/fs/FsGraphSerializer.d.ts +629 -0
  46. package/dist/infrastructure/fs/FsGraphSerializer.d.ts.map +1 -0
  47. package/dist/infrastructure/fs/FsGraphSerializer.js +60 -0
  48. package/dist/infrastructure/fs/FsGraphSerializer.js.map +1 -0
  49. package/dist/infrastructure/fs/FsProposalRepository.d.ts.map +1 -1
  50. package/dist/infrastructure/fs/FsProposalRepository.js +8 -0
  51. package/dist/infrastructure/fs/FsProposalRepository.js.map +1 -1
  52. package/dist/infrastructure/fs/FsRunRepository.d.ts +4 -1
  53. package/dist/infrastructure/fs/FsRunRepository.d.ts.map +1 -1
  54. package/dist/infrastructure/fs/FsRunRepository.js +75 -3
  55. package/dist/infrastructure/fs/FsRunRepository.js.map +1 -1
  56. package/dist/infrastructure/fs/FsSkillRegistry.d.ts.map +1 -1
  57. package/dist/infrastructure/fs/FsSkillRegistry.js +15 -3
  58. package/dist/infrastructure/fs/FsSkillRegistry.js.map +1 -1
  59. package/dist/infrastructure/fs/FsSourceUnitDigest.d.ts +19 -0
  60. package/dist/infrastructure/fs/FsSourceUnitDigest.d.ts.map +1 -0
  61. package/dist/infrastructure/fs/FsSourceUnitDigest.js +58 -0
  62. package/dist/infrastructure/fs/FsSourceUnitDigest.js.map +1 -0
  63. package/dist/infrastructure/fs/FsSourceUnitStateRepository.d.ts +30 -0
  64. package/dist/infrastructure/fs/FsSourceUnitStateRepository.d.ts.map +1 -0
  65. package/dist/infrastructure/fs/FsSourceUnitStateRepository.js +87 -0
  66. package/dist/infrastructure/fs/FsSourceUnitStateRepository.js.map +1 -0
  67. package/dist/infrastructure/fs/FsWorkspaceRepository.d.ts.map +1 -1
  68. package/dist/infrastructure/fs/FsWorkspaceRepository.js +12 -3
  69. package/dist/infrastructure/fs/FsWorkspaceRepository.js.map +1 -1
  70. package/dist/infrastructure/fs/WorkspaceDiscovery.d.ts +15 -0
  71. package/dist/infrastructure/fs/WorkspaceDiscovery.d.ts.map +1 -0
  72. package/dist/infrastructure/fs/WorkspaceDiscovery.js +46 -0
  73. package/dist/infrastructure/fs/WorkspaceDiscovery.js.map +1 -0
  74. package/dist/infrastructure/fs/WorkspaceRegistryFile.d.ts +26 -2
  75. package/dist/infrastructure/fs/WorkspaceRegistryFile.d.ts.map +1 -1
  76. package/dist/infrastructure/fs/WorkspaceRegistryFile.js +109 -5
  77. package/dist/infrastructure/fs/WorkspaceRegistryFile.js.map +1 -1
  78. package/dist/infrastructure/fs/intentScan.d.ts +9 -0
  79. package/dist/infrastructure/fs/intentScan.d.ts.map +1 -0
  80. package/dist/infrastructure/fs/intentScan.js +135 -0
  81. package/dist/infrastructure/fs/intentScan.js.map +1 -0
  82. package/dist/infrastructure/fs/paths.d.ts +14 -0
  83. package/dist/infrastructure/fs/paths.d.ts.map +1 -1
  84. package/dist/infrastructure/fs/paths.js +43 -0
  85. package/dist/infrastructure/fs/paths.js.map +1 -1
  86. package/dist/infrastructure/fs/productManifestWriter.d.ts +1 -1
  87. package/dist/infrastructure/fs/productManifestWriter.d.ts.map +1 -1
  88. package/dist/infrastructure/fs/productManifestWriter.js +1 -3
  89. package/dist/infrastructure/fs/productManifestWriter.js.map +1 -1
  90. package/dist/infrastructure/git/GitWorkspaceHistory.d.ts +18 -0
  91. package/dist/infrastructure/git/GitWorkspaceHistory.d.ts.map +1 -0
  92. package/dist/infrastructure/git/GitWorkspaceHistory.js +303 -0
  93. package/dist/infrastructure/git/GitWorkspaceHistory.js.map +1 -0
  94. package/dist/infrastructure/git/commitMessage.d.ts +4 -0
  95. package/dist/infrastructure/git/commitMessage.d.ts.map +1 -0
  96. package/dist/infrastructure/git/commitMessage.js +71 -0
  97. package/dist/infrastructure/git/commitMessage.js.map +1 -0
  98. package/dist/infrastructure/oauth/GoogleOAuth.d.ts +48 -1
  99. package/dist/infrastructure/oauth/GoogleOAuth.d.ts.map +1 -1
  100. package/dist/infrastructure/oauth/GoogleOAuth.js +68 -0
  101. package/dist/infrastructure/oauth/GoogleOAuth.js.map +1 -1
  102. package/dist/infrastructure/users/UserDirectoryFromRegistry.d.ts +14 -0
  103. package/dist/infrastructure/users/UserDirectoryFromRegistry.d.ts.map +1 -0
  104. package/dist/infrastructure/users/UserDirectoryFromRegistry.js +21 -0
  105. package/dist/infrastructure/users/UserDirectoryFromRegistry.js.map +1 -0
  106. package/dist/infrastructure/users/UserRegistryFile.d.ts +23 -0
  107. package/dist/infrastructure/users/UserRegistryFile.d.ts.map +1 -0
  108. package/dist/infrastructure/users/UserRegistryFile.js +91 -0
  109. package/dist/infrastructure/users/UserRegistryFile.js.map +1 -0
  110. package/dist/infrastructure/users/ensureLocalUser.d.ts +15 -0
  111. package/dist/infrastructure/users/ensureLocalUser.d.ts.map +1 -0
  112. package/dist/infrastructure/users/ensureLocalUser.js +38 -0
  113. package/dist/infrastructure/users/ensureLocalUser.js.map +1 -0
  114. package/dist/infrastructure/users/ensureWorkspaceOwners.d.ts +9 -0
  115. package/dist/infrastructure/users/ensureWorkspaceOwners.d.ts.map +1 -0
  116. package/dist/infrastructure/users/ensureWorkspaceOwners.js +22 -0
  117. package/dist/infrastructure/users/ensureWorkspaceOwners.js.map +1 -0
  118. package/dist/middleware/auth.d.ts +29 -0
  119. package/dist/middleware/auth.d.ts.map +1 -0
  120. package/dist/middleware/auth.js +81 -0
  121. package/dist/middleware/auth.js.map +1 -0
  122. package/dist/middleware/cors.js +1 -1
  123. package/dist/middleware/cors.js.map +1 -1
  124. package/dist/middleware/requireAdmin.d.ts +10 -0
  125. package/dist/middleware/requireAdmin.d.ts.map +1 -0
  126. package/dist/middleware/requireAdmin.js +24 -0
  127. package/dist/middleware/requireAdmin.js.map +1 -0
  128. package/dist/middleware/userId.d.ts +16 -0
  129. package/dist/middleware/userId.d.ts.map +1 -0
  130. package/dist/middleware/userId.js +27 -0
  131. package/dist/middleware/userId.js.map +1 -0
  132. package/dist/middleware/workspaceAccess.d.ts +47 -0
  133. package/dist/middleware/workspaceAccess.d.ts.map +1 -0
  134. package/dist/middleware/workspaceAccess.js +86 -0
  135. package/dist/middleware/workspaceAccess.js.map +1 -0
  136. package/dist/policy/Capability.d.ts +12 -0
  137. package/dist/policy/Capability.d.ts.map +1 -0
  138. package/dist/policy/Capability.js +31 -0
  139. package/dist/policy/Capability.js.map +1 -0
  140. package/dist/policy/CapabilityCheck.d.ts +14 -0
  141. package/dist/policy/CapabilityCheck.d.ts.map +1 -0
  142. package/dist/policy/CapabilityCheck.js +2 -0
  143. package/dist/policy/CapabilityCheck.js.map +1 -0
  144. package/dist/policy/PermissionRegistry.d.ts +23 -0
  145. package/dist/policy/PermissionRegistry.d.ts.map +1 -0
  146. package/dist/policy/PermissionRegistry.js +27 -0
  147. package/dist/policy/PermissionRegistry.js.map +1 -0
  148. package/dist/policy/ViewerContext.d.ts +27 -0
  149. package/dist/policy/ViewerContext.d.ts.map +1 -0
  150. package/dist/policy/ViewerContext.js +2 -0
  151. package/dist/policy/ViewerContext.js.map +1 -0
  152. package/dist/policy/checks.d.ts +15 -0
  153. package/dist/policy/checks.d.ts.map +1 -0
  154. package/dist/policy/checks.js +52 -0
  155. package/dist/policy/checks.js.map +1 -0
  156. package/dist/policy/defaultRegistry.d.ts +10 -0
  157. package/dist/policy/defaultRegistry.d.ts.map +1 -0
  158. package/dist/policy/defaultRegistry.js +16 -0
  159. package/dist/policy/defaultRegistry.js.map +1 -0
  160. package/dist/policy/index.d.ts +8 -0
  161. package/dist/policy/index.d.ts.map +1 -0
  162. package/dist/policy/index.js +5 -0
  163. package/dist/policy/index.js.map +1 -0
  164. package/dist/policy/resolveViewer.d.ts +16 -0
  165. package/dist/policy/resolveViewer.d.ts.map +1 -0
  166. package/dist/policy/resolveViewer.js +37 -0
  167. package/dist/policy/resolveViewer.js.map +1 -0
  168. package/dist/routes/_shared.d.ts +198 -0
  169. package/dist/routes/_shared.d.ts.map +1 -0
  170. package/dist/routes/_shared.js +29 -0
  171. package/dist/routes/_shared.js.map +1 -0
  172. package/dist/routes/admin.d.ts +13 -0
  173. package/dist/routes/admin.d.ts.map +1 -0
  174. package/dist/routes/admin.js +227 -0
  175. package/dist/routes/admin.js.map +1 -0
  176. package/dist/routes/auth.d.ts +33 -0
  177. package/dist/routes/auth.d.ts.map +1 -0
  178. package/dist/routes/auth.js +170 -0
  179. package/dist/routes/auth.js.map +1 -0
  180. package/dist/routes/batch.d.ts +7 -0
  181. package/dist/routes/batch.d.ts.map +1 -0
  182. package/dist/routes/batch.js +47 -0
  183. package/dist/routes/batch.js.map +1 -0
  184. package/dist/routes/clarify.d.ts +4 -3
  185. package/dist/routes/clarify.d.ts.map +1 -1
  186. package/dist/routes/clarify.js +247 -57
  187. package/dist/routes/clarify.js.map +1 -1
  188. package/dist/routes/decisions.d.ts +2 -2
  189. package/dist/routes/decisions.d.ts.map +1 -1
  190. package/dist/routes/decisions.js +50 -12
  191. package/dist/routes/decisions.js.map +1 -1
  192. package/dist/routes/edges.d.ts +2 -2
  193. package/dist/routes/edges.d.ts.map +1 -1
  194. package/dist/routes/edges.js +35 -13
  195. package/dist/routes/edges.js.map +1 -1
  196. package/dist/routes/health.d.ts +2 -2
  197. package/dist/routes/health.d.ts.map +1 -1
  198. package/dist/routes/health.js +21 -3
  199. package/dist/routes/health.js.map +1 -1
  200. package/dist/routes/history.d.ts +7 -0
  201. package/dist/routes/history.d.ts.map +1 -0
  202. package/dist/routes/history.js +88 -0
  203. package/dist/routes/history.js.map +1 -0
  204. package/dist/routes/model.d.ts +2 -2
  205. package/dist/routes/model.d.ts.map +1 -1
  206. package/dist/routes/model.js +20 -4
  207. package/dist/routes/model.js.map +1 -1
  208. package/dist/routes/nodes.d.ts +2 -2
  209. package/dist/routes/nodes.d.ts.map +1 -1
  210. package/dist/routes/nodes.js +75 -19
  211. package/dist/routes/nodes.js.map +1 -1
  212. package/dist/routes/ontology.d.ts +2 -2
  213. package/dist/routes/ontology.d.ts.map +1 -1
  214. package/dist/routes/ontology.js +22 -5
  215. package/dist/routes/ontology.js.map +1 -1
  216. package/dist/routes/proposals.d.ts +4 -3
  217. package/dist/routes/proposals.d.ts.map +1 -1
  218. package/dist/routes/proposals.js +170 -41
  219. package/dist/routes/proposals.js.map +1 -1
  220. package/dist/routes/runs.d.ts.map +1 -1
  221. package/dist/routes/runs.js +52 -1
  222. package/dist/routes/runs.js.map +1 -1
  223. package/dist/routes/skillInputOptions.d.ts +10 -0
  224. package/dist/routes/skillInputOptions.d.ts.map +1 -0
  225. package/dist/routes/skillInputOptions.js +161 -0
  226. package/dist/routes/skillInputOptions.js.map +1 -0
  227. package/dist/routes/skills.d.ts +2 -2
  228. package/dist/routes/skills.d.ts.map +1 -1
  229. package/dist/routes/skills.js +89 -19
  230. package/dist/routes/skills.js.map +1 -1
  231. package/dist/routes/sourceLoaders.d.ts +7 -0
  232. package/dist/routes/sourceLoaders.d.ts.map +1 -0
  233. package/dist/routes/sourceLoaders.js +29 -0
  234. package/dist/routes/sourceLoaders.js.map +1 -0
  235. package/dist/routes/sourceUnitStates.d.ts +7 -0
  236. package/dist/routes/sourceUnitStates.d.ts.map +1 -0
  237. package/dist/routes/sourceUnitStates.js +45 -0
  238. package/dist/routes/sourceUnitStates.js.map +1 -0
  239. package/dist/routes/users.d.ts +9 -0
  240. package/dist/routes/users.d.ts.map +1 -0
  241. package/dist/routes/users.js +127 -0
  242. package/dist/routes/users.js.map +1 -0
  243. package/dist/routes/workspaceMembers.d.ts +11 -0
  244. package/dist/routes/workspaceMembers.d.ts.map +1 -0
  245. package/dist/routes/workspaceMembers.js +81 -0
  246. package/dist/routes/workspaceMembers.js.map +1 -0
  247. package/dist/routes/workspaces.d.ts +19 -1
  248. package/dist/routes/workspaces.d.ts.map +1 -1
  249. package/dist/routes/workspaces.js +161 -45
  250. package/dist/routes/workspaces.js.map +1 -1
  251. package/dist/server.js +69 -25
  252. package/dist/server.js.map +1 -1
  253. package/package.json +48 -31
  254. package/LICENSE +0 -21
  255. package/dist/infrastructure/agent/ClaudeCodeAgentBinding.d.ts +0 -8
  256. package/dist/infrastructure/agent/ClaudeCodeAgentBinding.d.ts.map +0 -1
  257. package/dist/infrastructure/agent/ClaudeCodeAgentBinding.js +0 -62
  258. package/dist/infrastructure/agent/ClaudeCodeAgentBinding.js.map +0 -1
@@ -0,0 +1,227 @@
1
+ import { NotFoundError } from '@braidhq/core';
2
+ import { ServerRole, User, UserId, WorkspaceId, WorkspaceRole } from '@braidhq/schema';
3
+ import { createRoute, OpenAPIHono, z } from '@hono/zod-openapi';
4
+ import { requireAdmin } from '../middleware/requireAdmin.js';
5
+ import { getUserId } from '../middleware/userId.js';
6
+ import { NotFoundResponse, ValidationFailureResponse } from './_shared.js';
7
+ const Invite = z.object({
8
+ email: z.string().email(),
9
+ invitedAt: z.string().datetime({ offset: true }),
10
+ serverRole: ServerRole,
11
+ }).openapi('Invite');
12
+ const InviteListResponse = z.object({
13
+ items: z.array(Invite),
14
+ }).openapi('InviteListResponse');
15
+ const InviteDraft = z.object({
16
+ email: z.string().email(),
17
+ serverRole: ServerRole.optional(),
18
+ }).openapi('InviteDraft');
19
+ const EmailParam = z.object({
20
+ email: z.string().email().openapi({ param: { name: 'email', in: 'path' } }),
21
+ });
22
+ const UserIdParam = z.object({
23
+ userId: UserId.openapi({ param: { name: 'userId', in: 'path' } }),
24
+ });
25
+ const AdminUserPatch = z.object({
26
+ serverRole: ServerRole,
27
+ }).openapi('AdminUserPatch');
28
+ const AdminUserWorkspace = z.object({
29
+ workspaceId: WorkspaceId,
30
+ role: WorkspaceRole,
31
+ }).openapi('AdminUserWorkspace');
32
+ const AdminUserView = User.extend({
33
+ workspaces: z.array(AdminUserWorkspace),
34
+ }).openapi('AdminUserView');
35
+ const UserListResponse = z.object({
36
+ items: z.array(AdminUserView),
37
+ }).openapi('AdminUserListResponse');
38
+ const listInvitesRoute = createRoute({
39
+ method: 'get',
40
+ path: '/invites',
41
+ operationId: 'listInvites',
42
+ summary: 'List pending invites. Admin only.',
43
+ tags: ['admin'],
44
+ responses: {
45
+ 200: {
46
+ description: 'A list of invites.',
47
+ content: { 'application/json': { schema: InviteListResponse } },
48
+ },
49
+ 403: {
50
+ description: 'Caller is not an admin.',
51
+ content: { 'application/problem+json': { schema: z.object({}).passthrough() } },
52
+ },
53
+ },
54
+ });
55
+ const addInviteRoute = createRoute({
56
+ method: 'post',
57
+ path: '/invites',
58
+ operationId: 'addInvite',
59
+ summary: 'Invite an email to sign in via Google OAuth. Admin only.',
60
+ tags: ['admin'],
61
+ request: { body: { content: { 'application/json': { schema: InviteDraft } } } },
62
+ responses: {
63
+ 201: {
64
+ description: 'The created invite.',
65
+ content: { 'application/json': { schema: Invite } },
66
+ },
67
+ 400: ValidationFailureResponse,
68
+ 403: {
69
+ description: 'Caller is not an admin.',
70
+ content: { 'application/problem+json': { schema: z.object({}).passthrough() } },
71
+ },
72
+ },
73
+ });
74
+ const revokeInviteRoute = createRoute({
75
+ method: 'delete',
76
+ path: '/invites/{email}',
77
+ operationId: 'revokeInvite',
78
+ summary: 'Revoke a pending invite. Admin only. Idempotent.',
79
+ tags: ['admin'],
80
+ request: { params: EmailParam },
81
+ responses: {
82
+ 204: { description: 'Invite removed (or never existed).' },
83
+ 403: {
84
+ description: 'Caller is not an admin.',
85
+ content: { 'application/problem+json': { schema: z.object({}).passthrough() } },
86
+ },
87
+ },
88
+ });
89
+ const listUsersRoute = createRoute({
90
+ method: 'get',
91
+ path: '/users',
92
+ operationId: 'listUsersAdmin',
93
+ summary: 'List all users on this server. Admin only.',
94
+ tags: ['admin'],
95
+ responses: {
96
+ 200: {
97
+ description: 'A list of users.',
98
+ content: { 'application/json': { schema: UserListResponse } },
99
+ },
100
+ 403: {
101
+ description: 'Caller is not an admin.',
102
+ content: { 'application/problem+json': { schema: z.object({}).passthrough() } },
103
+ },
104
+ },
105
+ });
106
+ const updateUserRoute = createRoute({
107
+ method: 'patch',
108
+ path: '/users/{userId}',
109
+ operationId: 'updateUserAdmin',
110
+ summary: 'Flip a user\'s server role. Admin only.',
111
+ tags: ['admin'],
112
+ request: {
113
+ params: UserIdParam,
114
+ body: { content: { 'application/json': { schema: AdminUserPatch } } },
115
+ },
116
+ responses: {
117
+ 200: {
118
+ description: 'The updated user.',
119
+ content: { 'application/json': { schema: User } },
120
+ },
121
+ 404: NotFoundResponse,
122
+ 403: {
123
+ description: 'Caller is not an admin.',
124
+ content: { 'application/problem+json': { schema: z.object({}).passthrough() } },
125
+ },
126
+ },
127
+ });
128
+ const deleteUserRoute = createRoute({
129
+ method: 'delete',
130
+ path: '/users/{userId}',
131
+ operationId: 'deleteUserAdmin',
132
+ summary: 'Delete a user record. Admin only. Idempotent. Does NOT clean up workspace memberships referencing this userId; those rows become orphans.',
133
+ tags: ['admin'],
134
+ request: { params: UserIdParam },
135
+ responses: {
136
+ 204: { description: 'User removed (or never existed).' },
137
+ 400: {
138
+ description: 'Caller attempted to delete themselves.',
139
+ content: { 'application/problem+json': { schema: z.object({}).passthrough() } },
140
+ },
141
+ 403: {
142
+ description: 'Caller is not an admin.',
143
+ content: { 'application/problem+json': { schema: z.object({}).passthrough() } },
144
+ },
145
+ },
146
+ });
147
+ export function createAdminRouter(deps) {
148
+ const router = new OpenAPIHono();
149
+ router.use('*', requireAdmin(deps.userRegistry));
150
+ router.openapi(listInvitesRoute, async (context) => {
151
+ const items = await deps.accessPolicy.listInvites();
152
+ return context.json({ items }, 200);
153
+ });
154
+ router.openapi(addInviteRoute, async (context) => {
155
+ const draft = context.req.valid('json');
156
+ const created = await deps.accessPolicy.addInvite({
157
+ email: draft.email,
158
+ ...(draft.serverRole ? { serverRole: draft.serverRole } : {}),
159
+ });
160
+ return context.json(created, 201);
161
+ });
162
+ router.openapi(revokeInviteRoute, async (context) => {
163
+ const { email } = context.req.valid('param');
164
+ await deps.accessPolicy.removeInvite(email);
165
+ return context.body(null, 204);
166
+ });
167
+ router.openapi(listUsersRoute, async (context) => {
168
+ const [users, workspaces, entries] = await Promise.all([
169
+ deps.userRegistry.list(),
170
+ deps.workspaceService.list(),
171
+ deps.workspaceRegistry.listAllWithMembers(),
172
+ ]);
173
+ // Build rootPath → workspaceId so we can attach a human id to each
174
+ // membership without exposing rootPath to the client.
175
+ const idByRoot = new Map(workspaces.map(w => [w.rootPath, w.id]));
176
+ const membershipByUser = new Map();
177
+ for (const entry of entries) {
178
+ const workspaceId = idByRoot.get(entry.rootPath);
179
+ if (!workspaceId)
180
+ continue;
181
+ for (const member of entry.members) {
182
+ const list = membershipByUser.get(member.userId) ?? [];
183
+ list.push({ workspaceId, role: member.role });
184
+ membershipByUser.set(member.userId, list);
185
+ }
186
+ }
187
+ const items = users.map(u => ({
188
+ ...u,
189
+ workspaces: membershipByUser.get(u.id) ?? [],
190
+ }));
191
+ return context.json({ items }, 200);
192
+ });
193
+ router.openapi(updateUserRoute, async (context) => {
194
+ const { userId } = context.req.valid('param');
195
+ const patch = context.req.valid('json');
196
+ const existing = await deps.userRegistry.get(userId);
197
+ if (!existing)
198
+ throw new NotFoundError(`User "${userId}" not found`);
199
+ const updated = await deps.userRegistry.update(userId, { serverRole: patch.serverRole });
200
+ return context.json(updated, 200);
201
+ });
202
+ router.openapi(deleteUserRoute, async (context) => {
203
+ const { userId } = context.req.valid('param');
204
+ const callerId = getUserId(context);
205
+ // Don't let an admin delete themselves; a single-admin server
206
+ // would lock itself out otherwise.
207
+ if (userId === callerId) {
208
+ return context.json({
209
+ type: 'about:blank',
210
+ title: 'Bad Request',
211
+ status: 400,
212
+ detail: 'You cannot delete your own user record.',
213
+ }, 400, { 'Content-Type': 'application/problem+json' });
214
+ }
215
+ // Revoke persistent sign-in approval alongside the user record so
216
+ // the same email can't sneak back in next time they hit /auth/google.
217
+ // (They'd need a fresh invite from an admin.) Read the user first to
218
+ // grab their email; tolerated when missing for idempotency.
219
+ const existing = await deps.userRegistry.get(userId);
220
+ if (existing?.email)
221
+ await deps.accessPolicy.revokeApproval(existing.email);
222
+ await deps.userRegistry.delete(userId);
223
+ return context.body(null, 204);
224
+ });
225
+ return router;
226
+ }
227
+ //# sourceMappingURL=admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/routes/admin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { Clock } from '@braidhq/core';
2
+ import type { AccessPolicy } from '../infrastructure/auth/AccessPolicy.js';
3
+ import type { SessionStore } from '../infrastructure/auth/SessionStore.js';
4
+ import type { GoogleOAuth } from '../infrastructure/oauth/GoogleOAuth.js';
5
+ import type { UserRegistryFile } from '../infrastructure/users/UserRegistryFile.js';
6
+ import { Hono } from 'hono';
7
+ export interface AuthRouterDeps {
8
+ clock: Clock;
9
+ sessionStore: SessionStore;
10
+ accessPolicy: AccessPolicy;
11
+ userRegistry: UserRegistryFile;
12
+ /**
13
+ * When undefined, `/auth/google/*` respond with 503 so the operator
14
+ * knows the server is missing OAuth env config rather than failing
15
+ * silently mid-flow.
16
+ */
17
+ googleOAuth?: GoogleOAuth;
18
+ /**
19
+ * Where the Studio bundle is served from. Login redirects land here
20
+ * with `#token=...` in the fragment; Studio reads, stores, navigates.
21
+ * Defaults to the Vite dev server origin when unset.
22
+ */
23
+ studioUrl: string;
24
+ /**
25
+ * Mirrored into `/auth/config.requiresAuth` so the Studio knows
26
+ * whether to gate behind the Login page. `true` means anonymous
27
+ * sessions fall through to `local-user` — fine for embedded
28
+ * sidecar / dev; `false` means the Login page is mandatory.
29
+ */
30
+ localTrust: boolean;
31
+ }
32
+ export declare function createAuthRouter(deps: AuthRouterDeps): Hono;
33
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAG1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AAC1E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6CAA6C,CAAA;AAEnF,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAY3B,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,KAAK,CAAA;IACZ,YAAY,EAAE,YAAY,CAAA;IAC1B,YAAY,EAAE,YAAY,CAAA;IAC1B,YAAY,EAAE,gBAAgB,CAAA;IAC9B;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,UAAU,EAAE,OAAO,CAAA;CACpB;AAaD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CA4J3D"}
@@ -0,0 +1,170 @@
1
+ import { newUserId, NotFoundError } from '@braidhq/core';
2
+ import { Hono } from 'hono';
3
+ import { z } from 'zod';
4
+ import { createOAuthState, createPkceVerifier } from '../infrastructure/oauth/GoogleOAuth.js';
5
+ const PENDING_TTL_MS = 10 * 60 * 1000;
6
+ const StartQuery = z.object({
7
+ returnTo: z.string().url().optional(),
8
+ });
9
+ const CallbackQuery = z.object({
10
+ code: z.string().min(1).optional(),
11
+ state: z.string().min(1).optional(),
12
+ error: z.string().optional(),
13
+ error_description: z.string().optional(),
14
+ });
15
+ export function createAuthRouter(deps) {
16
+ const router = new Hono();
17
+ // In-memory pending flows. A single-process server is the only
18
+ // supported topology in v0.2; HA / multi-process would need this in
19
+ // a shared store, but that's well past current scope.
20
+ const pending = new Map();
21
+ function reapExpired() {
22
+ const cutoff = Date.now() - PENDING_TTL_MS;
23
+ for (const [state, flow] of pending) {
24
+ if (flow.createdAt < cutoff)
25
+ pending.delete(state);
26
+ }
27
+ }
28
+ router.get('/google/start', async (context) => {
29
+ if (!deps.googleOAuth) {
30
+ return context.json({
31
+ title: 'Google sign-in unavailable',
32
+ detail: 'Server is missing BRAID_GOOGLE_CLIENT_ID / BRAID_GOOGLE_CLIENT_SECRET; ask the admin to configure them.',
33
+ }, 503);
34
+ }
35
+ reapExpired();
36
+ const parsed = StartQuery.safeParse({
37
+ returnTo: context.req.query('returnTo'),
38
+ });
39
+ if (!parsed.success)
40
+ return context.json({ title: 'Invalid returnTo' }, 400);
41
+ const state = createOAuthState();
42
+ const codeVerifier = createPkceVerifier();
43
+ pending.set(state, {
44
+ codeVerifier,
45
+ returnTo: parsed.data.returnTo ?? deps.studioUrl,
46
+ createdAt: Date.now(),
47
+ });
48
+ const url = deps.googleOAuth.buildLoginUrl({ state, codeVerifier });
49
+ return context.json({ authorizationUrl: url });
50
+ });
51
+ router.get('/google/callback', async (context) => {
52
+ if (!deps.googleOAuth)
53
+ return context.text('Google sign-in is not configured on this server.', 503);
54
+ const parsed = CallbackQuery.safeParse({
55
+ code: context.req.query('code'),
56
+ state: context.req.query('state'),
57
+ error: context.req.query('error'),
58
+ error_description: context.req.query('error_description'),
59
+ });
60
+ if (!parsed.success)
61
+ return context.text('Invalid callback parameters.', 400);
62
+ const { code, state, error, error_description: errorDescription } = parsed.data;
63
+ if (error || !code || !state)
64
+ return redirectWithError(context, deps.studioUrl, error_description_or_default(error, errorDescription));
65
+ const flow = pending.get(state);
66
+ if (!flow)
67
+ return redirectWithError(context, deps.studioUrl, 'Session expired before sign-in completed. Try again.');
68
+ pending.delete(state);
69
+ let profile;
70
+ try {
71
+ profile = await deps.googleOAuth.loginWithCode({ code, codeVerifier: flow.codeVerifier });
72
+ }
73
+ catch (err) {
74
+ return redirectWithError(context, flow.returnTo, err instanceof Error ? err.message : String(err));
75
+ }
76
+ const decision = await deps.accessPolicy.decide(profile.email);
77
+ if (!decision.allow)
78
+ return redirectWithError(context, flow.returnTo, decision.reason ?? 'Not authorized.');
79
+ // Resolve / create the user record. Google `sub` is stable across
80
+ // email changes; prefer it as the join key, fall back to email
81
+ // for invited users who haven't logged in before.
82
+ const isAdmin = deps.accessPolicy.isAdmin(profile.email);
83
+ let user = await deps.userRegistry.getByGoogleSub(profile.sub);
84
+ if (!user) {
85
+ const inviteRole = decision.viaInvite?.serverRole;
86
+ user = await deps.userRegistry.create({
87
+ id: newUserId(),
88
+ googleSub: profile.sub,
89
+ email: profile.email,
90
+ displayName: profile.displayName,
91
+ serverRole: isAdmin ? 'admin' : inviteRole ?? 'user',
92
+ createdAt: deps.clock.now(),
93
+ });
94
+ if (decision.viaInvite)
95
+ await deps.accessPolicy.consumeInvite(profile.email);
96
+ }
97
+ else if (isAdmin && user.serverRole !== 'admin') {
98
+ // BRAID_ADMIN_EMAILS was edited after this user was created.
99
+ // Promote on next login so re-deploying with new admin emails
100
+ // doesn't require manual user-table surgery.
101
+ user = await deps.userRegistry.update(user.id, { serverRole: 'admin' });
102
+ }
103
+ const session = await deps.sessionStore.issue(user.id);
104
+ const target = `${flow.returnTo}#token=${encodeURIComponent(session.token)}`;
105
+ return context.redirect(target, 302);
106
+ });
107
+ router.post('/logout', async (context) => {
108
+ const header = context.req.header('Authorization');
109
+ const token = header?.startsWith('Bearer ') ? header.slice('Bearer '.length).trim() : null;
110
+ if (token)
111
+ await deps.sessionStore.revoke(token);
112
+ return context.body(null, 204);
113
+ });
114
+ router.get('/whoami', async (context) => {
115
+ // `/auth/*` is in the auth middleware's PUBLIC_PREFIXES, so the
116
+ // Bearer header isn't validated upstream. Whoami exists precisely
117
+ // to identify the caller, so resolve the Bearer token here
118
+ // directly rather than trusting `userIdMiddleware`'s local-user
119
+ // fallback. Absent / invalid token → `{ user: null }`.
120
+ const header = context.req.header('Authorization');
121
+ const token = header?.startsWith('Bearer ') ? header.slice('Bearer '.length).trim() : null;
122
+ let userId;
123
+ if (token) {
124
+ const session = await deps.sessionStore.resolve(token);
125
+ userId = session?.userId;
126
+ }
127
+ // Local-trust deployments don't issue tokens — fall back to the
128
+ // context userId so /whoami still works there. authMiddleware
129
+ // wouldn't have rejected the request anyway.
130
+ if (!userId && deps.localTrust)
131
+ userId = context.get('userId');
132
+ if (!userId)
133
+ return context.json({ user: null });
134
+ try {
135
+ const user = await deps.userRegistry.get(userId);
136
+ return context.json({ user: user ?? null });
137
+ }
138
+ catch (err) {
139
+ if (err instanceof NotFoundError)
140
+ return context.json({ user: null });
141
+ throw err;
142
+ }
143
+ });
144
+ // Hint at the configured studio URL + auth mode so the Login page
145
+ // can decide whether to gate at all (local trust skips), and
146
+ // whether Google is actually configured (otherwise the Sign-in
147
+ // button leads to a 503).
148
+ router.get('/config', (context) => {
149
+ return context.json({
150
+ googleEnabled: deps.googleOAuth !== undefined,
151
+ studioUrl: deps.studioUrl,
152
+ requiresAuth: !deps.localTrust,
153
+ });
154
+ });
155
+ return router;
156
+ }
157
+ function redirectWithError(context, target, reason) {
158
+ // Use a clearly named hash key so the Studio side knows to pull the
159
+ // error out and surface it on the Login page.
160
+ const url = `${target}#auth-error=${encodeURIComponent(reason)}`;
161
+ return context.redirect(url, 302);
162
+ }
163
+ function error_description_or_default(error, description) {
164
+ if (description && description.length > 0)
165
+ return description;
166
+ if (error && error.length > 0)
167
+ return error;
168
+ return 'Google sign-in failed.';
169
+ }
170
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { BatchService } from '@braidhq/core';
2
+ import { Hono } from 'hono';
3
+ export interface BatchRouterDeps {
4
+ batchService: BatchService;
5
+ }
6
+ export declare function createBatchRouter(deps: BatchRouterDeps): Hono;
7
+ //# sourceMappingURL=batch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../../src/routes/batch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAGjD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAS3B,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,YAAY,CAAA;CAC3B;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CA0C7D"}
@@ -0,0 +1,47 @@
1
+ import { NotFoundError } from '@braidhq/core';
2
+ import { zValidator } from '@hono/zod-validator';
3
+ import { Hono } from 'hono';
4
+ import { z } from 'zod';
5
+ import { extractBearerToken } from '../middleware/auth.js';
6
+ import { getWorkspaceId } from '../middleware/workspaceId.js';
7
+ const StartBody = z.object({
8
+ autoApply: z.boolean(),
9
+ });
10
+ export function createBatchRouter(deps) {
11
+ const router = new Hono();
12
+ router.post('/', zValidator('json', StartBody), async (context) => {
13
+ const workspaceId = getWorkspaceId(context);
14
+ const { autoApply } = context.req.valid('json');
15
+ const callerToken = extractBearerToken(context);
16
+ const plan = await deps.batchService.start(workspaceId, {
17
+ autoApply,
18
+ ...(callerToken ? { callerToken } : {}),
19
+ });
20
+ return context.json(plan.toData(), 202);
21
+ });
22
+ router.get('/', async (context) => {
23
+ const workspaceId = getWorkspaceId(context);
24
+ const plan = await deps.batchService.getStatus(workspaceId);
25
+ if (!plan)
26
+ throw new NotFoundError(`No batch plan for workspace ${workspaceId}`);
27
+ return context.json(plan.toData());
28
+ });
29
+ router.post('/stop', async (context) => {
30
+ const workspaceId = getWorkspaceId(context);
31
+ await deps.batchService.stop(workspaceId);
32
+ return context.body(null, 204);
33
+ });
34
+ router.post('/resume', async (context) => {
35
+ const workspaceId = getWorkspaceId(context);
36
+ const callerToken = extractBearerToken(context);
37
+ const plan = await deps.batchService.resume(workspaceId, callerToken ? { callerToken } : {});
38
+ return context.json(plan.toData(), 202);
39
+ });
40
+ router.post('/archive', async (context) => {
41
+ const workspaceId = getWorkspaceId(context);
42
+ const plan = await deps.batchService.archive(workspaceId);
43
+ return context.json(plan.toData());
44
+ });
45
+ return router;
46
+ }
47
+ //# sourceMappingURL=batch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch.js","sourceRoot":"","sources":["../../src/routes/batch.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,9 @@
1
- import type { ClarifyTicketRepository, HITLService } from '@braidhq/core';
2
- import { Hono } from 'hono';
1
+ import type { ClarifyTicketRepository, DecisionRepository, HITLService } from '@braidhq/core';
2
+ import { OpenAPIHono } from '@hono/zod-openapi';
3
3
  export interface ClarifyRouterDeps {
4
4
  hitlService: HITLService;
5
5
  clarifyRepository: ClarifyTicketRepository;
6
+ decisionRepository: DecisionRepository;
6
7
  }
7
- export declare function createClarifyRouter(deps: ClarifyRouterDeps): Hono;
8
+ export declare function createClarifyRouter(deps: ClarifyRouterDeps): OpenAPIHono;
8
9
  //# sourceMappingURL=clarify.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"clarify.d.ts","sourceRoot":"","sources":["../../src/routes/clarify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAGzE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AA4B3B,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,WAAW,CAAA;IACxB,iBAAiB,EAAE,uBAAuB,CAAA;CAC3C;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CA6EjE"}
1
+ {"version":3,"file":"clarify.d.ts","sourceRoot":"","sources":["../../src/routes/clarify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAI7F,OAAO,EAAe,WAAW,EAAK,MAAM,mBAAmB,CAAA;AAwF/D,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,WAAW,CAAA;IACxB,iBAAiB,EAAE,uBAAuB,CAAA;IAC1C,kBAAkB,EAAE,kBAAkB,CAAA;CACvC;AAiHD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,WAAW,CAsGxE"}