@chaoset/session-archive 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/LICENSE +21 -0
- package/README.md +1 -1
- package/client/client.cjs +215 -135
- package/client/index.tsx +501 -0
- package/lib/config-store.d.ts +32 -0
- package/lib/config-store.d.ts.map +1 -0
- package/lib/config-store.js +107 -0
- package/lib/config-store.js.map +1 -0
- package/lib/index.d.ts +130 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +334 -0
- package/lib/index.js.map +1 -0
- package/lib/remote.d.ts +36 -0
- package/lib/remote.d.ts.map +1 -0
- package/lib/remote.js +108 -0
- package/lib/remote.js.map +1 -0
- package/lib/typert.host.d.ts +62 -0
- package/lib/typert.host.d.ts.map +1 -0
- package/lib/typert.host.js +68 -68
- package/lib/typert.host.js.map +1 -0
- package/package.json +10 -9
- package/lib/config-store.mjs +0 -97
- package/lib/index.mjs +0 -314
- package/lib/remote.mjs +0 -110
package/client/index.tsx
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { TypertRemoteContribution } from "@deepseek-ai/dsh-typert-protocol";
|
|
3
|
+
|
|
4
|
+
// ── 样式(注入 style 标签,复用 DSH 设计变量)────────────────────────
|
|
5
|
+
var css = [
|
|
6
|
+
".sa_badge{width:100%;height:49px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;padding:0 8px 0 6px;font-family:inherit;font-size:14px;display:inline-flex;overflow:hidden}",
|
|
7
|
+
".sa_badge:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
8
|
+
".sa_badgeIcon{flex:none;display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;color:var(--dsw-alias-label-primary)}",
|
|
9
|
+
".sa_badgeIcon svg{width:18px;height:18px;display:block}",
|
|
10
|
+
".sa_badgeLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}",
|
|
11
|
+
".sa_badgeCount{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;margin-left:auto;font-size:12px;line-height:16px}",
|
|
12
|
+
".sa_badge--collapsed{width:auto;justify-content:center;padding:0}",
|
|
13
|
+
".sa_badge--collapsed .sa_badgeLabel,.sa_badge--collapsed .sa_badgeCount{display:none}",
|
|
14
|
+
".sa_panel{z-index:30;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:440px;max-width:calc(100vw - 24px);max-height:62vh;box-shadow:var(--dsw-shadow-lv2);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:12px;flex-direction:column;display:flex;position:fixed;bottom:128px;left:12px;overflow:hidden}",
|
|
15
|
+
".sa_header{box-sizing:border-box;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);flex:none;justify-content:space-between;align-items:center;min-height:44px;padding:8px 12px;display:flex}",
|
|
16
|
+
".sa_title{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}",
|
|
17
|
+
".sa_iconBtn{font:inherit;cursor:pointer;border:0;border-radius:8px;width:36px;height:36px;color:var(--dsw-alias-label-secondary,#666);background:0 0;display:inline-flex;align-items:center;justify-content:center;font-size:18px}",
|
|
18
|
+
".sa_refresh{font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;min-height:36px;padding:5px 14px;font-size:13px;line-height:20px;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);display:inline-flex;align-items:center;gap:5px}",
|
|
19
|
+
".sa_refresh:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}",
|
|
20
|
+
".sa_refresh:disabled{opacity:.4;cursor:default}",
|
|
21
|
+
".sa_iconBtn:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
22
|
+
".sa_iconBtn:disabled{opacity:.4;cursor:default}",
|
|
23
|
+
".sa_toolbar{flex:none;border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:8px 12px;display:flex;flex-wrap:wrap}",
|
|
24
|
+
".sa_check{accent-color:var(--dsw-alias-label-primary);width:14px;height:14px;flex:none;cursor:pointer}",
|
|
25
|
+
".sa_check:disabled{cursor:default;opacity:.45}",
|
|
26
|
+
".sa_toolLabel{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;user-select:none;cursor:pointer;display:inline-flex;align-items:center;gap:6px}",
|
|
27
|
+
".sa_count{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;flex:1}",
|
|
28
|
+
".sa_action{font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-border-l1);border-radius:6px;padding:3px 10px;font-size:12px;line-height:18px;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary)}",
|
|
29
|
+
".sa_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}",
|
|
30
|
+
".sa_action:disabled{opacity:.4;cursor:default}",
|
|
31
|
+
".sa_actionDanger{border-color:transparent;background:var(--dsw-alias-state-error-primary);color:#fff}",
|
|
32
|
+
".sa_actionDanger:hover:not(:disabled){background:var(--dsw-alias-state-error-primary)}",
|
|
33
|
+
".sa_actionDanger:disabled{opacity:.4}",
|
|
34
|
+
".sa_confirm{color:var(--dsw-alias-state-error-primary);border:1px solid var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent)}",
|
|
35
|
+
".sa_confirm:hover:not(:disabled){background:var(--dsw-alias-state-error-primary);color:#fff}",
|
|
36
|
+
".sa_body{flex:1;min-height:0;padding:4px 12px 12px;overflow-y:auto}",
|
|
37
|
+
".sa_empty{color:var(--dsw-alias-label-tertiary);margin:24px 0;text-align:center;font-size:12px;line-height:18px}",
|
|
38
|
+
".sa_error{color:var(--dsw-alias-state-error-primary);margin:8px 0;font-size:12px;line-height:18px}",
|
|
39
|
+
".sa_rows{flex-direction:column;gap:8px;margin:0;padding:0;list-style:none;display:flex}",
|
|
40
|
+
".sa_row{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);border-radius:12px;flex-direction:column;gap:6px;padding:8px 10px;display:flex}",
|
|
41
|
+
".sa_rowHead{align-items:center;gap:8px;display:flex}",
|
|
42
|
+
".sa_rowTitle{min-width:0;color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;font-size:13px;font-weight:500;line-height:20px;overflow:hidden;cursor:pointer}",
|
|
43
|
+
".sa_rowTitle:hover{text-decoration:underline}",
|
|
44
|
+
".sa_live{background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-label);height:18px;border-radius:9px;flex:none;align-items:center;padding:0 6px;font-size:11px;line-height:18px;display:inline-flex}",
|
|
45
|
+
".sa_rowMeta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;overflow-wrap:anywhere}",
|
|
46
|
+
".sa_rowMeta code{font-family:var(--dsh-font-mono,monospace)}",
|
|
47
|
+
".sa_rowFoot{justify-content:space-between;align-items:center;gap:8px;display:flex}",
|
|
48
|
+
".sa_rowActions{flex:none;align-items:center;gap:8px;display:flex}",
|
|
49
|
+
".sa_detail{border-top:1px dashed var(--dsw-alias-border-l2);padding-top:8px;flex-direction:column;gap:6px;display:flex;max-height:260px;overflow-y:auto}",
|
|
50
|
+
".sa_msg{flex-direction:column;gap:2px;display:flex}",
|
|
51
|
+
".sa_msgRole{color:var(--dsw-alias-label-tertiary);font-size:10px;line-height:14px;text-transform:uppercase;letter-spacing:.04em}",
|
|
52
|
+
".sa_msgText{color:var(--dsw-alias-label-primary);white-space:pre-wrap;overflow-wrap:anywhere;font-size:12px;line-height:18px}",
|
|
53
|
+
".sa_msgTextUser{color:var(--dsw-alias-label-secondary)}",
|
|
54
|
+
".sa_busy{opacity:.55;pointer-events:none}"
|
|
55
|
+
].join("");
|
|
56
|
+
var tagId = "@chaoset/session-archive/client.css";
|
|
57
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId + "\"]") === null) {
|
|
58
|
+
var tag = document.createElement("style");
|
|
59
|
+
tag.dataset.plugin = "@chaoset/session-archive";
|
|
60
|
+
tag.dataset.pluginCss = tagId;
|
|
61
|
+
tag.textContent = css;
|
|
62
|
+
document.head.appendChild(tag);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── 字典 ─────────────────────────────────────────────────────────────
|
|
66
|
+
const NS = "sidebar.sessionArchive";
|
|
67
|
+
const zh = {
|
|
68
|
+
badge: "归档",
|
|
69
|
+
panelTitle: "归档会话",
|
|
70
|
+
refresh: "刷新",
|
|
71
|
+
close: "关闭",
|
|
72
|
+
empty: "暂无归档会话。在会话列表的更多菜单中归档会话后会出现在这里。",
|
|
73
|
+
loadFailed: "加载归档列表失败",
|
|
74
|
+
selectAll: "全选",
|
|
75
|
+
selected: "已选 {n} 项",
|
|
76
|
+
restore: "恢复所选",
|
|
77
|
+
restoreDone: "已恢复 {n} 个归档会话",
|
|
78
|
+
restoreFailed: "恢复失败",
|
|
79
|
+
delete: "删除所选",
|
|
80
|
+
deleteConfirm: "再次点击确认删除",
|
|
81
|
+
deleteDone: "已删除 {n} 个归档会话",
|
|
82
|
+
deleteFailed: "部分删除失败:{n} 个",
|
|
83
|
+
confirmAll: "确认删除全部 {n} 个?",
|
|
84
|
+
noSelection: "请先勾选会话",
|
|
85
|
+
view: "查看",
|
|
86
|
+
collapse: "收起",
|
|
87
|
+
live: "运行中",
|
|
88
|
+
detailLoadFailed: "读取会话内容失败",
|
|
89
|
+
noneTitle: "(无标题)",
|
|
90
|
+
user: "用户",
|
|
91
|
+
assistant: "助手",
|
|
92
|
+
messages: "共 {n} 条消息",
|
|
93
|
+
noMessages: "(无文本消息)",
|
|
94
|
+
sizeBytes: "{n} B",
|
|
95
|
+
sizeKB: "{n} KB",
|
|
96
|
+
sizeMB: "{n} MB",
|
|
97
|
+
runningHint: "运行中的会话不能删除,请先停止"
|
|
98
|
+
};
|
|
99
|
+
const en = {
|
|
100
|
+
badge: "Archive",
|
|
101
|
+
panelTitle: "Archived sessions",
|
|
102
|
+
refresh: "Refresh",
|
|
103
|
+
close: "Close",
|
|
104
|
+
empty: "No archived sessions. Archive a session from its menu in the session list and it will show up here.",
|
|
105
|
+
loadFailed: "Failed to load archive list",
|
|
106
|
+
selectAll: "Select all",
|
|
107
|
+
selected: "{n} selected",
|
|
108
|
+
restore: "Restore",
|
|
109
|
+
restoreDone: "Restored {n} archived sessions",
|
|
110
|
+
restoreFailed: "Restore failed",
|
|
111
|
+
delete: "Delete",
|
|
112
|
+
deleteConfirm: "Click again to confirm delete",
|
|
113
|
+
deleteDone: "Deleted {n} archived sessions",
|
|
114
|
+
deleteFailed: "Some deletions failed: {n}",
|
|
115
|
+
confirmAll: "Delete all {n}?",
|
|
116
|
+
noSelection: "Select sessions first",
|
|
117
|
+
view: "View",
|
|
118
|
+
collapse: "Collapse",
|
|
119
|
+
live: "Running",
|
|
120
|
+
detailLoadFailed: "Failed to read session content",
|
|
121
|
+
noneTitle: "(no title)",
|
|
122
|
+
user: "User",
|
|
123
|
+
assistant: "Assistant",
|
|
124
|
+
messages: "{n} messages",
|
|
125
|
+
noMessages: "(no text messages)",
|
|
126
|
+
sizeBytes: "{n} B",
|
|
127
|
+
sizeKB: "{n} KB",
|
|
128
|
+
sizeMB: "{n} MB",
|
|
129
|
+
runningHint: "Running sessions cannot be deleted; stop them first"
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// ── 工具函数 ─────────────────────────────────────────────────────────
|
|
133
|
+
function formatBytes(bytes: any, t: any) {
|
|
134
|
+
if (bytes < 1024) return t("sizeBytes").replace("{n}", String(bytes));
|
|
135
|
+
if (bytes < 1024 * 1024) return t("sizeKB").replace("{n}", (bytes / 1024).toFixed(1));
|
|
136
|
+
return t("sizeMB").replace("{n}", (bytes / (1024 * 1024)).toFixed(1));
|
|
137
|
+
}
|
|
138
|
+
function formatTime(ms: any) {
|
|
139
|
+
try { return new Date(ms).toLocaleString(); } catch { return String(ms); }
|
|
140
|
+
}
|
|
141
|
+
function shortId(id: any) {
|
|
142
|
+
return id.length > 12 ? id.slice(0, 12) + "…" : id;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ── 归档面板 ─────────────────────────────────────────────────────────
|
|
146
|
+
function ArchivePanel(props: any) {
|
|
147
|
+
const t = props.t;
|
|
148
|
+
const call = props.call;
|
|
149
|
+
// 侧边栏收起(icon rail)时,DSH 通过 wide=false 告知(侧边栏 footer 注入点
|
|
150
|
+
// 下发的是 wide,而非 collapsed)。以 wide 为准;仅当宿主未下发 wide 时,才用
|
|
151
|
+
// ResizeObserver 观察所在格宽度(<80px 视为收起)兜底,避免窄格把文字标签挤变形。
|
|
152
|
+
const wideExplicit = typeof props.wide === "boolean" ? props.wide : void 0;
|
|
153
|
+
const [narrow, setNarrow] = React.useState(false);
|
|
154
|
+
const collapsed = wideExplicit === void 0 ? narrow : !wideExplicit;
|
|
155
|
+
const badgeRef = React.useRef<any>(null);
|
|
156
|
+
React.useEffect(() => {
|
|
157
|
+
const el = badgeRef.current?.parentElement;
|
|
158
|
+
if (el === undefined || el === null || typeof (globalThis as any).ResizeObserver === "undefined") return;
|
|
159
|
+
const ro = new (globalThis as any).ResizeObserver((entries: any[]) => {
|
|
160
|
+
for (const entry of entries) {
|
|
161
|
+
const w = entry.contentRect?.width ?? el.clientWidth;
|
|
162
|
+
setNarrow(w > 0 && w < 80);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
ro.observe(el);
|
|
166
|
+
return () => ro.disconnect();
|
|
167
|
+
}, []);
|
|
168
|
+
// 有显式 wide 时完全以宿主状态为准,避免 ResizeObserver 的窄格判断在
|
|
169
|
+
// 展开后仍残留,导致展开态误用 collapsed 样式(图标不居中、文字丢失)。
|
|
170
|
+
const iconOnly = collapsed;
|
|
171
|
+
const rootRef = React.useRef<any>(null);
|
|
172
|
+
const [open, setOpen] = React.useState(false);
|
|
173
|
+
const [items, setItems] = React.useState<any[]>([]);
|
|
174
|
+
const [loading, setLoading] = React.useState(false);
|
|
175
|
+
const [error, setError] = React.useState<any>(null);
|
|
176
|
+
const [selected, setSelected] = React.useState<Set<any>>(new Set());
|
|
177
|
+
const [expanded, setExpanded] = React.useState<any>(null);
|
|
178
|
+
const [details, setDetails] = React.useState<Map<any, any>>(new Map());
|
|
179
|
+
const [detailLoading, setDetailLoading] = React.useState<Set<any>>(new Set());
|
|
180
|
+
const [busy, setBusy] = React.useState(false);
|
|
181
|
+
const [confirmingDelete, setConfirmingDelete] = React.useState(false);
|
|
182
|
+
const [notice, setNotice] = React.useState<any>(null);
|
|
183
|
+
|
|
184
|
+
const load = React.useCallback(async () => {
|
|
185
|
+
setLoading(true);
|
|
186
|
+
setError(null);
|
|
187
|
+
try {
|
|
188
|
+
const result = await call("list");
|
|
189
|
+
setItems(Array.isArray(result.items) ? result.items : []);
|
|
190
|
+
} catch (loadError: any) {
|
|
191
|
+
setError(t("loadFailed") + ": " + (loadError && loadError.message || loadError));
|
|
192
|
+
} finally {
|
|
193
|
+
setLoading(false);
|
|
194
|
+
}
|
|
195
|
+
}, [call, t]);
|
|
196
|
+
|
|
197
|
+
React.useEffect(() => {
|
|
198
|
+
if (open) { setNotice(null); load(); }
|
|
199
|
+
}, [open, load]);
|
|
200
|
+
|
|
201
|
+
// 后台静默刷新归档计数:侧边栏徽标要在「聊天列表里出现归档」时就同步显示数量,
|
|
202
|
+
// 而不是等到打开弹窗才更新。面板打开时由上面的 load() 负责,这里只在关闭态轮询,
|
|
203
|
+
// 避免刷新打断用户在面板里的勾选/操作;同时监听标签页重新可见。
|
|
204
|
+
const refreshSilently = React.useCallback(async () => {
|
|
205
|
+
try {
|
|
206
|
+
const result = await call("list");
|
|
207
|
+
setItems(Array.isArray(result.items) ? result.items : []);
|
|
208
|
+
} catch {}
|
|
209
|
+
}, [call]);
|
|
210
|
+
React.useEffect(() => {
|
|
211
|
+
if (open) return;
|
|
212
|
+
refreshSilently();
|
|
213
|
+
const id = globalThis.setInterval(refreshSilently, 5000);
|
|
214
|
+
const onVis = () => { if (document.visibilityState === "visible") refreshSilently(); };
|
|
215
|
+
document.addEventListener("visibilitychange", onVis);
|
|
216
|
+
return () => { clearInterval(id); document.removeEventListener("visibilitychange", onVis); };
|
|
217
|
+
}, [open, refreshSilently]);
|
|
218
|
+
|
|
219
|
+
// 提示(如「已恢复/已删除 N 个会话」)几秒后自动消失,避免残留误导用户,
|
|
220
|
+
// 也避免删除全部归档后仍挂着上一条提示。
|
|
221
|
+
React.useEffect(() => {
|
|
222
|
+
if (notice === null) return;
|
|
223
|
+
const id = globalThis.setTimeout(() => setNotice(null), 4000);
|
|
224
|
+
return () => clearTimeout(id);
|
|
225
|
+
}, [notice]);
|
|
226
|
+
|
|
227
|
+
React.useEffect(() => {
|
|
228
|
+
if (!open) return;
|
|
229
|
+
const onPointerDown = (event: any) => {
|
|
230
|
+
if (rootRef.current !== null && !rootRef.current.contains(event.target)) {
|
|
231
|
+
setOpen(false);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
document.addEventListener("pointerdown", onPointerDown);
|
|
235
|
+
return () => document.removeEventListener("pointerdown", onPointerDown);
|
|
236
|
+
}, [open]);
|
|
237
|
+
|
|
238
|
+
const selectable = items.filter((item) => !item.live);
|
|
239
|
+
const allSelected = selectable.length > 0 && selectable.every((item) => selected.has(item.sessionId));
|
|
240
|
+
|
|
241
|
+
const toggleAll = (checked: any) => {
|
|
242
|
+
if (checked) {
|
|
243
|
+
setSelected(new Set(selectable.map((item) => item.sessionId)));
|
|
244
|
+
} else {
|
|
245
|
+
setSelected(new Set());
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
const toggleOne = (sessionId: any, checked: any) => {
|
|
249
|
+
setSelected((current) => {
|
|
250
|
+
const next = new Set(current);
|
|
251
|
+
if (checked) next.add(sessionId); else next.delete(sessionId);
|
|
252
|
+
return next;
|
|
253
|
+
});
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const toggleDetail = (item: any) => {
|
|
257
|
+
if (expanded === item.sessionId) {
|
|
258
|
+
setExpanded(null);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
setExpanded(item.sessionId);
|
|
262
|
+
if (!details.has(item.sessionId)) {
|
|
263
|
+
setDetailLoading((current) => new Set(current).add(item.sessionId));
|
|
264
|
+
call("detail", item.sessionId).then((detail: any) => {
|
|
265
|
+
setDetails((current) => new Map(current).set(item.sessionId, detail));
|
|
266
|
+
}).catch((detailError: any) => {
|
|
267
|
+
setDetails((current) => new Map(current).set(item.sessionId, {
|
|
268
|
+
error: t("detailLoadFailed") + ": " + (detailError && detailError.message || detailError)
|
|
269
|
+
}));
|
|
270
|
+
}).finally(() => {
|
|
271
|
+
setDetailLoading((current) => {
|
|
272
|
+
const next = new Set(current);
|
|
273
|
+
next.delete(item.sessionId);
|
|
274
|
+
return next;
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const runBatch = async (action: any, doneKey: any, failKey: any) => {
|
|
281
|
+
const ids = [...selected];
|
|
282
|
+
if (ids.length === 0) {
|
|
283
|
+
setNotice({ kind: "warn", text: t("noSelection") });
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
setBusy(true);
|
|
287
|
+
setNotice(null);
|
|
288
|
+
try {
|
|
289
|
+
const result = await call(action, ids);
|
|
290
|
+
const doneIds = result.deleted || result.restored || [];
|
|
291
|
+
const n = doneIds.length;
|
|
292
|
+
if (result.failed && result.failed.length > 0) {
|
|
293
|
+
setNotice({ kind: "error", text: t(failKey).replace("{n}", String(result.failed.length)) });
|
|
294
|
+
} else {
|
|
295
|
+
setNotice({ kind: "ok", text: t(doneKey).replace("{n}", String(n)) });
|
|
296
|
+
}
|
|
297
|
+
if (doneIds.length > 0) {
|
|
298
|
+
const done = new Set(doneIds);
|
|
299
|
+
setItems((current) => current.filter((item) => !done.has(item.sessionId)));
|
|
300
|
+
}
|
|
301
|
+
setSelected(new Set());
|
|
302
|
+
setConfirmingDelete(false);
|
|
303
|
+
await load();
|
|
304
|
+
} catch (actionError: any) {
|
|
305
|
+
setNotice({ kind: "error", text: t(failKey).replace("{n}", "?") + ": " + (actionError && actionError.message || actionError) });
|
|
306
|
+
} finally {
|
|
307
|
+
setBusy(false);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
const restoreSelected = () => runBatch("unarchive", "restoreDone", "restoreFailed");
|
|
311
|
+
const deleteSelected = () => {
|
|
312
|
+
if (!confirmingDelete) {
|
|
313
|
+
setConfirmingDelete(true);
|
|
314
|
+
window.setTimeout(() => setConfirmingDelete(false), 4000);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
runBatch("delete", "deleteDone", "deleteFailed");
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
const hasSelection = selected.size > 0;
|
|
321
|
+
|
|
322
|
+
return React.createElement(
|
|
323
|
+
"div",
|
|
324
|
+
{ className: "sa_root", ref: rootRef },
|
|
325
|
+
React.createElement(
|
|
326
|
+
"button",
|
|
327
|
+
{
|
|
328
|
+
ref: badgeRef,
|
|
329
|
+
className: "sa_badge" + (iconOnly ? " sa_badge--collapsed" : ""),
|
|
330
|
+
type: "button",
|
|
331
|
+
onClick: () => setOpen(!open),
|
|
332
|
+
"aria-expanded": open,
|
|
333
|
+
title: t("badge")
|
|
334
|
+
},
|
|
335
|
+
React.createElement("span", { className: "sa_badgeIcon", "aria-hidden": true },
|
|
336
|
+
React.createElement("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" },
|
|
337
|
+
React.createElement("rect", { x: 3, y: 4, width: 18, height: 4, rx: 1 }),
|
|
338
|
+
React.createElement("path", { d: "M5 8v11a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V8" }),
|
|
339
|
+
React.createElement("path", { d: "M10 12h4" })
|
|
340
|
+
)
|
|
341
|
+
),
|
|
342
|
+
iconOnly ? null : React.createElement("span", { className: "sa_badgeLabel" }, t("badge")),
|
|
343
|
+
iconOnly ? null : React.createElement("span", { className: "sa_badgeCount" }, String(items.length))
|
|
344
|
+
),
|
|
345
|
+
open ? React.createElement(
|
|
346
|
+
"div",
|
|
347
|
+
{ className: "sa_panel" },
|
|
348
|
+
React.createElement(
|
|
349
|
+
"div",
|
|
350
|
+
{ className: "sa_header" },
|
|
351
|
+
React.createElement("span", { className: "sa_title" }, t("panelTitle")),
|
|
352
|
+
React.createElement(
|
|
353
|
+
"span",
|
|
354
|
+
{ style: { display: "inline-flex", gap: "6px", alignItems: "center" } },
|
|
355
|
+
React.createElement("button", { className: "sa_refresh", type: "button", title: t("refresh"), disabled: busy || loading, onClick: load }, t("refresh")),
|
|
356
|
+
React.createElement("button", { className: "sa_iconBtn", type: "button", title: t("close"), disabled: busy, onClick: () => setOpen(false) }, "✕")
|
|
357
|
+
)
|
|
358
|
+
),
|
|
359
|
+
React.createElement(
|
|
360
|
+
"div",
|
|
361
|
+
{ className: "sa_toolbar" },
|
|
362
|
+
React.createElement("label", { className: "sa_toolLabel" },
|
|
363
|
+
React.createElement("input", {
|
|
364
|
+
className: "sa_check",
|
|
365
|
+
type: "checkbox",
|
|
366
|
+
checked: allSelected,
|
|
367
|
+
disabled: busy || selectable.length === 0,
|
|
368
|
+
onChange: (e) => toggleAll(e.target.checked)
|
|
369
|
+
}),
|
|
370
|
+
t("selectAll")
|
|
371
|
+
),
|
|
372
|
+
React.createElement("span", { className: "sa_count" }, t("selected").replace("{n}", String(selected.size))),
|
|
373
|
+
React.createElement("button", {
|
|
374
|
+
className: "sa_action",
|
|
375
|
+
type: "button",
|
|
376
|
+
disabled: busy || !hasSelection,
|
|
377
|
+
onClick: restoreSelected
|
|
378
|
+
}, t("restore")),
|
|
379
|
+
React.createElement("button", {
|
|
380
|
+
className: "sa_action " + (confirmingDelete ? "sa_actionDanger sa_confirm" : "sa_actionDanger"),
|
|
381
|
+
type: "button",
|
|
382
|
+
disabled: busy || !hasSelection,
|
|
383
|
+
onClick: deleteSelected
|
|
384
|
+
}, confirmingDelete ? t("deleteConfirm") : t("delete"))
|
|
385
|
+
),
|
|
386
|
+
React.createElement(
|
|
387
|
+
"div",
|
|
388
|
+
{ className: "sa_body" },
|
|
389
|
+
notice !== null ? React.createElement("p", { className: "sa_error" + (notice.kind === "ok" ? "" : ""), role: "status" }, notice.text) : null,
|
|
390
|
+
error !== null ? React.createElement("p", { className: "sa_error", role: "alert" }, error) : null,
|
|
391
|
+
loading && items.length === 0 ? React.createElement("p", { className: "sa_empty" }, "…") : null,
|
|
392
|
+
!loading && items.length === 0 && error === null ? React.createElement("p", { className: "sa_empty" }, t("empty")) : null,
|
|
393
|
+
items.length > 0 ? React.createElement(
|
|
394
|
+
"ul",
|
|
395
|
+
{ className: "sa_rows" },
|
|
396
|
+
items.map((item) => {
|
|
397
|
+
const isExpanded = expanded === item.sessionId;
|
|
398
|
+
const detail = details.get(item.sessionId);
|
|
399
|
+
const detailPending = detailLoading.has(item.sessionId);
|
|
400
|
+
return React.createElement(
|
|
401
|
+
"li",
|
|
402
|
+
{ className: "sa_row" + (busy ? " sa_busy" : ""), key: item.sessionId },
|
|
403
|
+
React.createElement(
|
|
404
|
+
"div",
|
|
405
|
+
{ className: "sa_rowHead" },
|
|
406
|
+
React.createElement("input", {
|
|
407
|
+
className: "sa_check",
|
|
408
|
+
type: "checkbox",
|
|
409
|
+
checked: selected.has(item.sessionId),
|
|
410
|
+
disabled: busy || item.live,
|
|
411
|
+
title: item.live ? t("runningHint") : void 0,
|
|
412
|
+
onChange: (e: any) => toggleOne(item.sessionId, e.target.checked)
|
|
413
|
+
}),
|
|
414
|
+
React.createElement(
|
|
415
|
+
"span",
|
|
416
|
+
{ className: "sa_rowTitle", onClick: () => toggleDetail(item), title: t("view") },
|
|
417
|
+
item.title !== null && item.title !== void 0 && item.title !== "" ? item.title : t("noneTitle")
|
|
418
|
+
),
|
|
419
|
+
item.live ? React.createElement("span", { className: "sa_live" }, t("live")) : null
|
|
420
|
+
),
|
|
421
|
+
React.createElement("div", { className: "sa_rowMeta" },
|
|
422
|
+
React.createElement("span", null, formatTime(item.updatedAt)),
|
|
423
|
+
item.cwd !== null && item.cwd !== void 0 ? React.createElement("span", null, " · ", React.createElement("code", null, item.cwd)) : null,
|
|
424
|
+
React.createElement("span", null, " · ", formatBytes(item.size, t))
|
|
425
|
+
),
|
|
426
|
+
React.createElement(
|
|
427
|
+
"div",
|
|
428
|
+
{ className: "sa_rowFoot" },
|
|
429
|
+
React.createElement("span", { className: "sa_rowMeta" },
|
|
430
|
+
React.createElement("code", null, shortId(item.sessionId)),
|
|
431
|
+
detail !== void 0 && !detail.error && detail.messageCount !== void 0
|
|
432
|
+
? " · " + t("messages").replace("{n}", String(detail.messageCount))
|
|
433
|
+
: null
|
|
434
|
+
),
|
|
435
|
+
React.createElement(
|
|
436
|
+
"div",
|
|
437
|
+
{ className: "sa_rowActions" },
|
|
438
|
+
React.createElement("button", {
|
|
439
|
+
className: "sa_action",
|
|
440
|
+
type: "button",
|
|
441
|
+
disabled: busy || detailPending,
|
|
442
|
+
onClick: () => toggleDetail(item)
|
|
443
|
+
}, isExpanded ? t("collapse") : t("view"))
|
|
444
|
+
)
|
|
445
|
+
),
|
|
446
|
+
isExpanded ? React.createElement(
|
|
447
|
+
"div",
|
|
448
|
+
{ className: "sa_detail" },
|
|
449
|
+
detailPending ? React.createElement("p", { className: "sa_empty" }, "…") : null,
|
|
450
|
+
detail === void 0 ? null :
|
|
451
|
+
detail.error !== void 0 ? React.createElement("p", { className: "sa_error" }, detail.error) :
|
|
452
|
+
detail.messages.length === 0 ? React.createElement("p", { className: "sa_empty" }, t("noMessages")) :
|
|
453
|
+
detail.messages.map((message: any, index: any) => React.createElement(
|
|
454
|
+
"div",
|
|
455
|
+
{ className: "sa_msg", key: index },
|
|
456
|
+
React.createElement("span", { className: "sa_msgRole" }, message.role === "user" ? t("user") : t("assistant") + " · " + formatTime(message.time)),
|
|
457
|
+
React.createElement("span", { className: "sa_msgText" + (message.role === "user" ? " sa_msgTextUser" : "") }, message.text)
|
|
458
|
+
))
|
|
459
|
+
) : null
|
|
460
|
+
);
|
|
461
|
+
})
|
|
462
|
+
) : null
|
|
463
|
+
)
|
|
464
|
+
) : null
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// ── 插件 apply ───────────────────────────────────────────────────────
|
|
469
|
+
// DSH 客户端的 remote.<ns> 服务不会自动生成:必须由客户端代码用
|
|
470
|
+
// ctx.remote.$mount(contribution) 显式挂载(官方 dsh-api-remotes 即如此)。
|
|
471
|
+
const inject = ["slots", "locale", "remote"];
|
|
472
|
+
const passthroughSchema = { parse: (value: any) => value };
|
|
473
|
+
const REMOTE_CONTRIBUTION: TypertRemoteContribution = {
|
|
474
|
+
package: "@chaoset/session-archive",
|
|
475
|
+
descriptors: [
|
|
476
|
+
{ id: "@chaoset/session-archive#sessionArchive/list", service: "sessionArchive", namespace: "sessionArchive", method: "list", invocation: { kind: "direct" }, parameters: [], result: { mode: "strict", typeSymbol: "sessionArchive/list:result", schema: passthroughSchema } },
|
|
477
|
+
{ id: "@chaoset/session-archive#sessionArchive/detail", service: "sessionArchive", namespace: "sessionArchive", method: "detail", invocation: { kind: "direct" }, parameters: [{ name: "sessionId", wire: "sessionId", source: "json", codec: { mode: "strict", typeSymbol: "sessionArchive/detail:sessionId", schema: passthroughSchema } }], result: { mode: "strict", typeSymbol: "sessionArchive/detail:result", schema: passthroughSchema } },
|
|
478
|
+
{ id: "@chaoset/session-archive#sessionArchive/delete", service: "sessionArchive", namespace: "sessionArchive", method: "delete", invocation: { kind: "direct" }, parameters: [{ name: "sessionIds", wire: "sessionIds", source: "json", codec: { mode: "strict", typeSymbol: "sessionArchive/delete:sessionIds", schema: passthroughSchema } }], result: { mode: "strict", typeSymbol: "sessionArchive/delete:result", schema: passthroughSchema } },
|
|
479
|
+
{ id: "@chaoset/session-archive#sessionArchive/unarchive", service: "sessionArchive", namespace: "sessionArchive", method: "unarchive", invocation: { kind: "direct" }, parameters: [{ name: "sessionIds", wire: "sessionIds", source: "json", codec: { mode: "strict", typeSymbol: "sessionArchive/unarchive:sessionIds", schema: passthroughSchema } }], result: { mode: "strict", typeSymbol: "sessionArchive/unarchive:result", schema: passthroughSchema } }
|
|
480
|
+
]
|
|
481
|
+
};
|
|
482
|
+
async function apply(ctx: any) {
|
|
483
|
+
const t = ctx.locale.bind(NS);
|
|
484
|
+
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "session-archive: dictionaries");
|
|
485
|
+
await ctx.remote.$mount(REMOTE_CONTRIBUTION);
|
|
486
|
+
const archiveService = ctx.get("remote.sessionArchive");
|
|
487
|
+
if (archiveService === void 0) throw new Error("session-archive: remote.sessionArchive did not materialize after mount");
|
|
488
|
+
const call = (method: any, ...args: any[]) => archiveService[method](...args).then((result: any) => {
|
|
489
|
+
if (!result.ok) throw new Error(method + " failed: " + result.error.code + ": " + result.error.message);
|
|
490
|
+
return result.value;
|
|
491
|
+
});
|
|
492
|
+
ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
|
|
493
|
+
name: "sidebar.footer.action",
|
|
494
|
+
id: "session-archive",
|
|
495
|
+
order: 20,
|
|
496
|
+
locale: NS,
|
|
497
|
+
inject: () => ({ call })
|
|
498
|
+
}, ArchivePanel));
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
export { apply, inject };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config-store — 插件配置持久化
|
|
3
|
+
*
|
|
4
|
+
* 配置文件位于 $DSH_HOME/plugins/<name>/config.json(默认 ~/.dsh,与
|
|
5
|
+
* file:// 部署模式共用)。生效顺序(后者覆盖前者):
|
|
6
|
+
* 1. 插件内置默认值(DEFAULT_CONFIG)
|
|
7
|
+
* 2. cordis.patch.yml 传入的 config(安装时生成的默认块)
|
|
8
|
+
* 3. config.json(设置页 UI 保存,权威)
|
|
9
|
+
*
|
|
10
|
+
* 保存后通过 onUpdate 回调立即热更新运行中的配置(包装闭包读共享 state)。
|
|
11
|
+
* 写入采用随机临时文件 + fsync + rename,权限 0600;损坏 JSON 只告警并回退。
|
|
12
|
+
*/
|
|
13
|
+
export interface ConfigStoreOptions {
|
|
14
|
+
name: string;
|
|
15
|
+
defaults: Record<string, any>;
|
|
16
|
+
patchConfig: Record<string, any>;
|
|
17
|
+
onUpdate?: (merged: Record<string, any>, next: Record<string, any>) => void;
|
|
18
|
+
validate?: (partial: Record<string, any>) => void;
|
|
19
|
+
warn?: (message: string) => void;
|
|
20
|
+
}
|
|
21
|
+
export interface ConfigStore {
|
|
22
|
+
file: string;
|
|
23
|
+
effective(): Record<string, any>;
|
|
24
|
+
set(partial: Record<string, any>): Record<string, any>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 创建配置存储。
|
|
28
|
+
* @param options - { name, defaults, patchConfig, onUpdate,
|
|
29
|
+
* validate?: (partial) => void, warn?: (message) => void }
|
|
30
|
+
*/
|
|
31
|
+
export declare function createConfigStore(options: ConfigStoreOptions): ConfigStore;
|
|
32
|
+
//# sourceMappingURL=config-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-store.d.ts","sourceRoot":"","sources":["../src/config-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;IAC5E,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;IAClD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACxD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAyE1E"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config-store — 插件配置持久化
|
|
3
|
+
*
|
|
4
|
+
* 配置文件位于 $DSH_HOME/plugins/<name>/config.json(默认 ~/.dsh,与
|
|
5
|
+
* file:// 部署模式共用)。生效顺序(后者覆盖前者):
|
|
6
|
+
* 1. 插件内置默认值(DEFAULT_CONFIG)
|
|
7
|
+
* 2. cordis.patch.yml 传入的 config(安装时生成的默认块)
|
|
8
|
+
* 3. config.json(设置页 UI 保存,权威)
|
|
9
|
+
*
|
|
10
|
+
* 保存后通过 onUpdate 回调立即热更新运行中的配置(包装闭包读共享 state)。
|
|
11
|
+
* 写入采用随机临时文件 + fsync + rename,权限 0600;损坏 JSON 只告警并回退。
|
|
12
|
+
*/
|
|
13
|
+
import { randomUUID } from "node:crypto";
|
|
14
|
+
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { dirname, join, resolve } from "node:path";
|
|
16
|
+
import { homedir } from "node:os";
|
|
17
|
+
/**
|
|
18
|
+
* 创建配置存储。
|
|
19
|
+
* @param options - { name, defaults, patchConfig, onUpdate,
|
|
20
|
+
* validate?: (partial) => void, warn?: (message) => void }
|
|
21
|
+
*/
|
|
22
|
+
export function createConfigStore(options) {
|
|
23
|
+
// 与 harness 的 DSH_HOME 约定保持一致:默认 ~/.dsh,可用 $DSH_HOME 覆盖。
|
|
24
|
+
const dshHome = process.env.DSH_HOME?.trim() ? resolve(process.env.DSH_HOME) : join(homedir(), ".dsh");
|
|
25
|
+
const file = join(dshHome, "plugins", options.name, "config.json");
|
|
26
|
+
const warn = options.warn ?? (() => { });
|
|
27
|
+
let readWarningShown = false;
|
|
28
|
+
function readJson() {
|
|
29
|
+
try {
|
|
30
|
+
if (!existsSync(file))
|
|
31
|
+
return {};
|
|
32
|
+
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
33
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
34
|
+
readWarningShown = false;
|
|
35
|
+
return parsed;
|
|
36
|
+
}
|
|
37
|
+
if (!readWarningShown) {
|
|
38
|
+
readWarningShown = true;
|
|
39
|
+
warn(`config file ${file} must contain a JSON object; using empty config`);
|
|
40
|
+
}
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (!readWarningShown) {
|
|
45
|
+
readWarningShown = true;
|
|
46
|
+
warn(`failed to read config file ${file}: ${error?.message ?? String(error)}; using empty config`);
|
|
47
|
+
}
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** 当前生效配置(默认 + patch + json 合并)。 */
|
|
52
|
+
function effective() {
|
|
53
|
+
return { ...options.defaults, ...options.patchConfig, ...readJson() };
|
|
54
|
+
}
|
|
55
|
+
/** 保存部分配置到 config.json 并触发热更新,返回新的生效配置。 */
|
|
56
|
+
function set(partial) {
|
|
57
|
+
if (partial === null || typeof partial !== "object" || Array.isArray(partial)) {
|
|
58
|
+
throw new TypeError("set expects a plain config object");
|
|
59
|
+
}
|
|
60
|
+
options.validate?.(partial);
|
|
61
|
+
const merged = { ...readJson(), ...partial };
|
|
62
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
63
|
+
const tmp = `${file}.${process.pid}.${randomUUID()}.tmp`;
|
|
64
|
+
let fd;
|
|
65
|
+
try {
|
|
66
|
+
fd = openSync(tmp, "w", 0o600);
|
|
67
|
+
writeFileSync(fd, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
68
|
+
fsyncSync(fd);
|
|
69
|
+
closeSync(fd);
|
|
70
|
+
fd = void 0;
|
|
71
|
+
renameSync(tmp, file);
|
|
72
|
+
// POSIX 目录项持久化(Windows 上目录句柄不可打开时忽略)。
|
|
73
|
+
try {
|
|
74
|
+
const dir = openSync(dirname(file), "r");
|
|
75
|
+
try {
|
|
76
|
+
fsyncSync(dir);
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
closeSync(dir);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch { }
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
if (fd !== void 0)
|
|
86
|
+
try {
|
|
87
|
+
closeSync(fd);
|
|
88
|
+
}
|
|
89
|
+
catch { }
|
|
90
|
+
try {
|
|
91
|
+
unlinkSync(tmp);
|
|
92
|
+
}
|
|
93
|
+
catch { }
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
const next = { ...options.defaults, ...options.patchConfig, ...merged };
|
|
97
|
+
try {
|
|
98
|
+
options.onUpdate?.(merged, next);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
warn(`onUpdate failed after config save: ${error?.message ?? String(error)}`);
|
|
102
|
+
}
|
|
103
|
+
return next;
|
|
104
|
+
}
|
|
105
|
+
return { file, effective, set };
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=config-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-store.js","sourceRoot":"","sources":["../src/config-store.ts"],"names":[],"mappings":"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"}
|