relay-dsh-plugin-claude 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -13
- package/README.zh.md +85 -12
- package/docs/spec/claude-local-plugins.md +56 -0
- package/docs/spec/claude-native-session-import.md +114 -0
- package/docs/spec/claude-search-tools.md +40 -0
- package/docs/spec/claude-tool-output-redaction.md +68 -0
- package/lib/client.js +575 -0
- package/lib/client.js.map +1 -1
- package/lib/host-plugin.js +1032 -34
- package/lib/host-plugin.js.map +1 -1
- package/package.json +4 -4
package/lib/client.js
CHANGED
|
@@ -50,6 +50,496 @@ window.__ModuleLoader__.load({
|
|
|
50
50
|
return off;
|
|
51
51
|
}
|
|
52
52
|
//#endregion
|
|
53
|
+
//#region claude-import-contract.mjs
|
|
54
|
+
const CLAUDE_IMPORT_PATH = "/api/relay/claude/import";
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/client/claude-session-import-client.mjs
|
|
57
|
+
function resolveImportWorkspace(workspaces, sessions) {
|
|
58
|
+
const current = sessions?.current;
|
|
59
|
+
if (current !== void 0) {
|
|
60
|
+
const owner = workspaces?.items?.find((workspace) => workspace.sessionIds?.includes(current));
|
|
61
|
+
if (owner) return owner;
|
|
62
|
+
}
|
|
63
|
+
const recent = workspaces?.recentWorkspaceId;
|
|
64
|
+
return workspaces?.items?.find((workspace) => workspace.workspaceId === recent) ?? null;
|
|
65
|
+
}
|
|
66
|
+
async function scanClaudeWorkspace(cwd, fetchImpl = fetch) {
|
|
67
|
+
const response = await fetchImpl(CLAUDE_IMPORT_PATH, requestInit("scan", cwd));
|
|
68
|
+
const body = await readJsonResponse(response);
|
|
69
|
+
if (!response.ok) throw new Error(body?.message ?? `Claude import scan failed with HTTP ${response.status}`);
|
|
70
|
+
return body;
|
|
71
|
+
}
|
|
72
|
+
async function importClaudeWorkspace(cwd, { sessionIds, onProgress } = {}, fetchImpl = fetch) {
|
|
73
|
+
const response = await fetchImpl(CLAUDE_IMPORT_PATH, requestInit("import", cwd, sessionIds));
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
const body = await readJsonResponse(response);
|
|
76
|
+
throw new Error(body?.message ?? `Claude import failed with HTTP ${response.status}`);
|
|
77
|
+
}
|
|
78
|
+
let completed = null;
|
|
79
|
+
for await (const frame of ndjsonFrames(response.body)) {
|
|
80
|
+
if (frame?.type === "progress") onProgress?.(frame);
|
|
81
|
+
if (frame?.type === "complete") completed = frame.result;
|
|
82
|
+
if (frame?.type === "error") throw new Error(frame.message ?? "Claude import failed");
|
|
83
|
+
}
|
|
84
|
+
if (completed === null) throw new Error("Claude import response ended before completion");
|
|
85
|
+
return completed;
|
|
86
|
+
}
|
|
87
|
+
async function refreshImportedWorkspace(sessions, workspaces) {
|
|
88
|
+
await sessions.refresh();
|
|
89
|
+
await workspaces.refresh();
|
|
90
|
+
}
|
|
91
|
+
async function* ndjsonFrames(body) {
|
|
92
|
+
if (!body || typeof body.getReader !== "function") throw new Error("Claude import response has no readable body");
|
|
93
|
+
const reader = body.getReader();
|
|
94
|
+
const decoder = new TextDecoder();
|
|
95
|
+
let buffer = "";
|
|
96
|
+
try {
|
|
97
|
+
while (true) {
|
|
98
|
+
const { done, value } = await reader.read();
|
|
99
|
+
buffer += decoder.decode(value, { stream: !done });
|
|
100
|
+
let newline = buffer.indexOf("\n");
|
|
101
|
+
while (newline !== -1) {
|
|
102
|
+
const line = buffer.slice(0, newline).trim();
|
|
103
|
+
buffer = buffer.slice(newline + 1);
|
|
104
|
+
if (line) yield parseFrame(line);
|
|
105
|
+
newline = buffer.indexOf("\n");
|
|
106
|
+
}
|
|
107
|
+
if (done) break;
|
|
108
|
+
}
|
|
109
|
+
const finalLine = buffer.trim();
|
|
110
|
+
if (finalLine) yield parseFrame(finalLine);
|
|
111
|
+
} finally {
|
|
112
|
+
reader.releaseLock();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function requestInit(action, cwd, sessionIds) {
|
|
116
|
+
return {
|
|
117
|
+
method: "POST",
|
|
118
|
+
headers: { "content-type": "application/json" },
|
|
119
|
+
body: JSON.stringify({
|
|
120
|
+
action,
|
|
121
|
+
cwd,
|
|
122
|
+
...sessionIds === void 0 ? {} : { sessionIds }
|
|
123
|
+
})
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
async function readJsonResponse(response) {
|
|
127
|
+
try {
|
|
128
|
+
return await response.json();
|
|
129
|
+
} catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function parseFrame(line) {
|
|
134
|
+
try {
|
|
135
|
+
return JSON.parse(line);
|
|
136
|
+
} catch {
|
|
137
|
+
throw new Error("Claude import returned malformed progress data");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region src/client/claude-session-import-ui-policy.mjs
|
|
142
|
+
function claudeSessionImportUiPolicy(phase, selected = 0, failed = 0) {
|
|
143
|
+
if (phase === "importing") return Object.freeze({
|
|
144
|
+
canClose: false,
|
|
145
|
+
primary: "importing",
|
|
146
|
+
primaryDisabled: true
|
|
147
|
+
});
|
|
148
|
+
if (phase === "summary") return Object.freeze({
|
|
149
|
+
canClose: true,
|
|
150
|
+
secondary: "cancel",
|
|
151
|
+
primary: "import-selected",
|
|
152
|
+
primaryDisabled: selected === 0
|
|
153
|
+
});
|
|
154
|
+
if (phase === "error") return Object.freeze({
|
|
155
|
+
canClose: true,
|
|
156
|
+
secondary: "cancel",
|
|
157
|
+
primary: "retry",
|
|
158
|
+
primaryDisabled: false
|
|
159
|
+
});
|
|
160
|
+
if (phase === "complete" && failed > 0) return Object.freeze({
|
|
161
|
+
canClose: true,
|
|
162
|
+
secondary: "close",
|
|
163
|
+
primary: "retry",
|
|
164
|
+
primaryDisabled: false
|
|
165
|
+
});
|
|
166
|
+
return Object.freeze({
|
|
167
|
+
canClose: true,
|
|
168
|
+
primary: "close",
|
|
169
|
+
primaryDisabled: false
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
function claudeSessionImportUpdatedAtDate(value) {
|
|
173
|
+
if (value === null || value === void 0) return null;
|
|
174
|
+
const milliseconds = typeof value === "number" && Number.isFinite(value) && Math.abs(value) < 0xe8d4a51000 ? value * 1e3 : value;
|
|
175
|
+
const date = new Date(milliseconds);
|
|
176
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region \0relay-css-module:./src/client/ClaudeSessionImportAction.module.css.mjs
|
|
180
|
+
const css$1 = "._8iKb1W_trigger{width:100%;min-width:0;height:34px;color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;letter-spacing:0;background:0 0;border:0;border-radius:6px;justify-content:flex-start;align-items:center;gap:10px;padding:0 12px;font-size:13px;display:flex}._8iKb1W_trigger:hover{background:var(--dsw-alias-fill-l2);color:var(--dsw-alias-label-primary)}._8iKb1W_trigger:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}._8iKb1W_trigger span{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}._8iKb1W_dialog{width:min(700px,100vw - 32px)}._8iKb1W_body{flex-direction:column;gap:18px;min-height:128px;display:flex}._8iKb1W_workspace{border-bottom:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:3px;min-width:0;padding-bottom:12px;display:flex}._8iKb1W_workspace strong{overflow-wrap:anywhere;color:var(--dsw-alias-label-primary);letter-spacing:0;font-size:14px;font-weight:500;line-height:20px}._8iKb1W_workspace span{color:var(--dsw-alias-label-tertiary);letter-spacing:0;overflow-wrap:anywhere;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:17px;overflow:hidden}._8iKb1W_message,._8iKb1W_error,._8iKb1W_success,._8iKb1W_partial{letter-spacing:0;margin:0;font-size:13px;line-height:20px}._8iKb1W_message{color:var(--dsw-alias-label-secondary)}._8iKb1W_error,._8iKb1W_dangerValue{color:var(--dsw-alias-state-error-primary)}._8iKb1W_success,._8iKb1W_accentValue{color:var(--dsw-alias-state-success-primary)}._8iKb1W_partial{color:var(--dsw-alias-state-error-secondary)}._8iKb1W_metrics{border-top:1px solid var(--dsw-alias-border-l2);border-left:1px solid var(--dsw-alias-border-l2);grid-template-columns:repeat(2,minmax(0,1fr));margin:0;display:grid}._8iKb1W_summary{flex-direction:column;gap:14px;min-width:0;display:flex}._8iKb1W_selectionToolbar{min-height:28px;color:var(--dsw-alias-label-secondary);letter-spacing:0;justify-content:space-between;align-items:center;gap:12px;font-size:12px;line-height:18px;display:flex}._8iKb1W_selectionToolbar>div{gap:4px;display:flex}._8iKb1W_selectionToolbar button{min-height:28px;color:var(--dsw-alias-state-business-primary);cursor:pointer;font:inherit;letter-spacing:0;background:0 0;border:0;border-radius:4px;padding:0 8px}._8iKb1W_selectionToolbar button:hover{background:var(--dsw-alias-fill-l2)}._8iKb1W_selectionToolbar button:focus-visible,._8iKb1W_candidates input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}._8iKb1W_candidates{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;max-height:min(390px,48vh);margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}._8iKb1W_candidates li{border-bottom:1px solid var(--dsw-alias-border-l2);min-width:0}._8iKb1W_candidates label{cursor:pointer;grid-template-columns:18px minmax(0,1fr);gap:10px;min-width:0;padding:11px 4px;display:grid}._8iKb1W_candidates input{width:16px;height:16px;accent-color:var(--dsw-alias-state-business-primary);margin:2px 0 0}._8iKb1W_candidateBody{flex-direction:column;gap:2px;min-width:0;display:flex}._8iKb1W_candidateHeading{justify-content:space-between;align-items:flex-start;gap:12px;min-width:0;display:flex}._8iKb1W_candidateHeading strong{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-primary);letter-spacing:0;font-size:13px;font-weight:500;line-height:19px}._8iKb1W_candidateHeading>span{color:var(--dsw-alias-state-success-primary);letter-spacing:0;flex:none;font-size:11px;line-height:18px}._8iKb1W_candidateBody code,._8iKb1W_candidateBody>span,._8iKb1W_candidateBody time{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-tertiary);letter-spacing:0;font-size:11px;line-height:17px}._8iKb1W_candidateBody code{color:var(--dsw-alias-label-secondary);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}._8iKb1W_metric{border-right:1px solid var(--dsw-alias-border-l2);border-bottom:1px solid var(--dsw-alias-border-l2);min-width:0;padding:10px 12px}._8iKb1W_metric dt{color:var(--dsw-alias-label-tertiary);letter-spacing:0;font-size:11px;line-height:16px}._8iKb1W_metric dd{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;letter-spacing:0;margin:2px 0 0;font-size:18px;font-weight:500;line-height:24px}._8iKb1W_progress{flex-direction:column;gap:10px;display:flex}._8iKb1W_progressCopy{color:var(--dsw-alias-label-secondary);letter-spacing:0;justify-content:space-between;align-items:center;gap:16px;font-size:13px;line-height:20px;display:flex}._8iKb1W_progressCopy strong{color:var(--dsw-alias-label-primary);font-weight:500}._8iKb1W_progress progress{width:100%;height:6px;accent-color:var(--dsw-alias-state-business-primary)}._8iKb1W_failures{flex-direction:column;gap:8px;margin:14px 0 0;padding:0;list-style:none;display:flex}._8iKb1W_failures li{min-width:0;color:var(--dsw-alias-label-secondary);letter-spacing:0;grid-template-columns:minmax(88px,auto) minmax(0,1fr);gap:10px;font-size:12px;line-height:18px;display:grid}._8iKb1W_failures code,._8iKb1W_failures span{overflow-wrap:anywhere}._8iKb1W_failures code{color:var(--dsw-alias-state-error-primary)}@media (width<=520px){._8iKb1W_dialog{width:calc(100vw - 20px)}._8iKb1W_metrics{grid-template-columns:minmax(0,1fr)}._8iKb1W_selectionToolbar,._8iKb1W_candidateHeading{flex-direction:column;align-items:flex-start}._8iKb1W_candidateHeading{gap:2px}}";
|
|
181
|
+
const tagId$1 = "relay-dsh-plugin-claude/ClaudeSessionImportAction.module.css";
|
|
182
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
183
|
+
const tag = document.createElement("style");
|
|
184
|
+
tag.dataset.plugin = "relay-dsh-plugin-claude";
|
|
185
|
+
tag.dataset.pluginCss = tagId$1;
|
|
186
|
+
tag.textContent = css$1;
|
|
187
|
+
document.head.appendChild(tag);
|
|
188
|
+
}
|
|
189
|
+
var ClaudeSessionImportAction_module_css_default = {
|
|
190
|
+
"accentValue": "_8iKb1W_accentValue",
|
|
191
|
+
"body": "_8iKb1W_body",
|
|
192
|
+
"candidateBody": "_8iKb1W_candidateBody",
|
|
193
|
+
"candidateHeading": "_8iKb1W_candidateHeading",
|
|
194
|
+
"candidates": "_8iKb1W_candidates",
|
|
195
|
+
"dangerValue": "_8iKb1W_dangerValue",
|
|
196
|
+
"dialog": "_8iKb1W_dialog",
|
|
197
|
+
"error": "_8iKb1W_error",
|
|
198
|
+
"failures": "_8iKb1W_failures",
|
|
199
|
+
"message": "_8iKb1W_message",
|
|
200
|
+
"metric": "_8iKb1W_metric",
|
|
201
|
+
"metrics": "_8iKb1W_metrics",
|
|
202
|
+
"partial": "_8iKb1W_partial",
|
|
203
|
+
"progress": "_8iKb1W_progress",
|
|
204
|
+
"progressCopy": "_8iKb1W_progressCopy",
|
|
205
|
+
"selectionToolbar": "_8iKb1W_selectionToolbar",
|
|
206
|
+
"success": "_8iKb1W_success",
|
|
207
|
+
"summary": "_8iKb1W_summary",
|
|
208
|
+
"trigger": "_8iKb1W_trigger",
|
|
209
|
+
"workspace": "_8iKb1W_workspace"
|
|
210
|
+
};
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/client/ClaudeSessionImportAction.tsx
|
|
213
|
+
function ClaudeSessionImportAction({ wide, useClaudeSessionImportWorkspaces, useClaudeSessionImportSessions, scanWorkspace, importWorkspace, refreshWorkspaceState, t }) {
|
|
214
|
+
const availableTarget = resolveImportWorkspace(useClaudeSessionImportWorkspaces((value) => value), useClaudeSessionImportSessions((value) => value));
|
|
215
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
216
|
+
const [target, setTarget] = (0, react.useState)(null);
|
|
217
|
+
const [phase, setPhase] = (0, react.useState)("idle");
|
|
218
|
+
const [summary, setSummary] = (0, react.useState)(null);
|
|
219
|
+
const [candidates, setCandidates] = (0, react.useState)([]);
|
|
220
|
+
const [selectedIds, setSelectedIds] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
221
|
+
const [progress, setProgress] = (0, react.useState)(null);
|
|
222
|
+
const [result, setResult] = (0, react.useState)(null);
|
|
223
|
+
const [error, setError] = (0, react.useState)("");
|
|
224
|
+
const request = (0, react.useRef)(0);
|
|
225
|
+
const close = () => {
|
|
226
|
+
if (!claudeSessionImportUiPolicy(phase, selectedIds.size, result?.failed).canClose) return;
|
|
227
|
+
request.current += 1;
|
|
228
|
+
setOpen(false);
|
|
229
|
+
};
|
|
230
|
+
const scan = (workspace) => {
|
|
231
|
+
const generation = ++request.current;
|
|
232
|
+
setPhase("scanning");
|
|
233
|
+
setSummary(null);
|
|
234
|
+
setCandidates([]);
|
|
235
|
+
setSelectedIds(/* @__PURE__ */ new Set());
|
|
236
|
+
setProgress(null);
|
|
237
|
+
setResult(null);
|
|
238
|
+
setError("");
|
|
239
|
+
scanWorkspace(workspace.path).then((response) => {
|
|
240
|
+
if (request.current !== generation) return;
|
|
241
|
+
setSummary(response.summary);
|
|
242
|
+
setCandidates(response.candidates);
|
|
243
|
+
setSelectedIds(new Set(response.candidates.map((candidate) => candidate.id)));
|
|
244
|
+
setPhase("summary");
|
|
245
|
+
}, (reason) => {
|
|
246
|
+
if (request.current !== generation) return;
|
|
247
|
+
setError(messageOf(reason));
|
|
248
|
+
setPhase("error");
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
const begin = () => {
|
|
252
|
+
const selected = availableTarget;
|
|
253
|
+
setTarget(selected);
|
|
254
|
+
setOpen(true);
|
|
255
|
+
if (selected === null) {
|
|
256
|
+
setPhase("no-workspace");
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
scan(selected);
|
|
260
|
+
};
|
|
261
|
+
const importSelected = () => {
|
|
262
|
+
const sessionIds = candidates.filter((candidate) => selectedIds.has(candidate.id)).map((candidate) => candidate.id);
|
|
263
|
+
if (target === null || summary === null || sessionIds.length === 0 || phase === "importing") return;
|
|
264
|
+
const generation = ++request.current;
|
|
265
|
+
setPhase("importing");
|
|
266
|
+
setProgress({
|
|
267
|
+
completed: 0,
|
|
268
|
+
total: sessionIds.length,
|
|
269
|
+
found: sessionIds.length,
|
|
270
|
+
imported: 0,
|
|
271
|
+
existing: 0,
|
|
272
|
+
failed: 0,
|
|
273
|
+
failures: []
|
|
274
|
+
});
|
|
275
|
+
setError("");
|
|
276
|
+
(async () => {
|
|
277
|
+
try {
|
|
278
|
+
const completed = await importWorkspace(target.path, sessionIds, (update) => {
|
|
279
|
+
if (request.current === generation) setProgress(update);
|
|
280
|
+
});
|
|
281
|
+
await refreshWorkspaceState();
|
|
282
|
+
if (request.current !== generation) return;
|
|
283
|
+
setResult(completed);
|
|
284
|
+
setPhase("complete");
|
|
285
|
+
} catch (reason) {
|
|
286
|
+
if (request.current !== generation) return;
|
|
287
|
+
setError(messageOf(reason));
|
|
288
|
+
setPhase("error");
|
|
289
|
+
}
|
|
290
|
+
})();
|
|
291
|
+
};
|
|
292
|
+
const retry = () => {
|
|
293
|
+
if (target !== null) scan(target);
|
|
294
|
+
};
|
|
295
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
296
|
+
label: t("importAction"),
|
|
297
|
+
delayMs: 500,
|
|
298
|
+
disabled: wide,
|
|
299
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
300
|
+
type: "button",
|
|
301
|
+
className: ClaudeSessionImportAction_module_css_default.trigger,
|
|
302
|
+
"aria-label": t("importAction"),
|
|
303
|
+
onClick: begin,
|
|
304
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, { size: wide ? 16 : 18 }), wide && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("importAction") })]
|
|
305
|
+
})
|
|
306
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
307
|
+
open,
|
|
308
|
+
onClose: close,
|
|
309
|
+
title: t("importTitle"),
|
|
310
|
+
closeLabel: t("close"),
|
|
311
|
+
description: t("importDescription"),
|
|
312
|
+
className: ClaudeSessionImportAction_module_css_default.dialog,
|
|
313
|
+
footer: modalFooter({
|
|
314
|
+
phase,
|
|
315
|
+
selectedCount: selectedIds.size,
|
|
316
|
+
result,
|
|
317
|
+
close,
|
|
318
|
+
retry,
|
|
319
|
+
importSelected,
|
|
320
|
+
t
|
|
321
|
+
}),
|
|
322
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
323
|
+
className: ClaudeSessionImportAction_module_css_default.body,
|
|
324
|
+
"aria-live": "polite",
|
|
325
|
+
children: [
|
|
326
|
+
target !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
327
|
+
className: ClaudeSessionImportAction_module_css_default.workspace,
|
|
328
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: target.title }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
329
|
+
title: target.path,
|
|
330
|
+
children: target.path
|
|
331
|
+
})]
|
|
332
|
+
}),
|
|
333
|
+
phase === "no-workspace" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
334
|
+
className: ClaudeSessionImportAction_module_css_default.message,
|
|
335
|
+
children: t("importNoWorkspace")
|
|
336
|
+
}),
|
|
337
|
+
phase === "scanning" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
338
|
+
className: ClaudeSessionImportAction_module_css_default.message,
|
|
339
|
+
children: t("importScanning")
|
|
340
|
+
}),
|
|
341
|
+
phase === "summary" && summary !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SummaryView, {
|
|
342
|
+
summary,
|
|
343
|
+
candidates,
|
|
344
|
+
selectedIds,
|
|
345
|
+
onSelectionChange: setSelectedIds,
|
|
346
|
+
t
|
|
347
|
+
}),
|
|
348
|
+
phase === "importing" && progress !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProgressView, {
|
|
349
|
+
progress,
|
|
350
|
+
t
|
|
351
|
+
}),
|
|
352
|
+
phase === "complete" && result !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResultView, {
|
|
353
|
+
result,
|
|
354
|
+
t
|
|
355
|
+
}),
|
|
356
|
+
phase === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
357
|
+
className: ClaudeSessionImportAction_module_css_default.error,
|
|
358
|
+
role: "alert",
|
|
359
|
+
children: error || t("importFailed")
|
|
360
|
+
})
|
|
361
|
+
]
|
|
362
|
+
})
|
|
363
|
+
})] });
|
|
364
|
+
}
|
|
365
|
+
function SummaryView({ summary, candidates, selectedIds, onSelectionChange, t }) {
|
|
366
|
+
const select = (id, checked) => {
|
|
367
|
+
const next = new Set(selectedIds);
|
|
368
|
+
if (checked) next.add(id);
|
|
369
|
+
else next.delete(id);
|
|
370
|
+
onSelectionChange(next);
|
|
371
|
+
};
|
|
372
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
373
|
+
className: ClaudeSessionImportAction_module_css_default.summary,
|
|
374
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
|
|
375
|
+
className: ClaudeSessionImportAction_module_css_default.metrics,
|
|
376
|
+
children: [
|
|
377
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
|
|
378
|
+
label: t("importFound"),
|
|
379
|
+
value: summary.found
|
|
380
|
+
}),
|
|
381
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
|
|
382
|
+
label: t("importExisting"),
|
|
383
|
+
value: summary.existing
|
|
384
|
+
}),
|
|
385
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
|
|
386
|
+
label: t("importRecoverable"),
|
|
387
|
+
value: summary.recoverable
|
|
388
|
+
}),
|
|
389
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
|
|
390
|
+
label: t("importReady"),
|
|
391
|
+
value: summary.ready,
|
|
392
|
+
accent: true
|
|
393
|
+
})
|
|
394
|
+
]
|
|
395
|
+
}), candidates.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
396
|
+
className: ClaudeSessionImportAction_module_css_default.message,
|
|
397
|
+
children: t("importEmpty")
|
|
398
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
399
|
+
className: ClaudeSessionImportAction_module_css_default.selectionToolbar,
|
|
400
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
401
|
+
t("importSelected"),
|
|
402
|
+
": ",
|
|
403
|
+
selectedIds.size,
|
|
404
|
+
" / ",
|
|
405
|
+
candidates.length
|
|
406
|
+
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
407
|
+
type: "button",
|
|
408
|
+
onClick: () => onSelectionChange(new Set(candidates.map((candidate) => candidate.id))),
|
|
409
|
+
children: t("importSelectAll")
|
|
410
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
411
|
+
type: "button",
|
|
412
|
+
onClick: () => onSelectionChange(/* @__PURE__ */ new Set()),
|
|
413
|
+
children: t("importClearSelection")
|
|
414
|
+
})] })]
|
|
415
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
416
|
+
className: ClaudeSessionImportAction_module_css_default.candidates,
|
|
417
|
+
"aria-label": t("importCandidates"),
|
|
418
|
+
children: candidates.map((candidate) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
419
|
+
type: "checkbox",
|
|
420
|
+
checked: selectedIds.has(candidate.id),
|
|
421
|
+
onChange: (event) => select(candidate.id, event.currentTarget.checked)
|
|
422
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
423
|
+
className: ClaudeSessionImportAction_module_css_default.candidateBody,
|
|
424
|
+
children: [
|
|
425
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
426
|
+
className: ClaudeSessionImportAction_module_css_default.candidateHeading,
|
|
427
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: candidate.title }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: candidate.status === "recoverable" ? t("importStatusRecoverable") : t("importStatusReady") })]
|
|
428
|
+
}),
|
|
429
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: candidate.id }),
|
|
430
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
431
|
+
title: candidate.cwd,
|
|
432
|
+
children: candidate.cwd
|
|
433
|
+
}),
|
|
434
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("time", {
|
|
435
|
+
dateTime: dateTimeValue(candidate.updatedAt),
|
|
436
|
+
children: formatUpdatedAt(candidate.updatedAt)
|
|
437
|
+
})
|
|
438
|
+
]
|
|
439
|
+
})] }) }, candidate.id))
|
|
440
|
+
})] })]
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
function ProgressView({ progress, t }) {
|
|
444
|
+
const maximum = Math.max(1, progress.total);
|
|
445
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
446
|
+
className: ClaudeSessionImportAction_module_css_default.progress,
|
|
447
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
448
|
+
className: ClaudeSessionImportAction_module_css_default.progressCopy,
|
|
449
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("importImporting") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
450
|
+
progress.completed,
|
|
451
|
+
" / ",
|
|
452
|
+
progress.total
|
|
453
|
+
] })]
|
|
454
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("progress", {
|
|
455
|
+
value: progress.completed,
|
|
456
|
+
max: maximum,
|
|
457
|
+
"aria-label": t("importImporting")
|
|
458
|
+
})]
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
function ResultView({ result, t }) {
|
|
462
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
463
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
464
|
+
className: result.failed > 0 ? ClaudeSessionImportAction_module_css_default.partial : ClaudeSessionImportAction_module_css_default.success,
|
|
465
|
+
children: result.failed > 0 ? t("importPartial") : t("importComplete")
|
|
466
|
+
}),
|
|
467
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
|
|
468
|
+
className: ClaudeSessionImportAction_module_css_default.metrics,
|
|
469
|
+
children: [
|
|
470
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
|
|
471
|
+
label: t("importImported"),
|
|
472
|
+
value: result.imported,
|
|
473
|
+
accent: true
|
|
474
|
+
}),
|
|
475
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
|
|
476
|
+
label: t("importExisting"),
|
|
477
|
+
value: result.existing
|
|
478
|
+
}),
|
|
479
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
|
|
480
|
+
label: t("importFailures"),
|
|
481
|
+
value: result.failed,
|
|
482
|
+
danger: result.failed > 0
|
|
483
|
+
})
|
|
484
|
+
]
|
|
485
|
+
}),
|
|
486
|
+
result.failures.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
487
|
+
className: ClaudeSessionImportAction_module_css_default.failures,
|
|
488
|
+
"aria-label": t("importFailures"),
|
|
489
|
+
children: result.failures.map((failure) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: failure.session }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: failure.message })] }, failure.session))
|
|
490
|
+
})
|
|
491
|
+
] });
|
|
492
|
+
}
|
|
493
|
+
function Metric({ label, value, accent = false, danger = false }) {
|
|
494
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
495
|
+
className: ClaudeSessionImportAction_module_css_default.metric,
|
|
496
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
|
|
497
|
+
className: danger ? ClaudeSessionImportAction_module_css_default.dangerValue : accent ? ClaudeSessionImportAction_module_css_default.accentValue : void 0,
|
|
498
|
+
children: value
|
|
499
|
+
})]
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
function modalFooter({ phase, selectedCount, result, close, retry, importSelected, t }) {
|
|
503
|
+
const policy = claudeSessionImportUiPolicy(phase, selectedCount, result?.failed);
|
|
504
|
+
const actions = {
|
|
505
|
+
cancel: close,
|
|
506
|
+
close,
|
|
507
|
+
"import-selected": importSelected,
|
|
508
|
+
importing: void 0,
|
|
509
|
+
retry
|
|
510
|
+
};
|
|
511
|
+
const labels = {
|
|
512
|
+
cancel: t("cancel"),
|
|
513
|
+
close: t("close"),
|
|
514
|
+
"import-selected": t("importSelectedAction"),
|
|
515
|
+
importing: t("importImporting"),
|
|
516
|
+
retry: t("retry")
|
|
517
|
+
};
|
|
518
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [policy.secondary !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
519
|
+
variant: "outline",
|
|
520
|
+
onClick: actions[policy.secondary],
|
|
521
|
+
children: labels[policy.secondary]
|
|
522
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
523
|
+
variant: policy.primary === "close" ? "outline" : void 0,
|
|
524
|
+
disabled: policy.primaryDisabled,
|
|
525
|
+
onClick: actions[policy.primary],
|
|
526
|
+
children: labels[policy.primary]
|
|
527
|
+
})] });
|
|
528
|
+
}
|
|
529
|
+
function dateTimeValue(value) {
|
|
530
|
+
return claudeSessionImportUpdatedAtDate(value)?.toISOString();
|
|
531
|
+
}
|
|
532
|
+
function formatUpdatedAt(value) {
|
|
533
|
+
const date = claudeSessionImportUpdatedAtDate(value);
|
|
534
|
+
return date === null ? "-" : new Intl.DateTimeFormat(void 0, {
|
|
535
|
+
dateStyle: "medium",
|
|
536
|
+
timeStyle: "short"
|
|
537
|
+
}).format(date);
|
|
538
|
+
}
|
|
539
|
+
function messageOf(reason) {
|
|
540
|
+
return reason instanceof Error ? reason.message : String(reason);
|
|
541
|
+
}
|
|
542
|
+
//#endregion
|
|
53
543
|
//#region \0relay-css-module:./src/client/ClaudeActivityView.module.css.mjs
|
|
54
544
|
const css = ".ts8jCW_activity{width:min(100%,960px);color:var(--dsw-alias-label-secondary)}.ts8jCW_summary{min-width:0;color:var(--dsw-alias-label-tertiary);flex:1;align-items:center;gap:8px;font-size:13px;display:flex}.ts8jCW_summary :first-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.ts8jCW_detail{border-left:1px solid var(--dsw-alias-border-l2);margin:4px 0 8px 28px;padding-left:12px;overflow:hidden}.ts8jCW_detail pre{max-height:320px;color:var(--dsw-alias-label-secondary);font:var(--dsw-font-markdown-code-block-small);white-space:pre-wrap;word-break:break-word;margin:0 0 8px;overflow:auto}.ts8jCW_provenance{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;margin-bottom:8px;font-size:12px;line-height:18px;overflow:hidden}";
|
|
55
545
|
const tagId = "relay-dsh-plugin-claude/ClaudeActivityView.module.css";
|
|
@@ -160,6 +650,64 @@ window.__ModuleLoader__.load({
|
|
|
160
650
|
}
|
|
161
651
|
};
|
|
162
652
|
//#endregion
|
|
653
|
+
//#region src/client/locales.ts
|
|
654
|
+
const en = {
|
|
655
|
+
importAction: "Import Claude Sessions",
|
|
656
|
+
importTitle: "Import Claude Sessions",
|
|
657
|
+
importDescription: "Select existing Claude terminal Sessions from this Workspace to continue in DSH.",
|
|
658
|
+
importNoWorkspace: "Open or select a Workspace before importing Sessions.",
|
|
659
|
+
importScanning: "Scanning native Claude Sessions...",
|
|
660
|
+
importFound: "Found",
|
|
661
|
+
importExisting: "Already linked",
|
|
662
|
+
importRecoverable: "Recoverable",
|
|
663
|
+
importReady: "Available",
|
|
664
|
+
importEmpty: "No unlinked Claude Sessions are available in this Workspace.",
|
|
665
|
+
importSelected: "Selected",
|
|
666
|
+
importSelectAll: "Select all",
|
|
667
|
+
importClearSelection: "Clear",
|
|
668
|
+
importCandidates: "Claude Session candidates",
|
|
669
|
+
importStatusReady: "Ready",
|
|
670
|
+
importStatusRecoverable: "Retry import",
|
|
671
|
+
importSelectedAction: "Import selected",
|
|
672
|
+
importImporting: "Importing Sessions...",
|
|
673
|
+
importComplete: "Import completed.",
|
|
674
|
+
importPartial: "Some Sessions could not be imported.",
|
|
675
|
+
importImported: "Imported",
|
|
676
|
+
importFailures: "Failed",
|
|
677
|
+
importFailed: "Claude Session import failed.",
|
|
678
|
+
cancel: "Cancel",
|
|
679
|
+
close: "Close",
|
|
680
|
+
retry: "Retry"
|
|
681
|
+
};
|
|
682
|
+
const zh = {
|
|
683
|
+
importAction: "导入 Claude 会话",
|
|
684
|
+
importTitle: "导入 Claude 会话",
|
|
685
|
+
importDescription: "选择当前工作区中已有的 Claude 终端会话,并在 DSH 中继续。",
|
|
686
|
+
importNoWorkspace: "请先打开或选择一个工作区。",
|
|
687
|
+
importScanning: "正在扫描原生 Claude 会话...",
|
|
688
|
+
importFound: "发现",
|
|
689
|
+
importExisting: "已绑定",
|
|
690
|
+
importRecoverable: "可恢复",
|
|
691
|
+
importReady: "可导入",
|
|
692
|
+
importEmpty: "当前工作区没有未绑定的 Claude 会话。",
|
|
693
|
+
importSelected: "已选择",
|
|
694
|
+
importSelectAll: "全选",
|
|
695
|
+
importClearSelection: "清空",
|
|
696
|
+
importCandidates: "Claude 会话候选列表",
|
|
697
|
+
importStatusReady: "可导入",
|
|
698
|
+
importStatusRecoverable: "重试导入",
|
|
699
|
+
importSelectedAction: "导入所选会话",
|
|
700
|
+
importImporting: "正在导入会话...",
|
|
701
|
+
importComplete: "导入完成。",
|
|
702
|
+
importPartial: "部分会话未能导入。",
|
|
703
|
+
importImported: "已导入",
|
|
704
|
+
importFailures: "失败",
|
|
705
|
+
importFailed: "Claude 会话导入失败。",
|
|
706
|
+
cancel: "取消",
|
|
707
|
+
close: "关闭",
|
|
708
|
+
retry: "重试"
|
|
709
|
+
};
|
|
710
|
+
//#endregion
|
|
163
711
|
//#region src/client/index.ts
|
|
164
712
|
const inject = [
|
|
165
713
|
"slots",
|
|
@@ -167,20 +715,47 @@ window.__ModuleLoader__.load({
|
|
|
167
715
|
"locale",
|
|
168
716
|
"remote",
|
|
169
717
|
"sessions",
|
|
718
|
+
"workspaces",
|
|
170
719
|
"connection",
|
|
171
720
|
"conversationEvents"
|
|
172
721
|
];
|
|
173
722
|
async function apply(ctx) {
|
|
723
|
+
ctx.effect(() => ctx.locale.register("relay.claude", {
|
|
724
|
+
zh,
|
|
725
|
+
en
|
|
726
|
+
}), "relay-claude: dictionaries");
|
|
174
727
|
ctx.conversationEvents.register(claudeActivityDefinition);
|
|
175
728
|
ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
|
|
176
729
|
name: "conversation.chat.node",
|
|
177
730
|
key: "relay-claude-activity"
|
|
178
731
|
}, ClaudeActivityView));
|
|
732
|
+
applySessionImport(ctx);
|
|
179
733
|
const unsubscribe = installModelSelection(ctx, "relay-claude", "relay-claude", "relay-codex");
|
|
180
734
|
return async () => {
|
|
181
735
|
unsubscribe();
|
|
182
736
|
};
|
|
183
737
|
}
|
|
738
|
+
function applySessionImport(ctx) {
|
|
739
|
+
const injected = () => ({
|
|
740
|
+
hooks: {
|
|
741
|
+
claudeSessionImportWorkspaces: ctx.workspaces.list,
|
|
742
|
+
claudeSessionImportSessions: ctx.sessions.list
|
|
743
|
+
},
|
|
744
|
+
scanWorkspace: (cwd) => scanClaudeWorkspace(cwd),
|
|
745
|
+
importWorkspace: (cwd, sessionIds, onProgress) => importClaudeWorkspace(cwd, {
|
|
746
|
+
sessionIds,
|
|
747
|
+
onProgress
|
|
748
|
+
}),
|
|
749
|
+
refreshWorkspaceState: () => refreshImportedWorkspace(ctx.sessions, ctx.workspaces)
|
|
750
|
+
});
|
|
751
|
+
ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
|
|
752
|
+
name: "sidebar.footer.action",
|
|
753
|
+
id: "relay-claude-session-import",
|
|
754
|
+
order: -9,
|
|
755
|
+
inject: injected,
|
|
756
|
+
locale: "relay.claude"
|
|
757
|
+
}, ClaudeSessionImportAction));
|
|
758
|
+
}
|
|
184
759
|
//#endregion
|
|
185
760
|
exports.apply = apply;
|
|
186
761
|
exports.inject = inject;
|