dsh-webui-studio 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/PRODUCT.md +11 -7
  2. package/README.md +69 -21
  3. package/README.zh-CN.md +64 -20
  4. package/dist/bridge.js +10 -10
  5. package/dist/studio.css +1 -1
  6. package/dist/studio.js +16691 -10202
  7. package/docs/bidirectional-connection-handoff.md +729 -0
  8. package/docs/harmony-api-requirements.md +17 -13
  9. package/docs/remote-development.md +80 -0
  10. package/lib/bridge/element-style-selector.d.ts +1 -0
  11. package/lib/bridge/element-style-selector.js +53 -0
  12. package/lib/contracts.d.ts +152 -83
  13. package/lib/contracts.js +0 -2
  14. package/lib/host/agent.d.ts +15 -13
  15. package/lib/host/agent.js +213 -56
  16. package/lib/host/automatic-patch.d.ts +9 -0
  17. package/lib/host/automatic-patch.js +433 -0
  18. package/lib/host/backend.d.ts +134 -4
  19. package/lib/host/backend.js +465 -114
  20. package/lib/host/drafts.d.ts +1 -1
  21. package/lib/host/drafts.js +65 -15
  22. package/lib/host/element-source.d.ts +9 -0
  23. package/lib/host/element-source.js +295 -0
  24. package/lib/host/mcp.d.ts +4 -0
  25. package/lib/host/mcp.js +97 -0
  26. package/lib/host/preview-draft.d.ts +22 -0
  27. package/lib/host/preview-draft.js +162 -0
  28. package/lib/host/preview-port.d.ts +8 -0
  29. package/lib/host/preview-port.js +32 -0
  30. package/lib/host/preview-worker.d.ts +65 -2
  31. package/lib/host/preview-worker.js +203 -76
  32. package/lib/host/preview.d.ts +26 -5
  33. package/lib/host/preview.js +130 -49
  34. package/lib/host/readiness.d.ts +2 -2
  35. package/lib/host/readiness.js +14 -17
  36. package/lib/host/routes.d.ts +1 -7
  37. package/lib/host/routes.js +41 -50
  38. package/lib/host/runtime-profile.d.ts +2 -1
  39. package/lib/host/runtime-profile.js +30 -8
  40. package/lib/host/source-resolution.d.ts +11 -1
  41. package/lib/host/source-resolution.js +69 -24
  42. package/lib/host/studio-service.d.ts +129 -0
  43. package/lib/host/studio-service.js +53 -0
  44. package/lib/index.d.ts +5 -0
  45. package/lib/index.js +64 -30
  46. package/lib/studio-remote.d.ts +126 -0
  47. package/lib/studio-remote.js +188 -0
  48. package/lib/variable-tree.d.ts +2 -0
  49. package/lib/variable-tree.js +13 -0
  50. package/package.json +62 -25
  51. package/studio.patch.yml +12 -0
