@sleetdrop/dsh-plugin-topology 0.3.1 → 0.5.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/lib/index.d.ts CHANGED
@@ -49,6 +49,6 @@ export declare class PluginTopologyService extends TypertRemoteService {
49
49
  * @param rankdir - Graphviz layout direction for `dot`/`svg`; ignored by `json`.
50
50
  * @returns the serialized document for the format.
51
51
  */
52
- render(format: RenderFormat, rankdir: Rankdir): Promise<string>;
52
+ render(format: RenderFormat, rankdir: Rankdir, theme?: 'light' | 'dark'): Promise<string>;
53
53
  }
54
54
  export default PluginTopologyService;
package/lib/index.js CHANGED
@@ -55,7 +55,7 @@ import { Context } from '@deepseek-ai/cordis';
55
55
  import { Graphviz } from '@hpcc-js/wasm-graphviz';
56
56
  import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
57
57
  import { analyzeGraph } from "./metrics.js";
58
- import { composeGraphvizSvgs, isolatedNodes, renderCompleteDot, renderDot, renderIsolatedDot, renderJson } from "./render.js";
58
+ import { composeGraphvizSvgs, DARK_PALETTE, isolatedNodes, LIGHT_PALETTE, renderCompleteDot, renderDot, renderIsolatedDot, renderJson } from "./render.js";
59
59
  export { analyzeGraph, collapse } from "./metrics.js";
60
60
  export { composeGraphvizSvgs, isolatedNodes, nodeLink, renderCompleteDot, renderDot, renderIsolatedDot, renderJson } from "./render.js";
61
61
  /** Map a Cordis fiber state number to its stable name. */
@@ -179,18 +179,19 @@ let PluginTopologyService = (() => {
179
179
  * @param rankdir - Graphviz layout direction for `dot`/`svg`; ignored by `json`.
180
180
  * @returns the serialized document for the format.
181
181
  */
182
- async render(format, rankdir) {
182
+ async render(format, rankdir, theme) {
183
+ const palette = theme === 'dark' ? DARK_PALETTE : LIGHT_PALETTE;
183
184
  const analysis = this.analyze();
184
185
  if (format === 'json')
185
186
  return renderJson(analysis);
186
187
  if (format === 'dot')
187
- return renderCompleteDot(analysis, rankdir);
188
+ return renderCompleteDot(analysis, rankdir, palette);
188
189
  const graphviz = await (this.graphvizPromise ??= Graphviz.load());
189
- const mainSvg = graphviz.dot(renderDot(analysis, rankdir), 'svg');
190
+ const mainSvg = graphviz.dot(renderDot(analysis, rankdir, palette), 'svg');
190
191
  if (isolatedNodes(analysis).length === 0)
191
192
  return mainSvg;
192
- const isolatedSvg = graphviz.dot(renderIsolatedDot(analysis, rankdir), 'svg');
193
- return composeGraphvizSvgs(mainSvg, isolatedSvg, rankdir);
193
+ const isolatedSvg = graphviz.dot(renderIsolatedDot(analysis, rankdir, palette), 'svg');
194
+ return composeGraphvizSvgs(mainSvg, isolatedSvg, rankdir, palette.canvas);
194
195
  }
195
196
  };
196
197
  })();
package/lib/render.d.ts CHANGED
@@ -10,6 +10,22 @@
10
10
  * @module @sleetdrop/dsh-plugin-topology/render
11
11
  */
12
12
  import type { GraphMetrics, PluginNode, PluginState, Rankdir, TopologyAnalysis } from './types.ts';
13
+ /** Color palette for DOT rendering, keyed by theme. */
14
+ export interface DotPalette {
15
+ /** Graph background; matches the app viewport so the inline SVG looks seamless. */
16
+ readonly canvas: string;
17
+ readonly nodeFill: string;
18
+ readonly nodeStroke: string;
19
+ readonly nodeFont: string;
20
+ readonly edgeStroke: string;
21
+ readonly edgeFont: string;
22
+ readonly errorStroke: string;
23
+ readonly errorFont: string;
24
+ readonly clusterFill: string;
25
+ readonly clusterStroke: string;
26
+ }
27
+ export declare const LIGHT_PALETTE: DotPalette;
28
+ export declare const DARK_PALETTE: DotPalette;
13
29
  /** One plugin node with its metrics in the node-link document. */
