@sleetdrop/dsh-plugin-topology 0.1.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/typert.js ADDED
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Host-face Typert manifest.
3
+ *
4
+ * The 0.1.1-rc.2 typert-loader resolves each loader entry's `exports["./typert"]`
5
+ * and registers its TYPERT manifest into `ctx.typert`; packages without the
6
+ * export are skipped silently, which leaves their `@Remote` methods
7
+ * undiscoverable. This module mirrors the artifact the
8
+ * `@deepseek-ai/dsh-typert-generator` would emit for this package, so the host
9
+ * gateway can dispatch `pluginTopology/analyze` and `pluginTopology/render`.
10
+ * @module @sleetdrop/dsh-plugin-topology/typert
11
+ */
12
+ import { z } from 'zod';
13
+ const pluginStateSchema = z.union([
14
+ z.literal('PENDING'), z.literal('LOADING'), z.literal('ACTIVE'),
15
+ z.literal('FAILED'), z.literal('DISPOSED'), z.literal('UNLOADING'),
16
+ ]);
17
+ const pluginNodeSchema = z.object({
18
+ 'id': z.string().readonly(),
19
+ 'kind': z.literal('plugin').readonly(),
20
+ 'label': z.string().readonly(),
21
+ 'state': pluginStateSchema.readonly(),
22
+ 'parent': z.union([z.literal(null), z.string()]).readonly(),
23
+ 'inject': z.array(z.string()).readonly(),
24
+ 'source': z.string().readonly().optional(),
25
+ });
26
+ const serviceNodeSchema = z.object({
27
+ 'id': z.string().readonly(),
28
+ 'kind': z.literal('service').readonly(),
29
+ 'label': z.string().readonly(),
30
+ 'scope': z.string().readonly(),
31
+ });
32
+ const topologyGraphSchema = z.object({
33
+ 'kind': z.literal('bipartite').readonly(),
34
+ 'generatedAt': z.string().readonly(),
35
+ 'nodes': z.array(z.union([pluginNodeSchema, serviceNodeSchema])).readonly(),
36
+ 'edges': z.array(z.object({
37
+ 'source': z.string().readonly(),
38
+ 'target': z.string().readonly(),
39
+ 'kind': z.union([z.literal('provides'), z.literal('injects')]).readonly(),
40
+ 'service': z.string().readonly(),
41
+ })).readonly(),
42
+ 'unresolved': z.array(z.object({
43
+ 'plugin': z.string().readonly(),
44
+ 'service': z.string().readonly(),
45
+ 'state': pluginStateSchema.readonly(),
46
+ })).readonly(),
47
+ });
48
+ const collapsedGraphSchema = z.object({
49
+ 'kind': z.literal('collapsed').readonly(),
50
+ 'generatedAt': z.string().readonly(),
51
+ 'nodes': z.array(pluginNodeSchema).readonly(),
52
+ 'edges': z.array(z.object({
53
+ 'source': z.string().readonly(),
54
+ 'target': z.string().readonly(),
55
+ 'service': z.string().readonly(),
56
+ })).readonly(),
57
+ });
58
+ const topologyAnalysisSchema = z.object({
59
+ 'graph': topologyGraphSchema.readonly(),
60
+ 'collapsed': collapsedGraphSchema.readonly(),
61
+ 'nodeMetrics': z.record(z.string(), z.object({
62
+ 'inDegree': z.number().readonly(),
63
+ 'outDegree': z.number().readonly(),
64
+ 'degreeCentrality': z.number().readonly(),
65
+ 'betweennessCentrality': z.number().readonly(),
66
+ 'eigenvectorCentrality': z.number().readonly(),
67
+ 'pagerank': z.number().readonly(),
68
+ })).readonly(),
69
+ 'graphMetrics': z.object({
70
+ 'nodeCount': z.number().readonly(),
71
+ 'edgeCount': z.number().readonly(),
72
+ 'density': z.number().readonly(),
73
+ 'stronglyConnectedComponents': z.number().readonly(),
74
+ 'largestSccSize': z.number().readonly(),
75
+ 'isDag': z.boolean().readonly(),
76
+ 'weaklyConnectedComponents': z.number().readonly(),
77
+ 'diameter': z.number().readonly(),
78
+ 'averagePathLength': z.number().readonly(),
79
+ 'sourceCount': z.number().readonly(),
80
+ 'sinkCount': z.number().readonly(),
81
+ }).readonly(),
82
+ });
83
+ export const TYPERT = {
84
+ package: '@sleetdrop/dsh-plugin-topology',
85
+ face: 'host',
86
+ schemas: [],
87
+ invocations: [
88
+ {
89
+ id: '@sleetdrop/dsh-plugin-topology#pluginTopology/analyze',
90
+ service: 'pluginTopology',
91
+ namespace: 'pluginTopology',
92
+ method: 'analyze',
93
+ invocation: { kind: 'direct' },
94
+ parameters: [],
95
+ result: {
96
+ mode: 'strict',
97
+ typeSymbol: '@sleetdrop/dsh-plugin-topology/types#TopologyAnalysis',
98
+ schema: topologyAnalysisSchema,
99
+ },
100
+ },
101
+ {
102
+ id: '@sleetdrop/dsh-plugin-topology#pluginTopology/render',
103
+ service: 'pluginTopology',
104
+ namespace: 'pluginTopology',
105
+ method: 'render',
106
+ invocation: { kind: 'direct' },
107
+ parameters: [
108
+ {
109
+ name: 'format',
110
+ wire: 'format',
111
+ source: 'json',
112
+ codec: {
113
+ mode: 'strict',
114
+ typeSymbol: '@sleetdrop/dsh-plugin-topology/types#RenderFormat',
115
+ schema: z.union([z.literal('json'), z.literal('dot'), z.literal('svg')]),
116
+ },
117
+ },
118
+ {
119
+ name: 'rankdir',
120
+ wire: 'rankdir',
121
+ source: 'json',
122
+ codec: {
123
+ mode: 'strict',
124
+ typeSymbol: '@sleetdrop/dsh-plugin-topology/types#Rankdir',
125
+ schema: z.union([z.literal('TB'), z.literal('LR')]),
126
+ },
127
+ },
128
+ ],
129
+ result: {
130
+ mode: 'strict',
131
+ typeSymbol: '@sleetdrop/dsh-plugin-topology#pluginTopology/render:result',
132
+ schema: z.string(),
133
+ },
134
+ },
135
+ ],
136
+ model: {
137
+ services: [],
138
+ events: [],
139
+ objects: [],
140
+ },
141
+ };
142
+ export default TYPERT;
package/lib/types.d.ts ADDED
@@ -0,0 +1,126 @@
1
+ /**
2
+ * JSON-safe types for the plugin topology snapshot.
3
+ * @module @sleetdrop/dsh-plugin-topology
4
+ */
5
+ /** Plugin fiber lifecycle states, in Cordis {@link FiberState} declaration order. */
6
+ export type PluginState = 'PENDING' | 'LOADING' | 'ACTIVE' | 'FAILED' | 'DISPOSED' | 'UNLOADING';
7
+ /** Export format selectors for the topology renderer. */
8
+ export type RenderFormat = 'json' | 'dot' | 'svg';
9
+ /** Graphviz layout direction for SVG/DOT rendering. */
10
+ export type Rankdir = 'TB' | 'LR';
11
+ /** Node kinds in the service dependency graph. */
12
+ export type TopologyNodeKind = 'plugin' | 'service';
13
+ /** Edge kinds in the service dependency graph. */
14
+ export type TopologyEdgeKind = 'provides' | 'injects';
15
+ /** One plugin fiber (or the synthetic root) in the dependency graph. */
16
+ export interface PluginNode {
17
+ /** Stable node id `p:<uid>`; the synthetic root is `p:0`. */
18
+ readonly id: string;
19
+ readonly kind: 'plugin';
20
+ /** Fiber display name; the synthetic root is `root`. */
21
+ readonly label: string;
22
+ /** Lifecycle state at snapshot time. */
23
+ readonly state: PluginState;
24
+ /** Node id of the fiber that mounted this one; `null` for the root. */
25
+ readonly parent: string | null;
26
+ /** Service names this fiber declares it injects. */
27
+ readonly inject: string[];
28
+ /** Module specifier the loader imported this plugin from; absent for programmatic mounts. */
29
+ readonly source?: string;
30
+ }
31
+ /** One provided service in the dependency graph. */
32
+ export interface ServiceNode {
33
+ /** Stable node id `s:<scope>:<name>`. */
34
+ readonly id: string;
35
+ readonly kind: 'service';
36
+ /** Service name. */
37
+ readonly label: string;
38
+ /** Isolation scope discriminator: `root` or `isolate-<n>`. */
39
+ readonly scope: string;
40
+ }
41
+ export type TopologyNode = PluginNode | ServiceNode;
42
+ /** A directed edge in the service dependency graph. */
43
+ export interface TopologyEdge {
44
+ readonly source: string;
45
+ readonly target: string;
46
+ readonly kind: TopologyEdgeKind;
47
+ /** Service name the edge crosses. */
48
+ readonly service: string;
49
+ }
50
+ /** A declared inject with no resolvable provider at snapshot time. */
51
+ export interface UnresolvedDependency {
52
+ /** Plugin node id that declared the dependency. */
53
+ readonly plugin: string;
54
+ /** Service name that had no active provider. */
55
+ readonly service: string;
56
+ /** Plugin lifecycle state at snapshot time. */
57
+ readonly state: PluginState;
58
+ }
59
+ /** A point-in-time snapshot of the runtime plugin topology. */
60
+ export interface TopologyGraph {
61
+ /** Fixed discriminator; the snapshot is always the bipartite service graph. */
62
+ readonly kind: 'bipartite';
63
+ /** ISO timestamp of the capture. */
64
+ readonly generatedAt: string;
65
+ readonly nodes: TopologyNode[];
66
+ readonly edges: TopologyEdge[];
67
+ readonly unresolved: UnresolvedDependency[];
68
+ }
69
+ /** One plugin-to-plugin dependency edge in the collapsed projection. */
70
+ export interface DependencyEdge {
71
+ /** Consumer plugin id; it depends on `target`. */
72
+ readonly source: string;
73
+ /** Provider plugin id; it is depended on by `source`. */
74
+ readonly target: string;
75
+ /** Service name that mediates the dependency. */
76
+ readonly service: string;
77
+ }
78
+ /** Plugin-only projection of the dependency graph. */
79
+ export interface CollapsedGraph {
80
+ readonly kind: 'collapsed';
81
+ readonly generatedAt: string;
82
+ readonly nodes: PluginNode[];
83
+ readonly edges: DependencyEdge[];
84
+ }
85
+ /** Graph-theory metrics for one plugin node in the collapsed graph. */
86
+ export interface NodeMetrics {
87
+ /** Number of plugins that depend on this one. */
88
+ readonly inDegree: number;
89
+ /** Number of plugins this one depends on. */
90
+ readonly outDegree: number;
91
+ /** `(inDegree + outDegree)` normalized by `2 * (n - 1)`. */
92
+ readonly degreeCentrality: number;
93
+ /** Unweighted directed betweenness centrality. */
94
+ readonly betweennessCentrality: number;
95
+ /** Eigenvector centrality from in-neighbors; 0 on an acyclic graph. */
96
+ readonly eigenvectorCentrality: number;
97
+ /** PageRank from in-neighbors with damping 0.85. */
98
+ readonly pagerank: number;
99
+ }
100
+ /** Aggregate metrics over the collapsed plugin graph. */
101
+ export interface GraphMetrics {
102
+ readonly nodeCount: number;
103
+ /** Unique plugin dependency pairs, deduplicating service-mediated repeats. */
104
+ readonly edgeCount: number;
105
+ readonly density: number;
106
+ readonly stronglyConnectedComponents: number;
107
+ readonly largestSccSize: number;
108
+ /** True when every node is its own strongly connected component. */
109
+ readonly isDag: boolean;
110
+ readonly weaklyConnectedComponents: number;
111
+ /** Largest finite shortest-path length; unreachable pairs are excluded. */
112
+ readonly diameter: number;
113
+ /** Mean finite shortest-path length over reachable pairs. */
114
+ readonly averagePathLength: number;
115
+ /** Nodes that depend on nothing. */
116
+ readonly sourceCount: number;
117
+ /** Nodes nothing depends on. */
118
+ readonly sinkCount: number;
119
+ }
120
+ /** Snapshot plus its graph-theory analysis. */
121
+ export interface TopologyAnalysis {
122
+ readonly graph: TopologyGraph;
123
+ readonly collapsed: CollapsedGraph;
124
+ readonly nodeMetrics: Record<string, NodeMetrics>;
125
+ readonly graphMetrics: GraphMetrics;
126
+ }
package/lib/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * JSON-safe types for the plugin topology snapshot.
3
+ * @module @sleetdrop/dsh-plugin-topology
4
+ */
5
+ export {};
@@ -0,0 +1,19 @@
1
+ # Node-half only overlay for `dsh --profile <name> --patch <this file>`.
2
+ # Use this to test the `graph.render` tool and the host dependency service
3
+ # WITHOUT installing the bundle into a profile (the browser panel needs the
4
+ # web profile; see README "Local development").
5
+ #
6
+ # Copy this to overlay.yml and replace the path with the absolute path to this
7
+ # checkout's built node entry. Build first:
8
+ #
9
+ # pnpm install && pnpm build
10
+ # cp overlay.example.yml overlay.yml # then edit the name below
11
+ # dsh --profile headless --patch ./overlay.yml "render the plugin topology as json"
12
+ #
13
+ - insert:
14
+ - id: plugin-topology
15
+ name: /absolute/path/to/dsh-plugin-topology/lib/index.js
16
+
17
+ - insert:
18
+ - id: tool-plugin-topology
19
+ name: /absolute/path/to/dsh-plugin-topology/lib/tool.js
package/package.json ADDED
@@ -0,0 +1,114 @@
1
+ {
2
+ "name": "@sleetdrop/dsh-plugin-topology",
3
+ "version": "0.1.0",
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
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/sleetdrop/dsh-plugin-topology.git"
9
+ },
10
+ "keywords": [
11
+ "deepseek-harness",
12
+ "dsh-plugin",
13
+ "plugin-topology",
14
+ "cordis",
15
+ "graphviz"
16
+ ],
17
+ "type": "module",
18
+ "main": "lib/index.js",
19
+ "types": "lib/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./lib/index.d.ts",
23
+ "default": "./lib/index.js"
24
+ },
25
+ "./invariant": {
26
+ "types": "./lib/invariant.d.ts",
27
+ "default": "./lib/invariant.js"
28
+ },
29
+ "./client": {
30
+ "types": "./lib/client/index.d.ts",
31
+ "default": "./lib/client.js"
32
+ },
33
+ "./types": {
34
+ "types": "./lib/types.d.ts",
35
+ "default": "./lib/types.js"
36
+ },
37
+ "./package.json": "./package.json",
38
+ "./tool": {
39
+ "types": "./lib/tool.d.ts",
40
+ "default": "./lib/tool.js"
41
+ },
42
+ "./remote": {
43
+ "types": "./lib/client/remote-client.d.ts",
44
+ "default": "./lib/client/remote-client.js"
45
+ },
46
+ "./typert": {
47
+ "types": "./lib/typert.d.ts",
48
+ "default": "./lib/typert.js"
49
+ }
50
+ },
51
+ "files": [
52
+ "lib",
53
+ "cordis.patch.yml",
54
+ "overlay.example.yml",
55
+ "NEXT-STEPS.md"
56
+ ],
57
+ "dsh": {
58
+ "bundle": {
59
+ "patch": "./cordis.patch.yml"
60
+ },
61
+ "client": {
62
+ "platform": "web",
63
+ "inject": [
64
+ "@deepseek-ai/dsh-client-runtime",
65
+ "@deepseek-ai/dsh-client-locale",
66
+ "@deepseek-ai/dsh-api-remotes",
67
+ "@deepseek-ai/dsh-client-ui-sidebar"
68
+ ]
69
+ }
70
+ },
71
+ "peerDependencies": {
72
+ "@deepseek-ai/cordis": "^4.0.1",
73
+ "@deepseek-ai/dsh-api-remotes": "^0.1.1-rc.2",
74
+ "@deepseek-ai/dsh-client-locale": "^0.1.1-rc.2",
75
+ "@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.2",
76
+ "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.1-rc.2",
77
+ "@deepseek-ai/dsh-tools": "^0.1.1-rc.2",
78
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.1-rc.2",
79
+ "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2"
80
+ },
81
+ "devDependencies": {
82
+ "@deepseek-ai/cordis": "4.0.1",
83
+ "@deepseek-ai/dsh-api-remotes": "0.1.1-rc.2",
84
+ "@deepseek-ai/dsh-client-locale": "0.1.1-rc.2",
85
+ "@deepseek-ai/dsh-client-runtime": "0.1.1-rc.2",
86
+ "@deepseek-ai/dsh-client-ui-sidebar": "0.1.1-rc.2",
87
+ "@deepseek-ai/dsh-tools": "0.1.1-rc.2",
88
+ "@deepseek-ai/dsh-typert-protocol": "0.1.1-rc.2",
89
+ "@deepseek-ai/dsh-invariants": "0.1.1-rc.2",
90
+ "@types/node": "^24.0.0",
91
+ "@types/react": "~18.3.1",
92
+ "react": "^18.2.0",
93
+ "tsdown": "^0.15.0",
94
+ "typescript": "^5.8.0",
95
+ "lightningcss": "1.32.0",
96
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.1-rc.2"
97
+ },
98
+ "dependencies": {
99
+ "@hpcc-js/wasm-graphviz": "^1.28.0",
100
+ "zod": "^4.4.3"
101
+ },
102
+ "publishConfig": {
103
+ "access": "public",
104
+ "registry": "https://registry.npmjs.org/"
105
+ },
106
+ "engines": {
107
+ "node": "^22.19.0 || >=24.0.0"
108
+ },
109
+ "scripts": {
110
+ "build": "tsc && tsdown",
111
+ "typecheck": "tsc -p tsconfig.json --noEmit",
112
+ "test": "tsc -p tsconfig.test.json && node --test 'lib-test/test/*.test.js'"
113
+ }
114
+ }