@@ -0,0 +1,188 @@
1
+ import { z } from 'zod';
2
+ export function invokeStudioRemote(remote, method, payload, signal) {
3
+ const calls = {
4
+ 'studio.current.get': () => remote.currentGet(signal),
5
+ 'studio.current.preview.status': () => remote.currentPreviewStatus(signal),
6
+ 'studio.current.preview.update': () => remote.currentPreviewUpdate(payload, signal),
7
+ 'studio.current.resolveSource': () => remote.currentResolveSource(payload, signal),
8
+ 'studio.current.agent.create': () => remote.currentAgentCreate(payload, signal),
9
+ 'studio.current.agent.attach': () => remote.currentAgentAttach(payload, signal),
10
+ 'studio.current.agent.leave': () => remote.currentAgentLeave(signal),
11
+ 'studio.drafts.list': () => remote.draftsList(signal),
12
+ 'studio.drafts.create': () => remote.draftsCreate(payload, signal),
13
+ 'studio.drafts.rename': () => remote.draftsRename(payload, signal),
14
+ 'studio.drafts.export': () => remote.draftsExport(payload, signal),
15
+ 'studio.drafts.start': () => remote.draftsStart(payload, signal),
16
+ 'studio.drafts.stop': () => remote.draftsStop(payload, signal),
17
+ 'studio.workspace.get': () => remote.workspaceGet(signal),
18
+ 'studio.workspace.update': () => remote.workspaceUpdate(payload, signal),
19
+ 'studio.drafts.harmony.profile': () => remote.harmonyProfile(payload, signal),
20
+ 'studio.drafts.harmony.inspect': () => remote.harmonyInspect(payload, signal),
21
+ 'studio.drafts.harmony.updateProfile': () => remote.harmonyUpdateProfile(payload, signal),
22
+ 'studio.harmony.inspect': () => remote.harmonyInspect(payload, signal),
23
+ 'studio.project.state': () => remote.projectState(payload, signal),
24
+ 'studio.project.activate': () => remote.projectActivate(payload, signal),
25
+ 'studio.project.files': () => remote.projectFiles(payload, signal),
26
+ 'studio.project.readFile': () => remote.projectReadFile(payload, signal),
27
+ 'studio.project.writeFile': () => remote.projectWriteFile(payload, signal),
28
+ 'studio.project.build': () => remote.projectBuild(payload, signal),
29
+ 'studio.project.cancelBuild': () => remote.projectCancelBuild(payload, signal),
30
+ 'studio.elements.styles': () => remote.elementsStyles(payload, signal),
31
+ 'studio.elements.saveSource': () => remote.elementsSaveSource(payload, signal),
32
+ 'studio.patches.analyzeAutomatic': () => remote.patchesAnalyzeAutomatic(payload, signal),
33
+ 'studio.patches.createAutomatic': () => remote.patchesCreateAutomatic(payload, signal),
34
+ 'studio.readiness.inspect': () => remote.readinessInspect(payload, signal),
35
+ 'studio.readiness.pack': () => remote.readinessPack(payload, signal),
36
+ 'studio.preview.status': () => remote.previewStatus(payload, signal),
37
+ 'studio.preview.update': () => remote.previewUpdate(payload, signal),
38
+ 'studio.preview.resolveSource': () => remote.previewResolveSource(payload, signal),
39
+ 'studio.agent.create': () => remote.agentCreate(payload, signal),
40
+ 'studio.agent.attach': () => remote.agentAttach(payload, signal),
41
+ 'studio.agent.leave': () => remote.agentLeave(payload, signal),
42
+ };
43
+ return calls[method]?.();
44
+ }
45
+ const nonEmpty = z.string().min(1);
46
+ const draftIdSchema = z.object({ draftId: nonEmpty });
47
+ const stringList = z.array(nonEmpty);
48
+ const sourceLocationSchema = z.object({
49
+ file: nonEmpty,
50
+ line: z.number().int().min(1).optional(),
51
+ column: z.number().int().min(1).optional(),
52
+ });
53
+ const boundarySchema = z.object({ surfaceId: nonEmpty, path: stringList });
54
+ const targetSchema = z.object({ package: nonEmpty, file: nonEmpty });
55
+ const createDraftSchema = z.object({
56
+ source: z.discriminatedUnion('kind', [
57
+ z.object({ kind: z.literal('new'), packageName: nonEmpty }),
58
+ z.object({ kind: z.literal('existing'), directory: nonEmpty }),
59
+ ]),
60
+ profileMode: z.enum(['main-home', 'custom']),
61
+ profileDirectory: nonEmpty.optional(),
62
+ destinationDirectory: nonEmpty.optional(),
63
+ });
64
+ const workspaceSchema = z.object({
65
+ openDraftIds: z.array(nonEmpty),
66
+ selectedDraftId: nonEmpty.optional(),
67
+ });
68
+ const harmonyInputSchema = draftIdSchema.extend({ package: nonEmpty.optional(), file: nonEmpty.optional() });
69
+ const profileUpdateSchema = draftIdSchema.extend({
70
+ order: stringList.optional(),
71
+ patchOrder: stringList.optional(),
72
+ disabled: stringList.optional(),
73
+ });
74
+ const styleSchema = z.object({
75
+ elementId: nonEmpty,
76
+ rules: z.array(z.object({
77
+ selector: z.string(),
78
+ declarations: z.array(z.object({ property: nonEmpty, value: z.string() })),
79
+ })),
80
+ });
81
+ const automaticBase = {
82
+ draftId: nonEmpty,
83
+ targets: z.array(targetSchema).min(1),
84
+ clientFile: nonEmpty,
85
+ boundary: boundarySchema,
86
+ targetSelector: nonEmpty.optional(),
87
+ selector: nonEmpty,
88
+ elementId: nonEmpty,
89
+ elementLabel: nonEmpty,
90
+ };
91
+ const automaticPatchSchema = z.discriminatedUnion('kind', [
92
+ z.object({
93
+ ...automaticBase,
94
+ kind: z.literal('replace-string'),
95
+ text: z.string(),
96
+ replacement: z.string(),
97
+ elementSourceFile: nonEmpty.optional(),
98
+ }),
99
+ z.object({
100
+ ...automaticBase,
101
+ kind: z.literal('css-style'),
102
+ component: nonEmpty,
103
+ variables: z.array(z.object({
104
+ id: nonEmpty,
105
+ label: nonEmpty,
106
+ property: nonEmpty,
107
+ control: z.enum(['color', 'length', 'number', 'enum', 'string']),
108
+ value: z.union([z.string(), z.number()]),
109
+ options: stringList.optional(),
110
+ constraints: z.object({
111
+ min: z.number().optional(),
112
+ max: z.number().optional(),
113
+ step: z.number().optional(),
114
+ }).optional(),
115
+ })).min(1),
116
+ }),
117
+ ]);
118
+ const previewUpdateSchema = draftIdSchema.extend({
119
+ connected: z.boolean(),
120
+ graphRev: z.string().optional(),
121
+ mode: z.enum(['browse', 'inspect']),
122
+ selection: z.unknown().optional(),
123
+ registry: z.unknown().optional(),
124
+ });
125
+ const currentPreviewUpdateSchema = previewUpdateSchema.omit({ draftId: true });
126
+ function codec(typeSymbol, schema) {
127
+ return { mode: 'strict', typeSymbol, schema };
128
+ }
129
+ function invocation(method, input, result = z.unknown()) {
130
+ return {
131
+ id: `dsh-webui-studio#studio/${method}`,
132
+ service: 'studio',
133
+ namespace: 'studio',
134
+ method,
135
+ invocation: { kind: 'direct' },
136
+ parameters: input === undefined ? [] : [{
137
+ name: 'input',
138
+ wire: 'input',
139
+ source: 'json',
140
+ codec: codec(`dsh-webui-studio#studio/${method}:input`, input),
141
+ }],
142
+ cancellation: { parameter: 'signal' },
143
+ result: codec(`dsh-webui-studio#studio/${method}:result`, result),
144
+ };
145
+ }
146
+ export const STUDIO_INVOCATIONS = [
147
+ invocation('currentGet'),
148
+ invocation('currentPreviewStatus'),
149
+ invocation('currentPreviewUpdate', currentPreviewUpdateSchema),
150
+ invocation('currentResolveSource', z.object({ source: sourceLocationSchema })),
151
+ invocation('currentAgentCreate', z.object({ agentPreset: nonEmpty.optional() })),
152
+ invocation('currentAgentAttach', z.object({ sessionId: nonEmpty })),
153
+ invocation('currentAgentLeave'),
154
+ invocation('draftsList'),
155
+ invocation('draftsCreate', createDraftSchema),
156
+ invocation('draftsRename', draftIdSchema.extend({ label: z.string() })),
157
+ invocation('draftsExport', draftIdSchema),
158
+ invocation('draftsStart', draftIdSchema),
159
+ invocation('draftsStop', draftIdSchema),
160
+ invocation('workspaceGet'),
161
+ invocation('workspaceUpdate', workspaceSchema),
162
+ invocation('harmonyProfile', draftIdSchema),
163
+ invocation('harmonyInspect', harmonyInputSchema),
164
+ invocation('harmonyUpdateProfile', profileUpdateSchema),
165
+ invocation('projectState', draftIdSchema),
166
+ invocation('projectActivate', draftIdSchema.extend({ graphRev: nonEmpty })),
167
+ invocation('projectFiles', draftIdSchema),
168
+ invocation('projectReadFile', draftIdSchema.extend({ path: nonEmpty })),
169
+ invocation('projectWriteFile', draftIdSchema.extend({ path: nonEmpty, content: z.string() })),
170
+ invocation('projectBuild', draftIdSchema),
171
+ invocation('projectCancelBuild', draftIdSchema, z.object({ canceled: z.boolean() })),
172
+ invocation('elementsStyles', draftIdSchema),
173
+ invocation('elementsSaveSource', draftIdSchema.extend({ styles: z.array(styleSchema) })),
174
+ invocation('patchesAnalyzeAutomatic', automaticPatchSchema),
175
+ invocation('patchesCreateAutomatic', automaticPatchSchema),
176
+ invocation('readinessInspect', draftIdSchema),
177
+ invocation('readinessPack', draftIdSchema),
178
+ invocation('previewStatus', draftIdSchema),
179
+ invocation('previewUpdate', previewUpdateSchema),
180
+ invocation('previewResolveSource', draftIdSchema.extend({ source: sourceLocationSchema })),
181
+ invocation('agentCreate', draftIdSchema.extend({ agentPreset: nonEmpty.optional() })),
182
+ invocation('agentAttach', draftIdSchema.extend({ sessionId: nonEmpty })),
183
+ invocation('agentLeave', draftIdSchema),
184
+ ];
185
+ export const STUDIO_REMOTE = {
186
+ package: 'dsh-webui-studio/studio',
187
+ descriptors: STUDIO_INVOCATIONS,
188
+ };
@@ -0,0 +1,2 @@
1
+ import type { StudioVariableDefinition, StudioVariableNode } from 'dsh-harmony-react/studio';
2
+ export declare function flattenVariableTree(nodes: readonly StudioVariableNode[]): StudioVariableDefinition[];
@@ -0,0 +1,13 @@
1
+ export function flattenVariableTree(nodes) {
2
+ const definitions = [];
3
+ const visit = (items) => {
4
+ for (const item of items) {
5
+ if (item.kind === 'group')
6
+ visit(item.children);
7
+ else
8
+ definitions.push(item);
9
+ }
10
+ };
11
+ visit(nodes);
12
+ return definitions;
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-webui-studio",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Harmony WebUI Studio with a live DSH preview and an in-host assistant session",
5
5
  "keywords": [
6
6
  "deepseek",
@@ -15,13 +15,13 @@
15
15
  "visual-editor",
16
16
  "webui"
17
17
  ],
18
- "homepage": "https://github.com/CH4ACKO3/dsh-webui-studio#readme",
18
+ "homepage": "https://github.com/memorax-ai/dsh-webui-studio#readme",
19
19
  "bugs": {
20
- "url": "https://github.com/CH4ACKO3/dsh-webui-studio/issues"
20
+ "url": "https://github.com/memorax-ai/dsh-webui-studio/issues"
21
21
  },
22
22
  "repository": {
23
23
  "type": "git",
24
- "url": "git+https://github.com/CH4ACKO3/dsh-webui-studio.git"
24
+ "url": "git+https://github.com/memorax-ai/dsh-webui-studio.git"
25
25
  },
26
26
  "license": "MIT",
27
27
  "publishConfig": {
@@ -44,6 +44,7 @@
44
44
  "dist",
45
45
  "lib",
46
46
  "docs",
47
+ "studio.patch.yml",
47
48
  "LICENSE",
48
49
  "PRODUCT.md",
49
50
  "README.md",
@@ -55,8 +56,9 @@
55
56
  "build:browser": "vite build && vite build -c vite.bridge.config.ts",
56
57
  "typecheck": "tsc -p tsconfig.host.json --noEmit && tsc -p tsconfig.browser.json --noEmit && tsc -p tsconfig.bridge.json --noEmit && tsc -p tsconfig.test.json --noEmit",
57
58
  "test": "vitest run",
58
- "test:integration": "npm run build && node test/studio-extension.e2e.ts",
59
+ "test:integration": "npm run build && node test/studio-plugin.e2e.ts",
59
60
  "check": "npm run typecheck && npm test && npm run test:integration",
61
+ "remote": "node scripts/remote-studio.ts",
60
62
  "prepublishOnly": "npm run check",
61
63
  "prepare": "npm run build"
62
64
  },
@@ -76,38 +78,73 @@
76
78
  },
