@yadsh/dsh-qa-surface 0.2.0 → 0.4.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 (325) hide show
  1. package/README.md +236 -20
  2. package/compatibility.json +11 -3
  3. package/lib/account-remotes.js +100 -0
  4. package/lib/account-remotes.js.map +1 -0
  5. package/lib/accounts/credentials.js +27 -0
  6. package/lib/accounts/credentials.js.map +1 -0
  7. package/lib/accounts/errors.js +9 -0
  8. package/lib/accounts/errors.js.map +1 -0
  9. package/lib/accounts/file.js +101 -0
  10. package/lib/accounts/file.js.map +1 -0
  11. package/lib/accounts/index.js +2 -0
  12. package/lib/accounts/index.js.map +1 -0
  13. package/lib/accounts/store.js +461 -0
  14. package/lib/accounts/store.js.map +1 -0
  15. package/lib/accounts/token.js +61 -0
  16. package/lib/accounts/token.js.map +1 -0
  17. package/lib/attachment-rules.js +111 -0
  18. package/lib/attachment-rules.js.map +1 -0
  19. package/lib/attestation.js.map +1 -1
  20. package/lib/cli.js +256 -0
  21. package/lib/cli.js.map +1 -0
  22. package/lib/client/QaAccountsController.js +359 -0
  23. package/lib/client/QaAccountsController.js.map +1 -0
  24. package/lib/client/QaSessionController.js +238 -180
  25. package/lib/client/QaSessionController.js.map +1 -1
  26. package/lib/client/QaSurface.js +281 -105
  27. package/lib/client/QaSurface.js.map +1 -1
  28. package/lib/client/QaTranscriptAdapter.js +95 -244
  29. package/lib/client/QaTranscriptAdapter.js.map +1 -1
  30. package/lib/client/attachments.js +150 -0
  31. package/lib/client/attachments.js.map +1 -0
  32. package/lib/client/attestation.js +7 -1
  33. package/lib/client/attestation.js.map +1 -1
  34. package/lib/client/base64.js +16 -0
  35. package/lib/client/base64.js.map +1 -0
  36. package/lib/client/chat-files.js +48 -0
  37. package/lib/client/chat-files.js.map +1 -0
  38. package/lib/client/components/Markdown.js +54 -13
  39. package/lib/client/components/Markdown.js.map +1 -1
  40. package/lib/client/components/QaAuthGate.js +40 -0
  41. package/lib/client/components/QaAuthGate.js.map +1 -0
  42. package/lib/client/components/QaChangelog.js +131 -0
  43. package/lib/client/components/QaChangelog.js.map +1 -0
  44. package/lib/client/components/QaComposer.js +56 -74
  45. package/lib/client/components/QaComposer.js.map +1 -1
  46. package/lib/client/components/QaFileAttachment.js +19 -0
  47. package/lib/client/components/QaFileAttachment.js.map +1 -0
  48. package/lib/client/components/QaFilesPanel.js +45 -0
  49. package/lib/client/components/QaFilesPanel.js.map +1 -0
  50. package/lib/client/components/QaHeader.js +34 -0
  51. package/lib/client/components/QaHeader.js.map +1 -0
  52. package/lib/client/components/QaMessage.js +61 -18
  53. package/lib/client/components/QaMessage.js.map +1 -1
  54. package/lib/client/components/QaModal.js +31 -0
  55. package/lib/client/components/QaModal.js.map +1 -0
  56. package/lib/client/components/QaProfile.js +64 -0
  57. package/lib/client/components/QaProfile.js.map +1 -0
  58. package/lib/client/components/QaRightRail.js +15 -0
  59. package/lib/client/components/QaRightRail.js.map +1 -0
  60. package/lib/client/components/QaSidebar.js +68 -20
  61. package/lib/client/components/QaSidebar.js.map +1 -1
  62. package/lib/client/components/QaSourcesPanel.js +127 -0
  63. package/lib/client/components/QaSourcesPanel.js.map +1 -0
  64. package/lib/client/components/QaTurnRail.js +296 -0
  65. package/lib/client/components/QaTurnRail.js.map +1 -0
  66. package/lib/client/components/QaWelcomeNotice.js +125 -0
  67. package/lib/client/components/QaWelcomeNotice.js.map +1 -0
  68. package/lib/client/components/QaWidthHandle.js +147 -0
  69. package/lib/client/components/QaWidthHandle.js.map +1 -0
  70. package/lib/client/components/QaWorkGroup.js +19 -20
  71. package/lib/client/components/QaWorkGroup.js.map +1 -1
  72. package/lib/client/components/source-refs.js +78 -0
  73. package/lib/client/components/source-refs.js.map +1 -0
  74. package/lib/client/components/surface-utils.js +28 -0
  75. package/lib/client/components/surface-utils.js.map +1 -0
  76. package/lib/client/components/thinking-phrases.js +24 -0
  77. package/lib/client/components/thinking-phrases.js.map +1 -0
  78. package/lib/client/create-session.js +26 -0
  79. package/lib/client/create-session.js.map +1 -0
  80. package/lib/client/failure-copy.js +63 -0
  81. package/lib/client/failure-copy.js.map +1 -0
  82. package/lib/client/index.js +135 -16
  83. package/lib/client/index.js.map +1 -1
  84. package/lib/client/project-session-state.js +78 -0
  85. package/lib/client/project-session-state.js.map +1 -0
  86. package/lib/client/prompt-content.js +54 -0
  87. package/lib/client/prompt-content.js.map +1 -0
  88. package/lib/client/session-admission.js +38 -0
  89. package/lib/client/session-admission.js.map +1 -0
  90. package/lib/client/session-assets.js +52 -0
  91. package/lib/client/session-assets.js.map +1 -0
  92. package/lib/client/session-sources.js +61 -0
  93. package/lib/client/session-sources.js.map +1 -0
  94. package/lib/client/settings/card.js +143 -0
  95. package/lib/client/settings/card.js.map +1 -0
  96. package/lib/client/settings/fields.js +145 -0
  97. package/lib/client/settings/fields.js.map +1 -0
  98. package/lib/client/settings/format.js +255 -0
  99. package/lib/client/settings/format.js.map +1 -0
  100. package/lib/client/settings/sections/access.js +25 -0
  101. package/lib/client/settings/sections/access.js.map +1 -0
  102. package/lib/client/settings/sections/accounts.js +47 -0
  103. package/lib/client/settings/sections/accounts.js.map +1 -0
  104. package/lib/client/settings/sections/attachments.js +46 -0
  105. package/lib/client/settings/sections/attachments.js.map +1 -0
  106. package/lib/client/settings/sections/branding.js +28 -0
  107. package/lib/client/settings/sections/branding.js.map +1 -0
  108. package/lib/client/settings/sections/common.js +20 -0
  109. package/lib/client/settings/sections/common.js.map +1 -0
  110. package/lib/client/settings/sections/embedding.js +19 -0
  111. package/lib/client/settings/sections/embedding.js.map +1 -0
  112. package/lib/client/settings/sections/interface.js +60 -0
  113. package/lib/client/settings/sections/interface.js.map +1 -0
  114. package/lib/client/settings/sections/lockdown.js +82 -0
  115. package/lib/client/settings/sections/lockdown.js.map +1 -0
  116. package/lib/client/settings/sections/session.js +53 -0
  117. package/lib/client/settings/sections/session.js.map +1 -0
  118. package/lib/client/settings/sections/sources.js +72 -0
  119. package/lib/client/settings/sections/sources.js.map +1 -0
  120. package/lib/client/settings/sections/status.js +37 -0
  121. package/lib/client/settings/sections/status.js.map +1 -0
  122. package/lib/client/settings/sections.js +23 -0
  123. package/lib/client/settings/sections.js.map +1 -0
  124. package/lib/client/settings/styles.js +57 -0
  125. package/lib/client/settings/styles.js.map +1 -0
  126. package/lib/client/settlement.js +24 -0
  127. package/lib/client/settlement.js.map +1 -0
  128. package/lib/client/stream-publisher.js +47 -0
  129. package/lib/client/stream-publisher.js.map +1 -0
  130. package/lib/client/styles.js +225 -17
  131. package/lib/client/styles.js.map +1 -1
  132. package/lib/client/tool-presentation.js +102 -0
  133. package/lib/client/tool-presentation.js.map +1 -0
  134. package/lib/client/turn-sources.js +98 -0
  135. package/lib/client/turn-sources.js.map +1 -0
  136. package/lib/client/types.js +2 -0
  137. package/lib/client/types.js.map +1 -1
  138. package/lib/client/use-right-rail.js +96 -0
  139. package/lib/client/use-right-rail.js.map +1 -0
  140. package/lib/client/use-session-ui-state.js +75 -0
  141. package/lib/client/use-session-ui-state.js.map +1 -0
  142. package/lib/client/use-transcript-view.js +44 -0
  143. package/lib/client/use-transcript-view.js.map +1 -0
  144. package/lib/client.js +9052 -1345
  145. package/lib/client.js.map +1 -1
  146. package/lib/config-resolvers/accounts.js +60 -0
  147. package/lib/config-resolvers/accounts.js.map +1 -0
  148. package/lib/config-resolvers/attachments.js +28 -0
  149. package/lib/config-resolvers/attachments.js.map +1 -0
  150. package/lib/config-resolvers/basics.js +59 -0
  151. package/lib/config-resolvers/basics.js.map +1 -0
  152. package/lib/config-resolvers/defaults.js +132 -0
  153. package/lib/config-resolvers/defaults.js.map +1 -0
  154. package/lib/config-resolvers/lockdown.js +79 -0
  155. package/lib/config-resolvers/lockdown.js.map +1 -0
  156. package/lib/config-resolvers/session.js +42 -0
  157. package/lib/config-resolvers/session.js.map +1 -0
  158. package/lib/config-resolvers/shared.js +42 -0
  159. package/lib/config-resolvers/shared.js.map +1 -0
  160. package/lib/config-resolvers/sources.js +81 -0
  161. package/lib/config-resolvers/sources.js.map +1 -0
  162. package/lib/config-resolvers/ui.js +20 -0
  163. package/lib/config-resolvers/ui.js.map +1 -0
  164. package/lib/config.js +279 -102
  165. package/lib/config.js.map +1 -1
  166. package/lib/entry-redirect.js +45 -0
  167. package/lib/entry-redirect.js.map +1 -0
  168. package/lib/host-route.js +25 -1
  169. package/lib/host-route.js.map +1 -1
  170. package/lib/index.js +258 -23
  171. package/lib/index.js.map +1 -1
  172. package/lib/launch-token.js +42 -0
  173. package/lib/launch-token.js.map +1 -0
  174. package/lib/profile.js +214 -0
  175. package/lib/profile.js.map +1 -0
  176. package/lib/prompt-notes.js +231 -0
  177. package/lib/prompt-notes.js.map +1 -0
  178. package/lib/provenance/collector.js +40 -0
  179. package/lib/provenance/collector.js.map +1 -0
  180. package/lib/provenance/connectors.js +147 -0
  181. package/lib/provenance/connectors.js.map +1 -0
  182. package/lib/provenance/dedupe.js +89 -0
  183. package/lib/provenance/dedupe.js.map +1 -0
  184. package/lib/provenance/extractors.js +340 -0
  185. package/lib/provenance/extractors.js.map +1 -0
  186. package/lib/provenance/file-preview.js +39 -0
  187. package/lib/provenance/file-preview.js.map +1 -0
  188. package/lib/provenance/host-store.js +387 -0
  189. package/lib/provenance/host-store.js.map +1 -0
  190. package/lib/provenance/index.js +9 -0
  191. package/lib/provenance/index.js.map +1 -0
  192. package/lib/provenance/normalize.js +128 -0
  193. package/lib/provenance/normalize.js.map +1 -0
  194. package/lib/provenance/reported.js +53 -0
  195. package/lib/provenance/reported.js.map +1 -0
  196. package/lib/provenance/types.js +2 -0
  197. package/lib/provenance/types.js.map +1 -0
  198. package/lib/resolve-config.js +35 -249
  199. package/lib/resolve-config.js.map +1 -1
  200. package/lib/secure-session.js +122 -14
  201. package/lib/secure-session.js.map +1 -1
  202. package/lib/shared/session-key.js +18 -0
  203. package/lib/shared/session-key.js.map +1 -0
  204. package/lib/shared/settings.js +10 -0
  205. package/lib/shared/settings.js.map +1 -0
  206. package/lib/thinking-phrases.js +68 -0
  207. package/lib/thinking-phrases.js.map +1 -0
  208. package/lib/typert.host.js +749 -11
  209. package/lib/typert.remote-client.d.ts +23 -3
  210. package/lib/typert.remote-client.js +593 -6
  211. package/lib/types/account-remotes.d.ts +42 -0
  212. package/lib/types/accounts/credentials.d.ts +14 -0
  213. package/lib/types/accounts/errors.d.ts +11 -0
  214. package/lib/types/accounts/file.d.ts +82 -0
  215. package/lib/types/accounts/index.d.ts +2 -0
  216. package/lib/types/accounts/store.d.ts +144 -0
  217. package/lib/types/accounts/token.d.ts +31 -0
  218. package/lib/types/attachment-rules.d.ts +44 -0
  219. package/lib/types/attestation.d.ts +1 -1
  220. package/lib/types/cli.d.ts +9 -0
  221. package/lib/types/client/QaAccountsController.d.ts +102 -0
  222. package/lib/types/client/QaConfigController.d.ts +1 -1
  223. package/lib/types/client/QaSessionController.d.ts +61 -22
  224. package/lib/types/client/QaSurface.d.ts +14 -3
  225. package/lib/types/client/QaTranscriptAdapter.d.ts +5 -10
  226. package/lib/types/client/attachments.d.ts +54 -0
  227. package/lib/types/client/base64.d.ts +10 -0
  228. package/lib/types/client/chat-files.d.ts +30 -0
  229. package/lib/types/client/components/Markdown.d.ts +11 -1
  230. package/lib/types/client/components/QaAgentsDrawer.d.ts +1 -1
  231. package/lib/types/client/components/QaAuthGate.d.ts +15 -0
  232. package/lib/types/client/components/QaChangelog.d.ts +21 -0
  233. package/lib/types/client/components/QaComposer.d.ts +8 -4
  234. package/lib/types/client/components/QaFileAttachment.d.ts +14 -0
  235. package/lib/types/client/components/QaFilesPanel.d.ts +15 -0
  236. package/lib/types/client/components/QaHeader.d.ts +41 -0
  237. package/lib/types/client/components/QaMessage.d.ts +12 -3
  238. package/lib/types/client/components/QaModal.d.ts +26 -0
  239. package/lib/types/client/components/QaProfile.d.ts +25 -0
  240. package/lib/types/client/components/QaRightRail.d.ts +23 -0
  241. package/lib/types/client/components/QaSidebar.d.ts +44 -2
  242. package/lib/types/client/components/QaSourcesPanel.d.ts +23 -0
  243. package/lib/types/client/components/QaTurnRail.d.ts +65 -0
  244. package/lib/types/client/components/QaWelcomeNotice.d.ts +27 -0
  245. package/lib/types/client/components/QaWidthHandle.d.ts +43 -0
  246. package/lib/types/client/components/QaWorkGroup.d.ts +5 -3
  247. package/lib/types/client/components/source-refs.d.ts +16 -0
  248. package/lib/types/client/components/surface-utils.d.ts +11 -0
  249. package/lib/types/client/components/thinking-phrases.d.ts +9 -0
  250. package/lib/types/client/create-session.d.ts +13 -0
  251. package/lib/types/client/failure-copy.d.ts +19 -0
  252. package/lib/types/client/index.d.ts +1 -0
  253. package/lib/types/client/project-session-state.d.ts +30 -0
  254. package/lib/types/client/prompt-content.d.ts +30 -0
  255. package/lib/types/client/session-admission.d.ts +23 -0
  256. package/lib/types/client/session-assets.d.ts +20 -0
  257. package/lib/types/client/session-sources.d.ts +28 -0
  258. package/lib/types/client/settings/card.d.ts +23 -0
  259. package/lib/types/client/settings/fields.d.ts +139 -0
  260. package/lib/types/client/settings/format.d.ts +108 -0
  261. package/lib/types/client/settings/sections/access.d.ts +9 -0
  262. package/lib/types/client/settings/sections/accounts.d.ts +9 -0
  263. package/lib/types/client/settings/sections/attachments.d.ts +15 -0
  264. package/lib/types/client/settings/sections/branding.d.ts +8 -0
  265. package/lib/types/client/settings/sections/common.d.ts +28 -0
  266. package/lib/types/client/settings/sections/embedding.d.ts +8 -0
  267. package/lib/types/client/settings/sections/interface.d.ts +9 -0
  268. package/lib/types/client/settings/sections/lockdown.d.ts +9 -0
  269. package/lib/types/client/settings/sections/session.d.ts +9 -0
  270. package/lib/types/client/settings/sections/sources.d.ts +9 -0
  271. package/lib/types/client/settings/sections/status.d.ts +22 -0
  272. package/lib/types/client/settings/sections.d.ts +25 -0
  273. package/lib/types/client/settings/styles.d.ts +8 -0
  274. package/lib/types/client/settlement.d.ts +6 -0
  275. package/lib/types/client/stream-publisher.d.ts +28 -0
  276. package/lib/types/client/tool-presentation.d.ts +6 -0
  277. package/lib/types/client/turn-sources.d.ts +14 -0
  278. package/lib/types/client/types.d.ts +101 -9
  279. package/lib/types/client/use-right-rail.d.ts +56 -0
  280. package/lib/types/client/use-session-ui-state.d.ts +41 -0
  281. package/lib/types/client/use-transcript-view.d.ts +20 -0
  282. package/lib/types/config-resolvers/accounts.d.ts +15 -0
  283. package/lib/types/config-resolvers/attachments.d.ts +6 -0
  284. package/lib/types/config-resolvers/basics.d.ts +8 -0
  285. package/lib/types/config-resolvers/defaults.d.ts +4 -0
  286. package/lib/types/config-resolvers/lockdown.d.ts +10 -0
  287. package/lib/types/config-resolvers/session.d.ts +6 -0
  288. package/lib/types/config-resolvers/shared.d.ts +16 -0
  289. package/lib/types/config-resolvers/sources.d.ts +6 -0
  290. package/lib/types/config-resolvers/ui.d.ts +6 -0
  291. package/lib/types/entry-redirect.d.ts +20 -0
  292. package/lib/types/host-route.d.ts +14 -1
  293. package/lib/types/index.d.ts +54 -3
  294. package/lib/types/launch-token.d.ts +21 -0
  295. package/lib/types/profile.d.ts +76 -0
  296. package/lib/types/prompt-notes.d.ts +110 -0
  297. package/lib/types/provenance/collector.d.ts +26 -0
  298. package/lib/types/provenance/connectors.d.ts +11 -0
  299. package/lib/types/provenance/dedupe.d.ts +6 -0
  300. package/lib/types/provenance/extractors.d.ts +14 -0
  301. package/lib/types/provenance/file-preview.d.ts +10 -0
  302. package/lib/types/provenance/host-store.d.ts +54 -0
  303. package/lib/types/provenance/index.d.ts +10 -0
  304. package/lib/types/provenance/normalize.d.ts +13 -0
  305. package/lib/types/provenance/reported.d.ts +5 -0
  306. package/lib/types/provenance/types.d.ts +78 -0
  307. package/lib/types/resolve-config.d.ts +8 -3
  308. package/lib/types/secure-session.d.ts +36 -2
  309. package/lib/types/shared/session-key.d.ts +23 -0
  310. package/lib/types/shared/settings.d.ts +10 -0
  311. package/lib/types/thinking-phrases.d.ts +17 -0
  312. package/lib/types/types.d.ts +290 -16
  313. package/lib/types/user-workspace.d.ts +38 -0
  314. package/lib/user-workspace.js +231 -0
  315. package/lib/user-workspace.js.map +1 -0
  316. package/package.json +71 -47
  317. package/CHANGELOG.md +0 -93
  318. package/SPEC-dsh-qa-surface.md +0 -2425
  319. package/docs/ARCHITECTURE.md +0 -66
  320. package/docs/COMPATIBILITY.md +0 -14
  321. package/docs/CONFIGURATION.md +0 -112
  322. package/docs/EMBEDDING.md +0 -13
  323. package/lib/client/components/QaSourcesDrawer.js +0 -46
  324. package/lib/client/components/QaSourcesDrawer.js.map +0 -1
  325. package/lib/types/client/components/QaSourcesDrawer.d.ts +0 -8
