@h-ai/feedback 0.1.0-alpha.71 → 0.1.0-alpha.72
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.en.md +2 -0
- package/README.ja.md +2 -0
- package/README.md +2 -0
- package/dist/bundle/index.js +1 -1
- package/dist/{chunk-XQXX5WMV.js → chunk-5QWKURWU.js} +3 -3
- package/dist/chunk-5QWKURWU.js.map +1 -0
- package/dist/{chunk-KGHZL7K6.js → chunk-PYKGS35A.js} +5 -2
- package/dist/chunk-PYKGS35A.js.map +1 -0
- package/dist/client/index.js +64 -16
- package/dist/client/index.js.map +1 -1
- package/dist/index.js +3 -3
- package/package.json +7 -7
- package/dist/chunk-KGHZL7K6.js.map +0 -1
- package/dist/chunk-XQXX5WMV.js.map +0 -1
package/README.en.md
CHANGED
|
@@ -58,6 +58,8 @@ The complete client exposes `init(config)`, `prepare/captureScreenshot(attachmen
|
|
|
58
58
|
|
|
59
59
|
`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.
|
|
60
60
|
|
|
61
|
+
Local attachments support PNG/JPEG/WebP, WebM/MP4, TXT/LOG, JSON and PDF. Missing MIME types and `application/octet-stream` are inferred from supported extensions; common MIME aliases are normalized. The server still validates file bytes and checksums. Unsupported formats show a specific message. Screenshots preserve ordinary controls' backgrounds and borders while replacing their values with fixed mask characters. Passwords, `data-feedback-private`, `.rr-block` and `.rr-mask` remain fully covered. Only the capture clone changes; the original page and values remain intact.
|
|
62
|
+
|
|
61
63
|
`annotate(id, { ...rect, mode, coordinateSpace: 'ratio' })` accepts UI 0–1 coordinates directly. The SDK converts decoded intrinsic dimensions, including fractional/tiny selections, reverse dragging, edges and CSS scaling. Omitting coordinateSpace retains pixel coordinates. `getView()` owns and reuses preview URLs, releasing them on replacement, removal and close. Hosts need no image decoding or object URL bookkeeping.
|
|
62
64
|
|
|
63
65
|
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.
|
package/README.ja.md
CHANGED
|
@@ -58,6 +58,8 @@ return submitted
|
|
|
58
58
|
|
|
59
59
|
`addFiles` は一括追加を原子的に行い、全ファイルの検証に成功してから統合します。一つでも失敗した場合、既存の添付と欠落マーカーを維持し、一部だけを保存しません。並行して成功した追加を上書きせず、統合後も最大 20 件です。読取中に下書きが変わるか初回送信内容が固定された場合は `CONFLICT` を返し、送信内容に含まれない添付を後から隠れてアップロードしません。
|
|
60
60
|
|
|
61
|
+
ローカル添付は PNG/JPEG/WebP、WebM/MP4、TXT/LOG、JSON、PDF に対応します。MIME が空または `application/octet-stream` の場合は対応する拡張子で補い、一般的な MIME 別名も正規化します。サーバーは実際のバイトとチェックサムを引き続き検証し、未対応形式には具体的な案内を表示します。通常の入力欄は背景と枠を保持し、値だけを固定マスク文字に置き換えます。パスワード、`data-feedback-private`、`.rr-block`、`.rr-mask` は全面を遮蔽します。撮影用クローンだけを変更し、元の画面と入力値は保持します。
|
|
62
|
+
|
|
61
63
|
`annotate(id, { ...rect, mode, coordinateSpace: 'ratio' })` は UI の 0–1 座標を直接受け取り、SDK がデコード後の元画像サイズで換算します。小数、1% 未満、逆方向、端や CSS 拡大縮小に対応します。省略時は従来のピクセル座標です。`getView()` がプレビュー URL を管理し、置換・削除・終了時に解放するため、ホストの画像デコードや URL 管理は不要です。
|
|
62
64
|
|
|
63
65
|
並行 `init` は `INIT_IN_PROGRESS` を返します。`close` は時間制限付き画面取得の終了後にセッションを解放します。取得中の終了は `prepare` を失敗させ、草稿を復活させません。送信、終了、画像削除、別の注釈更新後に完了した注釈は `CONFLICT` を返し、送信済みバイト列と checksum を変更しません。
|
package/README.md
CHANGED
|
@@ -60,6 +60,8 @@ return submitted
|
|
|
60
60
|
|
|
61
61
|
`addFiles` 按批次原子添加:全部文件通过校验才合并,任一失败均保留原附件和缺失标记,不保存半批文件。并发成功批次不会相互覆盖,合并后仍最多 20 项;读取期间草稿切换或首次包络冻结时返回 `CONFLICT`,不会补入包络之外的隐藏上传。
|
|
62
62
|
|
|
63
|
+
本地附件支持 PNG/JPEG/WebP、WebM/MP4、TXT/LOG、JSON 和 PDF。浏览器未提供 MIME 或只提供 `application/octet-stream` 时按受支持扩展名补齐,常见 MIME 别名会规范化;服务端仍校验真实文件字节和 checksum。不支持的格式显示具体提示。普通输入控件的截图保留底色与边框,内容替换为固定遮罩字符;密码框、`data-feedback-private`、`.rr-block` 和 `.rr-mask` 继续整块遮挡。仅修改截图克隆,原页面和输入值不变。
|
|
64
|
+
|
|
63
65
|
`annotate(id, { ...rect, mode, coordinateSpace: 'ratio' })` 直接接受 UI 的 0–1 坐标,由 SDK 按解码后的原图换算,支持小数、小于 1% 的区域、反向拖动、边缘与 CSS 缩放。省略 coordinateSpace 仍使用原图像素。`getView()` 管理并复用预览 URL;替换、删除和关闭时释放。业务无需 Image.decode 或 URL.createObjectURL。
|
|
64
66
|
|
|
65
67
|
并发 `init` 返回 `INIT_IN_PROGRESS`;`close` 等待有界截图退出后清理会话。截图期间关闭使 `prepare` 返回失败,不复活草稿。异步标注完成时若草稿已冻结、关闭、删除图片或被另一标注更新,则返回 `CONFLICT`,不改变已提交附件的字节或 checksum。
|
package/dist/bundle/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { createFeedbackBundle, getFeedbackBundleViewerFiles, queryFeedbackBundle, readFeedbackBundle, summarizeFeedbackBundle } from '../chunk-
|
|
1
|
+
export { createFeedbackBundle, getFeedbackBundleViewerFiles, queryFeedbackBundle, readFeedbackBundle, summarizeFeedbackBundle } from '../chunk-5QWKURWU.js';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -229,7 +229,7 @@ async function createFeedbackBundle(input) {
|
|
|
229
229
|
if (typeof attachment.id !== "string" || attachment.id.length < 1 || attachment.id.length > 128 || ids.has(attachment.id) || attachment.bytes.byteLength > MAX_FILE_BYTES)
|
|
230
230
|
throw new Error("Invalid attachment");
|
|
231
231
|
ids.add(attachment.id);
|
|
232
|
-
const suffix = { "image/png": "png", "image/jpeg": "jpg", "image/webp": "webp", "video/webm": "webm", "video/mp4": "mp4", "text/plain": "txt", "application/json": "json" }[attachment.contentType] ?? "bin";
|
|
232
|
+
const suffix = { "image/png": "png", "image/jpeg": "jpg", "image/webp": "webp", "video/webm": "webm", "video/mp4": "mp4", "text/plain": "txt", "application/json": "json", "application/pdf": "pdf" }[attachment.contentType] ?? "bin";
|
|
233
233
|
const path = `attachments/${await checksum(strToU8(JSON.stringify(attachment.id)))}.${suffix}`;
|
|
234
234
|
const hash = await checksum(attachment.bytes);
|
|
235
235
|
if (attachment.checksum && attachment.checksum !== hash)
|
|
@@ -334,5 +334,5 @@ function getFeedbackBundleViewerFiles() {
|
|
|
334
334
|
}
|
|
335
335
|
|
|
336
336
|
export { createFeedbackBundle, getFeedbackBundleViewerFiles, queryFeedbackBundle, readFeedbackBundle, summarizeFeedbackBundle };
|
|
337
|
-
//# sourceMappingURL=chunk-
|
|
338
|
-
//# sourceMappingURL=chunk-
|
|
337
|
+
//# sourceMappingURL=chunk-5QWKURWU.js.map
|
|
338
|
+
//# sourceMappingURL=chunk-5QWKURWU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bundle/feedback-bundle-viewer.ts","../src/bundle/feedback-bundle-functions.ts"],"names":["offset","strToU8"],"mappings":"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","file":"chunk-5QWKURWU.js","sourcesContent":["import { strToU8 } from 'fflate'\n\ndeclare const __FEEDBACK_REPLAY_SCRIPT__: string\n\n/** 查看器只使用 textContent 与受限图片,不执行原应用或用户附件脚本。 */\nconst VIEWER_SCRIPT = `\nconst messages = {\n zh: {title:'问题反馈',facts:'采集事实',user:'用户描述',evidence:'证据',attachments:'截图与附件',missing:'缺失材料',history:'处理记录',error:'问题包读取失败',all:'全部',next:'下一页',previous:'上一页',download:'下载',empty:'未填写',replay:'页面回放',play:'播放',pause:'暂停',unavailable:'回放不可用,已保留原始证据',boundary:'回放展示采集的页面快照与交互,不执行原应用脚本。未内嵌的外部图片、字体和样式被离线策略阻止;缺失记录见下方。'},\n en: {title:'Feedback report',facts:'Collected facts',user:'User description',evidence:'Evidence',attachments:'Screenshots and attachments',missing:'Missing evidence',history:'Processing history',error:'Unable to read the bundle',all:'All',next:'Next',previous:'Previous',download:'Download',empty:'Not provided',replay:'Page replay',play:'Play',pause:'Pause',unavailable:'Replay unavailable; original evidence is preserved',boundary:'Replay shows captured snapshots and interactions without running application scripts. External images, fonts and styles that were not embedded are blocked by offline policy. See missing evidence below.'},\n ja: {title:'問題フィードバック',facts:'収集した事実',user:'ユーザーの説明',evidence:'証拠',attachments:'スクリーンショットと添付',missing:'不足資料',history:'処理記録',error:'パッケージを読み込めません',all:'すべて',next:'次へ',previous:'前へ',download:'ダウンロード',empty:'未入力',replay:'ページ再生',play:'再生',pause:'一時停止',unavailable:'再生できません。元の証拠は保持されています',boundary:'収集したスナップショットと操作を再生し、元のアプリのスクリプトは実行しません。埋め込まれていない外部画像、フォント、スタイルはオフライン方針で遮断されます。不足資料も確認してください。'}\n};\nconst language = navigator.language.startsWith('ja') ? 'ja' : navigator.language.startsWith('zh') ? 'zh' : 'en';\nconst m = messages[language];\nfunction text(tag,value,parent){const node=document.createElement(tag);node.textContent=String(value??'');parent.append(node);return node;}\nfunction section(title){const block=document.createElement('section');text('h2',title,block);document.querySelector('main').append(block);return block;}\nfunction safeImage(file){return ['image/png','image/jpeg','image/webp'].includes(file.contentType);}\nasync function load(path){const response=await fetch('../'+path);if(!response.ok)throw Error('Missing file');return response.json();}\ntry {\n const [manifest,report,evidence,history]=await Promise.all([load('manifest.json'),load('report.json'),load('evidence/records.json'),load('evidence/history.json')]);\n text('h1',m.title+' '+(report.number||report.id),document.querySelector('header'));\n text('pre',JSON.stringify(report,null,2),section(m.facts));\n text('p',report.description||m.empty,section(m.user));\n const attachments=section(m.attachments);\n for(const file of manifest.files.filter(item=>item.kind==='attachment')){\n if(!/^attachments\\\\/[\\\\w-]+\\\\.(png|jpg|webp|webm|mp4|txt|json|bin)$/.test(file.path))continue;\n const figure=document.createElement('figure');attachments.append(figure);text('figcaption',file.name||file.attachmentId,figure);\n if(safeImage(file)){const image=document.createElement('img');image.src='../'+file.path;image.alt=file.name||'';image.loading='lazy';figure.append(image);}\n if(['video/webm','video/mp4'].includes(file.contentType)){const video=document.createElement('video');video.src='../'+file.path;video.controls=true;video.preload='metadata';video.style.maxWidth='100%';figure.append(video);}\n const link=text('a',m.download,figure);link.href='../'+file.path;link.download=file.name||file.path.split('/').pop();\n }\n const replayEvents=evidence.filter(item=>item.kind==='replay'&&item.data&&item.data.event).map(item=>item.data.event).sort((a,b)=>a.timestamp-b.timestamp);\n\n if(replayEvents.length){\n const replay=section(m.replay);text('p',m.boundary,replay);\n try{\n await import('./replay.js');\n if(typeof globalThis.__HAI_FEEDBACK_REPLAY__!=='function')throw Error('Replay unavailable');\n const fullSnapshot=replayEvents.find(event=>event.type===2);\n if(!fullSnapshot)throw Error('Replay full snapshot unavailable');\n const target=document.createElement('div');replay.append(target);\n let player;\n const origin=replayEvents[0].timestamp;\n const duration=Math.max(0,replayEvents.at(-1).timestamp-origin);\n const snapshotOffset=Math.max(0,fullSnapshot.timestamp-origin);\n const play=text('button',m.play,replay);const pause=text('button',m.pause,replay);\n const slider=document.createElement('input');slider.type='range';slider.min='0';slider.max=String(Math.max(1,duration));slider.value='0';slider.setAttribute('aria-label',m.replay);replay.append(slider);\n let current=0;let playing=false;let frame=0;\n function position(value){return Math.max(0,Math.min(duration,Number.isFinite(value)?value:0));}\n function stop(){if(frame)cancelAnimationFrame(frame);frame=0;}\n // 每次定位重建实例,隔离旧回放末尾延迟到达的 finish,避免覆盖新的进度。\n function construct(){const instance=globalThis.__HAI_FEEDBACK_REPLAY__(replayEvents,target);player=instance;instance.on('finish',()=>{if(instance!==player||!playing)return;playing=false;stop();current=duration;slider.value=String(current);});}\n function update(){if(!playing)return;current=position(player.getCurrentTime());slider.value=String(current);frame=requestAnimationFrame(update);}\n // rrweb 仅同步应用严格早于偏移的事件;增加 1ms 使精确事件时间与首帧边界都包含在内。\n function seek(value){playing=false;stop();const requested=position(value);player?.destroy();construct();player.pause(Math.max(requested,snapshotOffset)+1);current=requested;slider.value=String(current);}\n\n play.onclick=()=>{stop();current=position(Number(slider.value));if(current>=duration)seek(0);playing=true;player.play(Math.max(current,snapshotOffset));update();};\n pause.onclick=()=>{player.pause();playing=false;stop();current=position(player.getCurrentTime());slider.value=String(current);};\n slider.oninput=()=>seek(Number(slider.value));\n window.addEventListener('pagehide',()=>{playing=false;stop();player.destroy();},{once:true});\n seek(0);\n }catch{text('p',m.unavailable,replay);}\n }\n const block=section(m.evidence);const filter=document.createElement('select');block.append(filter);const all=text('option',m.all,filter);all.value='';\n for(const kind of [...new Set(evidence.map(item=>item.kind))]){const option=text('option',kind,filter);option.value=kind;}\n const request=document.createElement('input');request.placeholder='requestId';request.setAttribute('aria-label','requestId');block.append(request);\n const output=text('pre','',block);const previous=text('button',m.previous,block);const next=text('button',m.next,block);const counter=text('span','',block);let page=1;\n function render(){const rows=evidence.filter(item=>(!filter.value||item.kind===filter.value)&&(!request.value||item.requestId===request.value));const pages=Math.max(1,Math.ceil(rows.length/20));page=Math.min(page,pages);output.textContent=JSON.stringify(rows.slice((page-1)*20,page*20),null,2);counter.textContent=' '+page+' / '+pages+' ('+rows.length+')';previous.disabled=page<=1;next.disabled=page>=pages;}\n filter.onchange=()=>{page=1;render();};request.oninput=()=>{page=1;render();};previous.onclick=()=>{page--;render();};next.onclick=()=>{page++;render();};render();\n text('pre',JSON.stringify(history,null,2),section(m.history));text('pre',manifest.missing.join('\\\\n')||'—',section(m.missing));\n} catch {text('p',m.error,document.querySelector('main'));}\n`\n\n/**\n * 包内可信查看器资源,CLI 从内置资源提供代码,绝不执行导入包自带的任意脚本。\n * @example 模块内部上下文\n * ```ts\n * const html = feedbackViewerFiles()['viewer/index.html']\n * const source: string = new TextDecoder().decode(html)\n * ```\n */\nexport function feedbackViewerFiles(): Record<string, Uint8Array> {\n return {\n 'viewer/index.html': strToU8(`<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' blob: data:; media-src 'self' blob:; connect-src 'self'; frame-src 'self' about: blob:; font-src 'self' data:; base-uri 'none'; form-action 'none'\"><title>Feedback</title><link rel=\"stylesheet\" href=\"style.css\"></head><body><header></header><main></main><script type=\"module\" src=\"app.js\"></script></body></html>`),\n 'viewer/style.css': strToU8('body{font:15px system-ui,sans-serif;line-height:1.6;margin:0;background:#f5f6f8;color:#172033}header,main{max-width:1080px;margin:auto;padding:24px}section{background:white;padding:24px;margin-bottom:20px;border:1px solid #dce1e8;border-radius:12px}h1{font-size:26px}h2{font-size:19px;margin-top:0}pre{white-space:pre-wrap;overflow-wrap:anywhere;font:13px/1.6 ui-monospace,monospace}img{max-width:100%;height:auto;border:1px solid #dce1e8}figure{margin:16px 0}input,select,button{font:inherit;padding:8px;margin:4px;border:1px solid #ccd2dc;border-radius:6px}button{cursor:pointer}a{display:block;color:#215dc0}'),\n 'viewer/app.js': strToU8(VIEWER_SCRIPT),\n 'viewer/replay.js': strToU8(typeof __FEEDBACK_REPLAY_SCRIPT__ === 'string' ? __FEEDBACK_REPLAY_SCRIPT__ : '/* Replay is available in built package artifacts. */'),\n }\n}\n","import type { HaiResult } from '@h-ai/core'\nimport type { FeedbackBundle, FeedbackBundleEvidence, FeedbackBundleEvidencePage, FeedbackBundleFile, FeedbackBundleInput, FeedbackBundleManifest, FeedbackBundleQuery, FeedbackBundleReadOptions } from './feedback-bundle-types.js'\nimport { core, err, HaiCommonError, ok } from '@h-ai/core'\nimport { strFromU8, strToU8, unzipSync, zipSync } from 'fflate'\nimport { feedbackViewerFiles } from './feedback-bundle-viewer.js'\n\nconst bundleM = core.i18n.createMessageGetter({\n 'zh-CN': { invalid: '问题包格式、容量或完整性校验失败', title: '问题反馈', facts: '采集事实', missing: '缺失材料', user: '用户描述', empty: '未填写', evidence: '证据', attachments: '附件' },\n 'en-US': { invalid: 'The feedback bundle failed format, size or integrity validation', title: 'Feedback report', facts: 'Collected facts', missing: 'Missing evidence', user: 'User description', empty: 'Not provided', evidence: 'Evidence', attachments: 'Attachments' },\n 'ja-JP': { invalid: '問題パッケージの形式、容量または整合性の検証に失敗しました', title: '問題フィードバック', facts: '収集した事実', missing: '不足している資料', user: 'ユーザーの説明', empty: '未入力', evidence: '証拠', attachments: '添付ファイル' },\n})\n\nconst MAX_FILES = 1024\nconst MAX_BYTES = 64 * 1024 * 1024\nconst MAX_FILE_BYTES = 16 * 1024 * 1024\nconst SHA256 = /^[a-f0-9]{64}$/\nconst SAFE_ID = /^[\\w-]{1,128}$/\n\n/** JSON 仅做数据边界,禁止保留原型和可执行值。 */\nfunction jsonObject(value: unknown): Record<string, unknown> {\n const parsed: unknown = JSON.parse(JSON.stringify(value))\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))\n throw new Error('Expected object')\n return parsed as Record<string, unknown>\n}\n\nfunction safePath(value: string): boolean {\n return /^[a-z0-9][\\w./-]*$/i.test(value) && !value.endsWith('/')\n && !value.split('/').some(segment => !segment || segment === '.' || segment === '..')\n}\n\nasync function checksum(bytes: Uint8Array): Promise<string> {\n const hash = await globalThis.crypto.subtle.digest('SHA-256', new Uint8Array(bytes).buffer)\n return Array.from(new Uint8Array(hash), byte => byte.toString(16).padStart(2, '0')).join('')\n}\n\nfunction objectFromFile(files: Readonly<Record<string, Uint8Array>>, path: string): Record<string, unknown> {\n const bytes = files[path]\n if (!bytes)\n throw new Error('Missing file')\n const parsed: unknown = JSON.parse(strFromU8(bytes))\n return jsonObject(parsed)\n}\n\nfunction evidenceFrom(value: unknown): FeedbackBundleEvidence {\n const record = jsonObject(value)\n if (typeof record.source !== 'string' || typeof record.kind !== 'string' || typeof record.timestamp !== 'number' || !Number.isFinite(record.timestamp))\n throw new Error('Invalid evidence')\n for (const key of ['id', 'requestId', 'errorId', 'missingReason']) {\n if (record[key] !== undefined && typeof record[key] !== 'string')\n throw new Error('Invalid evidence field')\n }\n if (record.sequence !== undefined && (typeof record.sequence !== 'number' || !Number.isSafeInteger(record.sequence) || record.sequence < 0))\n throw new Error('Invalid evidence sequence')\n return {\n source: record.source,\n kind: record.kind,\n timestamp: record.timestamp,\n ...(typeof record.id === 'string' ? { id: record.id } : {}),\n ...(typeof record.sequence === 'number' ? { sequence: record.sequence } : {}),\n ...(typeof record.requestId === 'string' ? { requestId: record.requestId } : {}),\n ...(typeof record.errorId === 'string' ? { errorId: record.errorId } : {}),\n ...(typeof record.missingReason === 'string' ? { missingReason: record.missingReason } : {}),\n ...(record.data !== undefined ? { data: record.data } : {}),\n }\n}\n\nfunction limitsFor(options: FeedbackBundleReadOptions = {}) {\n const limits = { maxFiles: options.maxFiles ?? MAX_FILES, maxBytes: options.maxBytes ?? MAX_BYTES, maxFileBytes: options.maxFileBytes ?? MAX_FILE_BYTES }\n if (Object.values(limits).some(value => !Number.isSafeInteger(value) || value <= 0))\n throw new Error('Invalid limits')\n return limits\n}\n\n/** 先检查 ZIP 中央目录,避免解压后才发现重复路径、符号链接或压缩炸弹。 */\nfunction preflight(bytes: Uint8Array, limits: ReturnType<typeof limitsFor>): void {\n if (bytes.byteLength > limits.maxBytes || bytes.byteLength < 22)\n throw new Error('Invalid archive size')\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)\n let end = -1\n for (let offset = bytes.length - 22; offset >= Math.max(0, bytes.length - 65557); offset--) {\n if (view.getUint32(offset, true) === 0x06054B50 && offset + 22 + view.getUint16(offset + 20, true) === bytes.length) {\n end = offset\n break\n }\n }\n if (end < 0 || view.getUint16(end + 4, true) !== 0 || view.getUint16(end + 6, true) !== 0)\n throw new Error('Invalid archive directory')\n const count = view.getUint16(end + 10, true)\n const size = view.getUint32(end + 12, true)\n let offset = view.getUint32(end + 16, true)\n if (count === 0 || count > limits.maxFiles || count !== view.getUint16(end + 8, true) || offset + size !== end)\n throw new Error('Invalid archive directory size')\n let total = 0\n const names = new Set<string>()\n for (let index = 0; index < count; index++) {\n if (offset + 46 > end || view.getUint32(offset, true) !== 0x02014B50)\n throw new Error('Invalid archive entry')\n const nameLength = view.getUint16(offset + 28, true)\n const next = offset + 46 + nameLength + view.getUint16(offset + 30, true) + view.getUint16(offset + 32, true)\n if (next > end)\n throw new Error('Invalid archive entry length')\n const name = strFromU8(bytes.subarray(offset + 46, offset + 46 + nameLength))\n const originalSize = view.getUint32(offset + 24, true)\n const method = view.getUint16(offset + 10, true)\n const mode = (view.getUint32(offset + 38, true) >>> 16) & 0xF000\n if (!safePath(name) || names.has(name.toLowerCase()) || originalSize > limits.maxFileBytes\n || (view.getUint16(offset + 8, true) & 1) || ![0, 8].includes(method) || (mode !== 0 && mode !== 0x8000)) {\n throw new Error('Unsafe archive entry')\n }\n total += originalSize\n if (total > limits.maxBytes)\n throw new Error('Archive expansion limit')\n names.add(name.toLowerCase())\n offset = next\n }\n if (offset !== end)\n throw new Error('Unexpected archive entry')\n}\n\nfunction parseManifest(value: Record<string, unknown>): FeedbackBundleManifest {\n if (value.format !== 'hai-feedback' || value.version !== 1 || typeof value.reportId !== 'string'\n || typeof value.evidenceRevision !== 'number' || !Number.isSafeInteger(value.evidenceRevision) || value.evidenceRevision < 0\n || typeof value.createdAt !== 'number' || !Number.isFinite(value.createdAt) || !Array.isArray(value.files)\n || !Array.isArray(value.missing) || value.missing.some(item => typeof item !== 'string')) {\n throw new Error('Unsupported manifest')\n }\n const files: FeedbackBundleFile[] = value.files.map((item) => {\n const file = jsonObject(item)\n if (typeof file.path !== 'string' || !safePath(file.path) || file.path === 'manifest.json'\n || typeof file.size !== 'number' || !Number.isSafeInteger(file.size) || file.size < 0\n || typeof file.sha256 !== 'string' || !SHA256.test(file.sha256)\n || !['report', 'evidence', 'history', 'attachment', 'summary', 'viewer'].includes(String(file.kind))) {\n throw new Error('Invalid manifest file')\n }\n return {\n path: file.path,\n size: file.size,\n sha256: file.sha256,\n kind: file.kind as FeedbackBundleFile['kind'],\n ...(typeof file.attachmentId === 'string' ? { attachmentId: file.attachmentId } : {}),\n ...(typeof file.name === 'string' ? { name: file.name } : {}),\n ...(typeof file.contentType === 'string' ? { contentType: file.contentType } : {}),\n }\n })\n if (new Set(files.map(file => file.path)).size !== files.length)\n throw new Error('Duplicate manifest file')\n return { format: 'hai-feedback', version: 1, reportId: value.reportId, evidenceRevision: value.evidenceRevision, createdAt: value.createdAt, missing: value.missing as string[], files }\n}\n\n/**\n * 生成含摘要、证据索引、附件与静态查看器的 ZIP;调用前应完成授权与脱敏。\n * @example\n * ```ts\n * import { createFeedbackBundle } from '@h-ai/feedback/bundle'\n * const result = await createFeedbackBundle({ report: { id: 'fb_1' }, evidence: [] })\n * if (result.success) { const bytes: Uint8Array = result.data }\n * ```\n */\nexport async function createFeedbackBundle<TReport extends { id: string }>(input: FeedbackBundleInput<TReport>): Promise<HaiResult<Uint8Array>> {\n try {\n const report = jsonObject(input.report)\n if (typeof report.id !== 'string' || !SAFE_ID.test(report.id))\n throw new Error('Invalid report')\n if (['appVersion', 'buildId', 'commit'].some(key => report[key] !== undefined && (typeof report[key] !== 'string' || report[key].length > 128)))\n throw new Error('Invalid build metadata')\n if (report.evidenceRevision !== undefined && (typeof report.evidenceRevision !== 'number' || !Number.isSafeInteger(report.evidenceRevision) || report.evidenceRevision < 0))\n throw new Error('Invalid evidence revision')\n const evidence = input.evidence.map(evidenceFrom)\n const history = (input.history ?? []).map(jsonObject)\n const missing = [...(input.missing ?? [])]\n if (missing.some(item => typeof item !== 'string'))\n throw new Error('Invalid missing evidence')\n const files: Record<string, Uint8Array> = {\n 'report.json': strToU8(JSON.stringify(report)),\n 'timeline.ndjson': strToU8(evidence.map(record => JSON.stringify(record)).join('\\n')),\n 'evidence/records.json': strToU8(JSON.stringify(evidence)),\n 'evidence/history.json': strToU8(JSON.stringify(history)),\n ...feedbackViewerFiles(),\n }\n const manifestFiles: FeedbackBundleFile[] = []\n const ids = new Set<string>()\n for (const attachment of input.attachments ?? []) {\n if (typeof attachment.id !== 'string' || attachment.id.length < 1 || attachment.id.length > 128 || ids.has(attachment.id) || attachment.bytes.byteLength > MAX_FILE_BYTES)\n throw new Error('Invalid attachment')\n ids.add(attachment.id)\n const suffix = ({ 'image/png': 'png', 'image/jpeg': 'jpg', 'image/webp': 'webp', 'video/webm': 'webm', 'video/mp4': 'mp4', 'text/plain': 'txt', 'application/json': 'json', 'application/pdf': 'pdf' } as Record<string, string>)[attachment.contentType] ?? 'bin'\n // 原始标识仅保存在清单,使用区分大小写且保留字符串编码的摘要构造安全路径。\n const path = `attachments/${await checksum(strToU8(JSON.stringify(attachment.id)))}.${suffix}`\n const hash = await checksum(attachment.bytes)\n if (attachment.checksum && attachment.checksum !== hash)\n throw new Error('Attachment checksum mismatch')\n files[path] = new Uint8Array(attachment.bytes)\n manifestFiles.push({ path, size: attachment.bytes.byteLength, sha256: hash, kind: 'attachment', attachmentId: attachment.id, name: attachment.name, contentType: attachment.contentType })\n }\n const manifest: FeedbackBundleManifest = { format: 'hai-feedback', version: 1, reportId: report.id, evidenceRevision: typeof report.evidenceRevision === 'number' ? report.evidenceRevision : 0, createdAt: Date.now(), missing, files: manifestFiles }\n const temporary: FeedbackBundle = { report: { ...report, id: report.id }, evidence, history, manifest, files }\n files['summary.md'] = strToU8(summarizeFeedbackBundle(temporary))\n for (const [path, bytes] of Object.entries(files)) {\n if (manifestFiles.some(file => file.path === path))\n continue\n const kind = path === 'report.json' ? 'report' : path === 'summary.md' ? 'summary' : path.startsWith('viewer/') ? 'viewer' : path.endsWith('history.json') ? 'history' : 'evidence'\n manifest.files.push({ path, size: bytes.byteLength, sha256: await checksum(bytes), kind })\n }\n files['manifest.json'] = strToU8(JSON.stringify(manifest, null, 2))\n const total = Object.values(files).reduce((sum, file) => sum + file.byteLength, 0)\n if (Object.keys(files).length > MAX_FILES || total > Math.min(input.maxBytes ?? MAX_BYTES, MAX_BYTES) || Object.values(files).some(file => file.byteLength > MAX_FILE_BYTES))\n throw new Error('Bundle limits exceeded')\n return ok(zipSync(files, { level: 1, mtime: new Date(1980, 0, 1) }))\n }\n catch {\n return err(HaiCommonError.VALIDATION_ERROR, bundleM('invalid'))\n }\n}\n\n/**\n * 校验 ZIP 路径、容量、协议与所有文件的 SHA-256 后读取;不执行包内代码。\n * @example\n * ```ts\n * import { createFeedbackBundle, readFeedbackBundle } from '@h-ai/feedback/bundle'\n * const created = await createFeedbackBundle({ report: { id: 'fb_1' }, evidence: [] })\n * if (created.success) {\n * const parsed = await readFeedbackBundle(created.data, { maxBytes: 32 * 1024 * 1024 })\n * if (parsed.success) { const reportId: string = parsed.data.report.id }\n * }\n * ```\n */\nexport async function readFeedbackBundle(bytes: Uint8Array, options?: FeedbackBundleReadOptions): Promise<HaiResult<FeedbackBundle>> {\n try {\n const limits = limitsFor(options)\n preflight(bytes, limits)\n const files = unzipSync(bytes)\n const manifest = parseManifest(objectFromFile(files, 'manifest.json'))\n if (Object.keys(files).length !== manifest.files.length + 1)\n throw new Error('Unexpected bundle file')\n let total = 0\n for (const file of manifest.files) {\n const content = files[file.path]\n if (!content || content.byteLength !== file.size || content.byteLength > limits.maxFileBytes || await checksum(content) !== file.sha256)\n throw new Error('File integrity failed')\n total += content.byteLength\n }\n if (total > limits.maxBytes)\n throw new Error('Bundle expansion limit')\n const report = objectFromFile(files, 'report.json')\n if (['appVersion', 'buildId', 'commit'].some(key => report[key] !== undefined && (typeof report[key] !== 'string' || report[key].length > 128)))\n throw new Error('Invalid build metadata')\n if (typeof report.id !== 'string' || report.id !== manifest.reportId)\n throw new Error('Report mismatch')\n const records: unknown = JSON.parse(strFromU8(files['evidence/records.json'] ?? new Uint8Array()))\n const history: unknown = JSON.parse(strFromU8(files['evidence/history.json'] ?? new Uint8Array()))\n if (!Array.isArray(records) || !Array.isArray(history))\n throw new Error('Invalid evidence collection')\n return ok({ manifest, report: { ...report, id: report.id }, evidence: records.map(evidenceFrom), history: history.map(jsonObject), files })\n }\n catch {\n return err(HaiCommonError.VALIDATION_ERROR, bundleM('invalid'))\n }\n}\n\n/**\n * 生成人工与 AI 可读摘要,明确用户描述与采集事实。\n * @example\n * ```ts\n * import { createFeedbackBundle, readFeedbackBundle, summarizeFeedbackBundle } from '@h-ai/feedback/bundle'\n * const created = await createFeedbackBundle({ report: { id: 'fb_1' }, evidence: [] })\n * if (created.success) {\n * const parsed = await readFeedbackBundle(created.data)\n * if (parsed.success) { const markdown: string = summarizeFeedbackBundle(parsed.data) }\n * }\n * ```\n */\nexport function summarizeFeedbackBundle(bundle: FeedbackBundle): string {\n const report = bundle.report\n const safeText = (value: unknown) => typeof value === 'string' || typeof value === 'number' ? String(value).replaceAll('\\r', '').replaceAll('\\n', ' ') : ''\n return [\n `# ${bundleM('title')} ${safeText(report.number ?? report.id)}`,\n '',\n `## ${bundleM('user')}`,\n '',\n safeText(report.description) || bundleM('empty'),\n '',\n `## ${bundleM('facts')}`,\n '',\n `- ID: ${safeText(report.id)}`,\n `- App: ${safeText(report.appId)} ${safeText(report.appVersion)}`,\n `- Build: ${safeText(report.buildId)} ${safeText(report.commit)}`,\n `- Revision: ${bundle.manifest.evidenceRevision}`,\n `- ${bundleM('evidence')}: ${bundle.evidence.length}`,\n `- ${bundleM('attachments')}: ${bundle.manifest.files.filter(file => file.kind === 'attachment').length}`,\n '',\n `## ${bundleM('missing')}`,\n '',\n ...bundle.manifest.missing.map(reason => `- ${safeText(reason)}`),\n '',\n ].join('\\n')\n}\n\n/**\n * 通过时间、请求、错误与类型筛选离线证据;错误按锚点来源和请求取前后各 100 条,分页最大 100 条。\n * @example\n * ```ts\n * import { createFeedbackBundle, queryFeedbackBundle, readFeedbackBundle } from '@h-ai/feedback/bundle'\n * const created = await createFeedbackBundle({ report: { id: 'fb_1' }, evidence: [] })\n * if (created.success) {\n * const parsed = await readFeedbackBundle(created.data)\n * if (parsed.success) { const page = queryFeedbackBundle(parsed.data, { requestId: 'req_1', page: 1, pageSize: 20 }) }\n * }\n * ```\n */\nexport function queryFeedbackBundle(bundle: FeedbackBundle, query: FeedbackBundleQuery = {}): FeedbackBundleEvidencePage {\n const page = Number.isSafeInteger(query.page) && (query.page ?? 0) > 0 ? query.page! : 1\n const pageSize = Number.isSafeInteger(query.pageSize) && (query.pageSize ?? 0) > 0 ? Math.min(100, query.pageSize!) : 20\n let candidates = bundle.evidence\n if (query.errorId) {\n const anchor = candidates.find(item => item.kind === 'error' && item.errorId === query.errorId)\n if (!anchor)\n return { items: [], total: 0, page, pageSize }\n const anchorSequence = anchor.sequence ?? anchor.timestamp\n const context = candidates.filter(item => item.source === anchor.source && item.requestId === anchor.requestId && ['log', 'error'].includes(item.kind))\n const before = context.filter(item => (item.sequence ?? item.timestamp) <= anchorSequence).sort((left, right) => (right.sequence ?? right.timestamp) - (left.sequence ?? left.timestamp)).slice(0, 101)\n const after = context.filter(item => (item.sequence ?? item.timestamp) > anchorSequence).sort((left, right) => (left.sequence ?? left.timestamp) - (right.sequence ?? right.timestamp)).slice(0, 100)\n const window = new Set([...before, ...after])\n candidates = candidates.filter(item => window.has(item))\n }\n const records = candidates.filter(item => (!query.kind || item.kind === query.kind)\n && (!query.requestId || item.requestId === query.requestId)\n && (query.from === undefined || item.timestamp >= query.from) && (query.to === undefined || item.timestamp <= query.to))\n .sort((left, right) => left.timestamp - right.timestamp || left.source.localeCompare(right.source) || (left.sequence ?? 0) - (right.sequence ?? 0))\n return { items: records.slice((page - 1) * pageSize, page * pageSize), total: records.length, page, pageSize }\n}\n\n/**\n * 获取内置可信离线查看器;本地服务必须使用此资源而不是执行导入包中的脚本。\n * @example\n * ```ts\n * import { getFeedbackBundleViewerFiles } from '@h-ai/feedback/bundle'\n * const html: Uint8Array | undefined = getFeedbackBundleViewerFiles()['viewer/index.html']\n * ```\n */\nexport function getFeedbackBundleViewerFiles(): Readonly<Record<string, Uint8Array>> {\n return feedbackViewerFiles()\n}\n"]}
|
|
@@ -27,6 +27,7 @@ var en_US_default = {
|
|
|
27
27
|
feedback_exportPending: "The feedback bundle is being generated. Please retry shortly",
|
|
28
28
|
feedback_limitExceeded: "Feedback evidence exceeds the allowed capacity",
|
|
29
29
|
feedback_invalidInput: "Invalid feedback input",
|
|
30
|
+
feedback_unsupportedAttachmentType: "Unsupported attachment format. Choose a PNG, JPEG, WebP, WebM, MP4, TXT, LOG, JSON or PDF file",
|
|
30
31
|
feedback_checksumMismatch: "Attachment integrity verification failed",
|
|
31
32
|
feedback_received: "New feedback received",
|
|
32
33
|
feedback_submittedAt: "Submitted at (UTC)",
|
|
@@ -51,6 +52,7 @@ var ja_JP_default = {
|
|
|
51
52
|
feedback_exportPending: "\u554F\u984C\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u751F\u6210\u3057\u3066\u3044\u307E\u3059\u3002\u3057\u3070\u3089\u304F\u3057\u3066\u304B\u3089\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
52
53
|
feedback_limitExceeded: "\u8CC7\u6599\u304C\u8A31\u5BB9\u5BB9\u91CF\u3092\u8D85\u3048\u3066\u3044\u307E\u3059",
|
|
53
54
|
feedback_invalidInput: "\u5165\u529B\u304C\u7121\u52B9\u3067\u3059",
|
|
55
|
+
feedback_unsupportedAttachmentType: "\u672A\u5BFE\u5FDC\u306E\u6DFB\u4ED8\u5F62\u5F0F\u3067\u3059\u3002PNG\u3001JPEG\u3001WebP\u3001WebM\u3001MP4\u3001TXT\u3001LOG\u3001JSON\u3001PDF \u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
54
56
|
feedback_checksumMismatch: "\u6DFB\u4ED8\u30D5\u30A1\u30A4\u30EB\u306E\u6574\u5408\u6027\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093",
|
|
55
57
|
feedback_received: "\u65B0\u3057\u3044\u30D5\u30A3\u30FC\u30C9\u30D0\u30C3\u30AF\u3092\u53D7\u4FE1\u3057\u307E\u3057\u305F",
|
|
56
58
|
feedback_submittedAt: "\u5831\u544A\u65E5\u6642\uFF08UTC\uFF09",
|
|
@@ -75,6 +77,7 @@ var zh_CN_default = {
|
|
|
75
77
|
feedback_exportPending: "\u95EE\u9898\u5305\u6B63\u5728\u751F\u6210\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
|
|
76
78
|
feedback_limitExceeded: "\u53CD\u9988\u6750\u6599\u8D85\u8FC7\u5141\u8BB8\u7684\u5BB9\u91CF",
|
|
77
79
|
feedback_invalidInput: "\u53CD\u9988\u53C2\u6570\u65E0\u6548",
|
|
80
|
+
feedback_unsupportedAttachmentType: "\u4E0D\u652F\u6301\u6B64\u9644\u4EF6\u683C\u5F0F\uFF0C\u8BF7\u9009\u62E9 PNG\u3001JPEG\u3001WebP\u3001WebM\u3001MP4\u3001TXT\u3001LOG\u3001JSON \u6216 PDF \u6587\u4EF6",
|
|
78
81
|
feedback_checksumMismatch: "\u9644\u4EF6\u5B8C\u6574\u6027\u6821\u9A8C\u5931\u8D25",
|
|
79
82
|
feedback_received: "\u6536\u5230\u65B0\u7684\u95EE\u9898\u53CD\u9988",
|
|
80
83
|
feedback_submittedAt: "\u53CD\u9988\u65F6\u95F4\uFF08UTC\uFF09",
|
|
@@ -94,5 +97,5 @@ function redactFeedbackText(text) {
|
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
export { HaiFeedbackError, feedbackM, redactFeedbackText };
|
|
97
|
-
//# sourceMappingURL=chunk-
|
|
98
|
-
//# sourceMappingURL=chunk-
|
|
100
|
+
//# sourceMappingURL=chunk-PYKGS35A.js.map
|
|
101
|
+
//# sourceMappingURL=chunk-PYKGS35A.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/feedback-types.ts","../messages/en-US.json","../messages/ja-JP.json","../messages/zh-CN.json","../src/feedback-i18n.ts","../src/feedback-sanitize-text.ts"],"names":["core"],"mappings":"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","file":"chunk-PYKGS35A.js","sourcesContent":["import type {\n FeedbackAppendEvidenceInput,\n FeedbackAttachment,\n FeedbackAttachmentData,\n FeedbackBudget,\n FeedbackCompleteInput,\n FeedbackDetail,\n FeedbackEvidence,\n FeedbackEvidenceQuery,\n FeedbackExportInput,\n FeedbackJob,\n FeedbackListInput,\n FeedbackReport,\n FeedbackRetryInput,\n FeedbackSetStatusInput,\n FeedbackSubmitInput,\n FeedbackUploadInput,\n} from '@h-ai/api-contract/feedback'\nimport type { ErrorInfo, HaiResult, PaginatedResult } from '@h-ai/core'\nimport type { FeedbackConfig, FeedbackConfigInput } from './feedback-config.js'\nimport { core } from '@h-ai/core'\n\nconst FeedbackErrorInfo = {\n NOT_INITIALIZED: '010:503',\n INIT_IN_PROGRESS: '011:409',\n CONFIG_ERROR: '013:500',\n OPERATION_FAILED: '020:500',\n NOT_FOUND: '021:404',\n FORBIDDEN: '022:403',\n CONFLICT: '023:409',\n LIMIT_EXCEEDED: '024:413',\n INVALID_INPUT: '025:400',\n CHECKSUM_MISMATCH: '026:422',\n} satisfies ErrorInfo\n\n/**\n * Feedback 错误定义。\n * @example\n * ```ts\n * import { HaiFeedbackError } from '@h-ai/feedback'\n * const code = HaiFeedbackError.NOT_FOUND.code\n * void code\n * ```\n */\nexport const HaiFeedbackError = core.error.buildHaiErrorsDef('feedback', FeedbackErrorInfo)\n\n/**\n * 由可信服务端认证与授权策略生成的范围;HTTP 输入不能指定此对象。\n * @example\n * ```ts\n * import type { FeedbackContext } from '@h-ai/feedback'\n * const context: FeedbackContext = { scopeId: 'tenant-1', userId: 'user-1', permissions: ['feedback:create'] }\n * void context\n * ```\n */\nexport interface FeedbackContext {\n scopeId: string\n userId: string\n permissions?: readonly string[]\n}\n\n/**\n * 反馈服务对象;领域操作返回 HaiResult,close 不释放宿主依赖连接。\n * @example\n * ```ts\n * import type { FeedbackContext, FeedbackFunctions } from '@h-ai/feedback'\n * async function submit(service: FeedbackFunctions, context: FeedbackContext) {\n * const result = await service.submit({ appId: 'orders', submissionId: 'stable-operation-id' }, context)\n * return result.success ? result.data.id : result.error.code\n * }\n * void submit\n * ```\n */\nexport interface FeedbackFunctions {\n init: (config: FeedbackConfigInput) => Promise<HaiResult<void>>\n close: () => Promise<HaiResult<void>>\n readonly isInitialized: boolean\n readonly config: FeedbackConfig | null\n readonly budget: FeedbackBudget\n submit: (input: FeedbackSubmitInput, context: FeedbackContext) => Promise<HaiResult<FeedbackReport>>\n list: (input: FeedbackListInput, context: FeedbackContext) => Promise<HaiResult<PaginatedResult<FeedbackReport>>>\n get: (id: string, context: FeedbackContext) => Promise<HaiResult<FeedbackDetail>>\n getEvidence: (input: FeedbackEvidenceQuery, context: FeedbackContext) => Promise<HaiResult<PaginatedResult<FeedbackEvidence>>>\n setStatus: (input: FeedbackSetStatusInput, context: FeedbackContext) => Promise<HaiResult<FeedbackReport>>\n appendEvidence: (input: FeedbackAppendEvidenceInput, context: FeedbackContext) => Promise<HaiResult<{ acknowledged: string[], evidenceRevision: number }>>\n completeCollection: (input: FeedbackCompleteInput, context: FeedbackContext) => Promise<HaiResult<FeedbackReport>>\n uploadAttachment: (input: FeedbackUploadInput, context: FeedbackContext) => Promise<HaiResult<FeedbackAttachment>>\n getAttachment: (input: { id: string, attachmentId: string }, context: FeedbackContext) => Promise<HaiResult<FeedbackAttachmentData>>\n retry: (input: FeedbackRetryInput, context: FeedbackContext) => Promise<HaiResult<void>>\n export: (input: FeedbackExportInput, context: FeedbackContext) => Promise<HaiResult<FeedbackJob>>\n getExport: (input: { id: string, jobId: string }, context: FeedbackContext) => Promise<HaiResult<FeedbackAttachmentData>>\n runJobs: () => Promise<HaiResult<{ processed: number }>>\n}\n\nexport type {\n FeedbackAppendEvidenceInput,\n FeedbackAttachment,\n FeedbackAttachmentData,\n FeedbackCompleteInput,\n FeedbackDetail,\n FeedbackEvidence,\n FeedbackEvidenceQuery,\n FeedbackExportInput,\n FeedbackJob,\n FeedbackListInput,\n FeedbackReport,\n FeedbackRetryInput,\n FeedbackSetStatusInput,\n FeedbackSubmitInput,\n FeedbackUploadInput,\n} from '@h-ai/api-contract/feedback'\n","{\n \"feedback_notInitialized\": \"Feedback has not been initialized\",\n \"feedback_initInProgress\": \"Feedback initialization is in progress\",\n \"feedback_configError\": \"Invalid feedback configuration or unavailable dependency\",\n \"feedback_operationFailed\": \"Feedback operation failed. Please retry\",\n \"feedback_notFound\": \"Feedback or evidence was not found\",\n \"feedback_forbidden\": \"Access to this feedback is denied\",\n \"feedback_conflict\": \"Feedback changed or an operation identifier conflicts. Refresh and retry\",\n \"feedback_exportPending\": \"The feedback bundle is being generated. Please retry shortly\",\n \"feedback_limitExceeded\": \"Feedback evidence exceeds the allowed capacity\",\n \"feedback_invalidInput\": \"Invalid feedback input\",\n \"feedback_unsupportedAttachmentType\": \"Unsupported attachment format. Choose a PNG, JPEG, WebP, WebM, MP4, TXT, LOG, JSON or PDF file\",\n \"feedback_checksumMismatch\": \"Attachment integrity verification failed\",\n \"feedback_received\": \"New feedback received\",\n \"feedback_submittedAt\": \"Submitted at (UTC)\",\n \"feedback_materialStatus\": \"Material status\",\n \"feedback_materialCollecting\": \"Collecting\",\n \"feedback_materialComplete\": \"Complete\",\n \"feedback_materialPartial\": \"Partially missing\",\n \"feedback_materialFailed\": \"Collection failed\",\n \"feedback_defaultSummary\": \"User feedback\",\n \"feedback_retryInputChanged\": \"The first submission has already been sent. Restore the original text and attachments to retry, then open a new feedback.\"\n}\n","{\n \"feedback_notInitialized\": \"フィードバックが初期化されていません\",\n \"feedback_initInProgress\": \"フィードバックを初期化しています\",\n \"feedback_configError\": \"設定が無効、または依存サービスが利用できません\",\n \"feedback_operationFailed\": \"操作に失敗しました。再試行してください\",\n \"feedback_notFound\": \"フィードバックまたは資料が見つかりません\",\n \"feedback_forbidden\": \"このフィードバックへのアクセス権限がありません\",\n \"feedback_conflict\": \"フィードバックが更新されたか操作識別子が重複しています。再読込してください\",\n \"feedback_exportPending\": \"問題パッケージを生成しています。しばらくしてから再試行してください\",\n \"feedback_limitExceeded\": \"資料が許容容量を超えています\",\n \"feedback_invalidInput\": \"入力が無効です\",\n \"feedback_unsupportedAttachmentType\": \"未対応の添付形式です。PNG、JPEG、WebP、WebM、MP4、TXT、LOG、JSON、PDF を選択してください\",\n \"feedback_checksumMismatch\": \"添付ファイルの整合性を確認できません\",\n \"feedback_received\": \"新しいフィードバックを受信しました\",\n \"feedback_submittedAt\": \"報告日時(UTC)\",\n \"feedback_materialStatus\": \"資料の状態\",\n \"feedback_materialCollecting\": \"収集中\",\n \"feedback_materialComplete\": \"完全\",\n \"feedback_materialPartial\": \"一部不足\",\n \"feedback_materialFailed\": \"収集失敗\",\n \"feedback_defaultSummary\": \"ユーザーのフィードバック\",\n \"feedback_retryInputChanged\": \"最初の送信は開始されています。元の説明と添付ファイルに戻して送信結果を確認してから、新しいフィードバックを作成してください。\"\n}\n","{\n \"feedback_notInitialized\": \"反馈模块尚未初始化\",\n \"feedback_initInProgress\": \"反馈模块正在初始化\",\n \"feedback_configError\": \"反馈配置无效或前置服务不可用\",\n \"feedback_operationFailed\": \"反馈操作失败,请重试\",\n \"feedback_notFound\": \"反馈或材料不存在\",\n \"feedback_forbidden\": \"没有访问该反馈的权限\",\n \"feedback_conflict\": \"反馈已更新或操作标识冲突,请刷新后重试\",\n \"feedback_exportPending\": \"问题包正在生成,请稍后重试\",\n \"feedback_limitExceeded\": \"反馈材料超过允许的容量\",\n \"feedback_invalidInput\": \"反馈参数无效\",\n \"feedback_unsupportedAttachmentType\": \"不支持此附件格式,请选择 PNG、JPEG、WebP、WebM、MP4、TXT、LOG、JSON 或 PDF 文件\",\n \"feedback_checksumMismatch\": \"附件完整性校验失败\",\n \"feedback_received\": \"收到新的问题反馈\",\n \"feedback_submittedAt\": \"反馈时间(UTC)\",\n \"feedback_materialStatus\": \"材料状态\",\n \"feedback_materialCollecting\": \"收集中\",\n \"feedback_materialComplete\": \"完整\",\n \"feedback_materialPartial\": \"部分缺失\",\n \"feedback_materialFailed\": \"采集失败\",\n \"feedback_defaultSummary\": \"用户提交的问题反馈\",\n \"feedback_retryInputChanged\": \"首次提交已发出,重试需保持原说明和附件。请恢复原输入完成确认后,再发起新反馈。\"\n}\n","import { core } from '@h-ai/core'\nimport en from '../messages/en-US.json'\nimport ja from '../messages/ja-JP.json'\nimport zh from '../messages/zh-CN.json'\n\n/**\n * 模块内消息获取器。\n * @example 模块内部上下文\n * ```ts\n * const message = feedbackM('feedback_notFound')\n * void message\n * ```\n */\nexport const feedbackM = core.i18n.createMessageGetter<keyof typeof zh>({ 'zh-CN': zh, 'en-US': en, 'ja-JP': ja })\n","import { core } from '@h-ai/core'\n\n/**\n * Node 与 Browser 共用的自由文本凭据脱敏;URL 与结构化字段仍复用 core。\n * @example 模块内部上下文\n * ```ts\n * const safeText = redactFeedbackText('{\"password\":\"example-only\"}')\n * void safeText\n * ```\n */\nexport function redactFeedbackText(text: string): string {\n return core.sanitize.sanitizeText(text)\n}\n"]}
|
package/dist/client/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { feedbackM, HaiFeedbackError, redactFeedbackText } from '../chunk-
|
|
1
|
+
import { feedbackM, HaiFeedbackError, redactFeedbackText } from '../chunk-PYKGS35A.js';
|
|
2
2
|
import { ok, core, err } from '@h-ai/core';
|
|
3
3
|
import { resolveFeedbackBudget, FeedbackSubmitInputSchema, FeedbackBudgetSchema, FeedbackEvidenceInputSchema, FeedbackReportSchema } from '@h-ai/api-contract/feedback';
|
|
4
4
|
|
|
@@ -43,7 +43,8 @@ async function saveFeedbackFile(bytes, filename, contentType = "application/zip"
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// src/client/feedback-client-image.ts
|
|
46
|
-
var PRIVATE_SELECTOR = 'input
|
|
46
|
+
var PRIVATE_SELECTOR = 'input[type="password"],[data-feedback-private],.rr-block,.rr-mask';
|
|
47
|
+
var CONTROL_SELECTOR = 'input,textarea,select,[contenteditable]:not([contenteditable="false"])';
|
|
47
48
|
async function captureFeedbackScreenshot() {
|
|
48
49
|
if (document.fonts) {
|
|
49
50
|
let timeout;
|
|
@@ -95,10 +96,28 @@ async function captureFeedbackScreenshot() {
|
|
|
95
96
|
}
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
|
-
for (const element of clone.querySelectorAll(PRIVATE_SELECTOR)) {
|
|
99
|
+
for (const element of clone.querySelectorAll(`${PRIVATE_SELECTOR},${CONTROL_SELECTOR}`)) {
|
|
100
|
+
if (!clone.documentElement.contains(element))
|
|
101
|
+
continue;
|
|
99
102
|
const bounds = element.getBoundingClientRect();
|
|
100
103
|
const replacement = clone.createElement("div");
|
|
101
|
-
|
|
104
|
+
const computed = clone.defaultView?.getComputedStyle(element);
|
|
105
|
+
if (computed) {
|
|
106
|
+
for (const property of Array.from(computed))
|
|
107
|
+
replacement.style.setProperty(property, computed.getPropertyValue(property), "important");
|
|
108
|
+
}
|
|
109
|
+
replacement.style.setProperty("width", `${bounds.width}px`, "important");
|
|
110
|
+
replacement.style.setProperty("height", `${bounds.height}px`, "important");
|
|
111
|
+
replacement.style.setProperty("box-sizing", "border-box", "important");
|
|
112
|
+
replacement.style.setProperty("overflow", "hidden", "important");
|
|
113
|
+
if (element.matches(PRIVATE_SELECTOR)) {
|
|
114
|
+
replacement.style.setProperty("background", "#111", "important");
|
|
115
|
+
replacement.style.setProperty("color", "transparent", "important");
|
|
116
|
+
} else {
|
|
117
|
+
const control = element;
|
|
118
|
+
const value = "value" in control ? control.value : element.textContent;
|
|
119
|
+
replacement.textContent = value ? "\u2022\u2022\u2022\u2022" : element.getAttribute("placeholder") ?? "";
|
|
120
|
+
}
|
|
102
121
|
replacement.setAttribute("aria-hidden", "true");
|
|
103
122
|
element.replaceWith(replacement);
|
|
104
123
|
}
|
|
@@ -295,6 +314,33 @@ async function restoreFeedbackCheckpoint(key) {
|
|
|
295
314
|
}
|
|
296
315
|
|
|
297
316
|
// src/client/feedback-client-main.ts
|
|
317
|
+
function feedbackFileContentType(file) {
|
|
318
|
+
const type = file.type.split(";", 1)[0]?.trim().toLowerCase() ?? "";
|
|
319
|
+
const extensions = /* @__PURE__ */ new Map([
|
|
320
|
+
["png", "image/png"],
|
|
321
|
+
["jpg", "image/jpeg"],
|
|
322
|
+
["jpeg", "image/jpeg"],
|
|
323
|
+
["webp", "image/webp"],
|
|
324
|
+
["webm", "video/webm"],
|
|
325
|
+
["mp4", "video/mp4"],
|
|
326
|
+
["txt", "text/plain"],
|
|
327
|
+
["log", "text/plain"],
|
|
328
|
+
["json", "application/json"],
|
|
329
|
+
["pdf", "application/pdf"]
|
|
330
|
+
]);
|
|
331
|
+
if (!type || type === "application/octet-stream")
|
|
332
|
+
return extensions.get(file.name.split(".").at(-1)?.toLowerCase() ?? "");
|
|
333
|
+
const aliases = /* @__PURE__ */ new Map([
|
|
334
|
+
["image/x-png", "image/png"],
|
|
335
|
+
["image/jpg", "image/jpeg"],
|
|
336
|
+
["image/pjpeg", "image/jpeg"],
|
|
337
|
+
["text/json", "application/json"],
|
|
338
|
+
["text/x-log", "text/plain"],
|
|
339
|
+
["application/x-pdf", "application/pdf"]
|
|
340
|
+
]);
|
|
341
|
+
const normalized = aliases.get(type) ?? type;
|
|
342
|
+
return [...extensions.values()].includes(normalized) ? normalized : void 0;
|
|
343
|
+
}
|
|
298
344
|
function createFeedbackClient() {
|
|
299
345
|
let config;
|
|
300
346
|
let draft;
|
|
@@ -357,10 +403,10 @@ function createFeedbackClient() {
|
|
|
357
403
|
const cleanups = [];
|
|
358
404
|
const missing = /* @__PURE__ */ new Set();
|
|
359
405
|
let replayChunks = [];
|
|
360
|
-
function failure(code = "OPERATION_FAILED", cause) {
|
|
406
|
+
function failure(code = "OPERATION_FAILED", cause, message) {
|
|
361
407
|
const key = code === "NOT_INITIALIZED" ? "feedback_notInitialized" : code === "LIMIT_EXCEEDED" ? "feedback_limitExceeded" : code === "INVALID_INPUT" ? "feedback_invalidInput" : "feedback_operationFailed";
|
|
362
408
|
const traceId = core.context.get().traceId ?? core.context.get().requestId ?? core.id.uuid();
|
|
363
|
-
const error = { ...HaiFeedbackError[code], traceId, message: feedbackM(key), cause };
|
|
409
|
+
const error = { ...HaiFeedbackError[code], traceId, message: message ?? feedbackM(key), cause };
|
|
364
410
|
core.logger[cause === void 0 ? "warn" : "error"]("Feedback client operation failed", { module: "feedback", traceId, error });
|
|
365
411
|
return err({ ...error, message: core.error.formatMessage(error) });
|
|
366
412
|
}
|
|
@@ -928,20 +974,21 @@ function createFeedbackClient() {
|
|
|
928
974
|
for (const file of files) {
|
|
929
975
|
if (items.length >= budget.maxAttachmentCount || file.size > budget.maxAttachmentBytes)
|
|
930
976
|
return failure("LIMIT_EXCEEDED");
|
|
931
|
-
|
|
932
|
-
|
|
977
|
+
const contentType = feedbackFileContentType(file);
|
|
978
|
+
if (!contentType)
|
|
979
|
+
return failure("INVALID_INPUT", void 0, feedbackM("feedback_unsupportedAttachmentType"));
|
|
933
980
|
let bytes;
|
|
934
|
-
if (
|
|
981
|
+
if (contentType.startsWith("text/") || contentType === "application/json") {
|
|
935
982
|
const raw = await file.text();
|
|
936
|
-
if (
|
|
983
|
+
if (contentType === "application/json" && raw.length > 8192)
|
|
937
984
|
newMissing.push(`attachment-text-truncated:${String(sanitizeFeedbackData({ text: file.name }).text).slice(0, 100)}`);
|
|
938
|
-
bytes =
|
|
985
|
+
bytes = contentType === "application/json" ? new TextEncoder().encode(JSON.stringify(sanitizeFeedbackData(JSON.parse(raw)))) : new TextEncoder().encode(sanitizeFeedbackText(raw));
|
|
939
986
|
} else {
|
|
940
987
|
bytes = new Uint8Array(await file.arrayBuffer());
|
|
941
988
|
}
|
|
942
989
|
if (bytes.byteLength > budget.maxAttachmentBytes)
|
|
943
990
|
return failure("LIMIT_EXCEEDED");
|
|
944
|
-
const item = await attachment(file.name,
|
|
991
|
+
const item = await attachment(file.name, contentType, bytes);
|
|
945
992
|
if (!items.some((existing) => existing.name === item.name && existing.checksum === item.checksum))
|
|
946
993
|
items.push(item);
|
|
947
994
|
}
|
|
@@ -979,12 +1026,13 @@ function createFeedbackClient() {
|
|
|
979
1026
|
for (const file of input.files ?? []) {
|
|
980
1027
|
if (attachments.length >= budget.maxAttachmentCount || file.size > budget.maxAttachmentBytes)
|
|
981
1028
|
return failure("LIMIT_EXCEEDED");
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1029
|
+
const contentType = feedbackFileContentType(file);
|
|
1030
|
+
if (!contentType)
|
|
1031
|
+
return failure("INVALID_INPUT", void 0, feedbackM("feedback_unsupportedAttachmentType"));
|
|
1032
|
+
const bytes = contentType === "application/json" ? new TextEncoder().encode(JSON.stringify(sanitizeFeedbackData(JSON.parse(await file.text())))) : contentType.startsWith("text/") ? new TextEncoder().encode(sanitizeFeedbackText(await file.text())) : new Uint8Array(await file.arrayBuffer());
|
|
985
1033
|
if (bytes.byteLength > budget.maxAttachmentBytes)
|
|
986
1034
|
return failure("LIMIT_EXCEEDED");
|
|
987
|
-
const item = await attachment(file.name,
|
|
1035
|
+
const item = await attachment(file.name, contentType, bytes);
|
|
988
1036
|
if (!attachments.some((existing) => existing.name === item.name && existing.checksum === item.checksum))
|
|
989
1037
|
attachments.push(item);
|
|
990
1038
|
}
|