77
79
  "category": "plugin-development"
78
80
  },
79
- "harmony": {
80
- "extension": "./lib/index.js"
81
+ "bundle": {
82
+ "patch": "./studio.patch.yml"
81
83
  }
82
84
  },
83
85
  "peerDependencies": {
84
86
  "@deepseek-ai/cordis": "^4.0.1",
85
- "@deepseek-ai/dsh-agent": "0.1.0-rc.6",
86
- "@deepseek-ai/dsh-host-apiproxy": "0.1.0-rc.6",
87
- "@deepseek-ai/dsh-host-webserver": "0.1.0-rc.6",
88
- "@deepseek-ai/dsh-session": "0.1.0-rc.6",
89
- "@deepseek-ai/dsh-subprocess": "0.1.0-rc.6",
90
- "@deepseek-ai/dsh-system-prompt": "0.1.0-rc.6",
91
- "@deepseek-ai/dsh-tools": "0.1.0-rc.6"
87
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.2",
88
+ "@deepseek-ai/dsh-agent": "^0.1.1-rc.2",
89
+ "@deepseek-ai/dsh-api-gateway": "^0.1.1-rc.2",
90
+ "@deepseek-ai/dsh-client-connection": "^0.1.1-rc.2",
91
+ "@deepseek-ai/dsh-client-modules": "^0.1.1-rc.2",
92
+ "@deepseek-ai/dsh-host-apiproxy": "^0.1.1-rc.2",
93
+ "@deepseek-ai/dsh-host-webserver": "^0.1.1-rc.2",
94
+ "@deepseek-ai/dsh-session": "^0.1.1-rc.2",
95
+ "@deepseek-ai/dsh-skill": "^0.1.1-rc.2",
96
+ "@deepseek-ai/dsh-subprocess": "^0.1.1-rc.2",
97
+ "@deepseek-ai/dsh-system-prompt": "^0.1.1-rc.2",
98
+ "@deepseek-ai/dsh-tools": "^0.1.1-rc.2",
99
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.1-rc.2",
100
+ "@deepseek-ai/dsh-typert-registry": "^0.1.1-rc.2"
92
101
  },
