@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,303 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { writeFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { CommitSha, Timestamp, WorkspaceId } from '@braidhq/schema';
5
+ import { simpleGit } from 'simple-git';
6
+ import { GRAPH_JSON_VERSION, GraphJsonFile } from '../fs/FsGraphSerializer.js';
7
+ import { parseCommitMessage, serializeCommitMessage } from './commitMessage.js';
8
+ // ASCII control chars never appear in commit subjects / refnames.
9
+ const LOG_FIELD_SEPARATOR = '';
10
+ const LOG_RECORD_SEPARATOR = '';
11
+ const GIT_LOG_FORMAT_ARG = `--pretty=tformat:%H${LOG_FIELD_SEPARATOR}%an${LOG_FIELD_SEPARATOR}%ae${LOG_FIELD_SEPARATOR}%aI${LOG_FIELD_SEPARATOR}%P${LOG_FIELD_SEPARATOR}%B${LOG_RECORD_SEPARATOR}`;
12
+ const TAG_RECORD_SEPARATOR = '';
13
+ const TAG_FIELD_SEPARATOR = '';
14
+ // `%(*objectname)` dereferences annotated tags to the underlying commit.
15
+ const TAG_FORMAT = [
16
+ '%(refname:short)',
17
+ '%(if)%(*objectname)%(then)%(*objectname)%(else)%(objectname)%(end)',
18
+ '%(taggerdate:iso-strict)',
19
+ '%(creatordate:iso-strict)',
20
+ '%(taggername)',
21
+ '%(taggeremail)',
22
+ '%(contents:subject)',
23
+ ].join(TAG_FIELD_SEPARATOR) + TAG_RECORD_SEPARATOR;
24
+ // Intent docs + codebases are re-fetchable; tracking them would nest foreign `.git/` dirs and bloat history.
25
+ const DEFAULT_GITIGNORE = `# Braid auto-generated.
26
+
27
+ .braid/
28
+ .braid-sessions/
29
+ .braid-mcp-*.json
30
+ artifacts/runs/
31
+ intents/
32
+ codebases/
33
+ .DS_Store
34
+ `;
35
+ // Swapped for the calling user's identity once Theme 13 lands.
36
+ const BOOTSTRAP_USER_ID = 'braid-bootstrap';
37
+ const INITIAL_COMMIT = {
38
+ kind: 'initial',
39
+ subject: 'workspace registered',
40
+ userId: BOOTSTRAP_USER_ID,
41
+ };
42
+ // `core.quotePath=false` keeps non-ASCII paths intact in log / name-status output.
43
+ function openGit(baseDir) {
44
+ return simpleGit({
45
+ baseDir,
46
+ config: ['core.quotePath=false', 'i18n.logoutputencoding=utf-8'],
47
+ });
48
+ }
49
+ export class GitWorkspaceHistory {
50
+ async ensureInitialised(workspace) {
51
+ const root = workspace.rootPath;
52
+ if (existsSync(join(root, '.git')))
53
+ return;
54
+ const git = openGit(root);
55
+ await git.init();
56
+ const gitignorePath = join(root, '.gitignore');
57
+ if (!existsSync(gitignorePath))
58
+ await writeFile(gitignorePath, DEFAULT_GITIGNORE, 'utf-8');
59
+ await git.add(['-A']);
60
+ // Allow empty when the workspace dir has nothing tracked yet so HEAD still exists for later commits to parent on.
61
+ const allowEmpty = !(await this.hasPendingChanges(git));
62
+ await this.commitWithMeta(git, INITIAL_COMMIT, { allowEmpty });
63
+ }
64
+ async commit(workspace, message) {
65
+ const git = openGit(workspace.rootPath);
66
+ await git.add(['-A']);
67
+ // Redundant commit attempts (e.g. reject after the artifact was already persisted) resolve to a no-op.
68
+ if (!(await this.hasPendingChanges(git))) {
69
+ const head = await git.revparse(['HEAD']);
70
+ return CommitSha.parse(head.trim());
71
+ }
72
+ return this.commitWithMeta(git, message, { allowEmpty: false });
73
+ }
74
+ async listCommits(workspace, options) {
75
+ const git = openGit(workspace.rootPath);
76
+ const limit = options?.limit ?? 50;
77
+ const args = ['log', `--max-count=${limit}`, GIT_LOG_FORMAT_ARG];
78
+ if (options?.since)
79
+ args.splice(1, 0, `${options.since}^`);
80
+ const raw = await git.raw(args).catch((err) => {
81
+ // Fresh repo with no commits → `git log` exits non-zero; treat as empty.
82
+ if (looksLikeNoCommits(err))
83
+ return '';
84
+ throw err;
85
+ });
86
+ if (!raw.trim())
87
+ return [];
88
+ return splitLogRecords(raw).map(rec => parseLogRecord(rec, workspace.id));
89
+ }
90
+ async getCommit(workspace, sha) {
91
+ const git = openGit(workspace.rootPath);
92
+ try {
93
+ const raw = await git.raw(['show', '--no-patch', GIT_LOG_FORMAT_ARG, sha]);
94
+ if (!raw.trim())
95
+ return null;
96
+ return parseLogRecord(raw.trim(), workspace.id);
97
+ }
98
+ catch (err) {
99
+ if (looksLikeUnknownRevision(err))
100
+ return null;
101
+ throw err;
102
+ }
103
+ }
104
+ async readGraphAtCommit(workspace, sha) {
105
+ const git = openGit(workspace.rootPath);
106
+ // Pre-bootstrap commits don't carry `graph.json`; empty snapshot lets diffs classify everything as `added`.
107
+ const raw = await git.raw(['show', `${sha}:artifacts/graph.json`]).catch((err) => {
108
+ if (looksLikePathNotInCommit(err) || looksLikeUnknownRevision(err))
109
+ return '';
110
+ throw err;
111
+ });
112
+ if (!raw.trim())
113
+ return { nodes: [], edges: [] };
114
+ const parsed = GraphJsonFile.parse(JSON.parse(raw));
115
+ if (parsed.version !== GRAPH_JSON_VERSION) {
116
+ throw new Error(`graph.json version mismatch at ${sha}: expected ${GRAPH_JSON_VERSION}, got ${parsed.version}`);
117
+ }
118
+ return { nodes: parsed.nodes, edges: parsed.edges };
119
+ }
120
+ async getCommitDiff(workspace, sha) {
121
+ const git = openGit(workspace.rootPath);
122
+ // `--root` keeps the root commit diffable against the empty tree instead of erroring.
123
+ const raw = await git.raw(['show', '--name-status', '--root', '--format=', sha]).catch((err) => {
124
+ if (looksLikeUnknownRevision(err))
125
+ return '';
126
+ throw err;
127
+ });
128
+ return parseNameStatus(raw);
129
+ }
130
+ async restore(workspace, targetSha, message) {
131
+ const git = openGit(workspace.rootPath);
132
+ const headRaw = await git.revparse(['HEAD']);
133
+ const head = CommitSha.parse(headRaw.trim());
134
+ if (head === targetSha)
135
+ return head;
136
+ // `checkout <sha> -- .` only restores files present in <sha>; we need an EXACT match so newer files must also go.
137
+ await git.raw(['read-tree', targetSha]);
138
+ await git.raw(['checkout-index', '--force', '--all']);
139
+ await git.raw(['clean', '-fd']);
140
+ await git.add(['-A']);
141
+ return this.commitWithMeta(git, {
142
+ ...message,
143
+ kind: 'restore',
144
+ revertedFrom: head,
145
+ revertedTo: targetSha,
146
+ }, { allowEmpty: false });
147
+ }
148
+ async tag(workspace, sha, name, note) {
149
+ const git = openGit(workspace.rootPath);
150
+ if (note) {
151
+ // Annotated tags need a committer identity; pin inline so CI runners without global git config don't fail.
152
+ await git.raw([
153
+ '-c',
154
+ `user.name=${BOOTSTRAP_USER_ID}`,
155
+ '-c',
156
+ `user.email=${BOOTSTRAP_USER_ID}@braid.local`,
157
+ 'tag',
158
+ '-a',
159
+ name,
160
+ sha,
161
+ '-m',
162
+ note,
163
+ ]);
164
+ }
165
+ else {
166
+ await git.raw(['tag', name, sha]);
167
+ }
168
+ return readTagByName(git, name);
169
+ }
170
+ async listTags(workspace) {
171
+ const git = openGit(workspace.rootPath);
172
+ const raw = await git.raw(['tag', '-l', `--format=${TAG_FORMAT}`]).catch(() => '');
173
+ if (!raw.trim())
174
+ return [];
175
+ return raw.split(TAG_RECORD_SEPARATOR)
176
+ .map(s => s.trim())
177
+ .filter(s => s.length > 0)
178
+ .map(parseTagRecord)
179
+ .sort((a, b) => b.createdAt.localeCompare(a.createdAt));
180
+ }
181
+ async deleteTag(workspace, name) {
182
+ const git = openGit(workspace.rootPath);
183
+ await git.raw(['tag', '-d', name]);
184
+ }
185
+ async hasPendingChanges(git) {
186
+ const status = await git.status();
187
+ return status.staged.length > 0
188
+ || status.created.length > 0
189
+ || status.deleted.length > 0
190
+ || status.modified.length > 0
191
+ || status.renamed.length > 0
192
+ || status.not_added.length > 0;
193
+ }
194
+ async commitWithMeta(git, message, options) {
195
+ // `authorName` / `authorEmail` are snapshotted at commit time so
196
+ // a future rename of the user record doesn't rewrite git history.
197
+ // When absent (bootstrap, source-sync, pre-Phase-A artifacts) we
198
+ // fall back to the opaque `userId` for both — same behaviour as
199
+ // before Theme 13 landed.
200
+ const name = message.authorName ?? message.userId;
201
+ const email = message.authorEmail ?? `${message.userId}@braid.local`;
202
+ const args = ['commit', '-m', serializeCommitMessage(message), `--author=${name} <${email}>`];
203
+ if (options.allowEmpty)
204
+ args.push('--allow-empty');
205
+ await git.raw(['-c', `user.name=${name}`, '-c', `user.email=${email}`, ...args]);
206
+ const head = await git.revparse(['HEAD']);
207
+ return CommitSha.parse(head.trim());
208
+ }
209
+ }
210
+ function parseLogRecord(record, workspaceId) {
211
+ const [shaLine, authorName, authorEmail, committedAt, parents, ...bodyLines] = record.split(LOG_FIELD_SEPARATOR);
212
+ let body = bodyLines.join(LOG_FIELD_SEPARATOR);
213
+ while (body.endsWith(LOG_RECORD_SEPARATOR))
214
+ body = body.slice(0, -LOG_RECORD_SEPARATOR.length);
215
+ body = body.trim();
216
+ return {
217
+ sha: CommitSha.parse(shaLine.trim()),
218
+ workspaceId: WorkspaceId.parse(workspaceId),
219
+ message: parseCommitMessage(body),
220
+ author: { name: authorName.trim(), email: authorEmail.trim() },
221
+ committedAt: Timestamp.parse(committedAt.trim()),
222
+ parents: parents.trim()
223
+ ? parents.trim().split(' ').map(s => CommitSha.parse(s.trim()))
224
+ : [],
225
+ // Stats are derived lazily via `getCommitDiff` to avoid N extra diff calls per list page.
226
+ stats: null,
227
+ };
228
+ }
229
+ async function readTagByName(git, name) {
230
+ const raw = await git.raw(['tag', '-l', `--format=${TAG_FORMAT}`, name]);
231
+ const record = raw.split(TAG_RECORD_SEPARATOR).map(s => s.trim()).find(s => s.length > 0);
232
+ if (!record)
233
+ throw new Error(`Tag "${name}" not found immediately after creation`);
234
+ return parseTagRecord(record);
235
+ }
236
+ function parseTagRecord(record) {
237
+ const parts = record.split(TAG_FIELD_SEPARATOR);
238
+ const [name, sha, taggerDate, creatorDate, taggerName, taggerEmail, note] = parts;
239
+ const createdRaw = (taggerDate ?? '').trim() || (creatorDate ?? '').trim();
240
+ return {
241
+ name: (name ?? '').trim(),
242
+ sha: CommitSha.parse((sha ?? '').trim()),
243
+ createdAt: Timestamp.parse(createdRaw),
244
+ ...(note && note.trim() ? { note: note.trim() } : {}),
245
+ ...((taggerName?.trim() && taggerEmail?.trim())
246
+ ? {
247
+ taggedBy: {
248
+ name: taggerName.trim(),
249
+ email: taggerEmail.trim().replace(/^<|>$/g, ''),
250
+ },
251
+ }
252
+ : {}),
253
+ };
254
+ }
255
+ function parseNameStatus(raw) {
256
+ const out = [];
257
+ for (const line of raw.split('\n')) {
258
+ if (!line.trim())
259
+ continue;
260
+ const cols = line.split('\t');
261
+ const code = cols[0]?.trim() ?? '';
262
+ if (code.startsWith('R') && cols.length >= 3) {
263
+ out.push({ path: cols[2].trim(), status: 'renamed', previousPath: cols[1].trim() });
264
+ continue;
265
+ }
266
+ const path = cols[1]?.trim();
267
+ if (!path)
268
+ continue;
269
+ out.push({ path, status: statusFromCode(code[0]) });
270
+ }
271
+ return out;
272
+ }
273
+ function splitLogRecords(raw) {
274
+ return raw.split(LOG_RECORD_SEPARATOR).map(s => s.trim()).filter(s => s.length > 0);
275
+ }
276
+ // Unknown letters fall back to `modified` so the row still surfaces.
277
+ function statusFromCode(code) {
278
+ switch (code) {
279
+ case 'A':
280
+ case 'C':
281
+ return 'added';
282
+ case 'D':
283
+ return 'removed';
284
+ case 'M':
285
+ return 'modified';
286
+ default:
287
+ return 'modified';
288
+ }
289
+ }
290
+ function looksLikeNoCommits(err) {
291
+ const msg = err instanceof Error ? err.message : String(err);
292
+ return /does not have any commits yet|bad default revision/i.test(msg);
293
+ }
294
+ function looksLikeUnknownRevision(err) {
295
+ const msg = err instanceof Error ? err.message : String(err);
296
+ return /unknown revision|bad revision|ambiguous argument/i.test(msg);
297
+ }
298
+ // `git show <sha>:<path>` for an untracked path reports "exists on disk, but not in" or "does not exist in".
299
+ function looksLikePathNotInCommit(err) {
300
+ const msg = err instanceof Error ? err.message : String(err);
301
+ return /exists on disk, but not in|does not exist in|path .* does not exist/i.test(msg);
302
+ }
303
+ //# sourceMappingURL=GitWorkspaceHistory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GitWorkspaceHistory.js","sourceRoot":"","sources":["../../../src/infrastructure/git/GitWorkspaceHistory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { CommitMessage } from '@braidhq/schema';
2
+ export declare function serializeCommitMessage(message: CommitMessage): string;
3
+ export declare function parseCommitMessage(raw: string): CommitMessage;
4
+ //# sourceMappingURL=commitMessage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commitMessage.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/git/commitMessage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAcpD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAgBrE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CA6B7D"}
@@ -0,0 +1,71 @@
1
+ import { ClarifyTicketId, CommitKind, CommitSha, ProposalId, SourceId, UserId, } from '@braidhq/schema';
2
+ // Trailer keys recognised by `git interpret-trailers` so future
3
+ // tooling can lean on the same parser.
4
+ const TRAILER_KEY_PATTERN = /^[a-z][a-z-]*$/i;
5
+ export function serializeCommitMessage(message) {
6
+ const trailers = [
7
+ `Kind: ${message.kind}`,
8
+ `Author: ${message.userId}`,
9
+ ];
10
+ if (message.proposalId)
11
+ trailers.push(`Proposal-Id: ${message.proposalId}`);
12
+ if (message.clarifyTicketId)
13
+ trailers.push(`Clarify-Ticket-Id: ${message.clarifyTicketId}`);
14
+ if (message.sourceId)
15
+ trailers.push(`Source-Id: ${message.sourceId}`);
16
+ if (message.revertedFrom)
17
+ trailers.push(`Reverted-From: ${message.revertedFrom}`);
18
+ if (message.revertedTo)
19
+ trailers.push(`Reverted-To: ${message.revertedTo}`);
20
+ return `${message.kind}: ${message.subject}\n\n${trailers.join('\n')}\n`;
21
+ }
22
+ export function parseCommitMessage(raw) {
23
+ const lines = raw.split('\n');
24
+ const subjectLine = lines[0] ?? '';
25
+ const colonIdx = subjectLine.indexOf(':');
26
+ const subject = colonIdx >= 0 ? subjectLine.slice(colonIdx + 1).trim() : subjectLine.trim();
27
+ const trailers = extractTrailers(lines);
28
+ const kindRaw = trailers.Kind ?? subjectLine.slice(0, colonIdx).trim();
29
+ // Fall back to `snapshot` on unknown kinds so a manual `git commit`
30
+ // from the CLI still renders in the timeline.
31
+ const kind = CommitKind.safeParse(kindRaw).data ?? 'snapshot';
32
+ const userId = UserId.parse(trailers.Author ?? 'unknown');
33
+ const out = {
34
+ kind,
35
+ subject: subject || `(unsubject ${kind})`,
36
+ userId,
37
+ };
38
+ if (trailers['Proposal-Id'])
39
+ Object.assign(out, { proposalId: ProposalId.parse(trailers['Proposal-Id']) });
40
+ if (trailers['Clarify-Ticket-Id'])
41
+ Object.assign(out, { clarifyTicketId: ClarifyTicketId.parse(trailers['Clarify-Ticket-Id']) });
42
+ if (trailers['Source-Id'])
43
+ Object.assign(out, { sourceId: SourceId.parse(trailers['Source-Id']) });
44
+ if (trailers['Reverted-From'])
45
+ Object.assign(out, { revertedFrom: CommitSha.parse(trailers['Reverted-From']) });
46
+ if (trailers['Reverted-To'])
47
+ Object.assign(out, { revertedTo: CommitSha.parse(trailers['Reverted-To']) });
48
+ return out;
49
+ }
50
+ // indexOf-based scan instead of a single regex: avoids polynomial
51
+ // backtracking risk against adversarial commit bodies.
52
+ function extractTrailers(lines) {
53
+ const out = {};
54
+ for (let i = lines.length - 1; i >= 0; i--) {
55
+ const line = lines[i];
56
+ if (line.trim().length === 0) {
57
+ if (Object.keys(out).length > 0)
58
+ return out;
59
+ continue;
60
+ }
61
+ const colon = line.indexOf(':');
62
+ if (colon < 1)
63
+ return out;
64
+ const key = line.slice(0, colon);
65
+ if (!TRAILER_KEY_PATTERN.test(key))
66
+ return out;
67
+ out[key] = line.slice(colon + 1).trim();
68
+ }
69
+ return out;
70
+ }
71
+ //# sourceMappingURL=commitMessage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commitMessage.js","sourceRoot":"","sources":["../../../src/infrastructure/git/commitMessage.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,32 @@
1
+ /**
2
+ * Profile fields Braid needs after Google authn. `sub` is the stable
3
+ * Google account id — used as the join key into `users.json`.
4
+ */
5
+ export interface GoogleProfile {
6
+ readonly sub: string;
7
+ readonly email: string;
8
+ readonly displayName: string;
9
+ readonly emailVerified?: boolean;
10
+ }
1
11
  export interface GoogleOAuthConfig {
2
12
  /** OAuth client id from the user's GCP project. */
3
13
  readonly clientId: string;
4
14
  /** OAuth client secret. */
5
15
  readonly clientSecret: string;
6
- /** Absolute redirect URI registered in the GCP OAuth client. */
16
+ /**
17
+ * Redirect URI used by the Drive ingestion flow (existing OAuth
18
+ * router). Both paths must be registered in the GCP OAuth client's
19
+ * authorised redirect URIs list.
20
+ */
7
21
  readonly redirectUri: string;
22
+ /**
23
+ * Redirect URI used by the user-login flow. Optional — falls back
24
+ * to the Drive `redirectUri` if unset, which is what local dev
25
+ * wants (one path = one registered URI). Production usually points
26
+ * this at `${apiUrl}/auth/google/callback` so the two flows are
27
+ * mounted on different routes.
28
+ */
29
+ readonly loginRedirectUri?: string;
8
30
  }
9
31
  export interface AuthorizationUrlInput {
10
32
  /** OAuth scope(s) requested. Braid passes Drive read-only. */
@@ -46,6 +68,31 @@ export declare class GoogleOAuth {
46
68
  code: string;
47
69
  codeVerifier: string;
48
70
  }): Promise<TokenSet>;
71
+ /**
72
+ * Login flow URL. Same client + redirect as the Drive flow but with
73
+ * `openid email profile` scopes and no `prompt=consent` — Google
74
+ * suppresses the consent screen when the user has already approved
75
+ * those scopes, which is what you want for login (not Drive where
76
+ * a refresh_token is required and `prompt=consent` is forced).
77
+ */
78
+ buildLoginUrl(input: {
79
+ state: string;
80
+ codeVerifier: string;
81
+ }): string;
82
+ /** Path Google sends the login callback to. Routed under `/auth/google/callback`. */
83
+ loginRedirectUri(): string;
84
+ /**
85
+ * Exchange an authorization code from the login flow for the user's
86
+ * profile. Does NOT require `refresh_token` (login is one-shot — the
87
+ * Braid server issues its own session afterwards, so Google's tokens
88
+ * are discarded after this call). Uses the userinfo endpoint instead
89
+ * of decoding the id_token, so we don't bring in a JWT verifier just
90
+ * to read 3 fields.
91
+ */
92
+ loginWithCode(input: {
93
+ code: string;
94
+ codeVerifier: string;
95
+ }): Promise<GoogleProfile>;
49
96
  refreshAccessToken(refreshToken: string): Promise<RefreshedAccessToken>;
50
97
  }
