@h-ai/feedback 0.0.0-stage → 0.1.0-alpha.70
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/LICENSE +202 -0
- package/README.en.md +115 -0
- package/README.ja.md +115 -0
- package/README.md +116 -2
- package/dist/bundle/index.d.ts +212 -0
- package/dist/bundle/index.js +3 -0
- package/dist/bundle/index.js.map +1 -0
- package/dist/chunk-43AOXE4W.js +333 -0
- package/dist/chunk-43AOXE4W.js.map +1 -0
- package/dist/chunk-DYEK2FNM.js +101 -0
- package/dist/chunk-DYEK2FNM.js.map +1 -0
- package/dist/client/index.d.ts +187 -0
- package/dist/client/index.js +889 -0
- package/dist/client/index.js.map +1 -0
- package/dist/index.d.ts +154 -0
- package/dist/index.js +1396 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [2026] [idealworld.group]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.en.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# @h-ai/feedback
|
|
2
|
+
|
|
3
|
+
[简体中文](README.md) | [English](README.en.md) | [日本語](README.ja.md)
|
|
4
|
+
|
|
5
|
+
[Framework](../../README.en.md)
|
|
6
|
+
|
|
7
|
+
## Introduction
|
|
8
|
+
|
|
9
|
+
Collect user feedback and diagnostic evidence, notify administrators, review reports and export offline bundles. Descriptions are optional. Reports and structured evidence use a relational database; screenshots and binary attachments use private object storage. A separate telemetry service is not required.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Bounded browser/server logs, requests, errors, interactions, versions and allowed environment fields; up to 100 log records before and after an error by default.
|
|
14
|
+
- Transactional intake, stable submission/evidence identifiers, checksum validation, independent processing/collection/notification state and durable leased jobs.
|
|
15
|
+
- Existing Storage S3 and Reach SMTP implementations; this version requires Reach DND disabled so feedback owns the only notification outbox.
|
|
16
|
+
- Browser screenshots, irreversible masking, annotations, optional rrweb recording, retries and local fallback export; `FeedbackModal`, `FeedbackList` and `FeedbackDetail` in `@h-ai/ui`.
|
|
17
|
+
- Self-contained ZIP files, SHA-256 validation, an offline viewer and paginated evidence queries without production credentials.
|
|
18
|
+
|
|
19
|
+
## Getting started
|
|
20
|
+
|
|
21
|
+
Supply `appVersion/buildId/commit` from each actual browser or server artifact. A browser must retain its own identity; one SSR build shares its buildId across client and server output. Admin Console and generated base/API/fullstack projects inline these values. `hai build` creates internal metadata and passes it through Docker build arguments. Each target reads its own package.json version; a missing owning Git commit is `unknown`. Template `build-metadata.json` contains only these three nonsecret fields and matches the inlined values.
|
|
22
|
+
|
|
23
|
+
Initialize core and check initialization of reldb, storage and reach before feedback. Production requires shared PostgreSQL/MySQL, private S3 and a real SMTP provider. The application owns credentials and connection lifecycles. Local development providers and Console notifications are not proof of actual mail delivery.
|
|
24
|
+
|
|
25
|
+
The following snippet runs inside an async function after dependencies are initialized. Trusted server authentication and authorization must supply the context:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { feedback } from '@h-ai/feedback'
|
|
29
|
+
|
|
30
|
+
const initialized = await feedback.init({
|
|
31
|
+
appId: 'orders',
|
|
32
|
+
appVersion: '1.0.0',
|
|
33
|
+
recipients: ['admin@example.test'],
|
|
34
|
+
notificationProvider: 'email',
|
|
35
|
+
adminUrl: 'https://app.example.test/admin/feedback',
|
|
36
|
+
})
|
|
37
|
+
if (!initialized.success)
|
|
38
|
+
return initialized
|
|
39
|
+
const context = { scopeId: 'tenant-1', userId: 'user-1', permissions: ['feedback:create'] }
|
|
40
|
+
const submitted = await feedback.submit({ appId: 'orders', submissionId: 'stable-client-operation' }, context)
|
|
41
|
+
if (!submitted.success)
|
|
42
|
+
return submitted
|
|
43
|
+
return submitted
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Check `await feedback.close()` before closing reach/storage/reldb. Retries retain the original submission ID. The report number confirms durable intake; evidence collection and mail have separate states.
|
|
47
|
+
|
|
48
|
+
Mount `createFeedbackProcedures({ feedback, resolveContext })` from `@h-ai/serv/features/feedback` with `apiContract.feedback`. The host resolves scope, user and permissions from trusted server state. Users can append only their own material; administrative read/process/reopen/export permissions and report scope are enforced for evidence and attachment access too.
|
|
49
|
+
|
|
50
|
+
Scope, user, application and evidence identifiers use exact string matching, including on MySQL with case/accent insensitive or trailing-space collations. Attachment IDs retain their original case and accept 1–128 characters of valid Unicode. ZIP paths use generated digests; consumers must read the manifest instead of deriving paths from IDs or display names. Unpaired surrogates are rejected before intake.
|
|
51
|
+
|
|
52
|
+
Use `createFeedbackClient()` from `/client`, then `init({ appId, actions })` with typed submit, appendEvidence, completeCollection and upload actions. Call `prepare()` before displaying the modal, submit for a receipt, and allow background flush to upload material. Close releases listeners and recorders. Cookie allowlists, persisted drafts and replay require explicit product policy; credential values are excluded.
|
|
53
|
+
|
|
54
|
+
Browser and server text redaction share rules for common credential keys, JSON/escaped JSON and Bearer/Basic authorization values. Stored evidence, API results and offline bundles retain the same redaction. The host should configure allowlists and sensitive DOM markers for application specific data.
|
|
55
|
+
|
|
56
|
+
`text/plain` attachments are redacted in full within the 5 MiB file limit, preserving safe content and trailing log entries without structured evidence's per-field truncation. Oversized original files are rejected before reading, and redacted output remains subject to the same attachment byte limit. JSON attachments and structured evidence retain field length, depth and count limits and may be trimmed; use plain text for complete logs. The same rules apply to pending attachments and files added to a local export after the first submission envelope is frozen.
|
|
57
|
+
|
|
58
|
+
The complete client exposes `init(config)`, `prepare/submit/flush/record/getDraft/annotate/addFiles/exportDraft(input?)/close`. Connect form `onfileschange` to `addFiles` and refresh draft attachment previews so new images can be marked, masked or removed before submission. Use `data-feedback-action` for key interactions, `data-feedback-private` for sensitive content and `data-feedback-ignore` for the feedback UI itself. The first transmitted envelope is frozen; changing input during a retry returns a conflict and preserves the input for local export.
|
|
59
|
+
|
|
60
|
+
`addFiles` adds each batch atomically: all files must pass validation before merging. Any failure preserves existing attachments and missing markers without saving a partial batch. Successful concurrent batches preserve each other's files, with at most 20 merged attachments. If the draft changes or the first envelope freezes during reading, it returns `CONFLICT` without adding hidden uploads outside that envelope.
|
|
61
|
+
|
|
62
|
+
`annotate(id, annotation)` uses pixels in the original image after browser decoding. The UI's `FeedbackAnnotation.rect` uses 0–1 coordinates: after `Image.decode()`, multiply x/width by `naturalWidth` and y/height by `naturalHeight` before calling the SDK. Use these intrinsic dimensions rather than CSS dimensions so JPEG orientation matches SDK decoding. The creator releases object URLs when replaced or disposed.
|
|
63
|
+
|
|
64
|
+
Concurrent `init` returns `INIT_IN_PROGRESS`; `close` drains bounded screenshots before clearing the session. Closing during capture makes `prepare` fail without resurrecting a draft. An annotation that finishes after submission, close, image removal or another annotation returns `CONFLICT`, preserving submitted bytes and checksums.
|
|
65
|
+
|
|
66
|
+
The SDK owns the draft and updates it through methods such as `annotate` and `addFiles`. Svelte hosts should keep this reference in `$state.raw<FeedbackDraft>()` and refresh attachment views from `getDraft()` after successful operations. A deep `$state` proxy can retain cached image bytes after the SDK replaces them on the original draft.
|
|
67
|
+
|
|
68
|
+
A 5 MiB raw attachment needs about 6.67 MiB of Base64 plus JSON fields. The reference admin and new API/fullstack projects with feedback explicitly set the existing `kit.transport.maxPlaintextBytes` or `serv.transport.maxPlaintextBytes` to `8388608` (8 MiB); the framework default remains 1 MiB. Clients follow the server handshake unless a smaller explicit local budget rejects the request. An 8 MiB plaintext budget derives an encrypted body budget of `16,809,984` bytes; production adapter-node `BODY_SIZE_LIMIT` and outer proxies must match it, or the actual JSON size when transport encryption is disabled. Framework deployment derives these limits automatically without a new feedback setting. `hai add` preserves existing host configuration, so the host must adjust capacity explicitly.
|
|
69
|
+
|
|
70
|
+
`captureConsole` and `persistDraft` default to false. Persistence requires a host supplied, noncredential user/tenant `persistenceKey`. `keyScreenshots` defaults to true and follows the main `screenshot` switch; `maxKeyScreenshots` defaults to 3 with a hard limit of 5, and `keyScreenshotThrottleMs` defaults to 10000 milliseconds.
|
|
71
|
+
|
|
72
|
+
Automatic screenshots reconstruct the visible page from a redacted DOM clone through SVG foreignObject. Cross-origin images, iframe/video content and network fonts that are not ready may be missing. Browser or CSP failures are recorded as missing, and users can still attach images manually.
|
|
73
|
+
|
|
74
|
+
### Reviewing and exporting
|
|
75
|
+
|
|
76
|
+
`list/get/getEvidence` return scoped reports, details and paginated evidence. `setStatus({ id, status, expectedVersion }, context)` protects processing/reopening against concurrent overwrites. `retry` queues recoverable operations. `export` fixes an evidence revision; `getExport` downloads the completed ZIP. Missing/truncated evidence is explicit. Replay displays recorded state and does not reexecute business behavior.
|
|
77
|
+
|
|
78
|
+
`getExport` first checks scope and `feedback:export` permission. A `pending/running` job with a valid snapshot returns `CONFLICT` (409, generating); clients should poll only this status with a bounded wait. A `failed` job returns `OPERATION_FAILED` (500); show the failure and let an administrator decide whether to `retry`. Missing jobs, jobs belonging to another report and expired unfinished snapshots return `NOT_FOUND` (404); missing export permission returns `FORBIDDEN` (403). Call `export` again for the current revision after its generation snapshot expires. Completed ZIP downloads retain storage-read and checksum checks.
|
|
79
|
+
|
|
80
|
+
An `errorId` query uses the anchor's source, request and sequence to select up to 100 log records before and after it. Overlapping errors keep separate anchors regardless of a record's first error assignment. Retention deletes reports, evidence, attachments, history and jobs containing snapshots, and removes orphan upload attempts by object prefix. Only a prefix/24-hour cutoff tombstone remains temporarily to sweep late objects before deletion; cleanup failures continue retrying.
|
|
81
|
+
|
|
82
|
+
### Offline commands
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
hai feedback summary feedback-FB-1.zip
|
|
86
|
+
hai feedback inspect feedback-FB-1.zip
|
|
87
|
+
hai feedback evidence feedback-FB-1.zip --request-id req-1 --page 1 --page-size 20
|
|
88
|
+
hai feedback view feedback-FB-1.zip
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The viewer binds only to loopback, uses a random URL and trusted built-in viewer resources, and closes on Ctrl+C. `/bundle` also exports create/read/summarize/query functions without server initialization or production connectivity.
|
|
92
|
+
|
|
93
|
+
### Configuration and errors
|
|
94
|
+
|
|
95
|
+
`FeedbackConfigSchema` validates appId, recipients, notificationProvider, adminUrl, collectionTimeoutMs, workerIntervalMs, leaseMs, maxAttempts, retentionDays, envAllowlist and attachment/evidence quotas. Explicit YAML environment mappings use `HAI_FEEDBACK_*`. Browser policy must not expose SMTP or S3 secrets.
|
|
96
|
+
|
|
97
|
+
Check `success` for every domain result. `HaiFeedbackError` covers initialization, configuration, scope, missing records, conflicts, quota and checksum failures. HTTP 200 and SMTP acceptance do not prove final business success or inbox delivery; uncertain sends can be repeated.
|
|
98
|
+
|
|
99
|
+
## Development and testing
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
pnpm --filter @h-ai/feedback typecheck
|
|
103
|
+
pnpm --filter @h-ai/feedback lint
|
|
104
|
+
pnpm --filter @h-ai/feedback build
|
|
105
|
+
pnpm --filter @h-ai/feedback test
|
|
106
|
+
pnpm --filter @h-ai/cli test:scaffold-gates
|
|
107
|
+
# After the builds above, verify the built viewer and replay in real Chromium without rebuilding dist.
|
|
108
|
+
node packages/cli/tests/feedback-offline-browser.mjs
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Validate browser flows, SQLite/PG/MySQL, real S3 and SMTP, multi-instance recovery and disconnected bundle viewing separately. Build/mock success cannot replace those checks. See the [usage guide](../../docs/usage/README.en.md) for application integration and deployment.
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
Apache-2.0
|
package/README.ja.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# @h-ai/feedback
|
|
2
|
+
|
|
3
|
+
[简体中文](README.md) | [English](README.en.md) | [日本語](README.ja.md)
|
|
4
|
+
|
|
5
|
+
[フレームワーク](../../README.ja.md)
|
|
6
|
+
|
|
7
|
+
## 概要
|
|
8
|
+
|
|
9
|
+
ユーザーの問題報告と診断情報の収集、管理者への通知、一覧処理、オフライン問題パッケージの出力を提供します。説明は任意です。報告と構造化された証拠は関係データベースに、スクリーンショットなどは非公開オブジェクトストレージに保存します。別の telemetry サービスは不要です。
|
|
10
|
+
|
|
11
|
+
## 主な機能
|
|
12
|
+
|
|
13
|
+
- ブラウザーとサーバーのログ、リクエスト、操作、バージョン、許可された環境情報を上限付きで収集します。既定ではエラーの前後それぞれ最大100件です。
|
|
14
|
+
- トランザクションによる受理、提出と証拠の冪等性、checksum 検証、処理・収集・通知状態の分離、永続ジョブとリース回復。
|
|
15
|
+
- Storage の S3 と Reach の SMTP を再利用します。初版は Reach DND を無効にし、feedback だけが通知再試行を管理します。
|
|
16
|
+
- スクリーンショット、復元不能なマスク、注釈、任意の rrweb 記録、再試行とローカル出力。UI は `FeedbackModal`、`FeedbackList`、`FeedbackDetail` を提供します。
|
|
17
|
+
- 自己完結 ZIP、SHA-256 検証、オフライン表示、証拠のページ検索。運用認証情報は含めません。
|
|
18
|
+
|
|
19
|
+
## はじめに
|
|
20
|
+
|
|
21
|
+
ブラウザーとサーバーには各実成果物の `appVersion/buildId/commit` を渡します。フロント側は自分の識別情報を保持し、同じ SSR ビルドの client/server 出力は buildId を共有します。管理画面と base/API/fullstack テンプレートにはコンパイル時の識別情報を組み込んでいます。`hai build` は内部で識別情報を生成し、Docker のビルド引数にも渡します。バージョンは各対象の package.json から取得し、所属する Git コミットがない場合は `unknown` を記録します。テンプレートの `build-metadata.json` には同じ三つの非機密項目のみを保存します。
|
|
22
|
+
|
|
23
|
+
core、reldb、storage、reach を初期化し、各結果を確認してから feedback を初期化します。本番は共有 PostgreSQL/MySQL、非公開 S3、実際の SMTP を使用します。接続と秘密情報はアプリケーションが管理します。開発用 Console 通知は実際のメール配信の証明にはなりません。
|
|
24
|
+
|
|
25
|
+
依存モジュール初期化済みの非同期関数内の例です。context は信頼できるサーバー認証・認可から取得します。
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { feedback } from '@h-ai/feedback'
|
|
29
|
+
|
|
30
|
+
const initialized = await feedback.init({
|
|
31
|
+
appId: 'orders',
|
|
32
|
+
appVersion: '1.0.0',
|
|
33
|
+
recipients: ['admin@example.test'],
|
|
34
|
+
notificationProvider: 'email',
|
|
35
|
+
adminUrl: 'https://app.example.test/admin/feedback',
|
|
36
|
+
})
|
|
37
|
+
if (!initialized.success)
|
|
38
|
+
return initialized
|
|
39
|
+
const context = { scopeId: 'tenant-1', userId: 'user-1', permissions: ['feedback:create'] }
|
|
40
|
+
const submitted = await feedback.submit({ appId: 'orders', submissionId: 'stable-client-operation' }, context)
|
|
41
|
+
if (!submitted.success)
|
|
42
|
+
return submitted
|
|
43
|
+
return submitted
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
終了時は `await feedback.close()` を確認した後で reach/storage/reldb を閉じます。再試行では同じ提出IDを使います。番号の発行は永続受理を示し、資料収集やメールの完了とは別です。
|
|
47
|
+
|
|
48
|
+
`@h-ai/serv/features/feedback` の `createFeedbackProcedures({ feedback, resolveContext })` を `apiContract.feedback` と組み合わせます。ホストは信頼できる scope、user、permissions を返します。一般ユーザーは自身の資料だけを追加でき、管理者向け read/process/reopen/export 権限と資料・添付の範囲をサーバーでも検証します。
|
|
49
|
+
|
|
50
|
+
範囲、ユーザー、アプリと証拠の識別子は文字列を厳密に比較します。MySQL の大小文字・アクセント・末尾空白の照合規則でも範囲を混同しません。添付 ID は元の値と大小文字を保ち、有効な Unicode の1〜128文字を受け付けます。ZIP 内のパスはダイジェストから生成するため、IDや表示名から推測せず manifest を参照してください。対応しないサロゲートは受理前に拒否します。
|
|
51
|
+
|
|
52
|
+
ブラウザーは `/client` の `createFeedbackClient()` を使用し、`init({ appId, actions })` に型付きAPI操作を渡します。モーダルを開く前に `prepare()` で現場を固定し、`submit()` 後も flush でアップロードを継続します。`close()` は記録器とイベントを解除します。Cookie の許可リスト、下書き永続化、再生記録は明示的な製品方針で有効化し、認証値は常に除外します。
|
|
53
|
+
|
|
54
|
+
ブラウザーとサーバーは共通のテキスト秘匿処理を使い、一般的な認証キー、JSON・エスケープされた JSON、Bearer/Basic の値を隠します。保存資料、API、オフライン ZIP でも同じ結果を維持します。業務固有の機密情報はホスト側の許可リストと機密 DOM マーカーで指定してください。
|
|
55
|
+
|
|
56
|
+
`text/plain` 添付は 5 MiB のファイル上限内で全文を秘匿処理し、安全な本文と末尾のログを保持します。構造化証拠のフィールド切り捨ては適用しません。上限を超える元ファイルは読取前に拒否し、秘匿処理後も同じ添付バイト上限を適用します。JSON 添付と構造化証拠には文字列長・深さ・件数の上限が残り、切り捨てられる場合があります。完全なログにはプレーンテキスト添付を使用してください。送信前の添付と、初回送信内容の固定後にローカル出力へ追加したファイルに同じ規則を適用します。
|
|
57
|
+
|
|
58
|
+
クライアントの全操作は `init(config)`、`prepare/submit/flush/record/getDraft/annotate/addFiles/exportDraft(input?)/close` です。フォームの `onfileschange` を `addFiles` に接続し、下書きの添付プレビューを更新すると、追加画像を送信前に注釈・遮蔽・削除できます。重要操作は `data-feedback-action`、機密部分は `data-feedback-private`、フィードバック画面自体の除外は `data-feedback-ignore` で指定します。初回送信後の内容は固定され、再試行中の入力変更は競合として入力を保持し、ローカル保存できます。
|
|
59
|
+
|
|
60
|
+
`addFiles` は一括追加を原子的に行い、全ファイルの検証に成功してから統合します。一つでも失敗した場合、既存の添付と欠落マーカーを維持し、一部だけを保存しません。並行して成功した追加を上書きせず、統合後も最大 20 件です。読取中に下書きが変わるか初回送信内容が固定された場合は `CONFLICT` を返し、送信内容に含まれない添付を後から隠れてアップロードしません。
|
|
61
|
+
|
|
62
|
+
`annotate(id, annotation)` の座標単位は、ブラウザーでデコードした元画像のピクセルです。UI の `FeedbackAnnotation.rect` は 0–1 の比率なので、`Image.decode()` 後に x/width を `naturalWidth`、y/height を `naturalHeight` と掛け合わせて SDK に渡します。CSS の表示サイズではなく元画像の寸法を使うことで、JPEG の向きを SDK のデコードと一致させます。オブジェクト URL は作成元が置換時または破棄時に解放します。
|
|
63
|
+
|
|
64
|
+
並行 `init` は `INIT_IN_PROGRESS` を返します。`close` は時間制限付き画面取得の終了後にセッションを解放します。取得中の終了は `prepare` を失敗させ、草稿を復活させません。送信、終了、画像削除、別の注釈更新後に完了した注釈は `CONFLICT` を返し、送信済みバイト列と checksum を変更しません。
|
|
65
|
+
|
|
66
|
+
下書きは SDK が保持し、`annotate` や `addFiles` で更新します。Svelte 側では参照を `$state.raw<FeedbackDraft>()` に保持し、操作成功後に `getDraft()` から添付ビューを更新してください。深い `$state` プロキシに格納すると、SDK が元の下書きの画像バイトを置き換えても、キャッシュされた古いバイトが返る場合があります。
|
|
67
|
+
|
|
68
|
+
添付の元データ上限は 5 MiB で、Base64 では約 6.67 MiB と JSON フィールドの容量が必要です。参照管理画面と feedback を有効にした新規 API/fullstack プロジェクトは、既存の `kit.transport.maxPlaintextBytes` または `serv.transport.maxPlaintextBytes` を `8388608`(8 MiB)に設定します。フレームワーク全体の既定値は 1 MiB のままです。クライアントは既定でサーバーのハンドシェイクに従いますが、明示したローカル上限が小さい場合は拒否します。8 MiB の平文から暗号化ボディ上限 `16,809,984` バイトを導出し、本番 adapter-node の `BODY_SIZE_LIMIT` と外側のプロキシも合わせます。暗号化を無効にする場合は実際の JSON サイズに合わせてください。フレームワークのデプロイが自動導出するため、新しい feedback 設定は不要です。`hai add` はホストの既存設定を保持するので、容量はホスト側で明示的に調整します。
|
|
69
|
+
|
|
70
|
+
`captureConsole` と `persistDraft` の既定値は false です。永続化にはホストが決定する非認証情報のユーザー/テナント `persistenceKey` が必要です。`keyScreenshots` は既定で true ですが `screenshot` 全体スイッチに従います。`maxKeyScreenshots` は既定 3、上限 5、`keyScreenshotThrottleMs` は既定 10000 ミリ秒です。
|
|
71
|
+
|
|
72
|
+
自動画像は機密部分を隠したクローン DOM から SVG foreignObject を通して可視ページを再構築します。クロスオリジン画像、iframe/video、未準備のネットワークフォントは欠落する場合があります。ブラウザーや CSP 制限による失敗は missing として記録され、画像の手動添付も利用できます。
|
|
73
|
+
|
|
74
|
+
### 一覧、処理、出力
|
|
75
|
+
|
|
76
|
+
`list/get/getEvidence` は認可された報告・詳細・証拠ページを返します。`setStatus({ id, status, expectedVersion }, context)` で処理と取り消しの競合を防ぎます。`retry` は回復可能な操作のみ再登録します。`export` は証拠の版を固定し、`getExport` は完成済みZIPを返します。欠落や切り捨ては明示され、ページ再生は業務処理の再実行ではありません。
|
|
77
|
+
|
|
78
|
+
`getExport` は最初に範囲と `feedback:export` 権限を検証します。有効なスナップショットの `pending/running` は `CONFLICT`(409、生成中)を返し、クライアントはこの状態だけを上限付きでポーリングします。`failed` は `OPERATION_FAILED`(500)を返し、失敗を表示して管理者が `retry` を判断します。存在しないタスク、別の報告に属するタスク、未完成で期限切れのスナップショットは `NOT_FOUND`(404)、権限不足は `FORBIDDEN`(403)です。現在の版の生成期限が切れた場合は `export` を再実行できます。完成済みZIPの取得時はストレージの読取結果と checksum を引き続き検証します。
|
|
79
|
+
|
|
80
|
+
`errorId` 検索はアンカーのソース、リクエスト、連番を基準に前後最大100件のログを選びます。重複するエラー窓でも初回の errorId 帰属に依存せず各アンカーを保持します。期限切れでは報告、証拠、添付、履歴、スナップショットを含むジョブと孤立アップロードを削除します。その後はオブジェクト接頭辞と24時間期限だけの墓標で遅延オブジェクトを再清掃し、墓標も削除します。清掃失敗は再試行を継続します。
|
|
81
|
+
|
|
82
|
+
### オフラインコマンド
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
hai feedback summary feedback-FB-1.zip
|
|
86
|
+
hai feedback inspect feedback-FB-1.zip
|
|
87
|
+
hai feedback evidence feedback-FB-1.zip --request-id req-1 --page 1 --page-size 20
|
|
88
|
+
hai feedback view feedback-FB-1.zip
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
表示サービスは loopback のランダムURLだけで待機し、内蔵の信頼済みコードを使います。Ctrl+Cで終了します。`/bundle` の作成・読込・要約・検索関数はサーバー初期化や本番接続なしで利用できます。
|
|
92
|
+
|
|
93
|
+
### 設定とエラー
|
|
94
|
+
|
|
95
|
+
`FeedbackConfigSchema` は appId、recipients、notificationProvider、adminUrl、collectionTimeoutMs、workerIntervalMs、leaseMs、maxAttempts、retentionDays、envAllowlist と資料の容量を検証します。YAML の環境参照は `HAI_FEEDBACK_*` を使い、ブラウザーへ SMTP/S3 の秘密を公開しません。
|
|
96
|
+
|
|
97
|
+
各業務結果の `success` を確認してください。`HaiFeedbackError` は初期化、設定、権限、存在しない記録、競合、容量、checksum エラーを表します。HTTP 200やSMTP受理だけでは、業務成功や受信箱への配信を証明できません。不確実な送信は重複する可能性があります。
|
|
98
|
+
|
|
99
|
+
## 開発とテスト
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
pnpm --filter @h-ai/feedback typecheck
|
|
103
|
+
pnpm --filter @h-ai/feedback lint
|
|
104
|
+
pnpm --filter @h-ai/feedback build
|
|
105
|
+
pnpm --filter @h-ai/feedback test
|
|
106
|
+
pnpm --filter @h-ai/cli test:scaffold-gates
|
|
107
|
+
# 上記の構築後、dist を再構築せず実 Chromium でオフライン表示・再生を確認します。
|
|
108
|
+
node packages/cli/tests/feedback-offline-browser.mjs
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
ブラウザー、SQLite/PG/MySQL、実際の S3/SMTP、複数インスタンス回復、ネットワーク切断時の表示をそれぞれ確認します。ビルドや mock の成功で代用しません。アプリの接続と配置は[利用ガイド](../../docs/usage/README.ja.md)を参照してください。
|
|
112
|
+
|
|
113
|
+
## ライセンス
|
|
114
|
+
|
|
115
|
+
Apache-2.0
|
package/README.md
CHANGED
|
@@ -1,3 +1,117 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @h-ai/feedback
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[简体中文](README.md) | [English](README.en.md) | [日本語](README.ja.md)
|
|
4
|
+
|
|
5
|
+
[框架首页](../../README.md)
|
|
6
|
+
|
|
7
|
+
## 简介
|
|
8
|
+
|
|
9
|
+
提供用户反馈、诊断现场、管理员处理与离线问题包。用户可以不填说明直接提交;反馈和结构化证据保存到关系型数据库,截图等附件进入私有对象存储,管理员通过邮件和管理界面处理。它不依赖通用 telemetry 服务。
|
|
10
|
+
|
|
11
|
+
## 核心功能清单
|
|
12
|
+
|
|
13
|
+
- 有界采集前后端日志、请求、错误、操作、版本与白名单环境信息;默认错误前后各最多 100 条。
|
|
14
|
+
- 数据库事务受理、提交/证据幂等、附件校验、独立的处理/采集/通知状态、持久化任务与租约恢复。
|
|
15
|
+
- 复用 `storage` 的 S3 和 `reach` 的 SMTP;首版要求 Reach DND 关闭,由 feedback 唯一持有通知重试,不等同邮件已投递。
|
|
16
|
+
- 浏览器截图、遮挡/标注、可选 rrweb 回放、失败重试与本地导出;`@h-ai/ui` 提供 `FeedbackModal`、`FeedbackList` 和 `FeedbackDetail`。
|
|
17
|
+
- 自包含 ZIP、SHA-256 校验、离线查看与按请求/错误/时间分页读取,不包含生产凭据。
|
|
18
|
+
|
|
19
|
+
## 使用入门
|
|
20
|
+
|
|
21
|
+
服务端先初始化 `core`,再检查 `reldb`、`storage`、`reach` 初始化结果,最后初始化 feedback。生产使用 PostgreSQL/MySQL、S3 私有桶和真实 SMTP,连接凭据由宿主私有配置提供;开发/隔离测试可使用非生产后端,但 Console Provider 不能证明邮件真实送达。
|
|
22
|
+
|
|
23
|
+
以下片段位于异步函数内,依赖已经初始化;`context` 必须由可信服务端认证和授权生成:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { feedback } from '@h-ai/feedback'
|
|
27
|
+
|
|
28
|
+
const initialized = await feedback.init({
|
|
29
|
+
appId: 'orders',
|
|
30
|
+
appVersion: '1.0.0',
|
|
31
|
+
recipients: ['admin@example.test'],
|
|
32
|
+
notificationProvider: 'email',
|
|
33
|
+
adminUrl: 'https://app.example.test/admin/feedback',
|
|
34
|
+
})
|
|
35
|
+
if (!initialized.success)
|
|
36
|
+
return initialized
|
|
37
|
+
|
|
38
|
+
const context = { scopeId: 'tenant-1', userId: 'user-1', permissions: ['feedback:create'] }
|
|
39
|
+
const submitted = await feedback.submit({ appId: 'orders', submissionId: 'stable-client-operation' }, context)
|
|
40
|
+
if (!submitted.success)
|
|
41
|
+
return submitted
|
|
42
|
+
// submitted.data.number 是可靠受理编号,材料和邮件仍有各自状态。
|
|
43
|
+
return submitted
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
应用退出时先检查 `await feedback.close()`,再关闭 reach/storage/reldb。重复提交使用原 `submissionId`;不能在超时后换新标识绕过幂等。
|
|
47
|
+
|
|
48
|
+
前后端分别传入其实际产物的 `appVersion/buildId/commit`;前端不能使用后端返回的版本替代自身版本。同一 SSR 构建的浏览器与服务产物复用 buildId。管理台与 CLI 生成的 base/API/fullstack 项目已接入编译期标识;`hai build` 内部生成标识,Docker 经构建参数传递。各目标从自己的 package.json 读取版本,没有所属 Git 提交时记录 `unknown`。模板的 `build-metadata.json` 仅含这三个非敏感字段,与运行代码内联值保持一致。
|
|
49
|
+
|
|
50
|
+
默认 HTTP 接入来自 `@h-ai/serv/features/feedback` 的 `createFeedbackProcedures({ feedback, resolveContext })`。配合 `apiContract.feedback` 挂载;`resolveContext` 提供服务端确定的 `scopeId/userId/permissions`。普通提交用户只能补充自己的材料;管理权限分别为 `feedback:read/process/reopen/export`,客户端的租户 ID、requestId、附件名称不能作为授权。
|
|
51
|
+
|
|
52
|
+
范围、用户、应用及证据关联标识按精确字符串匹配,MySQL 的大小写、重音及尾空格排序规则不会放宽隔离。附件 ID 保留原值并区分大小写,接受 1–128 字符的有效 Unicode;ZIP 文件路径使用摘要生成,不能从附件 ID 或显示名称推导。非成对代理字符在受理前拒绝。
|
|
53
|
+
|
|
54
|
+
浏览器从 `@h-ai/feedback/client` 导入 `createFeedbackClient()`,通过 `init({ appId, actions })` 注入 typed API 的 submit、appendEvidence、completeCollection、upload。先调用 `prepare()` 固定现场,成功后再显示 `FeedbackModal`;`submit()` 返回受理回执,后台 `flush()` 续传。`close()` 释放监听器和记录器。Cookie 白名单、持久草稿与回放均由产品显式启用,凭证类值始终排除。
|
|
55
|
+
|
|
56
|
+
浏览器和服务端共用文本脱敏规则,覆盖常见凭据键、JSON/转义 JSON、Bearer/Basic 授权值;入库、API 与离线包保持同一脱敏结果。业务专有敏感字段仍应由宿主配置白名单和敏感区域标记。
|
|
57
|
+
|
|
58
|
+
`text/plain` 附件在 5 MiB 文件上限内全文脱敏,保留安全正文和末尾日志,不套用结构化证据的单字段截断。超过上限的原文件在读取前拒绝,脱敏结果仍受同一附件字节上限约束。JSON 附件和结构化证据继续限制字段长度、深度及数量,可能被裁剪;需要完整日志时使用纯文本附件。待提交附件与首次包络冻结后本地导出的新增附件使用相同规则。
|
|
59
|
+
|
|
60
|
+
完整客户端方法为 `init(config)`、`prepare/submit/flush/record/getDraft/annotate/addFiles/exportDraft(input?)/close`。将表单 `onfileschange` 连接 `addFiles` 后刷新草稿附件预览,用户便可在提交前标注、遮挡或删除新增图片。`data-feedback-action` 标记关键操作,`data-feedback-private` 遮挡敏感内容,`data-feedback-ignore` 排除反馈界面自身。首次发送后包络冻结;同一提交重试时修改输入会返回冲突并保留输入,可先本地导出。
|
|
61
|
+
|
|
62
|
+
`addFiles` 按批次原子添加:全部文件通过校验才合并,任一失败均保留原附件和缺失标记,不保存半批文件。并发成功批次不会相互覆盖,合并后仍最多 20 项;读取期间草稿切换或首次包络冻结时返回 `CONFLICT`,不会补入包络之外的隐藏上传。
|
|
63
|
+
|
|
64
|
+
`annotate(id, annotation)` 的坐标单位是浏览器解码后原图的像素。UI 的 `FeedbackAnnotation.rect` 使用 0–1 比例;在 `Image.decode()` 后,将 x/width 乘 `naturalWidth`、y/height 乘 `naturalHeight` 再调用 SDK。使用原图尺寸而非 CSS 显示尺寸,使 JPEG 方向与 SDK 解码一致;对象 URL 仍由创建方在替换或销毁时释放。
|
|
65
|
+
|
|
66
|
+
并发 `init` 返回 `INIT_IN_PROGRESS`;`close` 等待有界截图退出后清理会话。截图期间关闭使 `prepare` 返回失败,不复活草稿。异步标注完成时若草稿已冻结、关闭、删除图片或被另一标注更新,则返回 `CONFLICT`,不改变已提交附件的字节或 checksum。
|
|
67
|
+
|
|
68
|
+
草稿由 SDK 持有并通过 `annotate`、`addFiles` 等方法更新。Svelte 宿主保存该引用时使用 `$state.raw<FeedbackDraft>()`,操作成功后从 `getDraft()` 刷新附件视图;不要将 SDK 草稿放入深度 `$state` 代理,否则 SDK 替换图片字节后,代理可能继续返回已缓存的旧字节。
|
|
69
|
+
|
|
70
|
+
单附件上限为 5 MiB 原始字节,Base64 约需 6.67 MiB,另需 JSON 字段空间。参考管理台及启用 feedback 的新 API/fullstack 项目在既有 `kit.transport.maxPlaintextBytes` 或 `serv.transport.maxPlaintextBytes` 显式配置 `8388608`(8 MiB);框架全局默认仍为 1 MiB。客户端缺省跟随服务端握手,显式较小的本地预算仍会拒绝超限请求。8 MiB 明文对应推导的加密请求体预算为 `16,809,984` 字节;生产 adapter-node 的 `BODY_SIZE_LIMIT` 与外层代理须匹配该预算,未启用传输加密时按实际 JSON 大小设置。框架部署自动推导,无需新增 feedback 配置。`hai add` 保留宿主既有配置,接入方需显式调整容量。
|
|
71
|
+
|
|
72
|
+
`captureConsole` 和 `persistDraft` 默认关闭;持久化启用时必须提供由宿主确定的非凭据用户/租户 `persistenceKey`。`keyScreenshots` 默认开启,仍受 `screenshot` 总开关控制;`maxKeyScreenshots` 默认 3、硬上限 5,`keyScreenshotThrottleMs` 默认 10000 毫秒。
|
|
73
|
+
|
|
74
|
+
自动截图由脱敏克隆 DOM 经 SVG foreignObject 重建可见页面。跨域图片、iframe、video 和尚未就绪的网络字体可能缺失;浏览器或 CSP 限制导致失败时明确标记 missing,用户仍可手动添加附件。
|
|
75
|
+
|
|
76
|
+
### 查询、处理与导出
|
|
77
|
+
|
|
78
|
+
`list/get/getEvidence` 返回受权记录、详情与分页证据;`setStatus({ id, status, expectedVersion }, context)` 以版本检查执行处理/撤销。`retry` 仅重排可恢复项。`export` 固定证据修订并创建任务,通过 `getExport` 获取已生成的 ZIP;导出未完成不伪装成功。材料缺失和截断随包保留,回放不是重新执行业务。
|
|
79
|
+
|
|
80
|
+
`getExport` 先检查范围及 `feedback:export` 权限。有效快照的 `pending/running` 任务返回 `CONFLICT`(409,生成中),调用方仅对此状态有界轮询;`failed` 返回 `OPERATION_FAILED`(500),需显示失败并由管理员决定是否 `retry`。任务不存在、不属于该反馈或待生成快照已过期时返回 `NOT_FOUND`(404),缺少导出权限返回 `FORBIDDEN`(403)。当前修订的生成快照过期后可重新 `export`;已完成 ZIP 的下载仍校验存储读取及 checksum。
|
|
81
|
+
|
|
82
|
+
按 `errorId` 查询时以错误锚点的来源、请求和序号取前后各最多 100 条日志;重叠窗口保留各自锚点,不按日志首次归属的 errorId 丢弃上下文。到期任务清理报告、证据、附件、历史及含快照的任务,并按对象前缀删除孤立上传 attempt。随后只保留不含现场的前缀/24 小时截止墓碑,反复清理迟到对象后删除;清理故障持续重试。
|
|
83
|
+
|
|
84
|
+
### 离线工具
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
hai feedback summary feedback-FB-1.zip
|
|
88
|
+
hai feedback inspect feedback-FB-1.zip
|
|
89
|
+
hai feedback evidence feedback-FB-1.zip --request-id req-1 --page 1 --page-size 20
|
|
90
|
+
hai feedback view feedback-FB-1.zip
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`view` 只监听本机 loopback,使用随机路径和内置查看器,Ctrl+C 关闭;无需生产网络。程序也可从 `@h-ai/feedback/bundle` 使用 `createFeedbackBundle`、`readFeedbackBundle`、`summarizeFeedbackBundle`、`queryFeedbackBundle`,无需初始化反馈服务。
|
|
94
|
+
|
|
95
|
+
### 配置与错误
|
|
96
|
+
|
|
97
|
+
`FeedbackConfigSchema` 校验所有输入;主要配置为 appId、recipients、notificationProvider、adminUrl、collectionTimeoutMs、workerIntervalMs、leaseMs、maxAttempts、retentionDays、envAllowlist 及附件/证据容量。通过 `HAI_FEEDBACK_*` 显式映射 YAML;浏览器只取得允许公开的采集策略,不能拿到 SMTP/S3 密钥。
|
|
98
|
+
|
|
99
|
+
领域错误使用 `HaiFeedbackError`,包括未初始化、配置、权限、找不到、冲突、容量及 checksum 失败。所有领域 API 先检查 `success`;HTTP 200 或 Promise 完成不代表反馈、上传或邮件成功。SMTP 受理不等于管理员收件箱已投递;不确定发送存在重发边界。
|
|
100
|
+
|
|
101
|
+
## 开发测试
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
pnpm --filter @h-ai/feedback typecheck
|
|
105
|
+
pnpm --filter @h-ai/feedback lint
|
|
106
|
+
pnpm --filter @h-ai/feedback build
|
|
107
|
+
pnpm --filter @h-ai/feedback test
|
|
108
|
+
pnpm --filter @h-ai/cli test:scaffold-gates
|
|
109
|
+
# 上述构建完成后,运行真实 Chromium 离线查看/回放验收;不会重建 dist。
|
|
110
|
+
node packages/cli/tests/feedback-offline-browser.mjs
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
验收需分别核对单元/浏览器、SQLite/PG/MySQL、真实 S3 与 SMTP、多节点恢复和断网离线查看;构建或 mock 通过不能代替真实 Provider 和用户流程。应用接入与部署流程见[使用指南](../../docs/usage/README.md)。
|
|
114
|
+
|
|
115
|
+
## 许可证
|
|
116
|
+
|
|
117
|
+
Apache-2.0
|