93
102
  "dependencies": {
94
- "dsh-harmony": "^0.1.3",
95
- "dsh-harmony-react": "^0.1.0",
96
- "pnpm": "^10.34.5"
103
+ "@modelcontextprotocol/sdk": "^1.30.0",
104
+ "@phenomnomnominal/tsquery": "^6.2.0",
105
+ "dsh-harmony": "^0.8.7",
106
+ "dsh-harmony-react": "^0.3.2",
107
+ "pnpm": "^10.34.5",
108
+ "the-binding-of-dsh": "0.1.6",
109
+ "zod": "^4.4.3"
97
110
  },
98
111
  "devDependencies": {
99
112
  "@codemirror/lang-javascript": "^6.2.4",
100
113
  "@codemirror/state": "^6.5.2",
101
114
  "@codemirror/view": "^6.38.6",
102
115
  "@deepseek-ai/cordis": "^4.0.1",
103
- "@deepseek-ai/dsh": "0.1.0-rc.6",
104
- "@deepseek-ai/dsh-agent": "0.1.0-rc.6",
105
- "@deepseek-ai/dsh-host-apiproxy": "0.1.0-rc.6",
106
- "@deepseek-ai/dsh-host-webserver": "0.1.0-rc.6",
107
- "@deepseek-ai/dsh-session": "0.1.0-rc.6",
108
- "@deepseek-ai/dsh-subprocess": "0.1.0-rc.6",
109
- "@deepseek-ai/dsh-system-prompt": "0.1.0-rc.6",
110
- "@deepseek-ai/dsh-tools": "0.1.0-rc.6",
116
+ "@deepseek-ai/cordis-plugin-group": "^1.0.1",
117
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.2",
118
+ "@deepseek-ai/dsh": "^0.1.1-rc.2",
119
+ "@deepseek-ai/dsh-agent": "^0.1.1-rc.2",
120
+ "@deepseek-ai/dsh-anonymous-user-id": "^0.1.1-rc.2",
121
+ "@deepseek-ai/dsh-api-gateway": "^0.1.1-rc.2",
122
+ "@deepseek-ai/dsh-authorization": "^0.1.1-rc.2",
123
+ "@deepseek-ai/dsh-bash-local": "^0.1.1-rc.2",
124
+ "@deepseek-ai/dsh-client-connection": "^0.1.1-rc.2",
125
+ "@deepseek-ai/dsh-client-modules": "^0.1.1-rc.2",
126
+ "@deepseek-ai/dsh-code-runtime": "^0.1.1-rc.2",
127
+ "@deepseek-ai/dsh-compaction": "^0.1.1-rc.2",
128
+ "@deepseek-ai/dsh-fs": "^0.1.1-rc.2",
129
+ "@deepseek-ai/dsh-host-apiproxy": "^0.1.1-rc.2",
130
+ "@deepseek-ai/dsh-host-webserver": "^0.1.1-rc.2",
131
+ "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
132
+ "@deepseek-ai/dsh-output-retention": "^0.1.1-rc.2",
133
+ "@deepseek-ai/dsh-sandbox": "^0.1.1-rc.2",
134
+ "@deepseek-ai/dsh-scope": "^0.1.1-rc.2",
135
+ "@deepseek-ai/dsh-session": "^0.1.1-rc.2",
136
+ "@deepseek-ai/dsh-session-telemetry": "^0.1.1-rc.2",
137
+ "@deepseek-ai/dsh-session-title-llm": "^0.1.1-rc.2",
138
+ "@deepseek-ai/dsh-shell": "^0.1.1-rc.2",
139
+ "@deepseek-ai/dsh-spill": "^0.1.1-rc.2",
140
+ "@deepseek-ai/dsh-subagent-in-process-driver": "^0.1.1-rc.2",
141
+ "@deepseek-ai/dsh-subprocess": "^0.1.1-rc.2",
142
+ "@deepseek-ai/dsh-system-prompt": "^0.1.1-rc.2",
143
+ "@deepseek-ai/dsh-timeout": "^0.1.1-rc.2",
144
+ "@deepseek-ai/dsh-tools": "^0.1.1-rc.2",
145
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.1-rc.2",
146
+ "@deepseek-ai/dsh-typert-registry": "^0.1.1-rc.2",
147
+ "@deepseek-ai/dsh-workflow": "^0.1.1-rc.2",
111
148
  "@types/node": "^24.10.0",
112
149
  "@types/react": "^18.3.0",
113
150
  "@types/react-dom": "^18.3.0",
@@ -0,0 +1,12 @@
1
+ # Ordinary DSH loads Harmony's existing approval dialog. The Harmony launcher
2
+ # supplies the active runtime itself, so this installer entry is disabled after
3
+ # installation.
4
+ - insert:
5
+ - id: harmony-studio-runtime
6
+ name: dsh-harmony
7
+ disabled: !!js process.env.DSH_HARMONY_ACTIVE === '1'
8
+ - id: harmony-studio-binding
9
+ name: the-binding-of-dsh
10
+ inject: [harmony]
11
+ - id: harmony-studio
12
+ name: dsh-webui-studio