deepseek-coordination 0.1.2 → 0.1.6
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 +263 -14
- package/README.zh-CN.md +158 -9
- package/client.js +338 -0
- package/cordis.patch.yml +40 -0
- package/package.json +20 -2
- package/src/plugin/settings.js +340 -0
- package/src/plugin/takeover.js +102 -62
- package/src/plugin/workflow.js +1475 -0
- package/tools/measure-cache.mjs +223 -0
package/client.js
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-os settings panel: the client half of the takeover switch.
|
|
3
|
+
*
|
|
4
|
+
* HOW THIS FILE IS LOADED, AND WHY IT LOOKS LIKE THIS
|
|
5
|
+
*
|
|
6
|
+
* The browser half of a DSH plugin is not an ordinary ES module. It is a self-registering lazy factory
|
|
7
|
+
* keyed by the package name, handed to the page's module table:
|
|
8
|
+
*
|
|
9
|
+
* window.__ModuleLoader__.load({ id: '<package name>', factory(require) { ... } })
|
|
10
|
+
*
|
|
11
|
+
* React is NOT imported and NOT bundled: it is taken from the module table with `require('react')`, and
|
|
12
|
+
* shipping a second copy would give the page two Reacts and break hooks. `<package.json>` therefore
|
|
13
|
+
* declares `dsh.client` and exposes this file as the `./client` subpath, which is what the loader reads.
|
|
14
|
+
*
|
|
15
|
+
* The file is plain ES5-flavoured JavaScript — no JSX, no TypeScript — deliberately. JSX would require a
|
|
16
|
+
* build step whose output format has to match the table's contract exactly, and a hand-written file in
|
|
17
|
+
* the documented format is verifiable by reading it. `../src/client/` remains the untouched Vite
|
|
18
|
+
* scaffold it always was; this is the artifact that ships.
|
|
19
|
+
*
|
|
20
|
+
* WHAT THE PANEL DOES
|
|
21
|
+
*
|
|
22
|
+
* It edits three things and shows one:
|
|
23
|
+
*
|
|
24
|
+
* * the master switch, which is the only control that turns interception on;
|
|
25
|
+
* * a per-tool switch for each of read / write / edit / pwsh;
|
|
26
|
+
* * the write convention (preserve the target's BOM and line endings, or reproduce the built-in
|
|
27
|
+
* UTF-8-without-BOM, LF behaviour);
|
|
28
|
+
* * an activity feed of what was actually taken over, which is the only view that can distinguish
|
|
29
|
+
* "configured" from "working".
|
|
30
|
+
*
|
|
31
|
+
* State lives on the host (see `settings.js`), reached over the same-origin endpoints
|
|
32
|
+
* `/api/dsh-os/settings` and `/api/dsh-os/set`. The panel is a pure view over that state, so a page
|
|
33
|
+
* reload costs nothing and two open windows cannot disagree.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
window.__ModuleLoader__.load({
|
|
37
|
+
id: 'deepseek-coordination',
|
|
38
|
+
factory(require) {
|
|
39
|
+
const React = require('react')
|
|
40
|
+
const h = React.createElement
|
|
41
|
+
|
|
42
|
+
/** Endpoints registered by the host half. Relative, so no port or origin is baked in. */
|
|
43
|
+
const STATE_URL = '/api/dsh-os/settings'
|
|
44
|
+
const SET_URL = '/api/dsh-os/set'
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Theme tokens only.
|
|
48
|
+
*
|
|
49
|
+
* Literal colours would be wrong in one of the two themes, and reaching into another plugin's
|
|
50
|
+
* stylesheet is forbidden by the UI rules. A renamed token degrades the appearance; it never
|
|
51
|
+
* breaks rendering, which is the property being bought here.
|
|
52
|
+
*/
|
|
53
|
+
const T = {
|
|
54
|
+
label: 'var(--dsw-alias-label-primary)',
|
|
55
|
+
labelSecondary: 'var(--dsw-alias-label-secondary)',
|
|
56
|
+
labelTertiary: 'var(--dsw-alias-label-tertiary)',
|
|
57
|
+
border: '1px solid var(--dsw-alias-border-l)',
|
|
58
|
+
brand: 'var(--dsw-alias-brand-primary)',
|
|
59
|
+
error: 'var(--dsw-alias-state-error-primary)',
|
|
60
|
+
warn: 'var(--dsw-alias-state-warn-primary)',
|
|
61
|
+
hover: 'var(--dsw-alias-interactive-bg-hover)',
|
|
62
|
+
module: 'var(--dsw-alias-bg-module-platform)',
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** One labelled row with a switch on the right, matching the host's settings row rhythm. */
|
|
66
|
+
function Switch(props) {
|
|
67
|
+
const { checked, disabled, onChange, label, hint } = props
|
|
68
|
+
return h('div', {
|
|
69
|
+
style: {
|
|
70
|
+
display: 'flex',
|
|
71
|
+
alignItems: 'flex-start',
|
|
72
|
+
gap: '12px',
|
|
73
|
+
padding: '10px 0',
|
|
74
|
+
borderBottom: T.border,
|
|
75
|
+
},
|
|
76
|
+
}, [
|
|
77
|
+
h('div', { key: 'text', style: { flex: '1 1 auto', minWidth: 0 } }, [
|
|
78
|
+
h('div', { key: 'label', style: { color: T.label, fontSize: '13px', lineHeight: '20px' } }, label),
|
|
79
|
+
hint === undefined ? null : h('div', {
|
|
80
|
+
key: 'hint',
|
|
81
|
+
style: { color: T.labelTertiary, fontSize: '12px', lineHeight: '18px', marginTop: '2px' },
|
|
82
|
+
}, hint),
|
|
83
|
+
]),
|
|
84
|
+
h('button', {
|
|
85
|
+
key: 'switch',
|
|
86
|
+
type: 'button',
|
|
87
|
+
role: 'switch',
|
|
88
|
+
'aria-checked': checked === true,
|
|
89
|
+
'aria-label': label,
|
|
90
|
+
disabled: disabled === true,
|
|
91
|
+
onClick: () => { if (disabled !== true) onChange(!checked) },
|
|
92
|
+
style: {
|
|
93
|
+
flex: '0 0 auto',
|
|
94
|
+
width: '38px',
|
|
95
|
+
height: '22px',
|
|
96
|
+
borderRadius: '11px',
|
|
97
|
+
border: 'none',
|
|
98
|
+
padding: '0',
|
|
99
|
+
cursor: disabled === true ? 'not-allowed' : 'pointer',
|
|
100
|
+
opacity: disabled === true ? 0.5 : 1,
|
|
101
|
+
background: checked === true ? T.brand : T.hover,
|
|
102
|
+
position: 'relative',
|
|
103
|
+
transition: 'background 120ms linear',
|
|
104
|
+
},
|
|
105
|
+
}, h('span', {
|
|
106
|
+
style: {
|
|
107
|
+
position: 'absolute',
|
|
108
|
+
top: '2px',
|
|
109
|
+
left: checked === true ? '18px' : '2px',
|
|
110
|
+
width: '18px',
|
|
111
|
+
height: '18px',
|
|
112
|
+
borderRadius: '50%',
|
|
113
|
+
background: '#fff',
|
|
114
|
+
transition: 'left 120ms ease',
|
|
115
|
+
},
|
|
116
|
+
})),
|
|
117
|
+
])
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** A small segmented control, used for the write convention. */
|
|
121
|
+
function Segmented(props) {
|
|
122
|
+
const { value, options, disabled, onChange } = props
|
|
123
|
+
return h('div', {
|
|
124
|
+
style: { display: 'inline-flex', border: T.border, borderRadius: '6px', overflow: 'hidden' },
|
|
125
|
+
}, options.map((option, index) => h('button', {
|
|
126
|
+
key: option.value,
|
|
127
|
+
type: 'button',
|
|
128
|
+
disabled: disabled === true,
|
|
129
|
+
'aria-pressed': value === option.value,
|
|
130
|
+
onClick: () => { if (disabled !== true) onChange(option.value) },
|
|
131
|
+
style: {
|
|
132
|
+
padding: '4px 10px',
|
|
133
|
+
fontSize: '12px',
|
|
134
|
+
lineHeight: '18px',
|
|
135
|
+
border: 'none',
|
|
136
|
+
borderLeft: index === 0 ? 'none' : T.border,
|
|
137
|
+
cursor: disabled === true ? 'not-allowed' : 'pointer',
|
|
138
|
+
background: value === option.value ? T.brand : 'transparent',
|
|
139
|
+
color: value === option.value ? '#fff' : T.labelSecondary,
|
|
140
|
+
},
|
|
141
|
+
}, option.label)))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Render one activity entry: what was taken over, or why a call fell back. */
|
|
145
|
+
function ActivityRow(props) {
|
|
146
|
+
const entry = props.entry
|
|
147
|
+
const took = entry.outcome === 'taken-over'
|
|
148
|
+
return h('li', {
|
|
149
|
+
style: {
|
|
150
|
+
display: 'flex',
|
|
151
|
+
gap: '8px',
|
|
152
|
+
alignItems: 'baseline',
|
|
153
|
+
padding: '4px 0',
|
|
154
|
+
fontSize: '12px',
|
|
155
|
+
lineHeight: '18px',
|
|
156
|
+
},
|
|
157
|
+
}, [
|
|
158
|
+
h('span', {
|
|
159
|
+
key: 'tool',
|
|
160
|
+
style: {
|
|
161
|
+
flex: '0 0 auto',
|
|
162
|
+
color: took ? T.brand : T.warn,
|
|
163
|
+
fontVariantNumeric: 'tabular-nums',
|
|
164
|
+
},
|
|
165
|
+
}, entry.tool),
|
|
166
|
+
h('span', {
|
|
167
|
+
key: 'subject',
|
|
168
|
+
title: entry.subject,
|
|
169
|
+
style: {
|
|
170
|
+
flex: '1 1 auto',
|
|
171
|
+
minWidth: 0,
|
|
172
|
+
overflow: 'hidden',
|
|
173
|
+
textOverflow: 'ellipsis',
|
|
174
|
+
whiteSpace: 'nowrap',
|
|
175
|
+
color: T.labelSecondary,
|
|
176
|
+
direction: 'rtl',
|
|
177
|
+
textAlign: 'left',
|
|
178
|
+
},
|
|
179
|
+
}, entry.subject === '' ? '—' : entry.subject),
|
|
180
|
+
h('span', {
|
|
181
|
+
key: 'outcome',
|
|
182
|
+
style: { flex: '0 0 auto', color: took ? T.labelTertiary : T.error },
|
|
183
|
+
}, took ? '接管' : '回退'),
|
|
184
|
+
])
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* The panel.
|
|
189
|
+
*
|
|
190
|
+
* It owns exactly one piece of state (the last document fetched) plus a pending flag. Every control
|
|
191
|
+
* posts and then adopts the response, so the host is always the source of truth and an optimistic
|
|
192
|
+
* local edit cannot drift from what the interception actually reads.
|
|
193
|
+
*/
|
|
194
|
+
function SettingsSection() {
|
|
195
|
+
const [state, setState] = React.useState(null)
|
|
196
|
+
const [error, setError] = React.useState(null)
|
|
197
|
+
const [busy, setBusy] = React.useState(false)
|
|
198
|
+
|
|
199
|
+
const load = React.useCallback(() => {
|
|
200
|
+
fetch(STATE_URL, { headers: { accept: 'application/json' } })
|
|
201
|
+
.then((response) => response.json())
|
|
202
|
+
.then((body) => {
|
|
203
|
+
if (body && body.ok) { setState(body); setError(null) } else { setError((body && body.error) || '读取失败') }
|
|
204
|
+
})
|
|
205
|
+
.catch((cause) => setError(String(cause && cause.message ? cause.message : cause)))
|
|
206
|
+
}, [])
|
|
207
|
+
|
|
208
|
+
React.useEffect(() => {
|
|
209
|
+
load()
|
|
210
|
+
// The feed is the point of the panel, so it refreshes on a slow interval while it is open. Ten
|
|
211
|
+
// seconds is chosen to be far below the cost of noticing: a five-minute poll would leave a user
|
|
212
|
+
// convinced that nothing is happening.
|
|
213
|
+
const timer = setInterval(load, 10000)
|
|
214
|
+
return () => clearInterval(timer)
|
|
215
|
+
}, [load])
|
|
216
|
+
|
|
217
|
+
const save = React.useCallback((patch) => {
|
|
218
|
+
setBusy(true)
|
|
219
|
+
fetch(SET_URL, {
|
|
220
|
+
method: 'POST',
|
|
221
|
+
headers: { 'content-type': 'application/json' },
|
|
222
|
+
body: JSON.stringify(patch),
|
|
223
|
+
})
|
|
224
|
+
.then((response) => response.json())
|
|
225
|
+
.then((body) => {
|
|
226
|
+
if (body && body.ok) { setState(body); setError(null) } else { setError((body && body.error) || '保存失败') }
|
|
227
|
+
})
|
|
228
|
+
.catch((cause) => setError(String(cause && cause.message ? cause.message : cause)))
|
|
229
|
+
.then(() => setBusy(false))
|
|
230
|
+
}, [])
|
|
231
|
+
|
|
232
|
+
if (state === null) {
|
|
233
|
+
return h('div', { style: { padding: '12px 0', color: error === null ? T.labelTertiary : T.error } },
|
|
234
|
+
error === null ? '正在读取 dsh-os 设置…' : `无法读取 dsh-os 设置:${error}`)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const settings = state.settings
|
|
238
|
+
const tools = settings.tools || {}
|
|
239
|
+
const activity = settings.activity || []
|
|
240
|
+
const master = settings.enabled === true
|
|
241
|
+
const names = {
|
|
242
|
+
read: '读取文件(read)',
|
|
243
|
+
write: '写入文件(write)',
|
|
244
|
+
edit: '编辑文件(edit)',
|
|
245
|
+
pwsh: '执行命令(pwsh)',
|
|
246
|
+
}
|
|
247
|
+
const hints = {
|
|
248
|
+
read: '经 dsh-os 读取:显式探测编码,剥掉 BOM 后再按行返回。',
|
|
249
|
+
write: '经 dsh-os 写入:保留目标文件原有的 BOM 与换行;新建文件仍按内置约定(UTF-8 无 BOM、LF)。',
|
|
250
|
+
edit: '经 dsh-os 编辑:字面替换语义与内置一致,写回时沿用目标文件的编码约定。',
|
|
251
|
+
pwsh: '经 dsh-os 执行:argv 数组直接传参,不拼 shell 字符串。后台任务与沙箱升级仍走内置实现。',
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return h('div', { style: { padding: '4px 0' } }, [
|
|
255
|
+
h('div', { key: 'head', style: { color: T.labelSecondary, fontSize: '12px', lineHeight: '18px', marginBottom: '4px' } },
|
|
256
|
+
'让 dsh-os 接管内置的文件与命令操作,从而获得字节级编码、BOM 与换行保证。关闭时全部走内置实现。'),
|
|
257
|
+
|
|
258
|
+
h(Switch, {
|
|
259
|
+
key: 'master',
|
|
260
|
+
checked: master,
|
|
261
|
+
disabled: busy,
|
|
262
|
+
label: '接管内置操作',
|
|
263
|
+
hint: '总开关。关闭后下面的逐项设置保留,但不再生效。',
|
|
264
|
+
onChange: (next) => save({ enabled: next }),
|
|
265
|
+
}),
|
|
266
|
+
|
|
267
|
+
h('div', { key: 'tools', style: { opacity: master ? 1 : 0.55 } },
|
|
268
|
+
['read', 'write', 'edit', 'pwsh'].map((tool) => h(Switch, {
|
|
269
|
+
key: tool,
|
|
270
|
+
checked: tools[tool] === true,
|
|
271
|
+
disabled: busy || !master,
|
|
272
|
+
label: names[tool],
|
|
273
|
+
hint: hints[tool],
|
|
274
|
+
onChange: (next) => save({ tools: { [tool]: next } }),
|
|
275
|
+
}))),
|
|
276
|
+
|
|
277
|
+
h('div', { key: 'format', style: { display: 'flex', alignItems: 'center', gap: '12px', padding: '12px 0', borderBottom: T.border, opacity: master ? 1 : 0.55 } }, [
|
|
278
|
+
h('div', { key: 'label', style: { flex: '1 1 auto' } }, [
|
|
279
|
+
h('div', { key: 't', style: { color: T.label, fontSize: '13px', lineHeight: '20px' } }, '写入编码约定'),
|
|
280
|
+
h('div', { key: 'h', style: { color: T.labelTertiary, fontSize: '12px', lineHeight: '18px', marginTop: '2px' } },
|
|
281
|
+
'新建文件一律 UTF-8 无 BOM、LF;此项决定改写已有文件时是否沿用其原有约定。'),
|
|
282
|
+
]),
|
|
283
|
+
h(Segmented, {
|
|
284
|
+
key: 'control',
|
|
285
|
+
value: settings.writeFormat === 'builtin' ? 'builtin' : 'target',
|
|
286
|
+
disabled: busy || !master,
|
|
287
|
+
options: [
|
|
288
|
+
{ value: 'target', label: '沿用原文件' },
|
|
289
|
+
{ value: 'builtin', label: '同内置' },
|
|
290
|
+
],
|
|
291
|
+
onChange: (next) => save({ writeFormat: next }),
|
|
292
|
+
}),
|
|
293
|
+
]),
|
|
294
|
+
|
|
295
|
+
h('div', { key: 'activity', style: { paddingTop: '12px' } }, [
|
|
296
|
+
h('div', { key: 'title', style: { display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' } }, [
|
|
297
|
+
h('span', { key: 't', style: { color: T.label, fontSize: '13px' } }, '最近活动'),
|
|
298
|
+
h('span', { key: 'c', style: { color: T.labelTertiary, fontSize: '12px' } },
|
|
299
|
+
`${activity.length} 条 · ${master ? '接管中' : '未启用'}`),
|
|
300
|
+
]),
|
|
301
|
+
activity.length === 0
|
|
302
|
+
? h('div', { key: 'empty', style: { color: T.labelTertiary, fontSize: '12px', padding: '8px 0' } },
|
|
303
|
+
master ? '尚未拦截到调用。' : '启用后这里会显示实际被接管的调用。')
|
|
304
|
+
: h('ul', {
|
|
305
|
+
key: 'list',
|
|
306
|
+
style: { listStyle: 'none', margin: '4px 0 0', padding: '0', maxHeight: '220px', overflowY: 'auto' },
|
|
307
|
+
}, activity.slice().reverse().map((entry, index) => h(ActivityRow, { key: `${entry.at}-${index}`, entry }))),
|
|
308
|
+
]),
|
|
309
|
+
|
|
310
|
+
error === null ? null : h('div', {
|
|
311
|
+
key: 'error',
|
|
312
|
+
style: { marginTop: '10px', color: T.error, fontSize: '12px', lineHeight: '18px' },
|
|
313
|
+
}, error),
|
|
314
|
+
|
|
315
|
+
h('div', {
|
|
316
|
+
key: 'path',
|
|
317
|
+
style: { marginTop: '10px', color: T.labelTertiary, fontSize: '11px', lineHeight: '16px', wordBreak: 'break-all' },
|
|
318
|
+
}, `设置文件:${settings.path}`),
|
|
319
|
+
])
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
// `slots` is the only service the panel needs: it renders into the settings page and talks to the
|
|
324
|
+
// host over plain HTTP, so it does not need the session, the store, or the connection.
|
|
325
|
+
inject: ['slots'],
|
|
326
|
+
apply(ctx) {
|
|
327
|
+
// `inject` waits for the slot to exist; a settings page that is disabled in this profile leaves
|
|
328
|
+
// the panel simply unregistered rather than throwing during activation.
|
|
329
|
+
ctx.slots.inject('settings.section', () => ctx.slots.register({
|
|
330
|
+
name: 'settings.section',
|
|
331
|
+
id: 'dsh-os',
|
|
332
|
+
order: 140,
|
|
333
|
+
label: () => 'DSH OS',
|
|
334
|
+
}, SettingsSection))
|
|
335
|
+
},
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
})
|
package/cordis.patch.yml
CHANGED
|
@@ -22,6 +22,30 @@
|
|
|
22
22
|
scriptDir: src/host/bin
|
|
23
23
|
# Default timeout for a single verb invocation, in milliseconds.
|
|
24
24
|
timeoutMs: 120000
|
|
25
|
+
# The settings service the panel edits and the takeover reads. It is its own row because BOTH of the
|
|
26
|
+
# other two need it without owning it: the takeover calls `enabled()` on every intercepted tool call,
|
|
27
|
+
# and the routes the browser panel talks to mutate the same object.
|
|
28
|
+
#
|
|
29
|
+
# It comes BEFORE the takeover row on purpose. The takeover declares `inject: ['dsh-os-settings']`,
|
|
30
|
+
# which is a hard dependency: a row that loads first would simply stay inactive until the service
|
|
31
|
+
# appears, and an inactive interception looks exactly like a broken one.
|
|
32
|
+
- id: dsh-os-settings
|
|
33
|
+
name: deepseek-coordination/settings
|
|
34
|
+
config:
|
|
35
|
+
# Defaults to $DSH_HOME/dsh-os/settings.json, or <package root>/.dsh-os-settings.json when
|
|
36
|
+
# DSH_HOME is unset. That order matters: the installed package directory is replaced on upgrade,
|
|
37
|
+
# so a setting stored there would not survive one.
|
|
38
|
+
# settingsPath: /absolute/path/to/settings.json
|
|
39
|
+
# The interception itself. It answers the built-in `read`/`write`/`edit`/`pwsh` calls through
|
|
40
|
+
# dsh-os while the user has the switch on, and calls through to the built-in implementation for
|
|
41
|
+
# everything else — including on any failure, so takeover can never make a call unanswerable.
|
|
42
|
+
- id: dsh-os-takeover
|
|
43
|
+
name: deepseek-coordination/takeover
|
|
44
|
+
config:
|
|
45
|
+
timeoutMs: 120000
|
|
46
|
+
# Must match the `tool-fs` row's `readLimit`: the interception must not apply a different read
|
|
47
|
+
# window than the built-in tool it replaces.
|
|
48
|
+
readLimit: 2000
|
|
25
49
|
# The permission consent delegate: the ONLY writer of permission.json. It is a separate row because
|
|
26
50
|
# the scripts must never write the file - there is no write verb in the verb table - so the row
|
|
27
51
|
# that can write it is deliberately a distinct component with its own tools.
|
|
@@ -32,3 +56,19 @@
|
|
|
32
56
|
# durable decisions outside the installed package.
|
|
33
57
|
# permissionPath: /absolute/path/to/permission.json
|
|
34
58
|
auditLimit: 200
|
|
59
|
+
# The intent-first workflow: start -> clarify -> design -> options -> choose -> execute -> finish.
|
|
60
|
+
# Its own row because it is its own concern: the rows above make an OS call correct, and this one
|
|
61
|
+
# makes a request decided before it is executed. It registers the `workflow_plan` tool and a system-prompt
|
|
62
|
+
# section that is empty until a workflow is started, so a profile that never uses it pays nothing.
|
|
63
|
+
- id: dsh-os-workflow
|
|
64
|
+
name: deepseek-coordination/workflow
|
|
65
|
+
config:
|
|
66
|
+
# Defaults to $DSH_HOME/dsh-os/workflows, or <package root>/.dsh-os-workflows when DSH_HOME is
|
|
67
|
+
# unset. The state and every artifact are per session, under their own subdirectory.
|
|
68
|
+
# workflowRoot: /absolute/path/to/workflows
|
|
69
|
+
# Records each phase boundary in state.json, so a compaction that follows has a record of what it
|
|
70
|
+
# may drop. The structural half - detail to disk, one line back - is not optional and not
|
|
71
|
+
# configurable: it is the reason the feature saves anything.
|
|
72
|
+
autoCompact: true
|
|
73
|
+
# Checkpoint entries kept per session.
|
|
74
|
+
maxCheckpoints: 20
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepseek-coordination",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Cross-platform atomic OS-interaction surface for the DeepSeek Harness: one argument-stable verb set implemented natively on each OS, with byte-exact charset, BOM and line-ending handling.",
|
|
@@ -37,6 +37,10 @@
|
|
|
37
37
|
"main": "src/plugin/index.js",
|
|
38
38
|
"exports": {
|
|
39
39
|
".": "./src/plugin/index.js",
|
|
40
|
+
"./takeover": "./src/plugin/takeover.js",
|
|
41
|
+
"./settings": "./src/plugin/settings.js",
|
|
42
|
+
"./workflow": "./src/plugin/workflow.js",
|
|
43
|
+
"./client": "./client.js",
|
|
40
44
|
"./permission": "./src/permission/index.js",
|
|
41
45
|
"./package.json": "./package.json"
|
|
42
46
|
},
|
|
@@ -50,6 +54,12 @@
|
|
|
50
54
|
"dsh": {
|
|
51
55
|
"bundle": {
|
|
52
56
|
"patch": "./cordis.patch.yml"
|
|
57
|
+
},
|
|
58
|
+
"client": {
|
|
59
|
+
"platform": "web",
|
|
60
|
+
"inject": [
|
|
61
|
+
"@deepseek-ai/dsh-client-ui-settings"
|
|
62
|
+
]
|
|
53
63
|
}
|
|
54
64
|
},
|
|
55
65
|
"files": [
|
|
@@ -64,10 +74,14 @@
|
|
|
64
74
|
"src/host/docs/permission-model.md",
|
|
65
75
|
"src/host/docs/command-original.txt",
|
|
66
76
|
"cordis.patch.yml",
|
|
77
|
+
"client.js",
|
|
67
78
|
"src/plugin/index.js",
|
|
68
79
|
"src/plugin/invoke.js",
|
|
80
|
+
"src/plugin/settings.js",
|
|
69
81
|
"src/plugin/takeover.js",
|
|
82
|
+
"src/plugin/workflow.js",
|
|
70
83
|
"src/permission/index.js",
|
|
84
|
+
"tools/measure-cache.mjs",
|
|
71
85
|
"README.md"
|
|
72
86
|
],
|
|
73
87
|
"scripts": {
|
|
@@ -79,12 +93,16 @@
|
|
|
79
93
|
"test:encoding": "powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -File tests/encoding.ps1",
|
|
80
94
|
"test:conformance": "powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -File tests/conformance.ps1",
|
|
81
95
|
"test:plugin": "node tests/plugin.test.mjs",
|
|
96
|
+
"test:takeover": "node tests/takeover.test.mjs",
|
|
82
97
|
"test:permission": "node tests/permission.test.mjs",
|
|
98
|
+
"test:workflow": "node tests/workflow.test.mjs",
|
|
83
99
|
"test:bundle": "node tools/verify-bundle.mjs",
|
|
84
100
|
"test:package": "node tools/verify-package.mjs",
|
|
85
101
|
"test:desktop": "bash tests/smoke-desktop-sh.sh",
|
|
86
102
|
"test:load": "node tools/verify-plugin-load.mjs && node tools/verify-editions.mjs",
|
|
87
|
-
"test:
|
|
103
|
+
"test:pipeline": "node tools/probe-pipeline.mjs",
|
|
104
|
+
"measure:cache": "node tools/measure-cache.mjs",
|
|
105
|
+
"test:all": "npm run test:conformance && npm run test:encoding && npm run test && npm run test:plugin && npm run test:takeover && npm run test:permission && npm run test:workflow && npm run test:bundle && npm run test:package && npm run test:load && npm run test:desktop",
|
|
88
106
|
"selfcheck": "powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -File src/host/bin/dsh-os.ps1 selfcheck"
|
|
89
107
|
},
|
|
90
108
|
"devDependencies": {
|