51
98
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"GoogleOAuth.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/oauth/GoogleOAuth.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,2BAA2B;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,gEAAgE;IAChE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,2DAA2D;IAC3D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;;GAOG;AACH,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,iBAAiB;IAEtD;;;;OAIG;IACH,qBAAqB,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM;IAgBrD,YAAY,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;IA2B9E,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAsB9E;AA4BD;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC"}
1
+ {"version":3,"file":"GoogleOAuth.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/oauth/GoogleOAuth.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CACjC;AAED,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,2BAA2B;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CACnC;AAED,MAAM,WAAW,qBAAqB;IACpC,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,2DAA2D;IAC3D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;;GAOG;AACH,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,iBAAiB;IAEtD;;;;OAIG;IACH,qBAAqB,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM;IAgBrD,YAAY,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;IA2BpF;;;;;;OAMG;IACH,aAAa,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAerE,qFAAqF;IACrF,gBAAgB,IAAI,MAAM;IAI1B;;;;;;;OAOG;IACG,aAAa,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,CAAC;IA0CpF,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAsB9E;AA4BD;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC"}
@@ -1,6 +1,7 @@
1
1
  import { createHash, randomBytes } from 'node:crypto';
2
2
  const AUTH_ENDPOINT = 'https://accounts.google.com/o/oauth2/v2/auth';
