@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.
- package/README.md +236 -20
- package/compatibility.json +11 -3
- package/lib/account-remotes.js +100 -0
- package/lib/account-remotes.js.map +1 -0
- package/lib/accounts/credentials.js +27 -0
- package/lib/accounts/credentials.js.map +1 -0
- package/lib/accounts/errors.js +9 -0
- package/lib/accounts/errors.js.map +1 -0
- package/lib/accounts/file.js +101 -0
- package/lib/accounts/file.js.map +1 -0
- package/lib/accounts/index.js +2 -0
- package/lib/accounts/index.js.map +1 -0
- package/lib/accounts/store.js +461 -0
- package/lib/accounts/store.js.map +1 -0
- package/lib/accounts/token.js +61 -0
- package/lib/accounts/token.js.map +1 -0
- package/lib/attachment-rules.js +111 -0
- package/lib/attachment-rules.js.map +1 -0
- package/lib/attestation.js.map +1 -1
- package/lib/cli.js +256 -0
- package/lib/cli.js.map +1 -0
- package/lib/client/QaAccountsController.js +359 -0
- package/lib/client/QaAccountsController.js.map +1 -0
- package/lib/client/QaSessionController.js +238 -180
- package/lib/client/QaSessionController.js.map +1 -1
- package/lib/client/QaSurface.js +281 -105
- package/lib/client/QaSurface.js.map +1 -1
- package/lib/client/QaTranscriptAdapter.js +95 -244
- package/lib/client/QaTranscriptAdapter.js.map +1 -1
- package/lib/client/attachments.js +150 -0
- package/lib/client/attachments.js.map +1 -0
- package/lib/client/attestation.js +7 -1
- package/lib/client/attestation.js.map +1 -1
- package/lib/client/base64.js +16 -0
- package/lib/client/base64.js.map +1 -0
- package/lib/client/chat-files.js +48 -0
- package/lib/client/chat-files.js.map +1 -0
- package/lib/client/components/Markdown.js +54 -13
- package/lib/client/components/Markdown.js.map +1 -1
- package/lib/client/components/QaAuthGate.js +40 -0
- package/lib/client/components/QaAuthGate.js.map +1 -0
- package/lib/client/components/QaChangelog.js +131 -0
- package/lib/client/components/QaChangelog.js.map +1 -0
- package/lib/client/components/QaComposer.js +56 -74
- package/lib/client/components/QaComposer.js.map +1 -1
- package/lib/client/components/QaFileAttachment.js +19 -0
- package/lib/client/components/QaFileAttachment.js.map +1 -0
- package/lib/client/components/QaFilesPanel.js +45 -0
- package/lib/client/components/QaFilesPanel.js.map +1 -0
- package/lib/client/components/QaHeader.js +34 -0
- package/lib/client/components/QaHeader.js.map +1 -0
- package/lib/client/components/QaMessage.js +61 -18
- package/lib/client/components/QaMessage.js.map +1 -1
- package/lib/client/components/QaModal.js +31 -0
- package/lib/client/components/QaModal.js.map +1 -0
- package/lib/client/components/QaProfile.js +64 -0
- package/lib/client/components/QaProfile.js.map +1 -0
- package/lib/client/components/QaRightRail.js +15 -0
- package/lib/client/components/QaRightRail.js.map +1 -0
- package/lib/client/components/QaSidebar.js +68 -20
- package/lib/client/components/QaSidebar.js.map +1 -1
- package/lib/client/components/QaSourcesPanel.js +127 -0
- package/lib/client/components/QaSourcesPanel.js.map +1 -0
- package/lib/client/components/QaTurnRail.js +296 -0
- package/lib/client/components/QaTurnRail.js.map +1 -0
- package/lib/client/components/QaWelcomeNotice.js +125 -0
- package/lib/client/components/QaWelcomeNotice.js.map +1 -0
- package/lib/client/components/QaWidthHandle.js +147 -0
- package/lib/client/components/QaWidthHandle.js.map +1 -0
- package/lib/client/components/QaWorkGroup.js +19 -20
- package/lib/client/components/QaWorkGroup.js.map +1 -1
- package/lib/client/components/source-refs.js +78 -0
- package/lib/client/components/source-refs.js.map +1 -0
- package/lib/client/components/surface-utils.js +28 -0
- package/lib/client/components/surface-utils.js.map +1 -0
- package/lib/client/components/thinking-phrases.js +24 -0
- package/lib/client/components/thinking-phrases.js.map +1 -0
- package/lib/client/create-session.js +26 -0
- package/lib/client/create-session.js.map +1 -0
- package/lib/client/failure-copy.js +63 -0
- package/lib/client/failure-copy.js.map +1 -0
- package/lib/client/index.js +135 -16
- package/lib/client/index.js.map +1 -1
- package/lib/client/project-session-state.js +78 -0
- package/lib/client/project-session-state.js.map +1 -0
- package/lib/client/prompt-content.js +54 -0
- package/lib/client/prompt-content.js.map +1 -0
- package/lib/client/session-admission.js +38 -0
- package/lib/client/session-admission.js.map +1 -0
- package/lib/client/session-assets.js +52 -0
- package/lib/client/session-assets.js.map +1 -0
- package/lib/client/session-sources.js +61 -0
- package/lib/client/session-sources.js.map +1 -0
- package/lib/client/settings/card.js +143 -0
- package/lib/client/settings/card.js.map +1 -0
- package/lib/client/settings/fields.js +145 -0
- package/lib/client/settings/fields.js.map +1 -0
- package/lib/client/settings/format.js +255 -0
- package/lib/client/settings/format.js.map +1 -0
- package/lib/client/settings/sections/access.js +25 -0
- package/lib/client/settings/sections/access.js.map +1 -0
- package/lib/client/settings/sections/accounts.js +47 -0
- package/lib/client/settings/sections/accounts.js.map +1 -0
- package/lib/client/settings/sections/attachments.js +46 -0
- package/lib/client/settings/sections/attachments.js.map +1 -0
- package/lib/client/settings/sections/branding.js +28 -0
- package/lib/client/settings/sections/branding.js.map +1 -0
- package/lib/client/settings/sections/common.js +20 -0
- package/lib/client/settings/sections/common.js.map +1 -0
- package/lib/client/settings/sections/embedding.js +19 -0
- package/lib/client/settings/sections/embedding.js.map +1 -0
- package/lib/client/settings/sections/interface.js +60 -0
- package/lib/client/settings/sections/interface.js.map +1 -0
- package/lib/client/settings/sections/lockdown.js +82 -0
- package/lib/client/settings/sections/lockdown.js.map +1 -0
- package/lib/client/settings/sections/session.js +53 -0
- package/lib/client/settings/sections/session.js.map +1 -0
- package/lib/client/settings/sections/sources.js +72 -0
- package/lib/client/settings/sections/sources.js.map +1 -0
- package/lib/client/settings/sections/status.js +37 -0
- package/lib/client/settings/sections/status.js.map +1 -0
- package/lib/client/settings/sections.js +23 -0
- package/lib/client/settings/sections.js.map +1 -0
- package/lib/client/settings/styles.js +57 -0
- package/lib/client/settings/styles.js.map +1 -0
- package/lib/client/settlement.js +24 -0
- package/lib/client/settlement.js.map +1 -0
- package/lib/client/stream-publisher.js +47 -0
- package/lib/client/stream-publisher.js.map +1 -0
- package/lib/client/styles.js +225 -17
- package/lib/client/styles.js.map +1 -1
- package/lib/client/tool-presentation.js +102 -0
- package/lib/client/tool-presentation.js.map +1 -0
- package/lib/client/turn-sources.js +98 -0
- package/lib/client/turn-sources.js.map +1 -0
- package/lib/client/types.js +2 -0
- package/lib/client/types.js.map +1 -1
- package/lib/client/use-right-rail.js +96 -0
- package/lib/client/use-right-rail.js.map +1 -0
- package/lib/client/use-session-ui-state.js +75 -0
- package/lib/client/use-session-ui-state.js.map +1 -0
- package/lib/client/use-transcript-view.js +44 -0
- package/lib/client/use-transcript-view.js.map +1 -0
- package/lib/client.js +9052 -1345
- package/lib/client.js.map +1 -1
- package/lib/config-resolvers/accounts.js +60 -0
- package/lib/config-resolvers/accounts.js.map +1 -0
- package/lib/config-resolvers/attachments.js +28 -0
- package/lib/config-resolvers/attachments.js.map +1 -0
- package/lib/config-resolvers/basics.js +59 -0
- package/lib/config-resolvers/basics.js.map +1 -0
- package/lib/config-resolvers/defaults.js +132 -0
- package/lib/config-resolvers/defaults.js.map +1 -0
- package/lib/config-resolvers/lockdown.js +79 -0
- package/lib/config-resolvers/lockdown.js.map +1 -0
- package/lib/config-resolvers/session.js +42 -0
- package/lib/config-resolvers/session.js.map +1 -0
- package/lib/config-resolvers/shared.js +42 -0
- package/lib/config-resolvers/shared.js.map +1 -0
- package/lib/config-resolvers/sources.js +81 -0
- package/lib/config-resolvers/sources.js.map +1 -0
- package/lib/config-resolvers/ui.js +20 -0
- package/lib/config-resolvers/ui.js.map +1 -0
- package/lib/config.js +279 -102
- package/lib/config.js.map +1 -1
- package/lib/entry-redirect.js +45 -0
- package/lib/entry-redirect.js.map +1 -0
- package/lib/host-route.js +25 -1
- package/lib/host-route.js.map +1 -1
- package/lib/index.js +258 -23
- package/lib/index.js.map +1 -1
- package/lib/launch-token.js +42 -0
- package/lib/launch-token.js.map +1 -0
- package/lib/profile.js +214 -0
- package/lib/profile.js.map +1 -0
- package/lib/prompt-notes.js +231 -0
- package/lib/prompt-notes.js.map +1 -0
- package/lib/provenance/collector.js +40 -0
- package/lib/provenance/collector.js.map +1 -0
- package/lib/provenance/connectors.js +147 -0
- package/lib/provenance/connectors.js.map +1 -0
- package/lib/provenance/dedupe.js +89 -0
- package/lib/provenance/dedupe.js.map +1 -0
- package/lib/provenance/extractors.js +340 -0
- package/lib/provenance/extractors.js.map +1 -0
- package/lib/provenance/file-preview.js +39 -0
- package/lib/provenance/file-preview.js.map +1 -0
- package/lib/provenance/host-store.js +387 -0
- package/lib/provenance/host-store.js.map +1 -0
- package/lib/provenance/index.js +9 -0
- package/lib/provenance/index.js.map +1 -0
- package/lib/provenance/normalize.js +128 -0
- package/lib/provenance/normalize.js.map +1 -0
- package/lib/provenance/reported.js +53 -0
- package/lib/provenance/reported.js.map +1 -0
- package/lib/provenance/types.js +2 -0
- package/lib/provenance/types.js.map +1 -0
- package/lib/resolve-config.js +35 -249
- package/lib/resolve-config.js.map +1 -1
- package/lib/secure-session.js +122 -14
- package/lib/secure-session.js.map +1 -1
- package/lib/shared/session-key.js +18 -0
- package/lib/shared/session-key.js.map +1 -0
- package/lib/shared/settings.js +10 -0
- package/lib/shared/settings.js.map +1 -0
- package/lib/thinking-phrases.js +68 -0
- package/lib/thinking-phrases.js.map +1 -0
- package/lib/typert.host.js +749 -11
- package/lib/typert.remote-client.d.ts +23 -3
- package/lib/typert.remote-client.js +593 -6
- package/lib/types/account-remotes.d.ts +42 -0
- package/lib/types/accounts/credentials.d.ts +14 -0
- package/lib/types/accounts/errors.d.ts +11 -0
- package/lib/types/accounts/file.d.ts +82 -0
- package/lib/types/accounts/index.d.ts +2 -0
- package/lib/types/accounts/store.d.ts +144 -0
- package/lib/types/accounts/token.d.ts +31 -0
- package/lib/types/attachment-rules.d.ts +44 -0
- package/lib/types/attestation.d.ts +1 -1
- package/lib/types/cli.d.ts +9 -0
- package/lib/types/client/QaAccountsController.d.ts +102 -0
- package/lib/types/client/QaConfigController.d.ts +1 -1
- package/lib/types/client/QaSessionController.d.ts +61 -22
- package/lib/types/client/QaSurface.d.ts +14 -3
- package/lib/types/client/QaTranscriptAdapter.d.ts +5 -10
- package/lib/types/client/attachments.d.ts +54 -0
- package/lib/types/client/base64.d.ts +10 -0
- package/lib/types/client/chat-files.d.ts +30 -0
- package/lib/types/client/components/Markdown.d.ts +11 -1
- package/lib/types/client/components/QaAgentsDrawer.d.ts +1 -1
- package/lib/types/client/components/QaAuthGate.d.ts +15 -0
- package/lib/types/client/components/QaChangelog.d.ts +21 -0
- package/lib/types/client/components/QaComposer.d.ts +8 -4
- package/lib/types/client/components/QaFileAttachment.d.ts +14 -0
- package/lib/types/client/components/QaFilesPanel.d.ts +15 -0
- package/lib/types/client/components/QaHeader.d.ts +41 -0
- package/lib/types/client/components/QaMessage.d.ts +12 -3
- package/lib/types/client/components/QaModal.d.ts +26 -0
- package/lib/types/client/components/QaProfile.d.ts +25 -0
- package/lib/types/client/components/QaRightRail.d.ts +23 -0
- package/lib/types/client/components/QaSidebar.d.ts +44 -2
- package/lib/types/client/components/QaSourcesPanel.d.ts +23 -0
- package/lib/types/client/components/QaTurnRail.d.ts +65 -0
- package/lib/types/client/components/QaWelcomeNotice.d.ts +27 -0
- package/lib/types/client/components/QaWidthHandle.d.ts +43 -0
- package/lib/types/client/components/QaWorkGroup.d.ts +5 -3
- package/lib/types/client/components/source-refs.d.ts +16 -0
- package/lib/types/client/components/surface-utils.d.ts +11 -0
- package/lib/types/client/components/thinking-phrases.d.ts +9 -0
- package/lib/types/client/create-session.d.ts +13 -0
- package/lib/types/client/failure-copy.d.ts +19 -0
- package/lib/types/client/index.d.ts +1 -0
- package/lib/types/client/project-session-state.d.ts +30 -0
- package/lib/types/client/prompt-content.d.ts +30 -0
- package/lib/types/client/session-admission.d.ts +23 -0
- package/lib/types/client/session-assets.d.ts +20 -0
- package/lib/types/client/session-sources.d.ts +28 -0
- package/lib/types/client/settings/card.d.ts +23 -0
- package/lib/types/client/settings/fields.d.ts +139 -0
- package/lib/types/client/settings/format.d.ts +108 -0
- package/lib/types/client/settings/sections/access.d.ts +9 -0
- package/lib/types/client/settings/sections/accounts.d.ts +9 -0
- package/lib/types/client/settings/sections/attachments.d.ts +15 -0
- package/lib/types/client/settings/sections/branding.d.ts +8 -0
- package/lib/types/client/settings/sections/common.d.ts +28 -0
- package/lib/types/client/settings/sections/embedding.d.ts +8 -0
- package/lib/types/client/settings/sections/interface.d.ts +9 -0
- package/lib/types/client/settings/sections/lockdown.d.ts +9 -0
- package/lib/types/client/settings/sections/session.d.ts +9 -0
- package/lib/types/client/settings/sections/sources.d.ts +9 -0
- package/lib/types/client/settings/sections/status.d.ts +22 -0
- package/lib/types/client/settings/sections.d.ts +25 -0
- package/lib/types/client/settings/styles.d.ts +8 -0
- package/lib/types/client/settlement.d.ts +6 -0
- package/lib/types/client/stream-publisher.d.ts +28 -0
- package/lib/types/client/tool-presentation.d.ts +6 -0
- package/lib/types/client/turn-sources.d.ts +14 -0
- package/lib/types/client/types.d.ts +101 -9
- package/lib/types/client/use-right-rail.d.ts +56 -0
- package/lib/types/client/use-session-ui-state.d.ts +41 -0
- package/lib/types/client/use-transcript-view.d.ts +20 -0
- package/lib/types/config-resolvers/accounts.d.ts +15 -0
- package/lib/types/config-resolvers/attachments.d.ts +6 -0
- package/lib/types/config-resolvers/basics.d.ts +8 -0
- package/lib/types/config-resolvers/defaults.d.ts +4 -0
- package/lib/types/config-resolvers/lockdown.d.ts +10 -0
- package/lib/types/config-resolvers/session.d.ts +6 -0
- package/lib/types/config-resolvers/shared.d.ts +16 -0
- package/lib/types/config-resolvers/sources.d.ts +6 -0
- package/lib/types/config-resolvers/ui.d.ts +6 -0
- package/lib/types/entry-redirect.d.ts +20 -0
- package/lib/types/host-route.d.ts +14 -1
- package/lib/types/index.d.ts +54 -3
- package/lib/types/launch-token.d.ts +21 -0
- package/lib/types/profile.d.ts +76 -0
- package/lib/types/prompt-notes.d.ts +110 -0
- package/lib/types/provenance/collector.d.ts +26 -0
- package/lib/types/provenance/connectors.d.ts +11 -0
- package/lib/types/provenance/dedupe.d.ts +6 -0
- package/lib/types/provenance/extractors.d.ts +14 -0
- package/lib/types/provenance/file-preview.d.ts +10 -0
- package/lib/types/provenance/host-store.d.ts +54 -0
- package/lib/types/provenance/index.d.ts +10 -0
- package/lib/types/provenance/normalize.d.ts +13 -0
- package/lib/types/provenance/reported.d.ts +5 -0
- package/lib/types/provenance/types.d.ts +78 -0
- package/lib/types/resolve-config.d.ts +8 -3
- package/lib/types/secure-session.d.ts +36 -2
- package/lib/types/shared/session-key.d.ts +23 -0
- package/lib/types/shared/settings.d.ts +10 -0
- package/lib/types/thinking-phrases.d.ts +17 -0
- package/lib/types/types.d.ts +290 -16
- package/lib/types/user-workspace.d.ts +38 -0
- package/lib/user-workspace.js +231 -0
- package/lib/user-workspace.js.map +1 -0
- package/package.json +71 -47
- package/CHANGELOG.md +0 -93
- package/SPEC-dsh-qa-surface.md +0 -2425
- package/docs/ARCHITECTURE.md +0 -66
- package/docs/COMPATIBILITY.md +0 -14
- package/docs/CONFIGURATION.md +0 -112
- package/docs/EMBEDDING.md +0 -13
- package/lib/client/components/QaSourcesDrawer.js +0 -46
- package/lib/client/components/QaSourcesDrawer.js.map +0 -1
- 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
|