@windypro-rourou/dsh-code-studio 0.1.0 → 0.1.2
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/lib/client.js +78 -5
- package/lib/index.js +114 -16
- package/package.json +56 -56
package/lib/client.js
CHANGED
|
@@ -166,6 +166,28 @@ window.__ModuleLoader__.load({
|
|
|
166
166
|
}
|
|
167
167
|
function subscribePanel(fn) { panel.listeners.add(fn); return () => panel.listeners.delete(fn); }
|
|
168
168
|
|
|
169
|
+
// current session id (per-session change isolation)
|
|
170
|
+
let currentSessionId = null;
|
|
171
|
+
const sessListeners = new Set();
|
|
172
|
+
function setSessionId(v) {
|
|
173
|
+
currentSessionId = v;
|
|
174
|
+
for (const fn of sessListeners) fn(v);
|
|
175
|
+
}
|
|
176
|
+
function subscribeSession(fn) { sessListeners.add(fn); return () => sessListeners.delete(fn); }
|
|
177
|
+
|
|
178
|
+
// per-session UI state memory (changes, tabs, width), persisted locally
|
|
179
|
+
const SESS_STATE_KEY = "cs.sessionStates.v1";
|
|
180
|
+
function loadSessionStates() {
|
|
181
|
+
try {
|
|
182
|
+
const raw = globalThis.localStorage?.getItem(SESS_STATE_KEY);
|
|
183
|
+
if (raw) return JSON.parse(raw);
|
|
184
|
+
} catch { /* ignore */ }
|
|
185
|
+
return {};
|
|
186
|
+
}
|
|
187
|
+
function persistSessionStates(map) {
|
|
188
|
+
try { globalThis.localStorage?.setItem(SESS_STATE_KEY, JSON.stringify(map)); } catch { /* ignore */ }
|
|
189
|
+
}
|
|
190
|
+
|
|
169
191
|
// ============================ CSS ============================
|
|
170
192
|
const CSS = [
|
|
171
193
|
".cs-root{position:absolute;top:0;right:0;bottom:0;display:flex;flex-direction:column;background:var(--dsw-alias-bg-layer-1,#151922);color:var(--dsw-alias-label-primary,#d7dde6);border-left:1px solid var(--dsw-alias-border-l1,#262a33);font:13px/1.55 -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;z-index:30;box-shadow:-12px 0 32px rgba(0,0,0,.18);animation:csSlideIn .18s ease-out;--cs-mono:ui-monospace,SFMono-Regular,Menlo,Consolas,\"Liberation Mono\",monospace}",
|
|
@@ -408,6 +430,32 @@ window.__ModuleLoader__.load({
|
|
|
408
430
|
});
|
|
409
431
|
const dragRef = React.useRef(null);
|
|
410
432
|
const changedSet = React.useMemo(() => new Set(changes.map((c) => c.path)), [changes]);
|
|
433
|
+
// current session for per-session isolation
|
|
434
|
+
const [sessId, setSessId] = React.useState(currentSessionId);
|
|
435
|
+
const sessIdRef = React.useRef(currentSessionId);
|
|
436
|
+
// per-session UI memory: save current snapshot on switch, restore target's
|
|
437
|
+
const sessStatesRef = React.useRef(loadSessionStates());
|
|
438
|
+
const snapRef = React.useRef({ changes, expandedPath, editor, tab, width });
|
|
439
|
+
snapRef.current = { changes, expandedPath, editor, tab, width };
|
|
440
|
+
React.useEffect(() => subscribeSession((v) => {
|
|
441
|
+
const cur = sessIdRef.current;
|
|
442
|
+
if (cur && cur !== v) {
|
|
443
|
+
sessStatesRef.current[cur] = snapRef.current;
|
|
444
|
+
persistSessionStates(sessStatesRef.current);
|
|
445
|
+
}
|
|
446
|
+
sessIdRef.current = v;
|
|
447
|
+
setSessId(v);
|
|
448
|
+
const st = sessStatesRef.current[v];
|
|
449
|
+
if (st) {
|
|
450
|
+
setChanges(st.changes ?? []);
|
|
451
|
+
setExpandedPath(st.expandedPath ?? null);
|
|
452
|
+
setEditor(st.editor ?? null);
|
|
453
|
+
setTab(st.tab ?? "changes");
|
|
454
|
+
setWidth(st.width ?? 400);
|
|
455
|
+
} else {
|
|
456
|
+
setChanges([]); setExpandedPath(null); setEditor(null); setTab("changes");
|
|
457
|
+
}
|
|
458
|
+
}), []);
|
|
411
459
|
|
|
412
460
|
React.useEffect(() => subscribePanel(setOpen), []);
|
|
413
461
|
React.useEffect(() => {
|
|
@@ -435,11 +483,16 @@ window.__ModuleLoader__.load({
|
|
|
435
483
|
try {
|
|
436
484
|
const ev = JSON.parse(msg.data);
|
|
437
485
|
if (!ev || typeof ev.path !== "string") return;
|
|
486
|
+
// per-session isolation: only react to changes owned by the active session
|
|
487
|
+
// (or unattributed ones, treated as the current session's/user's own edits)
|
|
488
|
+
// per-session isolation: only filter when we KNOW the active session.
|
|
489
|
+
// If current is null, do not filter - otherwise edits would never appear.
|
|
490
|
+
if (ev.sessionId && sessIdRef.current && ev.sessionId !== sessIdRef.current) return;
|
|
438
491
|
setConn("ok"); setConnText("已连接");
|
|
439
492
|
const name = ev.path.split(/[\\/]/).pop() || ev.path;
|
|
440
493
|
setChanges((prev) => {
|
|
441
494
|
const next = prev.filter((c) => c.path !== ev.path);
|
|
442
|
-
next.unshift({ path: ev.path, name, before: ev.before, after: ev.after, deleted: !!ev.deleted, ts: ev.ts });
|
|
495
|
+
next.unshift({ path: ev.path, name, before: ev.before, after: ev.after, deleted: !!ev.deleted, ts: ev.ts, sessionId: ev.sessionId });
|
|
443
496
|
return next.slice(0, 50);
|
|
444
497
|
});
|
|
445
498
|
setPanelOpen(true);
|
|
@@ -487,12 +540,17 @@ window.__ModuleLoader__.load({
|
|
|
487
540
|
setTab("changes");
|
|
488
541
|
setExpandedPath(path);
|
|
489
542
|
historyApi(path).then((data) => {
|
|
490
|
-
const
|
|
491
|
-
if (
|
|
543
|
+
const events = data.events && data.events.length > 0 ? data.events : [];
|
|
544
|
+
if (events.length === 0) return;
|
|
545
|
+
// prefer the latest event owned by the active session; fall back to the latest overall
|
|
546
|
+
let last = events[events.length - 1];
|
|
547
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
548
|
+
if (events[i].sessionId && events[i].sessionId === sessIdRef.current) { last = events[i]; break; }
|
|
549
|
+
}
|
|
492
550
|
const name = path.split(/[\\/]/).pop() || path;
|
|
493
551
|
setChanges((prev) => {
|
|
494
552
|
if (prev.some((c) => c.path === path)) return prev;
|
|
495
|
-
return [{ path, name, before: last.before, after: last.after, deleted: !!last.deleted, ts: last.ts }, ...prev];
|
|
553
|
+
return [{ path, name, before: last.before, after: last.after, deleted: !!last.deleted, ts: last.ts, sessionId: last.sessionId }, ...prev];
|
|
496
554
|
});
|
|
497
555
|
}).catch(() => {});
|
|
498
556
|
};
|
|
@@ -513,7 +571,10 @@ window.__ModuleLoader__.load({
|
|
|
513
571
|
if (!dragRef.current) return;
|
|
514
572
|
dragRef.current = null;
|
|
515
573
|
e.currentTarget.classList.remove("cs-drag");
|
|
516
|
-
|
|
574
|
+
if (sessIdRef.current) {
|
|
575
|
+
sessStatesRef.current[sessIdRef.current] = { ...snapRef.current, width };
|
|
576
|
+
persistSessionStates(sessStatesRef.current);
|
|
577
|
+
}
|
|
517
578
|
};
|
|
518
579
|
|
|
519
580
|
if (!open) {
|
|
@@ -596,6 +657,7 @@ window.__ModuleLoader__.load({
|
|
|
596
657
|
el("span", { className: "cs-sdot cs-" + conn }),
|
|
597
658
|
el("span", null, connText),
|
|
598
659
|
el("span", { style: { marginLeft: "auto" } }, "变更 " + changes.length + " 文件"),
|
|
660
|
+
sessId ? el("span", { title: "仅显示当前会话的变更" }, "会话 " + String(sessId).slice(-6)) : null,
|
|
599
661
|
el("span", null, editor && editor.dirty ? "未保存" : "就绪")
|
|
600
662
|
)
|
|
601
663
|
);
|
|
@@ -662,6 +724,17 @@ window.__ModuleLoader__.load({
|
|
|
662
724
|
} catch (error) {
|
|
663
725
|
console.error("[code-studio] sidebar entry failed", error);
|
|
664
726
|
}
|
|
727
|
+
// track the active session so the panel only reacts to this session's edits
|
|
728
|
+
try {
|
|
729
|
+
const sessions = ctx.get("sessions");
|
|
730
|
+
if (sessions && typeof sessions.list?.getSnapshot === "function" && typeof sessions.list.subscribe === "function") {
|
|
731
|
+
const sync = () => { try { setSessionId(sessions.list.getSnapshot()?.current ?? null); } catch { /* ignore */ } };
|
|
732
|
+
disposers.push(sessions.list.subscribe(sync));
|
|
733
|
+
sync();
|
|
734
|
+
}
|
|
735
|
+
} catch (error) {
|
|
736
|
+
console.error("[code-studio] session tracking failed", error);
|
|
737
|
+
}
|
|
665
738
|
try {
|
|
666
739
|
const slots = ctx.get("slots");
|
|
667
740
|
if (slots !== void 0) {
|
package/lib/index.js
CHANGED
|
@@ -61,7 +61,9 @@ function safePath(raw) {
|
|
|
61
61
|
|
|
62
62
|
class FileLedger {
|
|
63
63
|
constructor(root) {
|
|
64
|
-
|
|
64
|
+
/** watched workspace roots (one recursive watcher each) */
|
|
65
|
+
this.roots = new Set();
|
|
66
|
+
this.watchers = new Map();
|
|
65
67
|
/** baseline cache: absolute path -> { content, mtimeMs, size } */
|
|
66
68
|
this.baseline = new Map();
|
|
67
69
|
/** change history: absolute path -> [{ ts, before, after, deleted }] */
|
|
@@ -69,7 +71,41 @@ class FileLedger {
|
|
|
69
71
|
/** SSE subscribers */
|
|
70
72
|
this.subscribers = new Set();
|
|
71
73
|
this.debounce = new Map();
|
|
72
|
-
|
|
74
|
+
/** file path -> owning session { sessionId, ts } (learnt from tool/call events) */
|
|
75
|
+
this.owners = new Map();
|
|
76
|
+
if (root) this.addRoot(root);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Add one workspace root (recursive watcher). Idempotent. */
|
|
80
|
+
addRoot(root) {
|
|
81
|
+
if (typeof root !== "string" || root === "") return;
|
|
82
|
+
let resolved;
|
|
83
|
+
try { resolved = realpathSync(root); } catch { return; }
|
|
84
|
+
if (this.roots.has(resolved)) return;
|
|
85
|
+
this.roots.add(resolved);
|
|
86
|
+
try {
|
|
87
|
+
const watcher = fsWatch(resolved, { recursive: true }, (_eventType, filename) => {
|
|
88
|
+
if (typeof filename !== "string") return;
|
|
89
|
+
this.schedule(join(resolved, filename));
|
|
90
|
+
});
|
|
91
|
+
watcher.on?.("error", () => { /* poll covers us */ });
|
|
92
|
+
this.watchers.set(resolved, watcher);
|
|
93
|
+
} catch { /* poll covers us */ }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Attribute a file to a session (from a session tool/call event). */
|
|
97
|
+
claim(path, sessionId) {
|
|
98
|
+
if (typeof path !== "string" || path === "" || typeof sessionId !== "string") return;
|
|
99
|
+
this.owners.set(path, { sessionId, ts: Date.now() });
|
|
100
|
+
if (this.owners.size > 2000) {
|
|
101
|
+
const oldest = [...this.owners.entries()].sort((a, b) => a[1].ts - b[1].ts)[0];
|
|
102
|
+
if (oldest) this.owners.delete(oldest[0]);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Owning session for a path, if known. */
|
|
107
|
+
ownerOf(path) {
|
|
108
|
+
return this.owners.get(path)?.sessionId ?? null;
|
|
73
109
|
}
|
|
74
110
|
|
|
75
111
|
snapshot(path) {
|
|
@@ -111,8 +147,9 @@ class FileLedger {
|
|
|
111
147
|
if (error.code === "ENOENT") {
|
|
112
148
|
const before = this.baseline.get(path);
|
|
113
149
|
this.baseline.delete(path);
|
|
114
|
-
|
|
115
|
-
this.
|
|
150
|
+
const ev = { path, ts: Date.now(), before: before?.content ?? null, after: null, deleted: true, sessionId: this.ownerOf(path) };
|
|
151
|
+
this.push(ev);
|
|
152
|
+
this.addHistory(path, ev);
|
|
116
153
|
return;
|
|
117
154
|
}
|
|
118
155
|
return;
|
|
@@ -121,7 +158,7 @@ class FileLedger {
|
|
|
121
158
|
const before = prior && !prior.tooLarge ? prior.content : null;
|
|
122
159
|
this.baseline.set(path, { ...current });
|
|
123
160
|
if (prior && prior.mtimeMs === current.mtimeMs && prior.size === current.size && prior.content === current.content) return;
|
|
124
|
-
const event = { path, ts: Date.now(), before, after: current.tooLarge ? null : current.content, deleted: false, tooLarge: current.tooLarge };
|
|
161
|
+
const event = { path, ts: Date.now(), before, after: current.tooLarge ? null : current.content, deleted: false, tooLarge: current.tooLarge, sessionId: this.ownerOf(path) };
|
|
125
162
|
this.push(event);
|
|
126
163
|
this.addHistory(path, event);
|
|
127
164
|
}
|
|
@@ -163,21 +200,14 @@ class FileLedger {
|
|
|
163
200
|
}
|
|
164
201
|
|
|
165
202
|
start() {
|
|
166
|
-
|
|
167
|
-
this.watcher = fsWatch(this.root, { recursive: true }, (_eventType, filename) => {
|
|
168
|
-
if (typeof filename !== "string") return;
|
|
169
|
-
this.schedule(join(this.root, filename));
|
|
170
|
-
});
|
|
171
|
-
this.watcher.on?.("error", () => { /* recursive watch may fail on some mounts; poll covers us */ });
|
|
172
|
-
} catch (error) {
|
|
173
|
-
this.watcher = void 0;
|
|
174
|
-
}
|
|
203
|
+
for (const root of this.roots) this.addRoot(root);
|
|
175
204
|
}
|
|
176
205
|
|
|
177
206
|
stop() {
|
|
178
207
|
for (const timer of this.debounce.values()) clearTimeout(timer);
|
|
179
208
|
this.debounce.clear();
|
|
180
|
-
|
|
209
|
+
for (const watcher of this.watchers.values()) { try { watcher.close(); } catch { /* ignore */ } }
|
|
210
|
+
this.watchers.clear();
|
|
181
211
|
for (const res of this.subscribers) { try { res.destroy(); } catch { /* ignore */ } }
|
|
182
212
|
this.subscribers.clear();
|
|
183
213
|
}
|
|
@@ -277,7 +307,8 @@ function makeRoutes(ledger, root, cwd) {
|
|
|
277
307
|
before: ledger.snapshot(path)?.content ?? null,
|
|
278
308
|
after: body.content,
|
|
279
309
|
deleted: false,
|
|
280
|
-
source: "user"
|
|
310
|
+
source: "user",
|
|
311
|
+
sessionId: ledger.ownerOf(path)
|
|
281
312
|
};
|
|
282
313
|
ledger.recordBaseline(path, body.content, mtimeMs, body.content.length);
|
|
283
314
|
ledger.push(event);
|
|
@@ -322,6 +353,21 @@ function makeRoutes(ledger, root, cwd) {
|
|
|
322
353
|
|
|
323
354
|
/* ---------- plugin ---------- */
|
|
324
355
|
|
|
356
|
+
/** Tool names whose arguments carry a file path we can attribute to a session. */
|
|
357
|
+
const PATH_TOOLS = new Set(["write", "edit", "str-replace", "apply-patch", "fs-write", "write-file"]);
|
|
358
|
+
const PATH_ARGS = ["file_path", "path", "filePath", "target", "file"];
|
|
359
|
+
|
|
360
|
+
/** Resolve a tool argument to an absolute path, anchoring relative paths at the session cwd. */
|
|
361
|
+
function toolPath(args, sessionCwd) {
|
|
362
|
+
for (const key of PATH_ARGS) {
|
|
363
|
+
const p = args?.[key];
|
|
364
|
+
if (typeof p === "string" && p !== "") {
|
|
365
|
+
return isAbsolute(p) ? resolve(p) : resolve(sessionCwd ?? process.cwd(), p);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return void 0;
|
|
369
|
+
}
|
|
370
|
+
|
|
325
371
|
function apply(ctx, config) {
|
|
326
372
|
const root = realpathSync((config?.root && config.root !== "" ? config.root : process.env.DSH_WORKSPACE ?? process.cwd()));
|
|
327
373
|
const cwd = process.cwd();
|
|
@@ -335,6 +381,58 @@ function apply(ctx, config) {
|
|
|
335
381
|
const timer = setInterval(() => ledger.poll(), pollIntervalMs);
|
|
336
382
|
timer.unref();
|
|
337
383
|
disposers.push(() => clearInterval(timer));
|
|
384
|
+
// Watch every session's workspace, not just our cwd: agents edit files
|
|
385
|
+
// in whichever directory their session was started in.
|
|
386
|
+
const addSessionRoot = (session) => {
|
|
387
|
+
if (session?.header?.cwd) ledger.addRoot(session.header.cwd);
|
|
388
|
+
};
|
|
389
|
+
try {
|
|
390
|
+
const sessions = ctx.get("sessions");
|
|
391
|
+
if (sessions && typeof sessions.list === "function") {
|
|
392
|
+
for (const session of sessions.list()) addSessionRoot(session);
|
|
393
|
+
}
|
|
394
|
+
} catch { /* ignore */ }
|
|
395
|
+
// ---- reliable real-time change detection from tool events ----
|
|
396
|
+
// tool/call records { name, arguments, callId } per session; tool/result
|
|
397
|
+
// pairs by callId. On a file-writing tool completing, read the file NOW
|
|
398
|
+
// and push the change immediately (fs.watch on atomic writes/renames is
|
|
399
|
+
// unreliable and late), attributing it to the owning session.
|
|
400
|
+
const pendingCalls = new Map(); // callId -> { name, args, sessionId, cwd }
|
|
401
|
+
const offSession = ctx.on("session/event", (session, event) => {
|
|
402
|
+
addSessionRoot(session);
|
|
403
|
+
const data = event?.data;
|
|
404
|
+
if (!data || typeof data !== "object") return;
|
|
405
|
+
if (event.type === "tool/call") {
|
|
406
|
+
if (typeof data.name === "string" && data.arguments && typeof data.arguments === "object") {
|
|
407
|
+
pendingCalls.set(String(data.callId), {
|
|
408
|
+
name: data.name,
|
|
409
|
+
args: data.arguments,
|
|
410
|
+
sessionId: session?.id,
|
|
411
|
+
cwd: session?.header?.cwd
|
|
412
|
+
});
|
|
413
|
+
if (pendingCalls.size > 500) {
|
|
414
|
+
const oldest = pendingCalls.keys().next().value;
|
|
415
|
+
if (oldest !== void 0) pendingCalls.delete(oldest);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
if (event.type === "tool/result") {
|
|
421
|
+
const callId = data.message?.source?.callId ?? data.callId ?? data.call_id;
|
|
422
|
+
const call = callId !== void 0 ? pendingCalls.get(String(callId)) : void 0;
|
|
423
|
+
if (call === void 0) return;
|
|
424
|
+
pendingCalls.delete(String(callId));
|
|
425
|
+
if (!PATH_TOOLS.has(call.name)) return;
|
|
426
|
+
const abs = toolPath(call.args, call.cwd);
|
|
427
|
+
if (abs === void 0) return;
|
|
428
|
+
ledger.claim(abs, call.sessionId);
|
|
429
|
+
// proactive immediate read+push; fs.watch remains a fallback for
|
|
430
|
+
// writes that bypass tools (bash, user edits)
|
|
431
|
+
void ledger.handleChange(abs);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
disposers.push(offSession);
|
|
338
436
|
} catch (error) {
|
|
339
437
|
for (const dispose of disposers) dispose();
|
|
340
438
|
ledger.stop();
|
package/package.json
CHANGED
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@windypro-rourou/dsh-code-studio",
|
|
3
|
+
"description": "Code Studio for DSH Web GUI 鈥?a VS Code + Cline hybrid: file tree, syntax-highlighted editor, and Cline-style line-by-line diffs that auto-appear when the agent edits files.",
|
|
4
|
+
"version": "0.1.2",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"types": "lib/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/types/index.d.ts",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./client": {
|
|
14
|
+
"types": "./lib/types/client/index.d.ts",
|
|
15
|
+
"default": "./lib/client.js"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"dsh": {
|
|
20
|
+
"bundle": {
|
|
21
|
+
"patch": "./cordis.patch.yml"
|
|
22
|
+
},
|
|
23
|
+
"client": {
|
|
24
|
+
"inject": [
|
|
25
|
+
"@deepseek-ai/dsh-client-runtime"
|
|
26
|
+
],
|
|
27
|
+
"platform": "web"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"dsh-plugin",
|
|
32
|
+
"dsh",
|
|
33
|
+
"deepseek-harness",
|
|
34
|
+
"code-studio",
|
|
35
|
+
"cline",
|
|
36
|
+
"diff",
|
|
37
|
+
"editor",
|
|
38
|
+
"web-ui"
|
|
39
|
+
],
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/windypro-rourou/dsh-code-studio.git"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
46
|
+
"react": "^18.2.0"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"lib/**/*.js",
|
|
50
|
+
"lib/**/*.d.ts",
|
|
51
|
+
"cordis.patch.yml",
|
|
52
|
+
"README.md",
|
|
53
|
+
"scripts"
|
|
54
|
+
],
|
|
55
|
+
"license": "MIT"
|
|
56
|
+
}
|