@@ -0,0 +1,461 @@
1
+ import { randomBytes, randomUUID, scryptSync, timingSafeEqual, } from "node:crypto";
2
+ import { QA_PROFILE_DEFAULT_INSTRUCTIONS_MAX, normalizeProfile, validateProfileWrite, } from "../profile.js";
3
+ import { validateCredentials } from "./credentials.js";
4
+ import { QaAccountsError } from "./errors.js";
5
+ import { createFileStampRef, defaultAccountsFilePath, loadAccountsFile, persistAccountsFile, reloadAccountsFileIfChanged, } from "./file.js";
6
+ import { mintToken, verifyToken } from "./token.js";
7
+ export { QaAccountsError } from "./errors.js";
8
+ export { defaultAccountsFilePath } from "./file.js";
9
+ const SCRYPT_KEY_LENGTH = 32;
10
+ const MAX_CLAIM_BATCH = 50;
11
+ const MAX_SESSION_ID_LENGTH = 200;
12
+ function toPublic(user) {
13
+ return {
14
+ id: user.id,
15
+ email: user.email,
16
+ displayName: user.displayName,
17
+ role: user.role,
18
+ createdAt: user.createdAt,
19
+ lastLoginAt: user.lastLoginAt,
20
+ disabled: user.disabled === true,
21
+ profile: normalizeProfile(user.profile),
22
+ };
23
+ }
24
+ function hashPassword(password) {
25
+ const salt = randomBytes(16);
26
+ const hash = scryptSync(password, salt, SCRYPT_KEY_LENGTH);
27
+ return { salt: salt.toString("hex"), hash: hash.toString("hex") };
28
+ }
29
+ function passwordMatches(password, stored) {
30
+ const expected = Buffer.from(stored.hash, "hex");
31
+ const actual = scryptSync(password, Buffer.from(stored.salt, "hex"), expected.length);
32
+ return expected.length === actual.length && timingSafeEqual(expected, actual);
33
+ }
34
+ /**
35
+ * Host authority for QA accounts: the file-backed user list, the session
36
+ * ownership map and the HMAC account tokens. All mutations are synchronous
37
+ * and persisted atomically (temp file + rename); LAN scale keeps this trivial.
38
+ *
39
+ * The class is the facade: token mechanics live in token.ts, the file format
40
+ * and its atomic persistence plus the external-change probe in file.ts, and
41
+ * the credential rules shared by registration and the operator CLI in
42
+ * credentials.ts.
43
+ */
44
+ export class QaAccounts {
45
+ filePath;
46
+ file;
47
+ authAttempts = [];
48
+ maxAuthAttemptsPerMinute;
49
+ sessionTtlDays;
50
+ allowRegistration;
51
+ /** Character cap on a stored profile's agent guidance. */
52
+ instructionsMaxLength;
53
+ /** Declared handle fields; undefined lets an operator write any shape-valid key. */
54
+ identityFields;
55
+ /** mtime+size of the file as of the last load; the external-change probe. */
56
+ fileStamp = createFileStampRef();
57
+ constructor(filePath = defaultAccountsFilePath(), options) {
58
+ this.filePath = filePath;
59
+ this.sessionTtlDays = options.sessionTtlDays;
60
+ this.allowRegistration = options.allowRegistration;
61
+ this.maxAuthAttemptsPerMinute = options.maxAuthAttemptsPerMinute ?? 30;
62
+ this.instructionsMaxLength =
63
+ options.instructionsMaxLength ?? QA_PROFILE_DEFAULT_INSTRUCTIONS_MAX;
64
+ this.identityFields = options.identityFields;
65
+ this.file = loadAccountsFile(this.filePath, this.fileStamp);
66
+ }
67
+ /**
68
+ * Re-read the accounts file when it changed on disk, before every read or
69
+ * read-modify-write. The probe itself and its cross-process rationale live
70
+ * with the file layer (reloadAccountsFileIfChanged).
71
+ */
72
+ reloadIfChanged() {
73
+ const loaded = reloadAccountsFileIfChanged(this.filePath, this.fileStamp);
74
+ if (loaded !== undefined)
75
+ this.file = loaded;
76
+ }
77
+ save() {
78
+ persistAccountsFile(this.filePath, this.file, this.fileStamp);
79
+ }
80
+ pruneAuthAttempts() {
81
+ const cutoff = Date.now() - 60_000;
82
+ let stale = 0;
83
+ for (const at of this.authAttempts) {
84
+ if (at >= cutoff)
85
+ break;
86
+ stale += 1;
87
+ }
88
+ if (stale > 0)
89
+ this.authAttempts.splice(0, stale);
90
+ }
91
+ assertAuthBudget() {
92
+ this.pruneAuthAttempts();
93
+ if (this.authAttempts.length >= this.maxAuthAttemptsPerMinute) {
94
+ throw new QaAccountsError("rate-limited", "too many authentication attempts; retry in a minute");
95
+ }
96
+ this.authAttempts.push(Date.now());
97
+ }
98
+ userByEmail(email) {
99
+ return this.file.users.find((user) => user.email === email);
100
+ }
101
+ register(email, password, displayName) {
102
+ this.reloadIfChanged();
103
+ this.assertAuthBudget();
104
+ if (!this.allowRegistration) {
105
+ throw new QaAccountsError("registration-disabled", "self-registration is disabled on this deployment");
106
+ }
107
+ const { email: normalized, displayName: name } = validateCredentials(email, password, displayName);
108
+ if (this.userByEmail(normalized) !== undefined) {
109
+ throw new QaAccountsError("email-taken", "this email is already registered");
110
+ }
111
+ // The first account boots the deployment's admin; later ones are users.
112
+ const role = this.file.users.length === 0 ? "admin" : "user";
113
+ const user = {
114
+ id: randomUUID(),
115
+ email: normalized,
116
+ displayName: name === "" ? (normalized.split("@")[0] ?? normalized) : name,
117
+ role,
118
+ passwordHash: hashPassword(password),
119
+ createdAt: new Date().toISOString(),
120
+ lastLoginAt: new Date().toISOString(),
121
+ tokenVersion: 0,
122
+ };
123
+ this.file = { ...this.file, users: [...this.file.users, user] };
124
+ this.save();
125
+ return { token: this.mintToken(user.id), user: toPublic(user) };
126
+ }
127
+ login(email, password) {
128
+ this.reloadIfChanged();
129
+ this.assertAuthBudget();
130
+ const user = this.userByEmail(email.trim().toLowerCase());
131
+ // One message for unknown email and wrong password, both scrypt-checked
132
+ // in the known-user path so the timing does not enumerate addresses.
133
+ if (user === undefined) {
134
+ passwordMatches(password, hashPassword(password));
135
+ throw new QaAccountsError("invalid-credentials", "email or password is incorrect");
136
+ }
137
+ if (!passwordMatches(password, user.passwordHash)) {
138
+ throw new QaAccountsError("invalid-credentials", "email or password is incorrect");
139
+ }
140
+ if (user.disabled === true) {
141
+ throw new QaAccountsError("account-disabled", "this account has been disabled by the operator");
142
+ }
143
+ user.lastLoginAt = new Date().toISOString();
144
+ this.save();
145
+ return { token: this.mintToken(user.id), user: toPublic(user) };
146
+ }
147
+ whoami(token) {
148
+ this.reloadIfChanged();
149
+ const userId = this.verifyToken(token);
150
+ if (userId === null)
151
+ return { authenticated: false };
152
+ const user = this.file.users.find((candidate) => candidate.id === userId);
153
+ return user === undefined || user.disabled === true
154
+ ? { authenticated: false }
155
+ : { authenticated: true, user: toPublic(user) };
156
+ }
157
+ /** Resolve a token to its user, or null for absent/invalid/expired ones. */
158
+ verifyToken(token) {
159
+ // Signature, expiry and token-version mechanics live in token.ts.
160
+ return verifyToken(this.file.secret, token, this.file.users);
161
+ }
162
+ mintToken(userId) {
163
+ const user = this.file.users.find((candidate) => candidate.id === userId);
164
+ return mintToken(this.file.secret, {
165
+ uid: userId,
166
+ exp: Date.now() + this.sessionTtlDays * 86_400_000,
167
+ ver: user?.tokenVersion ?? 0,
168
+ });
169
+ }
170
+ /** The account behind a token, or an auth-required refusal. */
171
+ requireUser(token) {
172
+ const userId = this.verifyToken(token);
173
+ const user = userId === null
174
+ ? undefined
175
+ : this.file.users.find((candidate) => candidate.id === userId);
176
+ if (user === undefined || user.disabled === true) {
177
+ throw new QaAccountsError("auth-required", "a valid QA account token is required");
178
+ }
179
+ return user;
180
+ }
181
+ /**
182
+ * Ownership gate for one session: claim unowned sessions for the requesting
183
+ * user (first come, first served — the pre-accounts migration path), refuse
184
+ * foreign sessions unless the caller administers the deployment.
185
+ */
186
+ ensureSessionAccess(token, sessionId) {
187
+ this.reloadIfChanged();
188
+ const user = this.requireUser(token);
189
+ const owner = this.file.ownership[sessionId];
190
+ if (owner === undefined) {
191
+ this.file = {
192
+ ...this.file,
193
+ ownership: {
194
+ ...this.file.ownership,
195
+ [sessionId]: { userId: user.id, claimedAt: new Date().toISOString() },
196
+ },
197
+ };
198
+ this.save();
199
+ return toPublic(user);
200
+ }
201
+ if (owner.userId !== user.id && user.role !== "admin") {
202
+ throw new QaAccountsError("session-owned-elsewhere", "this session belongs to another QA user");
203
+ }
204
+ const ownerUser = this.file.users.find((candidate) => candidate.id === owner.userId);
205
+ if (ownerUser === undefined) {
206
+ throw new QaAccountsError("session-owned-elsewhere", "the session owner no longer exists");
207
+ }
208
+ return toPublic(ownerUser);
209
+ }
210
+ /**
211
+ * Atomically reserve a Host-generated session id for the token user before
212
+ * the Session exists. Browser callers therefore never get a race window in
213
+ * which they can claim another user's newly created chat.
214
+ */
215
+ reserveSession(token, sessionId) {
216
+ this.reloadIfChanged();
217
+ const user = this.requireUser(token);
218
+ if (sessionId.trim() === "" ||
219
+ sessionId.length > MAX_SESSION_ID_LENGTH ||
220
+ this.file.ownership[sessionId] !== undefined) {
221
+ throw new QaAccountsError("session-owned-elsewhere", "the requested session id is unavailable");
222
+ }
223
+ this.file = {
224
+ ...this.file,
225
+ ownership: {
226
+ ...this.file.ownership,
227
+ [sessionId]: { userId: user.id, claimedAt: new Date().toISOString() },
228
+ },
229
+ };
230
+ this.save();
231
+ return toPublic(user);
232
+ }
233
+ /** Roll back a reservation if Host creation failed before a chat existed. */
234
+ releaseSessionReservation(userId, sessionId) {
235
+ this.reloadIfChanged();
236
+ if (this.file.ownership[sessionId]?.userId !== userId)
237
+ return;
238
+ const ownership = { ...this.file.ownership };
239
+ delete ownership[sessionId];
240
+ this.file = { ...this.file, ownership };
241
+ this.save();
242
+ }
243
+ /** Bulk-claim a browser's local chat index; foreign ids come back as conflicts. */
244
+ claimSessions(token, sessionIds) {
245
+ this.reloadIfChanged();
246
+ const user = this.requireUser(token);
247
+ if (sessionIds.length > MAX_CLAIM_BATCH) {
248
+ throw new QaAccountsError("auth-required", "claim batch is too large");
249
+ }
250
+ let claimed = 0;
251
+ const conflicts = [];
252
+ const ownership = { ...this.file.ownership };
253
+ for (const raw of sessionIds) {
254
+ const sessionId = typeof raw === "string" ? raw.trim() : "";
255
+ if (sessionId === "" || sessionId.length > MAX_SESSION_ID_LENGTH)
256
+ continue;
257
+ const owner = ownership[sessionId];
258
+ if (owner === undefined) {
259
+ ownership[sessionId] = {
260
+ userId: user.id,
261
+ claimedAt: new Date().toISOString(),
262
+ };
263
+ claimed += 1;
264
+ }
265
+ else if (owner.userId !== user.id) {
266
+ conflicts.push(sessionId);
267
+ }
268
+ }
269
+ if (claimed > 0) {
270
+ this.file = { ...this.file, ownership };
271
+ this.save();
272
+ }
273
+ return { claimed, conflicts };
274
+ }
275
+ /** Session ids owned by the token's user, most recently claimed first. */
276
+ ownedSessionIds(token) {
277
+ this.reloadIfChanged();
278
+ const user = this.requireUser(token);
279
+ return Object.entries(this.file.ownership)
280
+ .filter(([, owner]) => owner.userId === user.id)
281
+ .sort((left, right) => right[1].claimedAt.localeCompare(left[1].claimedAt))
282
+ .map(([sessionId]) => sessionId);
283
+ }
284
+ /**
285
+ * Every ownership entry with the owner's display name resolved at read
286
+ * time, oldest claim first. Admin-only: this is the cross-user view the
287
+ * admin sidebar groups chats by; ordinary accounts get a dedicated refusal.
288
+ */
289
+ listOwnership(token) {
290
+ const user = this.requireUser(token);
291
+ if (user.role !== "admin") {
292
+ throw new QaAccountsError("admin-required", "listing chat ownership requires an admin account");
293
+ }
294
+ const names = new Map(this.file.users.map((candidate) => [candidate.id, candidate.displayName]));
295
+ return Object.entries(this.file.ownership)
296
+ .map(([sessionId, owner]) => ({
297
+ sessionId,
298
+ userId: owner.userId,
299
+ displayName: names.get(owner.userId) ?? owner.userId,
300
+ claimedAt: owner.claimedAt,
301
+ }))
302
+ .sort((left, right) => left.claimedAt.localeCompare(right.claimedAt));
303
+ }
304
+ // ---------------------------------------------------------------------------
305
+ // Operator management surface (the qa-accounts CLI; never browser-callable).
306
+ // ---------------------------------------------------------------------------
307
+ /** Every account, file order; for `qa-accounts list`. */
308
+ listUsers() {
309
+ this.reloadIfChanged();
310
+ return this.file.users.map((user) => ({
311
+ email: user.email,
312
+ displayName: user.displayName,
313
+ role: user.role,
314
+ disabled: user.disabled === true,
315
+ createdAt: user.createdAt,
316
+ lastLoginAt: user.lastLoginAt,
317
+ }));
318
+ }
319
+ /** Create an account outside the self-registration gate. */
320
+ addUser(email, password, options = {}) {
321
+ this.reloadIfChanged();
322
+ this.assertAuthBudget();
323
+ const { email: normalized, displayName: name } = validateCredentials(email, password, options.displayName);
324
+ if (options.role !== undefined &&
325
+ options.role !== "admin" &&
326
+ options.role !== "user") {
327
+ throw new QaAccountsError("invalid-role", "role must be admin or user");
328
+ }
329
+ if (this.userByEmail(normalized) !== undefined) {
330
+ throw new QaAccountsError("email-taken", "this email is already registered");
331
+ }
332
+ const user = {
333
+ id: randomUUID(),
334
+ email: normalized,
335
+ displayName: name === "" ? (normalized.split("@")[0] ?? normalized) : name,
336
+ role: options.role ?? (this.file.users.length === 0 ? "admin" : "user"),
337
+ passwordHash: hashPassword(password),
338
+ createdAt: new Date().toISOString(),
339
+ lastLoginAt: null,
340
+ tokenVersion: 0,
341
+ };
342
+ this.file = { ...this.file, users: [...this.file.users, user] };
343
+ this.save();
344
+ return toPublic(user);
345
+ }
346
+ editUser(email, edit) {
347
+ this.reloadIfChanged();
348
+ const normalized = email.trim().toLowerCase();
349
+ const index = this.file.users.findIndex((user) => user.email === normalized);
350
+ if (index === -1) {
351
+ throw new QaAccountsError("invalid-credentials", "no such account");
352
+ }
353
+ const updated = edit(this.file.users[index]);
354
+ const users = [...this.file.users];
355
+ users[index] = updated;
356
+ this.file = { ...this.file, users };
357
+ this.save();
358
+ return updated;
359
+ }
360
+ /** Grant or revoke the admin role. */
361
+ setUserRole(email, role) {
362
+ return toPublic(this.editUser(email, (user) => ({ ...user, role })));
363
+ }
364
+ /**
365
+ * Disable an account: future logins are refused and every live token is
366
+ * invalidated (the token version bumps). `enable` does not resurrect old
367
+ * tokens — the account signs in again.
368
+ */
369
+ setUserDisabled(email, disabled) {
370
+ return toPublic(this.editUser(email, (user) => {
371
+ const next = user;
372
+ if (disabled) {
373
+ next.disabled = true;
374
+ next.tokenVersion = (next.tokenVersion ?? 0) + 1;
375
+ }
376
+ else {
377
+ delete next.disabled;
378
+ }
379
+ return next;
380
+ }));
381
+ }
382
+ /** Invalidate every token of the account (password-leak response). */
383
+ revokeTokens(email) {
384
+ return toPublic(this.editUser(email, (user) => ({
385
+ ...user,
386
+ tokenVersion: (user.tokenVersion ?? 0) + 1,
387
+ })));
388
+ }
389
+ // ---------------------------------------------------------------------------
390
+ // Self-declared profiles: what the QA prompt says about the current user.
391
+ // ---------------------------------------------------------------------------
392
+ /**
393
+ * The account that owns one session, or undefined while nobody claimed it.
394
+ * The prompt renderer resolves a chat's owner through this map, so a session
395
+ * that predates accounts simply carries no identity.
396
+ */
397
+ ownerIdOf(sessionId) {
398
+ this.reloadIfChanged();
399
+ return this.file.ownership[sessionId]?.userId;
400
+ }
401
+ /**
402
+ * Email plus profile of one account, for the prompt renderer. A disabled
403
+ * account resolves to nothing: its identity leaves the prompt together with
404
+ * its access, so a revoked user is no longer addressed by name.
405
+ */
406
+ identityOf(userId) {
407
+ this.reloadIfChanged();
408
+ const user = this.file.users.find((candidate) => candidate.id === userId);
409
+ if (user === undefined || user.disabled === true)
410
+ return undefined;
411
+ return { email: user.email, profile: normalizeProfile(user.profile) };
412
+ }
413
+ /** One account in its public shape; undefined when the address is unknown. */
414
+ findUser(email) {
415
+ this.reloadIfChanged();
416
+ const user = this.userByEmail(email.trim().toLowerCase());
417
+ return user === undefined ? undefined : toPublic(user);
418
+ }
419
+ /**
420
+ * Operator write: replace one account's profile, addressed by email. The
421
+ * CLI merges field-level flags into a full profile before calling this.
422
+ */
423
+ setProfile(email, input) {
424
+ return toPublic(this.editUser(email, (user) => this.stampedProfile(user, input)));
425
+ }
426
+ /**
427
+ * Self-service write for the token's own account. The token is verified
428
+ * first, so a browser can only ever edit the profile it is signed in as.
429
+ */
430
+ updateOwnProfile(token, input) {
431
+ this.reloadIfChanged();
432
+ const user = this.requireUser(token);
433
+ return this.setProfile(user.email, input);
434
+ }
435
+ /**
436
+ * Validate one profile write and stamp it onto the account. Throws before
437
+ * anything is mutated, so a rejected write leaves the stored profile, and
438
+ * the address it is keyed to, exactly as they were.
439
+ */
440
+ stampedProfile(user, input) {
441
+ const result = validateProfileWrite(input, {
442
+ instructionsMaxLength: this.instructionsMaxLength,
443
+ ...(this.identityFields === undefined
444
+ ? {}
445
+ : { identities: this.identityFields }),
446
+ });
447
+ if (!result.ok) {
448
+ throw new QaAccountsError("invalid-profile", result.message);
449
+ }
450
+ return {
451
+ ...user,
452
+ profile: {
453
+ fullName: result.value.fullName,
454
+ identities: { ...result.value.identities },
455
+ instructions: result.value.instructions,
456
+ updatedAt: new Date().toISOString(),
457
+ },
458
+ };
459
+ }
460
+ }
461
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/accounts/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import { createHmac, timingSafeEqual } from "node:crypto";
2
+ export function base64Url(value) {
3
+ return Buffer.from(value).toString("base64url");
4
+ }
5
+ /**
6
+ * Mint a v1 account token: a base64url JSON payload plus an HMAC-SHA256
7
+ * signature keyed with the accounts file secret.
8
+ */
9
+ export function mintToken(secret, payload) {
10
+ const encoded = base64Url(JSON.stringify(payload));
11
+ const signature = base64Url(createHmac("sha256", secret).update(encoded).digest());
12
+ return `v1.${encoded}.${signature}`;
13
+ }
14
+ /**
15
+ * Verify a v1 account token against the current secret and account list and
16
+ * return its user id, or null for anything malformed, foreign-signed,
17
+ * expired or superseded. Signature computation, the length check and the
18
+ * payload decode share one try, so a hostile token can only ever resolve to
19
+ * null; the comparison is timing-safe (equal length first, then
20
+ * timingSafeEqual) so a signature guess cannot be accelerated by measuring
21
+ * HMAC rejection time.
22
+ */
23
+ export function verifyToken(secret, token, users) {
24
+ if (typeof token !== "string")
25
+ return null;
26
+ const parts = token.split(".");
27
+ if (parts.length !== 3 || parts[0] !== "v1")
28
+ return null;
29
+ const payload = parts[1];
30
+ const signature = parts[2];
31
+ if (payload === undefined || signature === undefined)
32
+ return null;
33
+ let expected;
34
+ let decoded;
35
+ try {
36
+ expected = createHmac("sha256", secret).update(payload).digest();
37
+ const expectedSignature = base64Url(expected);
38
+ if (signature.length !== expectedSignature.length ||
39
+ !timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) {
40
+ return null;
41
+ }
42
+ decoded = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ if (typeof decoded.uid !== "string" ||
48
+ typeof decoded.exp !== "number" ||
49
+ decoded.exp < Date.now()) {
50
+ return null;
51
+ }
52
+ // The account's current token version must match the one burned into the
53
+ // token: bumping it (disable/revoke) invalidates every issued token.
54
+ const user = users.find((candidate) => candidate.id === decoded.uid);
55
+ const version = typeof decoded.ver === "number" ? decoded.ver : undefined;
56
+ if (user === undefined || version !== (user.tokenVersion ?? 0)) {
57
+ return null;
58
+ }
59
+ return decoded.uid;
60
+ }
61
+ //# sourceMappingURL=token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/accounts/token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAiB1D,MAAM,UAAU,SAAS,CAAC,KAAsB;IAC9C,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,MAAc,EAAE,OAAqB;IAC7D,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,SAAS,CACzB,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CACtD,CAAC;IACF,OAAO,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;AACtC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CACzB,MAAc,EACd,KAAa,EACb,KAA2B;IAE3B,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACzD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACzB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,OAAO,KAAK,SAAS,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAClE,IAAI,QAAgB,CAAC;IACrB,IAAI,OAAwD,CAAC;IAC7D,IAAI,CAAC;QACH,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC;QACjE,MAAM,iBAAiB,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC9C,IACE,SAAS,CAAC,MAAM,KAAK,iBAAiB,CAAC,MAAM;YAC7C,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,EACxE,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IACE,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ;QAC/B,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ;QAC/B,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,EACxB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,yEAAyE;IACzE,qEAAqE;IACrE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1E,IAAI,IAAI,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC,EAAE,CAAC;QAC/D,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,OAAO,CAAC,GAAG,CAAC;AACrB,CAAC"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Attachment vocabulary shared by the Host configuration and the browser
3
+ * composer. The module stays free of Node and DOM imports: the client bundle
4
+ * imports it directly, and the Host resolver uses it to normalize the
5
+ * operator's configuration.
6
+ */
7
+ /**
8
+ * Text-file extensions the composer accepts out of the box. Compared
9
+ * lowercase and without the leading dot; an entry is a suffix, not a glob.
10
+ */
11
+ export const DEFAULT_QA_TEXT_EXTENSIONS = Object.freeze([
12
+ "md",
13
+ "markdown",
14
+ "txt",
15
+ "text",
16
+ "log",
17
+ "json",
18
+ "jsonl",
19
+ "yaml",
20
+ "yml",
21
+ "toml",
22
+ "ini",
23
+ "cfg",
24
+ "conf",
25
+ "env",
26
+ "csv",
27
+ "tsv",
28
+ "xml",
29
+ "html",
30
+ "htm",
31
+ "css",
32
+ "js",
33
+ "mjs",
34
+ "cjs",
35
+ "ts",
36
+ "tsx",
37
+ "jsx",
38
+ "py",
39
+ "rb",
40
+ "go",
41
+ "rs",
42
+ "java",
43
+ "kt",
44
+ "cs",
45
+ "php",
46
+ "sh",
47
+ "bash",
48
+ "zsh",
49
+ "ps1",
50
+ "bat",
51
+ "cmd",
52
+ "sql",
53
+ "patch",
54
+ "diff",
55
+ ]);
56
+ /** Bounds the resolver and the settings card agree on. */
57
+ export const QA_PASTED_TEXT_LINES_MIN = 0;
58
+ export const QA_PASTED_TEXT_LINES_MAX = 10_000;
59
+ export const QA_MAX_FILE_BYTES_MIN = 1024;
60
+ export const QA_MAX_FILE_BYTES_MAX = 52_428_800;
61
+ export const QA_MAX_PENDING_MIN = 1;
62
+ export const QA_MAX_PENDING_MAX = 40;
63
+ /** One accepted extension token: no dot, no separator, bounded length. */
64
+ const EXTENSION_PATTERN = /^[a-z0-9][a-z0-9_-]{0,15}$/u;
65
+ /**
66
+ * Normalize an operator-authored extension list: lowercase, leading dots
67
+ * stripped, malformed entries and duplicates dropped, order preserved.
68
+ * An empty result means no extension is accepted by name — the composer then
69
+ * still accepts whatever the browser reports as `text/*`.
70
+ * @param values - raw extension tokens from configuration.
71
+ * @returns the normalized token list.
72
+ */
73
+ export function normalizeTextExtensions(values) {
74
+ const seen = new Set();
75
+ const normalized = [];
76
+ for (const value of values) {
77
+ const token = value.trim().toLowerCase().replace(/^\.+/u, "");
78
+ if (!EXTENSION_PATTERN.test(token) || seen.has(token))
79
+ continue;
80
+ seen.add(token);
81
+ normalized.push(token);
82
+ }
83
+ return normalized;
84
+ }
85
+ /**
86
+ * Whether one file name carries an accepted text extension. The comparison is
87
+ * case-insensitive; a name without a dot, or with a hidden-file dot only
88
+ * (`.gitignore`), is matched against the whole leaf.
89
+ * @param name - display file name.
90
+ * @param extensions - normalized accepted extensions.
91
+ * @returns whether the name matches one accepted extension.
92
+ */
93
+ export function hasTextExtension(name, extensions) {
94
+ const leaf = name.trim().toLowerCase();
95
+ if (leaf === "")
96
+ return false;
97
+ const dot = leaf.lastIndexOf(".");
98
+ const suffix = dot <= 0 ? leaf : leaf.slice(dot + 1);
99
+ return extensions.includes(suffix);
100
+ }
101
+ /**
102
+ * Count the lines of a pasted blob the way a reader counts them: a single
103
+ * trailing newline does not open a new line, and CRLF counts once.
104
+ * @param text - pasted plain text.
105
+ * @returns the line count, at least 1 for any non-empty text.
106
+ */
107
+ export function countTextLines(text) {
108
+ const normalized = text.replace(/\r\n?/gu, "\n").replace(/\n$/u, "");
109
+ return normalized === "" ? 0 : normalized.split("\n").length;
110
+ }
111
+ //# sourceMappingURL=attachment-rules.js.map