@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
package/README.md
CHANGED
|
@@ -16,8 +16,21 @@ Session and Agent Loop.
|
|
|
16
16
|
- optionally shows a minimal per-browser chat-history sidebar
|
|
17
17
|
(`ui.showSessionList`) whose switching re-runs policy attestation;
|
|
18
18
|
- blocks unsupported approvals/questions instead of auto-approving them;
|
|
19
|
-
- pins locked sessions to `read-only`
|
|
19
|
+
- pins locked sessions to the configured `read-only` or isolated
|
|
20
|
+
`workspace-write` policy plus `approval=never` before Send is enabled;
|
|
20
21
|
- applies a Host-side tool allow-list plus a monotonic execution guard;
|
|
22
|
+
- optionally gates the surface behind email + password accounts
|
|
23
|
+
(`accounts.enabled`) with server-side session ownership, a first-login
|
|
24
|
+
migration of the browser's existing chats, a `qa-accounts` management CLI
|
|
25
|
+
(list/add/set-role/disable/revoke), and a coarse honest boundary:
|
|
26
|
+
accounts identify QA users, they do not fence the harness root;
|
|
27
|
+
- optionally redirects non-loopback hostnames from the harness root into the
|
|
28
|
+
QA route (`entry.redirectNonLoopback`), keeping the operator's localhost
|
|
29
|
+
harness UI untouched;
|
|
30
|
+
- ships an operator settings card (Settings → Plugins → plugin configuration →
|
|
31
|
+
«Помощник QA») that edits the `qa-surface` namespace in place — route,
|
|
32
|
+
branding, session, interface, lockdown, accounts, sources, embedding — and
|
|
33
|
+
reports the configuration the running Host resolved;
|
|
21
34
|
- uses the existing same-origin DSH connection and trust boundary.
|
|
22
35
|
|
|
23
36
|
It does not add another HTTP server, provider proxy, permissive CORS rule, or
|
|
@@ -43,8 +56,8 @@ Some DSH patch operations replace a row's complete `config` instead of deeply
|
|
|
43
56
|
merging it. When editing an existing row, provide every value that deployment
|
|
44
57
|
needs.
|
|
45
58
|
|
|
46
|
-
DSH
|
|
47
|
-
to `index.html`. The Host half therefore claims only the configured QA
|
|
59
|
+
Tested DSH releases serve unknown frontend paths as 404 rather than falling
|
|
60
|
+
back to `index.html`. The Host half therefore claims only the configured QA
|
|
48
61
|
navigation path and redirects it through the canonical `/` document with a
|
|
49
62
|
short-lived query marker. The browser restores the requested `/qa` URL with
|
|
50
63
|
`history.replaceState` before mounting the overlay. No second server, duplicate
|
|
@@ -56,6 +69,17 @@ Configuration is registered under the Host settings namespace `qa-surface`.
|
|
|
56
69
|
Composition values form the base layer; normal DSH user settings can override
|
|
57
70
|
them when the deployment provides writable settings.
|
|
58
71
|
|
|
72
|
+
The browser half carries a settings card for that namespace: **Settings →
|
|
73
|
+
Plugins → plugin configuration → «Помощник QA»**. It writes the user layer of
|
|
74
|
+
`qa-surface` — so every change is revertible through the card's own reset — and
|
|
75
|
+
shows the configuration the running Host resolved next to it. Combinations the
|
|
76
|
+
Host refuses are either written together in one mutation (a provider with its
|
|
77
|
+
model, per-user workspaces with the `workspace-write` sandbox) or disabled with
|
|
78
|
+
the reason stated. The card renders only where the settings namespace is
|
|
79
|
+
readable, which the DSH gateway pins to loopback; a browser served over the LAN
|
|
80
|
+
reads the same configuration read-only through `qaSurface/describe` on the QA
|
|
81
|
+
page itself.
|
|
82
|
+
|
|
59
83
|
```yaml
|
|
60
84
|
config:
|
|
61
85
|
enabled: true
|
|
@@ -88,11 +112,35 @@ config:
|
|
|
88
112
|
showToolActivity: false
|
|
89
113
|
showReasoning: false
|
|
90
114
|
renderMarkdown: true
|
|
91
|
-
|
|
115
|
+
minContentWidth: 650
|
|
92
116
|
showSessionList: false
|
|
117
|
+
accounts:
|
|
118
|
+
enabled: false
|
|
119
|
+
allowRegistration: true
|
|
120
|
+
sessionTtlDays: 30
|
|
121
|
+
showOtherUsersChats: false
|
|
122
|
+
perUserWorkspace: false
|
|
123
|
+
profile:
|
|
124
|
+
enabled: true
|
|
125
|
+
inject: true
|
|
126
|
+
identities: [] # e.g. [{ key: jira, label: Jira }]
|
|
127
|
+
instructionsMaxLength: 2000
|
|
93
128
|
suggestedQuestions:
|
|
94
129
|
- Как запросить доступ?
|
|
95
130
|
- Где лежит инструкция?
|
|
131
|
+
# Phrases the running indicator cycles through (optional). An empty list
|
|
132
|
+
# restores the built-in ones.
|
|
133
|
+
thinkingPhrases:
|
|
134
|
+
- Уточняю по регламенту…
|
|
135
|
+
- Сверяюсь с инструкцией…
|
|
136
|
+
# What a visitor may attach (optional). Files are staged on the Host and
|
|
137
|
+
# read from the stored copy, so keep `read` in lockdown.toolPolicy.allow.
|
|
138
|
+
attachments:
|
|
139
|
+
textFiles: true
|
|
140
|
+
pastedTextLines: 200
|
|
141
|
+
maxFileBytes: 10485760
|
|
142
|
+
maxPending: 8
|
|
143
|
+
extensions: [md, txt, log, json, yaml, csv, sql]
|
|
96
144
|
interaction:
|
|
97
145
|
approvals: blocked
|
|
98
146
|
questions: unsupported
|
|
@@ -114,6 +162,38 @@ config:
|
|
|
114
162
|
toolPolicy:
|
|
115
163
|
mode: allow-list
|
|
116
164
|
allow: []
|
|
165
|
+
sources:
|
|
166
|
+
enabled: true
|
|
167
|
+
collect:
|
|
168
|
+
parentAgent: true
|
|
169
|
+
subagents: true
|
|
170
|
+
persistTurnEvent: true
|
|
171
|
+
display:
|
|
172
|
+
sidebar: true
|
|
173
|
+
footer: true
|
|
174
|
+
groupByKind: true
|
|
175
|
+
showDiscovered: false
|
|
176
|
+
showOriginBadges: false
|
|
177
|
+
maxInitiallyVisiblePerGroup: 8
|
|
178
|
+
webSearch:
|
|
179
|
+
promoteSearchResultsWithoutFetch: true
|
|
180
|
+
maxPromotedPerSearch: 5
|
|
181
|
+
dedupe:
|
|
182
|
+
normalizeUrls: true
|
|
183
|
+
stripTrackingParams: true
|
|
184
|
+
mergeFileRanges: true
|
|
185
|
+
filePreview:
|
|
186
|
+
enabled: true
|
|
187
|
+
markdownRenderedByDefault: true
|
|
188
|
+
allowRawToggle: true
|
|
189
|
+
maxBytes: 2000000
|
|
190
|
+
maxMarkdownRenderBytes: 1000000
|
|
191
|
+
subagents:
|
|
192
|
+
inheritSources: true
|
|
193
|
+
enableReportToolFallback: true
|
|
194
|
+
markIncompleteOpaqueRuns: true
|
|
195
|
+
legacy:
|
|
196
|
+
parseAssistantSourcesBlock: false
|
|
117
197
|
```
|
|
118
198
|
|
|
119
199
|
`workspaceId` is recommended for a deterministic assistant. Without it, DSH
|
|
@@ -121,6 +201,44 @@ uses the Host's normal default working directory. Put the system prompt, tools,
|
|
|
121
201
|
skills, knowledge connections and permission policy in `agentPreset`, not in
|
|
122
202
|
this UI plugin.
|
|
123
203
|
|
|
204
|
+
### Writable per-user research space
|
|
205
|
+
|
|
206
|
+
Set `accounts.perUserWorkspace: true` only together with accounts, a registered
|
|
207
|
+
`session.workspaceId`, `lockdown.enforceFixedWorkspace: true`, and a
|
|
208
|
+
`workspace-write` + `never` permission preset:
|
|
209
|
+
|
|
210
|
+
```yaml
|
|
211
|
+
session:
|
|
212
|
+
workspaceId: "<registered-workspace-uuid>"
|
|
213
|
+
accounts:
|
|
214
|
+
enabled: true
|
|
215
|
+
perUserWorkspace: true
|
|
216
|
+
lockdown:
|
|
217
|
+
enabled: true
|
|
218
|
+
enforceFixedWorkspace: true
|
|
219
|
+
sandboxMode: workspace-write
|
|
220
|
+
approvalPolicy: never
|
|
221
|
+
permissionPreset: qa-workspace-write
|
|
222
|
+
toolPolicy:
|
|
223
|
+
mode: allow-list
|
|
224
|
+
allow: [read, read_image, glob, grep, write, edit, web_search, web_fetch]
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
The Host resolves that Workspace record's path and creates
|
|
228
|
+
`<workspace>/.qa-users/<account UUID>` with private Unix directory mode. It
|
|
229
|
+
passes the child as session `cwd` but deliberately does not register or attach
|
|
230
|
+
it as another DSH Workspace. Chats therefore remain ordinary entries in the
|
|
231
|
+
global DSH session list rather than creating one Workspace row per account.
|
|
232
|
+
|
|
233
|
+
The boundary combines DSH `workspace-write` with a Host tool guard for both
|
|
234
|
+
read and write paths, canonicalizes existing ancestors to reject symlink
|
|
235
|
+
escapes, propagates the root to subagent sessions, rejects shell/process/LSP
|
|
236
|
+
and `dsh_git_*` escape hatches, limits one model-controlled write to 10 MiB,
|
|
237
|
+
and limits an account directory to 256 MiB. `web_fetch` plus `write` is the
|
|
238
|
+
intended bounded research-download path; there is no unrestricted URL-to-disk
|
|
239
|
+
or shell downloader. Account directories are persistent scratch space and are
|
|
240
|
+
not deleted automatically.
|
|
241
|
+
|
|
124
242
|
Model override is opt-in: `provider` and `model` must be set together. Slash
|
|
125
243
|
commands are rejected as plain QA input. Reasoning and tool details remain
|
|
126
244
|
hidden by default. Enabling `ui.showReasoning` and `ui.showToolActivity` adds a
|
|
@@ -129,6 +247,42 @@ then collapses to `Worked for ...` before the final answer. Tool capability is
|
|
|
129
247
|
still controlled exclusively by `lockdown.toolPolicy.allow`; the display flags
|
|
130
248
|
do not grant tools.
|
|
131
249
|
|
|
250
|
+
### User profile
|
|
251
|
+
|
|
252
|
+
Clicking the account name in the sidebar footer opens the signed-in user's
|
|
253
|
+
profile: full name, one handle per external system the deployment declares,
|
|
254
|
+
and free-form instructions about how they want answers. The Host hands both
|
|
255
|
+
to the QA agent as a note in the conversation, so "покажи мои задачи" resolves
|
|
256
|
+
to a tracker lookup with the right login instead of a question. Because a QA
|
|
257
|
+
preset can declare its persona the complete system prompt (the shipped
|
|
258
|
+
`qa-research` one does, which discards every plugin prompt section), the note
|
|
259
|
+
travels as injected context on the conversation instead of as prompt text. It
|
|
260
|
+
is written once per profile, and subagents of the chat get their own copy.
|
|
261
|
+
Values are self-declared and the note says so: the agent names the identifier
|
|
262
|
+
it searched by and asks when the results contradict the request. The feature
|
|
263
|
+
needs no switch beyond accounts, though `accounts.profile.enabled` and
|
|
264
|
+
`inject` exist for deployments that want the form without the note, or
|
|
265
|
+
neither:
|
|
266
|
+
|
|
267
|
+
```yaml
|
|
268
|
+
accounts:
|
|
269
|
+
enabled: true
|
|
270
|
+
profile:
|
|
271
|
+
identities:
|
|
272
|
+
- key: jira
|
|
273
|
+
label: Jira
|
|
274
|
+
- key: gitlab
|
|
275
|
+
label: GitLab
|
|
276
|
+
instructionsMaxLength: 2000
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
`qa-accounts profile <email>` fills the same fields from an operator shell
|
|
280
|
+
(`--identity jira=i.ivanov`, `--instructions-file`, `--clear-identity`), which
|
|
281
|
+
is how a fresh deployment gets everyone's handles in place before users log
|
|
282
|
+
in. See
|
|
283
|
+
[Configuration](https://github.com/xarleyn/dsh-plugins/blob/main/plugins/dsh-qa-surface/docs/CONFIGURATION.md)
|
|
284
|
+
for the limits and the prompt's exact wording.
|
|
285
|
+
|
|
132
286
|
Session policies:
|
|
133
287
|
|
|
134
288
|
- `browser-persistent` restores the id stored in this browser, replacing stale
|
|
@@ -151,12 +305,26 @@ prompt and the answer arrives as a follow-up turn; the projection hides that
|
|
|
151
305
|
instruction and the consecutive turns read as variants of one question,
|
|
152
306
|
navigable with a `< 2/2 >` switcher (newest shown by default).
|
|
153
307
|
|
|
154
|
-
Sources
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
308
|
+
Sources are structured Host-owned provenance, independent of
|
|
309
|
+
`ui.showToolActivity`. Successful reads/fetches, bounded web-search evidence,
|
|
310
|
+
Jira/Confluence/knowledge results, and inherited subagent sources are
|
|
311
|
+
normalized and deduplicated into one turn bundle. That exact bundle feeds the
|
|
312
|
+
answer footer and the right rail's sources tab and is persisted as
|
|
313
|
+
`qa/sources`, so reload does not rerun tools. Search-only discovery stays
|
|
314
|
+
hidden by default.
|
|
315
|
+
|
|
316
|
+
Local file cards open a source-scoped, read-only preview after Host-side real
|
|
317
|
+
path validation against the attested session root. Markdown opens rendered by
|
|
318
|
+
default with an HTML-free renderer and offers `Rendered / Raw`; raw mode jumps
|
|
319
|
+
to recorded line ranges. The endpoint cannot browse or write files and refuses
|
|
320
|
+
paths that are not evidence in the canonical bundle.
|
|
321
|
+
|
|
322
|
+
Observable local subagents are inherited recursively. The internal
|
|
323
|
+
`qa_report_sources` tool covers opaque delegated providers and is admitted as
|
|
324
|
+
a provenance-only capability even when it is not listed among ordinary QA
|
|
325
|
+
tools. A provider that neither exposes events nor reports sources marks the
|
|
326
|
+
turn provenance incomplete. A note in the conversation tells the model not to
|
|
327
|
+
append a manual `Sources`/`Источники` bibliography.
|
|
160
328
|
|
|
161
329
|
Images: the composer accepts PNG/JPEG/WebP/GIF via drag & drop onto the
|
|
162
330
|
composer, paste, and the picker button, several at once (soft client caps:
|
|
@@ -165,6 +333,30 @@ promotes to durable attachments, so they survive reloads; sent images
|
|
|
165
333
|
render as clickable thumbnails on the message. Whether the model can see
|
|
166
334
|
them depends on the deployment's model (vision).
|
|
167
335
|
|
|
336
|
+
Attachments: the same picker, drop zone and paste path also take text files
|
|
337
|
+
(`md`, `txt`, `log`, and the other extensions in `attachments.extensions`), and
|
|
338
|
+
pasted text longer than `attachments.pastedTextLines` (default 200) becomes an
|
|
339
|
+
attachment named after its line count instead of filling the input field. A
|
|
340
|
+
file is staged on the Host through the browser upload service and the prompt
|
|
341
|
+
cites the returned receipt, so the durable copy survives reloads; the transcript
|
|
342
|
+
shows it as an extension badge, its name and its size. Unlike an image, a file
|
|
343
|
+
reaches the model as the path of that stored copy rather than as content, so
|
|
344
|
+
`lockdown.toolPolicy.allow` has to keep `read` for an attachment to be usable.
|
|
345
|
+
`attachments.textFiles: false` restricts the composer to images again, while
|
|
346
|
+
`maxFileBytes` and `maxPending` cap one file and the combined number of images
|
|
347
|
+
plus files per message.
|
|
348
|
+
|
|
349
|
+
The right rail is the chat's side panel, mirroring the Harness right Sidebar's
|
|
350
|
+
pattern: a tab strip is the panel's whole top edge, and the strip's close
|
|
351
|
+
control collapses the column. The sources tab carries the grouped list and
|
|
352
|
+
preview the sources drawer used to render — a message footnote opens it pinned
|
|
353
|
+
to that answer's subset, and «Все источники» returns to the whole chat. The
|
|
354
|
+
«Файлы» tab (header button with a live count) lists every attachment this chat
|
|
355
|
+
sent, grouped per message newest first, with the same file handles the
|
|
356
|
+
transcript shows and image thumbnails resolved from the session's asset
|
|
357
|
+
repository; each group jumps back to its message. Below 600px the rail goes
|
|
358
|
+
full-bleed. The agents drawer keeps its own header drawer for now.
|
|
359
|
+
|
|
168
360
|
Subagents: the deployment may opt the delegation family (`subagent`,
|
|
169
361
|
`subagent_fork`, `send_message`, `list_agents`, `interrupt_agent`) into the
|
|
170
362
|
lockdown allow-list; the preset must mount them. Launches then render as
|
|
@@ -175,18 +367,35 @@ live transcript (composer disabled, one click back to the chat) - viewing
|
|
|
175
367
|
never attests or writes.
|
|
176
368
|
|
|
177
369
|
`ui.showSessionList: true` renders a minimal chat-history sidebar beside the
|
|
178
|
-
conversation.
|
|
370
|
+
conversation. By default it lists only the current user's chats: the
|
|
179
371
|
client keeps a per-browser id index under
|
|
180
372
|
`<storageKey>:v1:<route>:chats` in localStorage (capped at 50, most recently
|
|
181
373
|
used first) and intersects it with the Host session list, so users sharing the
|
|
182
|
-
deployment never see each other's chats.
|
|
374
|
+
deployment never see each other's chats. An admin can explicitly enable
|
|
375
|
+
`accounts.showOtherUsersChats: true` to add chats owned by other QA accounts,
|
|
376
|
+
grouped by owner. Switching re-runs the full policy
|
|
183
377
|
attestation; a chat the Host no longer lists is pruned from the index. Each
|
|
184
378
|
row carries a two-click delete control that removes the chat from this
|
|
185
379
|
browser's index; deleting the chat that is currently open continues in a
|
|
186
380
|
fresh attested session. Host-side sessions are not deleted — DSH 0.1.x
|
|
187
381
|
exposes no session-deletion seam. The sidebar hides below 600px viewports.
|
|
188
382
|
|
|
189
|
-
|
|
383
|
+
The sidebar footer shows the deployed plugin version. Clicking it opens a
|
|
384
|
+
changelog dialog with a curated per-version summary (features and fixes);
|
|
385
|
+
Escape or a backdrop click closes it.
|
|
386
|
+
|
|
387
|
+
On `/qa`, the plugin shadows DSH's stock `welcome-notice` onboarding entry and
|
|
388
|
+
renders a route-owned Russian testing disclosure. Keeping the visible dialog
|
|
389
|
+
in the QA overlay prevents DSH's blank-session onboarding lifecycle from
|
|
390
|
+
dismissing it when a question is submitted. It explains the DeepSeek Harness
|
|
391
|
+
preview foundation, QA review of questions and answers, the work-related
|
|
392
|
+
scope, and local in-contour model processing. Explicit acknowledgement is
|
|
393
|
+
stored as a versioned browser-local flag; changing the disclosure version
|
|
394
|
+
shows it again. Other DSH routes retain the stock onboarding entry.
|
|
395
|
+
|
|
396
|
+
The default locked mode requires a deployment permission preset named
|
|
397
|
+
`qa-read-only`. Per-user writable mode uses a separate preset such as
|
|
398
|
+
`qa-workspace-write`.
|
|
190
399
|
Extend the existing `@deepseek-ai/dsh-permission-presets` row without changing
|
|
191
400
|
its process-wide default:
|
|
192
401
|
|
|
@@ -209,9 +418,14 @@ its process-wide default:
|
|
|
209
418
|
approval: never
|
|
210
419
|
name: QA Read Only
|
|
211
420
|
description: No filesystem mutations and no permission escalation.
|
|
421
|
+
qa-workspace-write:
|
|
422
|
+
sandbox: workspace-write
|
|
423
|
+
approval: never
|
|
424
|
+
name: QA User Workspace
|
|
425
|
+
description: Writes only inside the attested per-user workspace.
|
|
212
426
|
```
|
|
213
427
|
|
|
214
|
-
The shipped tool allow-list is empty. Add only reviewed
|
|
428
|
+
The shipped tool allow-list is empty. Add only reviewed tool names
|
|
215
429
|
from the actual deployment. A name that is not registered fails closed. The
|
|
216
430
|
Host restriction retains exact allow-listed tools from the agent preset's
|
|
217
431
|
ancestor scope, and the additional execution guard also denies session-scoped
|
|
@@ -264,14 +478,16 @@ What a LAN deployment does not change:
|
|
|
264
478
|
`/qa`. Scope the port's reachability (subnet-limited firewall rule, VPN or
|
|
265
479
|
tailnet, authenticating reverse proxy) and use a dedicated process identity.
|
|
266
480
|
- Loopback-only settings, directory picking and credential RPCs stay refused
|
|
267
|
-
for LAN clients
|
|
268
|
-
|
|
481
|
+
for LAN clients. On `/qa`, the plugin replaces DSH's non-persistent welcome
|
|
482
|
+
step with a QA-specific testing disclosure and remembers its exact copy
|
|
483
|
+
version in that browser's local storage. The stock DSH notice remains
|
|
484
|
+
unchanged on operator routes.
|
|
269
485
|
|
|
270
|
-
See [Configuration](docs/CONFIGURATION.md) for the config-channel details.
|
|
486
|
+
See [Configuration](https://github.com/xarleyn/dsh-plugins/blob/main/plugins/dsh-qa-surface/docs/CONFIGURATION.md) for the config-channel details.
|
|
271
487
|
|
|
272
|
-
See [Architecture](docs/ARCHITECTURE.md),
|
|
273
|
-
[Configuration](docs/CONFIGURATION.md), and
|
|
274
|
-
[Compatibility](docs/COMPATIBILITY.md).
|
|
488
|
+
See [Architecture](https://github.com/xarleyn/dsh-plugins/blob/main/plugins/dsh-qa-surface/docs/ARCHITECTURE.md),
|
|
489
|
+
[Configuration](https://github.com/xarleyn/dsh-plugins/blob/main/plugins/dsh-qa-surface/docs/CONFIGURATION.md), and
|
|
490
|
+
[Compatibility](https://github.com/xarleyn/dsh-plugins/blob/main/plugins/dsh-qa-surface/docs/COMPATIBILITY.md).
|
|
275
491
|
|
|
276
492
|
## Develop
|
|
277
493
|
|
package/compatibility.json
CHANGED
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"deepseekHarness": {
|
|
3
3
|
"channel": "next",
|
|
4
|
-
"range": ">=0.1.
|
|
5
|
-
"testedReleases": ["0.1.
|
|
4
|
+
"range": ">=0.1.5-rc.2 <0.2.0",
|
|
5
|
+
"testedReleases": ["0.1.5-rc.2"],
|
|
6
6
|
"requiredClientFeatures": [
|
|
7
7
|
"shell.overlay",
|
|
8
|
-
"
|
|
8
|
+
"remote.session.selectModel",
|
|
9
|
+
"remote.agentPresets.select",
|
|
10
|
+
"uiConversation.binding",
|
|
9
11
|
"sessions.binding",
|
|
10
12
|
"settingsScope",
|
|
13
|
+
"settings.onboarding list-slot shadowing",
|
|
11
14
|
"webServer.register",
|
|
12
15
|
"typert.remote",
|
|
13
16
|
"agentPresets.composedPreset",
|
|
14
17
|
"permissionPresets.set",
|
|
15
18
|
"tools.restrict",
|
|
16
19
|
"tools.guard",
|
|
20
|
+
"tools.register",
|
|
21
|
+
"session.event-extension",
|
|
22
|
+
"agent.turn-stopping",
|
|
23
|
+
"agent.currentInitiator",
|
|
24
|
+
"systemPrompt.section",
|
|
17
25
|
"workspaceRegistry.get"
|
|
18
26
|
]
|
|
19
27
|
},
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { QaAccounts, QaAccountsError, defaultAccountsFilePath, } from "./accounts/store.js";
|
|
2
|
+
const ACCOUNTS_DISABLED_ERROR = "QA accounts are not enabled on this deployment.";
|
|
3
|
+
/** Build the account-remotes context for one `QaSurface` service instance. */
|
|
4
|
+
export function createQaAccountRemotes(options) {
|
|
5
|
+
const { getConfig, logger } = options;
|
|
6
|
+
// Memoized across remotes: rebuilt only when the account-affecting options
|
|
7
|
+
// change; the file is shared across rebuilds.
|
|
8
|
+
let accounts;
|
|
9
|
+
let accountsOptions;
|
|
10
|
+
const resolve = (config) => {
|
|
11
|
+
if (!config.accounts.enabled)
|
|
12
|
+
return undefined;
|
|
13
|
+
const memoKey = JSON.stringify([
|
|
14
|
+
config.accounts.sessionTtlDays,
|
|
15
|
+
config.accounts.allowRegistration,
|
|
16
|
+
config.accounts.profile.instructionsMaxLength,
|
|
17
|
+
config.accounts.profile.identities,
|
|
18
|
+
]);
|
|
19
|
+
if (accounts === undefined || accountsOptions !== memoKey) {
|
|
20
|
+
accounts = new QaAccounts(defaultAccountsFilePath(), {
|
|
21
|
+
sessionTtlDays: config.accounts.sessionTtlDays,
|
|
22
|
+
allowRegistration: config.accounts.allowRegistration,
|
|
23
|
+
instructionsMaxLength: config.accounts.profile.instructionsMaxLength,
|
|
24
|
+
identityFields: config.accounts.profile.identities,
|
|
25
|
+
});
|
|
26
|
+
accountsOptions = memoKey;
|
|
27
|
+
}
|
|
28
|
+
return accounts;
|
|
29
|
+
};
|
|
30
|
+
/** The accounts store, or the disabled refusal the browser maps to copy. */
|
|
31
|
+
const requireAccounts = () => {
|
|
32
|
+
const store = resolve(getConfig());
|
|
33
|
+
if (store === undefined) {
|
|
34
|
+
throw new Error(ACCOUNTS_DISABLED_ERROR);
|
|
35
|
+
}
|
|
36
|
+
return store;
|
|
37
|
+
};
|
|
38
|
+
const run = (operation, sessionIdForLog) => {
|
|
39
|
+
try {
|
|
40
|
+
return operation();
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (error instanceof QaAccountsError) {
|
|
44
|
+
logger.warn("accounts.rejected", {
|
|
45
|
+
reason: error.reason,
|
|
46
|
+
sessionId: sessionIdForLog,
|
|
47
|
+
});
|
|
48
|
+
throw new Error(`QA accounts refused the request (reason: ${error.reason})`, {
|
|
49
|
+
cause: error,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
return {
|
|
56
|
+
resolve,
|
|
57
|
+
run,
|
|
58
|
+
register: (email, password) => {
|
|
59
|
+
const store = requireAccounts();
|
|
60
|
+
return run(() => store.register(email, password));
|
|
61
|
+
},
|
|
62
|
+
login: (email, password) => {
|
|
63
|
+
const store = requireAccounts();
|
|
64
|
+
return run(() => store.login(email, password));
|
|
65
|
+
},
|
|
66
|
+
whoami: (token) => {
|
|
67
|
+
if (!getConfig().accounts.enabled)
|
|
68
|
+
return { authenticated: false };
|
|
69
|
+
const store = requireAccounts();
|
|
70
|
+
return run(() => store.whoami(token));
|
|
71
|
+
},
|
|
72
|
+
claimSessions: (token, sessionIds) => {
|
|
73
|
+
const store = requireAccounts();
|
|
74
|
+
return run(() => store.claimSessions(token, sessionIds));
|
|
75
|
+
},
|
|
76
|
+
ownedSessions: (token) => {
|
|
77
|
+
if (!getConfig().accounts.enabled)
|
|
78
|
+
return { ids: [] };
|
|
79
|
+
const store = requireAccounts();
|
|
80
|
+
return run(() => ({ ids: store.ownedSessionIds(token) }));
|
|
81
|
+
},
|
|
82
|
+
listOwnership: (token) => {
|
|
83
|
+
if (!getConfig().accounts.enabled)
|
|
84
|
+
return { entries: [] };
|
|
85
|
+
const store = requireAccounts();
|
|
86
|
+
return run(() => ({ entries: store.listOwnership(token) }));
|
|
87
|
+
},
|
|
88
|
+
updateProfile: (token, input) => {
|
|
89
|
+
const config = getConfig();
|
|
90
|
+
const store = requireAccounts();
|
|
91
|
+
return run(() => {
|
|
92
|
+
if (!config.accounts.profile.enabled) {
|
|
93
|
+
throw new QaAccountsError("profile-disabled", "self-service profiles are disabled on this deployment");
|
|
94
|
+
}
|
|
95
|
+
return store.updateOwnProfile(token, input);
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=account-remotes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-remotes.js","sourceRoot":"","sources":["../src/account-remotes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { QaAccountsError } from "./errors.js";
|
|
2
|
+
const MIN_PASSWORD_LENGTH = 8;
|
|
3
|
+
const MAX_PASSWORD_LENGTH = 200;
|
|
4
|
+
const MAX_DISPLAY_NAME_LENGTH = 100;
|
|
5
|
+
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/u;
|
|
6
|
+
/**
|
|
7
|
+
* The email/password/display-name gate shared by self-registration and the
|
|
8
|
+
* operator's addUser, so both paths reject unusable input with the same
|
|
9
|
+
* errors and messages, in the same order. Returns the normalized address and
|
|
10
|
+
* trimmed display name; the caller still decides uniqueness and role.
|
|
11
|
+
*/
|
|
12
|
+
export function validateCredentials(email, password, displayName) {
|
|
13
|
+
const normalized = email.trim().toLowerCase();
|
|
14
|
+
if (!EMAIL_PATTERN.test(normalized) || normalized.length > 254) {
|
|
15
|
+
throw new QaAccountsError("invalid-email", "email is not a usable address");
|
|
16
|
+
}
|
|
17
|
+
if (password.length < MIN_PASSWORD_LENGTH ||
|
|
18
|
+
password.length > MAX_PASSWORD_LENGTH) {
|
|
19
|
+
throw new QaAccountsError("weak-password", `password must be ${MIN_PASSWORD_LENGTH} to ${MAX_PASSWORD_LENGTH} characters`);
|
|
20
|
+
}
|
|
21
|
+
const name = (displayName ?? "").trim();
|
|
22
|
+
if (name.length > MAX_DISPLAY_NAME_LENGTH) {
|
|
23
|
+
throw new QaAccountsError("invalid-display-name", `display name must be at most ${MAX_DISPLAY_NAME_LENGTH} characters`);
|
|
24
|
+
}
|
|
25
|
+
return { email: normalized, displayName: name };
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/accounts/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAC9B,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAChC,MAAM,uBAAuB,GAAG,GAAG,CAAC;AACpC,MAAM,aAAa,GAAG,6BAA6B,CAAC;AASpD;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAa,EACb,QAAgB,EAChB,WAA+B;IAE/B,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC9C,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QAC/D,MAAM,IAAI,eAAe,CAAC,eAAe,EAAE,+BAA+B,CAAC,CAAC;IAC9E,CAAC;IACD,IACE,QAAQ,CAAC,MAAM,GAAG,mBAAmB;QACrC,QAAQ,CAAC,MAAM,GAAG,mBAAmB,EACrC,CAAC;QACD,MAAM,IAAI,eAAe,CACvB,eAAe,EACf,oBAAoB,mBAAmB,OAAO,mBAAmB,aAAa,CAC/E,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,IAAI,IAAI,CAAC,MAAM,GAAG,uBAAuB,EAAE,CAAC;QAC1C,MAAM,IAAI,eAAe,CACvB,sBAAsB,EACtB,gCAAgC,uBAAuB,aAAa,CACrE,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/accounts/errors.ts"],"names":[],"mappings":"AAqBA,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAE7B,MAAM;IADjB,YACW,MAA6B,EACtC,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;sBAHN,MAAM;QAIf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { mkdirSync, readFileSync, renameSync, statSync, writeFileSync, } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { base64Url } from "./token.js";
|
|
5
|
+
/** A stamp no real file can match, so the first load always reads the disk. */
|
|
6
|
+
export function createFileStampRef() {
|
|
7
|
+
return { current: { mtimeMs: Number.NEGATIVE_INFINITY, size: -1 } };
|
|
8
|
+
}
|
|
9
|
+
export function stampOf(stat) {
|
|
10
|
+
return { mtimeMs: stat.mtimeMs, size: stat.size };
|
|
11
|
+
}
|
|
12
|
+
const SECRET_BYTES = 32;
|
|
13
|
+
/** The accounts file lives next to the DSH home the launcher exports. */
|
|
14
|
+
export function defaultAccountsFilePath() {
|
|
15
|
+
const home = process.env.DSH_HOME?.trim();
|
|
16
|
+
return path.join(home !== undefined && home !== "" ? home : process.cwd(), "qa-accounts.json");
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Read the accounts file, or create it on first run with a fresh token
|
|
20
|
+
* secret. The stamp ref is left pointing at the version just read.
|
|
21
|
+
*/
|
|
22
|
+
export function loadAccountsFile(filePath, stamp) {
|
|
23
|
+
let raw;
|
|
24
|
+
let stat;
|
|
25
|
+
try {
|
|
26
|
+
raw = readFileSync(filePath, "utf8");
|
|
27
|
+
stat = statSync(filePath);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
// Only a missing file means "first run": anything else (permissions,
|
|
31
|
+
// a directory in the way, I/O failure) must surface, or the store
|
|
32
|
+
// would silently reset every account it cannot read.
|
|
33
|
+
if (error.code !== "ENOENT")
|
|
34
|
+
throw error;
|
|
35
|
+
const created = {
|
|
36
|
+
version: 1,
|
|
37
|
+
secret: base64Url(randomBytes(SECRET_BYTES)),
|
|
38
|
+
users: [],
|
|
39
|
+
ownership: {},
|
|
40
|
+
};
|
|
41
|
+
persistAccountsFile(filePath, created, stamp);
|
|
42
|
+
return created;
|
|
43
|
+
}
|
|
44
|
+
const parsed = JSON.parse(raw);
|
|
45
|
+
if (typeof parsed !== "object" ||
|
|
46
|
+
parsed === null ||
|
|
47
|
+
parsed.version !== 1 ||
|
|
48
|
+
typeof parsed.secret !== "string" ||
|
|
49
|
+
!Array.isArray(parsed.users)) {
|
|
50
|
+
throw new Error(`qa-accounts: ${filePath} is not a recognizable accounts file; refusing to overwrite it`);
|
|
51
|
+
}
|
|
52
|
+
const file = parsed;
|
|
53
|
+
if (stat !== undefined)
|
|
54
|
+
stamp.current = stampOf(stat);
|
|
55
|
+
return {
|
|
56
|
+
version: 1,
|
|
57
|
+
secret: file.secret,
|
|
58
|
+
users: file.users,
|
|
59
|
+
ownership: file.ownership ?? {},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Persist the accounts file atomically (temp file + rename) and leave the
|
|
64
|
+
* stamp ref pointing at the freshly written version.
|
|
65
|
+
*/
|
|
66
|
+
export function persistAccountsFile(filePath, file, stamp) {
|
|
67
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
68
|
+
// A per-process temp name: the Host and the CLI never race on one file.
|
|
69
|
+
const temp = `${filePath}.${process.pid}.tmp`;
|
|
70
|
+
writeFileSync(temp, `${JSON.stringify(file, null, 2)}\n`, "utf8");
|
|
71
|
+
renameSync(temp, filePath);
|
|
72
|
+
stamp.current = stampOf(statSync(filePath));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The qa-accounts CLI runs in its own process against the same file. Re-read
|
|
76
|
+
* it whenever it changed on disk, before every read or read-modify-write:
|
|
77
|
+
* without this a running Host would keep honoring tokens the CLI revoked
|
|
78
|
+
* and its next save would resurrect the CLI's change from a stale snapshot.
|
|
79
|
+
* One file, one writer at a time remains the deployment's discipline; LAN
|
|
80
|
+
* scale keeps the stat per operation trivial.
|
|
81
|
+
*
|
|
82
|
+
* Returns the freshly loaded file, or undefined when the caller should keep
|
|
83
|
+
* serving its in-memory state.
|
|
84
|
+
*/
|
|
85
|
+
export function reloadAccountsFileIfChanged(filePath, stamp) {
|
|
86
|
+
let stat;
|
|
87
|
+
try {
|
|
88
|
+
stat = statSync(filePath);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// The file vanished between operations (never by our hand): keep
|
|
92
|
+
// serving the in-memory state instead of resetting every account.
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
if (stat.mtimeMs === stamp.current.mtimeMs &&
|
|
96
|
+
stat.size === stamp.current.size) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
return loadAccountsFile(filePath, stamp);
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/accounts/file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/accounts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,eAAe,EACf,uBAAuB,GAGxB,MAAM,YAAY,CAAC"}
|