@vibe-x/dsh-html-workbench 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/README.md +48 -0
- package/assets/workbench.html +901 -0
- package/cordis.patch.yml +6 -0
- package/dsh.plugin.json +16 -0
- package/lib/client.js +267 -0
- package/package.json +51 -0
- package/scripts/workbench.py +774 -0
- package/src/client.js +229 -0
- package/src/host.js +201 -0
- package/src/index.js +27 -0
package/cordis.patch.yml
ADDED
package/dsh.plugin.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "@vibe-x/dsh-html-workbench",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "./src/index.js",
|
|
5
|
+
"description": "在 DeepSeek Harness 右侧栏预览并可视化编辑 agent 生成的 HTML:复用/拉起本地 html-workbench 服务,在 iframe 中内嵌 GrapesJS 编辑器,插件卸载时停掉自己启动的服务。",
|
|
6
|
+
"engines": {
|
|
7
|
+
"dsh": ">=0.0.1"
|
|
8
|
+
},
|
|
9
|
+
"contributes": {
|
|
10
|
+
"tools": [],
|
|
11
|
+
"skills": []
|
|
12
|
+
},
|
|
13
|
+
"client": {
|
|
14
|
+
"main": "./lib/client.js"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@vibe-x/dsh-html-workbench",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
|
|
7
|
+
// Closure symbols — the same names the dynamic runner injects.
|
|
8
|
+
const React = require('react');
|
|
9
|
+
const styles = {
|
|
10
|
+
insert(css) {
|
|
11
|
+
if (typeof document === 'undefined') return () => {};
|
|
12
|
+
const id = 'html-workbench-dsh-plugin-styles';
|
|
13
|
+
if (document.getElementById(id)) return () => {};
|
|
14
|
+
const el = document.createElement('style');
|
|
15
|
+
el.id = id;
|
|
16
|
+
el.textContent = css;
|
|
17
|
+
document.head.appendChild(el);
|
|
18
|
+
return () => { if (el.parentNode) el.parentNode.removeChild(el); };
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
const host = {
|
|
22
|
+
call(method, args) {
|
|
23
|
+
const a = args || {};
|
|
24
|
+
let url = '/html-workbench/' + method;
|
|
25
|
+
if (method === 'open' && a.file) url += '?file=' + encodeURIComponent(a.file);
|
|
26
|
+
return fetch(url).then((r) => r.json());
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const plugin = (() => {
|
|
31
|
+
/**
|
|
32
|
+
* HTML Workbench DSH plugin — Client half (function body).
|
|
33
|
+
*
|
|
34
|
+
* This file is the plain-JavaScript function body consumed by DeepSeek
|
|
35
|
+
* Harness's dynamic Cordis plugin loader: pass this exact text as `code.client`
|
|
36
|
+
* to `cordis_define`. The static client bundle wraps this same body in
|
|
37
|
+
* `window.__ModuleLoader__.load(...)` at build time.
|
|
38
|
+
*
|
|
39
|
+
* Responsibilities (runs in the browser):
|
|
40
|
+
* - Register a right-side panel + corner trigger in `shell.overlay`.
|
|
41
|
+
* - Preview takes the full panel height; the HTML-asset picker is a compact,
|
|
42
|
+
* collapsible dock at the bottom (a <select> switcher + expandable list).
|
|
43
|
+
* - Open a file by calling Host `open`, then embed the workbench URL in an
|
|
44
|
+
* iframe. Left edge is draggable to resize the panel width.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
inject: ['timer'],
|
|
49
|
+
apply(ctx) {
|
|
50
|
+
const slots = ctx.get('slots')
|
|
51
|
+
if (slots === undefined) return
|
|
52
|
+
|
|
53
|
+
styles.insert(`
|
|
54
|
+
html #root { margin-right: var(--hwb-panel-width, 0px); transition: margin-right var(--ds-transition-duration-slow, 200ms) ease; }
|
|
55
|
+
body[data-hwb-dragging] #root { transition: none; }
|
|
56
|
+
body[data-hwb-dragging] { user-select: none; }
|
|
57
|
+
.hwb-panel, .hwb-panel * { box-sizing: border-box; }
|
|
58
|
+
.hwb-panel { position: fixed; top: 0; right: 0; bottom: 0; width: 820px; max-width: calc(100vw - 24px); display: flex; flex-direction: column; background: var(--dsw-alias-bg-base); color: var(--dsw-alias-label-primary); border-left: 1px solid var(--dsw-alias-border-l1); box-shadow: var(--dsw-shadow-lv2); z-index: 9999; font-family: var(--dsw-font-family, system-ui, sans-serif); font-size: 13px; line-height: 1.5; pointer-events: auto; }
|
|
59
|
+
.hwb-resize { position: absolute; left: -4px; top: 0; bottom: 0; width: 9px; cursor: col-resize; z-index: 3; touch-action: none; }
|
|
60
|
+
.hwb-resize::after { content: ""; position: absolute; left: 4px; top: 0; bottom: 0; width: 2px; background: transparent; transition: background .15s; }
|
|
61
|
+
.hwb-resize:hover::after, .hwb-resize:active::after { background: var(--dsw-alias-interactive-bg-hover-accent); }
|
|
62
|
+
.hwb-head { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-bottom: 1px solid var(--dsw-alias-border-l2); background: var(--dsw-alias-bg-layer-1); flex: none; }
|
|
63
|
+
.hwb-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--dsw-alias-state-error-primary); flex: none; }
|
|
64
|
+
.hwb-dot.on { background: var(--dsw-alias-state-success-primary); }
|
|
65
|
+
.hwb-titlewrap { min-width: 0; display: flex; flex-direction: column; }
|
|
66
|
+
.hwb-title { font-weight: 600; font-size: 13px; line-height: 1.2; white-space: nowrap; }
|
|
67
|
+
.hwb-sub { font-size: 11px; color: var(--dsw-alias-label-tertiary); line-height: 1.2; }
|
|
68
|
+
.hwb-spacer { flex: 1; }
|
|
69
|
+
.hwb-close { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border: none; background: transparent; color: var(--dsw-alias-label-secondary); cursor: pointer; border-radius: 6px; flex: none; }
|
|
70
|
+
.hwb-close:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
|
|
71
|
+
.hwb-preview { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; background: #fff; }
|
|
72
|
+
.hwb-frame { flex: 1; border: 0; width: 100%; }
|
|
73
|
+
.hwb-hint { flex: 1; display: flex; align-items: center; justify-content: center; color: var(--dsw-alias-label-tertiary); text-align: center; padding: 16px; }
|
|
74
|
+
.hwb-error { padding: 10px 14px; color: var(--dsw-alias-state-error-primary); font-size: 12px; }
|
|
75
|
+
.hwb-dock { flex: none; border-top: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-1); display: flex; flex-direction: column; }
|
|
76
|
+
.hwb-dockbar { display: flex; align-items: center; gap: 8px; padding: 6px 10px; }
|
|
77
|
+
.hwb-select { flex: 1; min-width: 0; height: 28px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 6px; background: var(--dsw-alias-bg-base); color: var(--dsw-alias-label-primary); font: inherit; font-size: 12px; padding: 0 8px; cursor: pointer; }
|
|
78
|
+
.hwb-count { font-size: 11px; color: var(--dsw-alias-label-tertiary); flex: none; white-space: nowrap; }
|
|
79
|
+
.hwb-docktoggle { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border: none; background: transparent; color: var(--dsw-alias-label-secondary); cursor: pointer; border-radius: 6px; flex: none; }
|
|
80
|
+
.hwb-docktoggle:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
|
|
81
|
+
.hwb-docklist { flex: none; max-height: 38vh; overflow-y: auto; border-top: 1px solid var(--dsw-alias-border-l2); }
|
|
82
|
+
.hwb-item { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1px solid var(--dsw-alias-border-l2); cursor: pointer; }
|
|
83
|
+
.hwb-item:hover { background: var(--dsw-alias-interactive-bg-hover); }
|
|
84
|
+
.hwb-item.active { background: var(--dsw-alias-interactive-bg-active); }
|
|
85
|
+
.hwb-item-main { flex: 1; min-width: 0; }
|
|
86
|
+
.hwb-item-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
|
|
87
|
+
.hwb-item-path { font-size: 11px; color: var(--dsw-alias-label-tertiary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
|
|
88
|
+
.hwb-item-btn { border: 1px solid var(--dsw-alias-border-l2); background: transparent; color: var(--dsw-alias-label-secondary); border-radius: 6px; padding: 2px 9px; cursor: pointer; font-size: 12px; flex: none; }
|
|
89
|
+
.hwb-item-btn:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }
|
|
90
|
+
.hwb-dockempty { padding: 14px; color: var(--dsw-alias-label-tertiary); text-align: center; font-size: 12px; }
|
|
91
|
+
.hwb-trigger { position: fixed; top: 10px; right: 12px; z-index: 10000; width: 36px; height: 36px; border: 1px solid var(--dsw-alias-border-l2); background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-secondary); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; border-radius: 8px; pointer-events: auto; }
|
|
92
|
+
.hwb-trigger:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
|
|
93
|
+
`)
|
|
94
|
+
|
|
95
|
+
const DEFAULT_WIDTH = 820
|
|
96
|
+
const MIN_WIDTH = 480
|
|
97
|
+
|
|
98
|
+
const store = {
|
|
99
|
+
open: false,
|
|
100
|
+
panelWidth: DEFAULT_WIDTH,
|
|
101
|
+
expanded: false,
|
|
102
|
+
assets: [],
|
|
103
|
+
running: false,
|
|
104
|
+
current: null,
|
|
105
|
+
loading: false,
|
|
106
|
+
error: null,
|
|
107
|
+
listeners: [],
|
|
108
|
+
subscribe(fn) { this.listeners.push(fn); return () => { this.listeners = this.listeners.filter((f) => f !== fn) } },
|
|
109
|
+
set(p) { Object.assign(this, p); this.listeners.forEach((fn) => { try { fn() } catch (e) {} }) },
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const useStore = () => {
|
|
113
|
+
const [, force] = React.useState(0)
|
|
114
|
+
React.useEffect(() => store.subscribe(() => force((x) => x + 1)), [])
|
|
115
|
+
return store
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const basename = (p) => {
|
|
119
|
+
const parts = String(p).split('/')
|
|
120
|
+
return parts[parts.length - 1] || p
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const refresh = () => {
|
|
124
|
+
host.call('list').then((res) => {
|
|
125
|
+
if (res && res.ok) store.set({ assets: res.assets || [], running: !!res.running })
|
|
126
|
+
else store.set({ error: (res && res.error) || 'list failed' })
|
|
127
|
+
}).catch((e) => store.set({ error: String(e && e.message ? e.message : e) }))
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const openFile = (path) => {
|
|
131
|
+
store.set({ loading: true, current: { path: path, url: null }, error: null })
|
|
132
|
+
host.call('open', { file: path }).then((res) => {
|
|
133
|
+
if (res && res.ok) store.set({ loading: false, current: { path: path, url: res.url }, running: true })
|
|
134
|
+
else store.set({ loading: false, error: (res && res.error) || 'open failed' })
|
|
135
|
+
}).catch((e) => store.set({ loading: false, error: String(e && e.message ? e.message : e) }))
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const startResize = (e) => {
|
|
139
|
+
e.preventDefault()
|
|
140
|
+
document.body.setAttribute('data-hwb-dragging', '')
|
|
141
|
+
const maxW = Math.max(MIN_WIDTH, window.innerWidth - 60)
|
|
142
|
+
const onMove = (ev) => {
|
|
143
|
+
const w = window.innerWidth - ev.clientX
|
|
144
|
+
store.set({ panelWidth: Math.max(MIN_WIDTH, Math.min(w, maxW)) })
|
|
145
|
+
}
|
|
146
|
+
const onUp = () => {
|
|
147
|
+
document.body.removeAttribute('data-hwb-dragging')
|
|
148
|
+
document.removeEventListener('mousemove', onMove)
|
|
149
|
+
document.removeEventListener('mouseup', onUp)
|
|
150
|
+
}
|
|
151
|
+
document.addEventListener('mousemove', onMove)
|
|
152
|
+
document.addEventListener('mouseup', onUp)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const chevronUp = React.createElement('svg', { width: 14, height: 14, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },
|
|
156
|
+
React.createElement('path', { d: 'M18 15l-6-6-6 6' }))
|
|
157
|
+
const chevronDown = React.createElement('svg', { width: 14, height: 14, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },
|
|
158
|
+
React.createElement('path', { d: 'M6 9l6 6 6-6' }))
|
|
159
|
+
|
|
160
|
+
const Panel = () => {
|
|
161
|
+
const s = useStore()
|
|
162
|
+
React.useEffect(() => {
|
|
163
|
+
const root = document.documentElement
|
|
164
|
+
root.style.setProperty('--hwb-panel-width', s.open ? (s.panelWidth + 'px') : '0px')
|
|
165
|
+
return () => { root.style.setProperty('--hwb-panel-width', '0px') }
|
|
166
|
+
}, [s.open, s.panelWidth])
|
|
167
|
+
React.useEffect(() => {
|
|
168
|
+
if (!s.open) return
|
|
169
|
+
refresh()
|
|
170
|
+
const dispose = ctx.interval(refresh, 3000)
|
|
171
|
+
return () => { if (dispose) dispose() }
|
|
172
|
+
}, [s.open])
|
|
173
|
+
if (!s.open) return null
|
|
174
|
+
|
|
175
|
+
const assets = s.assets || []
|
|
176
|
+
const currentPath = s.current && s.current.path ? s.current.path : ''
|
|
177
|
+
const opts = assets.map((a) => React.createElement('option', { key: a.id || a.path, value: a.path }, basename(a.path)))
|
|
178
|
+
const items = assets.map((a) => {
|
|
179
|
+
const active = currentPath === a.path
|
|
180
|
+
return React.createElement('div', { key: a.id || a.path, className: 'hwb-item' + (active ? ' active' : '') },
|
|
181
|
+
React.createElement('div', { className: 'hwb-item-main' },
|
|
182
|
+
React.createElement('div', { className: 'hwb-item-name' }, basename(a.path)),
|
|
183
|
+
React.createElement('div', { className: 'hwb-item-path', title: a.path }, a.path),
|
|
184
|
+
),
|
|
185
|
+
React.createElement('button', { type: 'button', className: 'hwb-item-btn', onClick: () => openFile(a.path) }, '打开'),
|
|
186
|
+
)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
return React.createElement('div', { className: 'hwb-panel', style: { width: s.panelWidth + 'px' }, role: 'dialog', 'aria-label': 'HTML Workbench' },
|
|
190
|
+
React.createElement('div', { className: 'hwb-resize', title: '拖动调整宽度', onMouseDown: startResize }),
|
|
191
|
+
React.createElement('div', { className: 'hwb-head' },
|
|
192
|
+
React.createElement('span', { className: 'hwb-dot' + (s.running ? ' on' : '') }),
|
|
193
|
+
React.createElement('div', { className: 'hwb-titlewrap' },
|
|
194
|
+
React.createElement('div', { className: 'hwb-title' }, 'HTML Workbench'),
|
|
195
|
+
React.createElement('div', { className: 'hwb-sub' }, s.running ? '服务运行中 · 拖动左边缘调整宽度' : '服务未就绪'),
|
|
196
|
+
),
|
|
197
|
+
React.createElement('span', { className: 'hwb-spacer' }),
|
|
198
|
+
React.createElement('button', { type: 'button', className: 'hwb-close', title: '关闭', onClick: () => store.set({ open: false }) },
|
|
199
|
+
React.createElement('svg', { width: 14, height: 14, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round' },
|
|
200
|
+
React.createElement('path', { d: 'M18 6L6 18' }),
|
|
201
|
+
React.createElement('path', { d: 'M6 6l12 12' }),
|
|
202
|
+
),
|
|
203
|
+
),
|
|
204
|
+
),
|
|
205
|
+
React.createElement('div', { className: 'hwb-preview' },
|
|
206
|
+
s.loading ? React.createElement('div', { className: 'hwb-hint' }, '正在启动 Workbench…')
|
|
207
|
+
: s.current && s.current.url ? React.createElement('iframe', { className: 'hwb-frame', src: s.current.url, title: 'HTML Workbench' })
|
|
208
|
+
: React.createElement('div', { className: 'hwb-hint' }, '从下方选择一个 HTML 文件开始编辑与预览'),
|
|
209
|
+
),
|
|
210
|
+
React.createElement('div', { className: 'hwb-dock' },
|
|
211
|
+
React.createElement('div', { className: 'hwb-dockbar' },
|
|
212
|
+
React.createElement('select', {
|
|
213
|
+
className: 'hwb-select',
|
|
214
|
+
value: currentPath,
|
|
215
|
+
onChange: (e) => { const v = e.target.value; if (v) openFile(v) },
|
|
216
|
+
},
|
|
217
|
+
React.createElement('option', { value: '', disabled: true }, assets.length ? '选择 HTML 文件…' : '暂无 HTML 产物'),
|
|
218
|
+
opts,
|
|
219
|
+
),
|
|
220
|
+
React.createElement('span', { className: 'hwb-count' }, assets.length + ' 个'),
|
|
221
|
+
React.createElement('button', { type: 'button', className: 'hwb-docktoggle', title: s.expanded ? '收起列表' : '展开列表', onClick: () => store.set({ expanded: !s.expanded }) },
|
|
222
|
+
s.expanded ? chevronDown : chevronUp,
|
|
223
|
+
),
|
|
224
|
+
),
|
|
225
|
+
s.expanded ? React.createElement('div', { className: 'hwb-docklist' },
|
|
226
|
+
items.length ? items : React.createElement('div', { className: 'hwb-dockempty' }, '暂无 HTML 产物 — 代理 write/edit 的 .html 文件会出现在这里。'),
|
|
227
|
+
s.error ? React.createElement('div', { className: 'hwb-error' }, s.error) : null,
|
|
228
|
+
) : null,
|
|
229
|
+
),
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const Trigger = () => {
|
|
234
|
+
const s = useStore()
|
|
235
|
+
if (s.open) return null
|
|
236
|
+
return React.createElement('button', {
|
|
237
|
+
type: 'button',
|
|
238
|
+
className: 'hwb-trigger',
|
|
239
|
+
title: 'HTML Workbench',
|
|
240
|
+
'aria-expanded': s.open,
|
|
241
|
+
onClick: () => { store.set({ open: true }); refresh() },
|
|
242
|
+
}, React.createElement('svg', { width: 18, height: 18, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },
|
|
243
|
+
React.createElement('rect', { x: 2, y: 4, width: 20, height: 14, rx: 2 }),
|
|
244
|
+
React.createElement('path', { d: 'M8 21h8' }),
|
|
245
|
+
React.createElement('path', { d: 'M12 18v3' }),
|
|
246
|
+
))
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
slots.inject('shell.overlay', () => slots.register(
|
|
250
|
+
{ name: 'shell.overlay', id: 'html-workbench-panel', order: 60, label: 'HTML Workbench' },
|
|
251
|
+
() => React.createElement(Panel),
|
|
252
|
+
))
|
|
253
|
+
|
|
254
|
+
slots.inject('shell.overlay', () => slots.register(
|
|
255
|
+
{ name: 'shell.overlay', id: 'html-workbench-trigger', order: 59, label: 'HTML Workbench 入口' },
|
|
256
|
+
() => React.createElement(Trigger),
|
|
257
|
+
))
|
|
258
|
+
},
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
})();
|
|
262
|
+
|
|
263
|
+
exports.inject = plugin.inject ?? [];
|
|
264
|
+
exports.apply = plugin.apply;
|
|
265
|
+
return module.exports;
|
|
266
|
+
},
|
|
267
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vibe-x/dsh-html-workbench",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "DeepSeek Harness 插件:在右侧栏预览并可视化编辑 agent 生成的 HTML(复用/拉起本地 html-workbench 服务)。",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "src/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/index.js",
|
|
10
|
+
"./client": "./lib/client.js",
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"dsh": {
|
|
14
|
+
"bundle": {
|
|
15
|
+
"patch": "./cordis.patch.yml"
|
|
16
|
+
},
|
|
17
|
+
"client": {
|
|
18
|
+
"platform": "web",
|
|
19
|
+
"inject": [
|
|
20
|
+
"@deepseek-ai/dsh-client-runtime"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"src",
|
|
26
|
+
"lib/client.js",
|
|
27
|
+
"scripts/workbench.py",
|
|
28
|
+
"assets/workbench.html",
|
|
29
|
+
"cordis.patch.yml",
|
|
30
|
+
"dsh.plugin.json"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "node build.mjs",
|
|
34
|
+
"prepublishOnly": "node build.mjs"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"dsh",
|
|
38
|
+
"deepseek-harness",
|
|
39
|
+
"plugin",
|
|
40
|
+
"cordis",
|
|
41
|
+
"html",
|
|
42
|
+
"editor",
|
|
43
|
+
"workbench"
|
|
44
|
+
],
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=20"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
}
|
|
51
|
+
}
|