3
3
  const TOKEN_ENDPOINT = 'https://oauth2.googleapis.com/token';
4
+ const USERINFO_ENDPOINT = 'https://openidconnect.googleapis.com/v1/userinfo';
4
5
  /**
5
6
  * Minimal Google OAuth 2.0 client for installed-app / web flow.
6
7
  *
@@ -60,6 +61,73 @@ export class GoogleOAuth {
60
61
  }
61
62
  return tokenSetFromPayload(payload);
62
63
  }
64
+ /**
65
+ * Login flow URL. Same client + redirect as the Drive flow but with
66
+ * `openid email profile` scopes and no `prompt=consent` — Google
67
+ * suppresses the consent screen when the user has already approved
68
+ * those scopes, which is what you want for login (not Drive where
69
+ * a refresh_token is required and `prompt=consent` is forced).
70
+ */
71
+ buildLoginUrl(input) {
72
+ const params = new URLSearchParams({
73
+ client_id: this.config.clientId,
74
+ redirect_uri: this.loginRedirectUri(),
75
+ response_type: 'code',
76
+ scope: 'openid email profile',
77
+ access_type: 'online',
78
+ include_granted_scopes: 'true',
79
+ state: input.state,
80
+ code_challenge: pkceChallenge(input.codeVerifier),
81
+ code_challenge_method: 'S256',
82
+ });
83
+ return `${AUTH_ENDPOINT}?${params.toString()}`;
84
+ }
85
+ /** Path Google sends the login callback to. Routed under `/auth/google/callback`. */
86
+ loginRedirectUri() {
87
+ return this.config.loginRedirectUri ?? this.config.redirectUri;
88
+ }
89
+ /**
90
+ * Exchange an authorization code from the login flow for the user's
91
+ * profile. Does NOT require `refresh_token` (login is one-shot — the
92
+ * Braid server issues its own session afterwards, so Google's tokens
93
+ * are discarded after this call). Uses the userinfo endpoint instead
94
+ * of decoding the id_token, so we don't bring in a JWT verifier just
95
+ * to read 3 fields.
96
+ */
97
+ async loginWithCode(input) {
98
+ const body = new URLSearchParams({
99
+ client_id: this.config.clientId,
100
+ client_secret: this.config.clientSecret,
101
+ redirect_uri: this.loginRedirectUri(),
102
+ grant_type: 'authorization_code',
103
+ code: input.code,
104
+ code_verifier: input.codeVerifier,
105
+ });
106
+ const tokenResponse = await fetch(TOKEN_ENDPOINT, {
107
+ method: 'POST',
108
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
109
+ body,
110
+ });
111
+ if (!tokenResponse.ok) {
112
+ const text = await tokenResponse.text().catch(() => '');
113
+ throw new Error(`Google login token exchange failed: ${tokenResponse.status} ${tokenResponse.statusText} ${text}`);
114
+ }
115
+ const { access_token } = await tokenResponse.json();
116
+ const userinfoResponse = await fetch(USERINFO_ENDPOINT, {
117
+ headers: { Authorization: `Bearer ${access_token}` },
118
+ });
119
+ if (!userinfoResponse.ok) {
120
+ const text = await userinfoResponse.text().catch(() => '');
121
+ throw new Error(`Google userinfo fetch failed: ${userinfoResponse.status} ${userinfoResponse.statusText} ${text}`);
122
+ }
123
+ const userinfo = await userinfoResponse.json();
124
+ return {
125
+ sub: userinfo.sub,
126
+ email: userinfo.email,
127
+ displayName: userinfo.name ?? userinfo.given_name ?? userinfo.email,
128
+ ...(userinfo.email_verified !== undefined ? { emailVerified: userinfo.email_verified } : {}),
129
+ };
130
+ }
63
131
  async refreshAccessToken(refreshToken) {
64
132
  const body = new URLSearchParams({
65
133
  client_id: this.config.clientId,
@@ -1 +1 @@
1
- {"version":3,"file":"GoogleOAuth.js","sourceRoot":"","sources":["../../../src/infrastructure/oauth/GoogleOAuth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GoogleOAuth.js","sourceRoot":"","sources":["../../../src/infrastructure/oauth/GoogleOAuth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { UserAuthor, UserDirectory } from '@braidhq/core';
2
+ import type { UserId } from '@braidhq/schema';
3
+ import type { UserRegistryFile } from './UserRegistryFile.js';
4
+ /**
5
+ * Adapt the file-backed user roster to the core `UserDirectory` port.
6
+ * Returns `null` for unknown ids (bootstrap / `braid-skill` etc.) so
7
+ * the git layer falls back to its synthetic author for those rows.
8
+ */
9
+ export declare class UserDirectoryFromRegistry implements UserDirectory {
10
+ private readonly registry;
11
+ constructor(registry: UserRegistryFile);
12
+ resolve(userId: UserId): Promise<UserAuthor | null>;
13
+ }
14
+ //# sourceMappingURL=UserDirectoryFromRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserDirectoryFromRegistry.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/users/UserDirectoryFromRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAE7D;;;;GAIG;AACH,qBAAa,yBAA0B,YAAW,aAAa;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,gBAAgB;IAEjD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;CAS1D"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Adapt the file-backed user roster to the core `UserDirectory` port.
3
+ * Returns `null` for unknown ids (bootstrap / `braid-skill` etc.) so
4
+ * the git layer falls back to its synthetic author for those rows.
5
+ */
6
+ export class UserDirectoryFromRegistry {
7
+ registry;
8
+ constructor(registry) {
9
+ this.registry = registry;
10
+ }
11
+ async resolve(userId) {
12
+ const user = await this.registry.get(userId);
13
+ if (!user)
14
+ return null;
15
+ return {
16
+ displayName: user.displayName,
17
+ ...(user.email ? { email: user.email } : {}),
18
+ };
19
+ }
20
+ }
21
+ //# sourceMappingURL=UserDirectoryFromRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserDirectoryFromRegistry.js","sourceRoot":"","sources":["../../../src/infrastructure/users/UserDirectoryFromRegistry.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,MAAM,OAAO,yBAAyB;IACP;IAA7B,YAA6B,QAA0B;QAA1B,aAAQ,GAAR,QAAQ,CAAkB;IAAG,CAAC;IAE3D,KAAK,CAAC,OAAO,CAAC,MAAc;QAC1B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAC5C,IAAI,CAAC,IAAI;YACP,OAAO,IAAI,CAAA;QACb,OAAO;YACL,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC7C,CAAA;IACH,CAAC;CACF"}