14
30
  export interface NodeLinkNode {
15
31
  readonly id: string;
@@ -52,21 +68,22 @@ export declare function renderJson(analysis: TopologyAnalysis): string;
52
68
  * never stretch the layout. Plugins with an unresolved dependency get a red
53
69
  * stroke.
54
70
  */
55
- export declare function renderDot(analysis: TopologyAnalysis, rankdir: Rankdir): string;
71
+ export declare function renderDot(analysis: TopologyAnalysis, rankdir: Rankdir, palette?: DotPalette): string;
56
72
  /** Plugins with no incident dependency edge (and not the synthetic root). */
57
73
  export declare function isolatedNodes(analysis: TopologyAnalysis): readonly PluginNode[];
58
74
  /** Serialize the isolated plugins as a standalone dashed-cluster DOT graph. */
59
- export declare function renderIsolatedDot(analysis: TopologyAnalysis, rankdir: Rankdir): string;
75
+ export declare function renderIsolatedDot(analysis: TopologyAnalysis, rankdir: Rankdir, palette?: DotPalette): string;
60
76
  /**
61
77
  * The complete DOT source: every plugin instance (connected and isolated) with
62
78
  * all dependency edges, isolated ones listed in a dashed cluster. Aligned with
63
79
  * the JSON export; used for the downloadable DOT. The SVG split (main/isolated)
64
80
  * is an internal layout tradeoff and is not exposed here.
65
81
  */
66
- export declare function renderCompleteDot(analysis: TopologyAnalysis, rankdir: Rankdir): string;
82
+ export declare function renderCompleteDot(analysis: TopologyAnalysis, rankdir: Rankdir, palette?: DotPalette): string;
67
83
  /**
68
84
  * Compose two Graphviz SVGs into one document. The isolated graph is placed to
69
85
  * the side (LR) or below (TB) of the main graph with a fixed gap, and centered
70
- * along the graph's perpendicular axis, so the exported SVG is one image.
86
+ * along the graph's perpendicular axis, so the exported SVG is one image. A
87
+ * canvas rect behind both keeps the gap opaque instead of showing through.
71
88
  */
72
- export declare function composeGraphvizSvgs(main: string, isolated: string, rankdir: Rankdir): string;
89
+ export declare function composeGraphvizSvgs(main: string, isolated: string, rankdir: Rankdir, canvas?: string): string;
package/lib/render.js CHANGED
@@ -9,6 +9,31 @@
9
9
  *
10
10
  * @module @sleetdrop/dsh-plugin-topology/render
11
11
  */
12
+ export const LIGHT_PALETTE = {
13
+ canvas: '#ffffff',
14
+ nodeFill: '#ffffff',
15
+ nodeStroke: '#e2e8f0',
16
+ nodeFont: '#334155',
17
+ edgeStroke: '#e2e8f0',
18
+ edgeFont: '#94a3b8',
19
+ errorStroke: '#ec1313',
20
+ errorFont: '#ec1313',
21
+ clusterFill: '#f8fafc',
22
+ clusterStroke: '#94a3b8',
23
+ };
24
+ export const DARK_PALETTE = {
25
+ /** DeepSeek Harness dark `--dsw-alias-bg-layer-1`. */
26
+ canvas: '#232324',
27
+ nodeFill: '#2c2c2e',
28
+ nodeStroke: '#ffffff29',
29
+ nodeFont: '#e2e8f0',
30
+ edgeStroke: '#ffffff1f',
31
+ edgeFont: '#81858c',
32
+ errorStroke: '#f25a5a',
33
+ errorFont: '#f25a5a',
34
+ clusterFill: '#151517',
35
+ clusterStroke: '#81858c',
36
+ };
12
37
  /** Merge the collapsed graph and its metrics into a node-link document. */
13
38
  export function nodeLink(analysis) {
14
39
  const nodes = analysis.collapsed.nodes.map((node) => {
@@ -57,7 +82,7 @@ function escapeDot(value) {
57
82
  * never stretch the layout. Plugins with an unresolved dependency get a red
58
83
  * stroke.
59
84
  */
60
- export function renderDot(analysis, rankdir) {
85
+ export function renderDot(analysis, rankdir, palette = LIGHT_PALETTE) {
61
86
  const unresolvedIds = new Set(analysis.graph.unresolved.map(dep => dep.plugin));
62
87
  const nodes = analysis.collapsed.nodes;
63
88
  const ROOT_ID = 'p:0';
@@ -73,15 +98,16 @@ export function renderDot(analysis, rankdir) {
73
98
  const lines = [
74
99
  'digraph plugin_topology {',
75
100
  ` rankdir=${rankdir};`,
101
+ ` bgcolor="${palette.canvas}";`,
76
102
  ' nodesep=0.3;',
77
103
  ' ranksep=0.7;',
78
104
  ' splines=true;',
79
- ' node [shape=oval, style="filled", fillcolor="#ffffff", color="#cbd5e1", fontcolor="#64748b", penwidth=0.8, fontname="Helvetica", fontsize=8, margin="0.16,0.11"];',
80
- ' edge [color="#cbd5e1", penwidth=0.8, arrowsize=0.55, fontname="Helvetica", fontsize=7, fontcolor="#94a3b8"];',
105
+ ` node [shape=oval, style="filled", fillcolor="${palette.nodeFill}", color="${palette.nodeStroke}", fontcolor="${palette.nodeFont}", penwidth=0.8, fontname="Helvetica", fontsize=8, margin="0.16,0.11"];`,
106
+ ` edge [color="${palette.edgeStroke}", penwidth=0.8, arrowsize=0.55, fontname="Helvetica", fontsize=7, fontcolor="${palette.edgeFont}"];`,
81
107
  ];
82
108
  for (const node of merged.nodes) {
83
- const stroke = node.unresolved ? ', color="#dc2626", fontcolor="#dc2626", penwidth=1.5' : '';
84
- lines.push(` "${node.id}" [label="${mergedLabel(node)}"${stroke}];`);
109
+ const stroke = node.unresolved ? `, color="${palette.errorStroke}", fontcolor="${palette.errorFont}", penwidth=1.5` : '';
110
+ lines.push(` "${node.id}" [id="node-${node.id}", label="${mergedLabel(node)}"${stroke}];`);
85
111
  }
86
112
  const seen = new Set();
87
113
  for (const edge of analysis.collapsed.edges) {
@@ -143,22 +169,23 @@ export function isolatedNodes(analysis) {
143
169
  return analysis.collapsed.nodes.filter(node => node.id !== 'p:0' && !connected.has(node.id));
144
170
  }
145
171
  /** Serialize the isolated plugins as a standalone dashed-cluster DOT graph. */
146
- export function renderIsolatedDot(analysis, rankdir) {
172
+ export function renderIsolatedDot(analysis, rankdir, palette = LIGHT_PALETTE) {
147
173
  const isolated = isolatedNodes(analysis);
148
174
  const merged = mergeByName(isolated, new Set());
149
175
  const lines = [
150
176
  'digraph plugin_topology_isolated {',
151
177
  ` rankdir=${rankdir};`,
178
+ ` bgcolor="${palette.canvas}";`,
152
179
  ' nodesep=0.18;',
153
180
  ' ranksep=0.4;',
154
- ' node [shape=oval, style="filled", fillcolor="#ffffff", color="#cbd5e1", fontcolor="#64748b", penwidth=0.8, fontname="Helvetica", fontsize=8, margin="0.16,0.11"];',
181
+ ` node [shape=oval, style="filled", fillcolor="${palette.nodeFill}", color="${palette.nodeStroke}", fontcolor="${palette.nodeFont}", penwidth=0.8, fontname="Helvetica", fontsize=8, margin="0.16,0.11"];`,
155
182
  ' subgraph "cluster_isolated" {',
156
183
  ' label="isolated";',
157
- ' style="rounded,dashed"; fillcolor="#f8fafc"; color="#94a3b8"; fontsize=9; fontcolor="#64748b";',
184
+ ` style="rounded,dashed"; fillcolor="${palette.clusterFill}"; color="${palette.clusterStroke}"; fontsize=9; fontcolor="${palette.nodeFont}";`,
158
185
  ' rank=same;',
159
186
  ];
160
187
  for (const node of merged.nodes) {
161
- lines.push(` "${node.id}" [label="${mergedLabel(node)}"];`);
188
+ lines.push(` "${node.id}" [id="node-${node.id}", label="${mergedLabel(node)}"];`);
162
189
  }
163
190
  lines.push(' }');
164
191
  lines.push('}');
@@ -174,24 +201,25 @@ function instanceLabel(node) {
174
201
  * the JSON export; used for the downloadable DOT. The SVG split (main/isolated)
175
202
  * is an internal layout tradeoff and is not exposed here.
176
203
  */
177
- export function renderCompleteDot(analysis, rankdir) {
204
+ export function renderCompleteDot(analysis, rankdir, palette = LIGHT_PALETTE) {
178
205
  const unresolvedIds = new Set(analysis.graph.unresolved.map(dep => dep.plugin));
179
206
  const isolated = isolatedNodes(analysis);
180
207
  const isolatedIds = new Set(isolated.map(node => node.id));
181
208
  const lines = [
182
209
  'digraph plugin_topology {',
183
210
  ` rankdir=${rankdir};`,
211
+ ` bgcolor="${palette.canvas}";`,
184
212
  ' nodesep=0.3;',
185
213
  ' ranksep=0.7;',
186
214
  ' splines=true;',
187
- ' node [shape=oval, style="filled", fillcolor="#ffffff", color="#cbd5e1", fontcolor="#64748b", penwidth=0.8, fontname="Helvetica", fontsize=8, margin="0.16,0.11"];',
188
- ' edge [color="#cbd5e1", penwidth=0.8, arrowsize=0.55, fontname="Helvetica", fontsize=7, fontcolor="#94a3b8"];',
215
+ ` node [shape=oval, style="filled", fillcolor="${palette.nodeFill}", color="${palette.nodeStroke}", fontcolor="${palette.nodeFont}", penwidth=0.8, fontname="Helvetica", fontsize=8, margin="0.16,0.11"];`,
216
+ ` edge [color="${palette.edgeStroke}", penwidth=0.8, arrowsize=0.55, fontname="Helvetica", fontsize=7, fontcolor="${palette.edgeFont}"];`,
189
217
  ];
190
218
  for (const node of analysis.collapsed.nodes) {
191
219
  if (isolatedIds.has(node.id))
192
220
  continue;
193
- const stroke = unresolvedIds.has(node.id) ? ', color="#dc2626", fontcolor="#dc2626", penwidth=1.5' : '';
194
- lines.push(` "${node.id}" [label="${instanceLabel(node)}"${stroke}];`);
221
+ const stroke = unresolvedIds.has(node.id) ? `, color="${palette.errorStroke}", fontcolor="${palette.errorFont}", penwidth=1.5` : '';
222
+ lines.push(` "${node.id}" [id="node-${node.id}", label="${instanceLabel(node)}"${stroke}];`);
195
223
  }
196
224
  for (const edge of analysis.collapsed.edges) {
197
225
  lines.push(` "${edge.source}" -> "${edge.target}" [label="${escapeDot(edge.service)}"];`);
@@ -199,10 +227,10 @@ export function renderCompleteDot(analysis, rankdir) {
199
227
  if (isolated.length > 0) {
200
228
  lines.push(' subgraph "cluster_isolated" {');
201
229
  lines.push(' label="isolated";');
202
- lines.push(' style="rounded,dashed"; fillcolor="#f8fafc"; color="#94a3b8"; fontsize=9; fontcolor="#64748b";');
230
+ lines.push(` style="rounded,dashed"; fillcolor="${palette.clusterFill}"; color="${palette.clusterStroke}"; fontsize=9; fontcolor="${palette.nodeFont}";`);
203
231
  lines.push(' rank=same;');
204
232
  for (const node of isolated) {
205
- lines.push(` "${node.id}" [label="${instanceLabel(node)}"];`);
233
+ lines.push(` "${node.id}" [id="node-${node.id}", label="${instanceLabel(node)}"];`);
206
234
  }
207
235
  lines.push(' }');
208
236
  }
@@ -226,9 +254,10 @@ function splitSvg(svg) {
226
254
  /**
227
255
  * Compose two Graphviz SVGs into one document. The isolated graph is placed to
228
256
  * the side (LR) or below (TB) of the main graph with a fixed gap, and centered
229
- * along the graph's perpendicular axis, so the exported SVG is one image.
257
+ * along the graph's perpendicular axis, so the exported SVG is one image. A
258
+ * canvas rect behind both keeps the gap opaque instead of showing through.
230
259
  */
231
- export function composeGraphvizSvgs(main, isolated, rankdir) {
260
+ export function composeGraphvizSvgs(main, isolated, rankdir, canvas = '#ffffff') {
232
261
  const m = splitSvg(main);
233
262
  const iso = splitSvg(isolated);
234
263
  if (iso.width === 0 || iso.height === 0)
@@ -258,5 +287,5 @@ export function composeGraphvizSvgs(main, isolated, rankdir) {
258
287
  width = Math.max(m.width, ix + iso.width);
259
288
  height = m.height + gap + iso.height;
260
289
  }
261
- return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 ${width} ${height}" width="${width}" height="${height}" role="img"><g transform="translate(${mx},${my})">${m.inner}</g><g transform="translate(${ix},${iy})">${isoInner}</g></svg>`;
290
+ return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 ${width} ${height}" width="${width}" height="${height}" role="img"><rect x="0" y="0" width="${width}" height="${height}" fill="${canvas}"/><g transform="translate(${mx},${my})">${m.inner}</g><g transform="translate(${ix},${iy})">${isoInner}</g></svg>`;
262
291
  }
package/lib/typert.d.ts CHANGED
@@ -26,7 +26,7 @@ export declare const TYPERT: {
26
26
  result: {
27
27
  mode: string;
28
28
  typeSymbol: string;
29
- schema: z.ZodObject<{
29
+ create: () => z.ZodObject<{
30
30
  graph: z.ZodReadonly<z.ZodObject<{
31
31
  kind: z.ZodReadonly<z.ZodLiteral<"bipartite">>;
32
32
  generatedAt: z.ZodReadonly<z.ZodString>;
@@ -112,8 +112,9 @@ export declare const TYPERT: {
112
112
  codec: {
113
113
  mode: string;
114
114
  typeSymbol: string;
115
- schema: z.ZodUnion<readonly [z.ZodLiteral<"json">, z.ZodLiteral<"dot">, z.ZodLiteral<"svg">]>;
115
+ create: () => z.ZodUnion<readonly [z.ZodLiteral<"json">, z.ZodLiteral<"dot">, z.ZodLiteral<"svg">]>;
116
116
  };
117
+ acceptsUndefined?: never;
117
118
  } | {
118
119
  name: string;
119
120
  wire: string;
@@ -121,13 +122,24 @@ export declare const TYPERT: {
121
122
  codec: {
122
123
  mode: string;
123
124
  typeSymbol: string;
124
- schema: z.ZodUnion<readonly [z.ZodLiteral<"TB">, z.ZodLiteral<"LR">]>;
125
+ create: () => z.ZodUnion<readonly [z.ZodLiteral<"TB">, z.ZodLiteral<"LR">]>;
126
+ };
127
+ acceptsUndefined?: never;
128
+ } | {
129
+ name: string;
130
+ wire: string;
131
+ source: string;
132
+ acceptsUndefined: boolean;
133
+ codec: {
134
+ mode: string;
135
+ typeSymbol: string;
136
+ create: () => z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"light">, z.ZodLiteral<"dark">]>>;
125
137
  };
126
138
  })[];
127
139
  result: {
128
140
  mode: string;
129
141
  typeSymbol: string;
130
- schema: z.ZodString;
142
+ create: () => z.ZodString;
131
143
  };
132
144
  })[];
133
145
  model: {
package/lib/typert.js CHANGED
@@ -95,7 +95,7 @@ export const TYPERT = {
95
95
  result: {
96
96
  mode: 'strict',
97
97
  typeSymbol: '@sleetdrop/dsh-plugin-topology/types#TopologyAnalysis',
98
- schema: topologyAnalysisSchema,
98
+ create: () => topologyAnalysisSchema,
99
99
  },
100
100
  },
101
101
  {
@@ -112,7 +112,7 @@ export const TYPERT = {
112
112
  codec: {
113
113
  mode: 'strict',
114
114
  typeSymbol: '@sleetdrop/dsh-plugin-topology/types#RenderFormat',
115
- schema: z.union([z.literal('json'), z.literal('dot'), z.literal('svg')]),
115
+ create: () => z.union([z.literal('json'), z.literal('dot'), z.literal('svg')]),
116
116
  },
117
117
  },
118
118
  {
@@ -122,14 +122,25 @@ export const TYPERT = {
122
122
  codec: {
123
123
  mode: 'strict',
124
124
  typeSymbol: '@sleetdrop/dsh-plugin-topology/types#Rankdir',
125
- schema: z.union([z.literal('TB'), z.literal('LR')]),
125
+ create: () => z.union([z.literal('TB'), z.literal('LR')]),
126
+ },
127
+ },
128
+ {
129
+ name: 'theme',
130
+ wire: 'theme',
131
+ source: 'json',
132
+ acceptsUndefined: true,
133
+ codec: {
134
+ mode: 'strict',
135
+ typeSymbol: '@sleetdrop/dsh-plugin-topology/types#Theme',
136
+ create: () => z.union([z.literal('light'), z.literal('dark')]).optional(),
126
137
  },
127
138
  },
128
139
  ],
129
140
  result: {
130
141
  mode: 'strict',
131
142
  typeSymbol: '@sleetdrop/dsh-plugin-topology#pluginTopology/render:result',
132
- schema: z.string(),
143
+ create: () => z.string(),
133
144
  },
134
145
  },
135
146
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleetdrop/dsh-plugin-topology",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "description": "Plugin dependency graph inspector for DeepSeek Harness: a host service that snapshots the live Cordis plugin fiber tree, a graph.render tool for the model, and a browser panel that renders the plugin/service topology as a zoomable graph with metrics, legend, and unresolved-dependency table.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -70,29 +70,29 @@
70
70
  }
71
71
  },
72
72
  "peerDependencies": {
73
- "@deepseek-ai/cordis": "^4.0.2",
74
- "@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.3",
75
- "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.3",
76
- "@deepseek-ai/dsh-client-store": "^0.1.5-rc.3",
77
- "@deepseek-ai/dsh-client-connection": "^0.1.5-rc.3",
78
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.3",
79
- "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.5-rc.3",
80
- "@deepseek-ai/dsh-tools": "^0.1.5-rc.3",
81
- "@deepseek-ai/dsh-typert-protocol": "^0.1.5-rc.3",
82
- "@deepseek-ai/dsh-invariants": "^0.1.5-rc.3"
73
+ "@deepseek-ai/cordis": "^4.0.4",
74
+ "@deepseek-ai/dsh-api-remotes": "^0.2.0-rc.2",
75
+ "@deepseek-ai/dsh-client-locale": "^0.2.0-rc.2",
76
+ "@deepseek-ai/dsh-client-store": "^0.2.0-rc.2",
77
+ "@deepseek-ai/dsh-client-connection": "^0.2.0-rc.2",
78
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.2.0-rc.2",
79
+ "@deepseek-ai/dsh-client-ui-sidebar": "^0.2.0-rc.2",
80
+ "@deepseek-ai/dsh-tools": "^0.2.0-rc.2",
81
+ "@deepseek-ai/dsh-typert-protocol": "^0.2.0-rc.2",
82
+ "@deepseek-ai/dsh-invariants": "^0.2.0-rc.2"
83
83
  },
84
84
  "devDependencies": {
85
- "@deepseek-ai/cordis": "4.0.2",
86
- "@deepseek-ai/dsh-api-remotes": "0.1.5-rc.3",
87
- "@deepseek-ai/dsh-client-locale": "0.1.5-rc.3",
88
- "@deepseek-ai/dsh-client-store": "0.1.5-rc.3",
89
- "@deepseek-ai/dsh-client-connection": "0.1.5-rc.3",
90
- "@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.3",
91
- "@deepseek-ai/dsh-client-ui-sidebar": "0.1.5-rc.3",
92
- "@deepseek-ai/dsh-tools": "0.1.5-rc.3",
93
- "@deepseek-ai/dsh-typert-protocol": "0.1.5-rc.3",
94
- "@deepseek-ai/dsh-invariants": "0.1.5-rc.3",
95
- "@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.3",
85
+ "@deepseek-ai/cordis": "4.0.4",
86
+ "@deepseek-ai/dsh-api-remotes": "0.2.0-rc.2",
87
+ "@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
88
+ "@deepseek-ai/dsh-client-store": "0.2.0-rc.2",
89
+ "@deepseek-ai/dsh-client-connection": "0.2.0-rc.2",
90
+ "@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
91
+ "@deepseek-ai/dsh-client-ui-sidebar": "0.2.0-rc.2",
92
+ "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
93
+ "@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2",
94
+ "@deepseek-ai/dsh-invariants": "0.2.0-rc.2",
95
+ "@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
96
96
  "@types/node": "^24.0.0",
97
97
  "@types/react": "~18.3.1",
98
98
  "react": "^18.2.0",