@linxin666/dsh-client-ui-task-board 0.3.23 → 0.4.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/icon.svg +3 -0
- package/lib/client.js +351 -85
- package/lib/client.js.map +1 -1
- package/lib/index.js +174 -916
- package/lib/types/client/PluginSettingsCard.d.ts +4 -0
- package/lib/types/client/TaskBoardSettingsCard.d.ts +5 -5
- package/lib/types/client/board/TaskForm.d.ts +6 -1
- package/lib/types/client/board/parse-model-pref.d.ts +4 -0
- package/lib/types/client/index.d.ts +43 -10
- package/lib/types/client/locales.d.ts +1 -0
- package/lib/types/client/main-session.d.ts +32 -0
- package/lib/types/client/plugin-card-seat.d.ts +103 -0
- package/lib/types/client/settings-form.d.ts +17 -17
- package/lib/types/core/controller.d.ts +10 -8
- package/lib/types/core/session-reuse.d.ts +4 -2
- package/lib/types/host-ledger.d.ts +15 -0
- package/lib/types/http.d.ts +18 -1
- package/lib/types/index.d.ts +72 -17
- package/package.json +30 -24
- package/src/client/PluginSettingsCard.tsx +7 -2
- package/src/client/TaskBoardSettingsCard.tsx +7 -5
- package/src/client/board/NewTaskModal.tsx +33 -7
- package/src/client/board/TaskForm.tsx +13 -0
- package/src/client/board/parse-model-pref.ts +35 -0
- package/src/client/board.module.css +31 -12
- package/src/client/index.ts +121 -38
- package/src/client/locales.ts +2 -0
- package/src/client/main-session.ts +43 -0
- package/src/client/plugin-card-seat.ts +192 -0
- package/src/client/settings-form.ts +34 -31
- package/src/client/sidebar-entry-core.ts +3 -3
- package/src/client/sidebar-entry.ts +2 -2
- package/src/core/controller.ts +12 -8
- package/src/core/session-reuse.ts +14 -5
- package/src/host-ledger.ts +59 -11
- package/src/http.ts +23 -1
- package/src/index.ts +97 -52
package/src/index.ts
CHANGED
|
@@ -6,11 +6,10 @@
|
|
|
6
6
|
* a same-origin asynchronous view over that service.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { Context } from '@deepseek-ai/cordis'
|
|
9
|
+
import type { Context, Volatile } from '@deepseek-ai/cordis'
|
|
10
10
|
import type {} from '@deepseek-ai/dsh-agent'
|
|
11
11
|
import type {} from '@deepseek-ai/dsh-commands'
|
|
12
|
-
import
|
|
13
|
-
import z from 'schemastery'
|
|
12
|
+
import z from '@deepseek-ai/schemastery'
|
|
14
13
|
import type {} from '@deepseek-ai/dsh-system-prompt'
|
|
15
14
|
import type {} from '@deepseek-ai/dsh-api-gateway'
|
|
16
15
|
import type {} from '@deepseek-ai/dsh-workspace'
|
|
@@ -35,25 +34,30 @@ export const inject = ['systemPrompt', 'typertGateway', 'workspaceRegistry', 'we
|
|
|
35
34
|
export const TASK_BOARD_GUIDANCE = '本机已安装 dsh-task-board 插件(DSH Web GUI 的任务看板):侧边栏「任务看板」入口;在 dsh-web 插件全家桶仓库(packages/dsh-task-board)统一维护,经聚合包 web-ui-all 一键安装。能力:多列看板管理任务;Host 权威账本;关闭浏览器后仍由 Host 执行和结算;任务可钉住工作区、agent 预设和权限;支持 Host 本地时区的 5 段 cron,错过的触发点不补跑;可选且默认关闭的空闲系统睡眠保护允许屏幕熄灭,但不承诺拦截合盖、手动睡眠、休眠、关机或唤醒已睡眠机器。执行消耗 API 额度。用户提到「任务看板 / 看板 / 定时任务」时即指本插件,请据此协作。若你同时用 todo_write 维护会话顶部的可见计划列表,最终回复前必须再次调用 todo_write 收尾:没有剩余工作时不要保留 in_progress,已完成的最后一步要标为 completed。'
|
|
36
35
|
|
|
37
36
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
37
|
+
* Plugin config, validated by the same-named schemastery schema.
|
|
38
|
+
*
|
|
39
|
+
* This schema IS the board's settings page: the 0.1.7 Host serves one
|
|
40
|
+
* configuration form per profile entry from the entry's own Config, so the
|
|
41
|
+
* three fields the browser card edits are marked volatile — the Loader commits
|
|
42
|
+
* an edit into the running fiber's references without remounting the row (no
|
|
43
|
+
* settings document of the plugin's own exists any more).
|
|
41
44
|
*/
|
|
42
|
-
export const TASK_BOARD_SETTINGS_NAMESPACE = 'task-board' as SettingsNamespace
|
|
43
|
-
|
|
44
|
-
/** Plugin config, validated by the same-named schemastery schema. */
|
|
45
45
|
export interface Config {
|
|
46
46
|
/**
|
|
47
47
|
* When true (default), a system-prompt section announces the board to every
|
|
48
48
|
* agent. Set false to keep the board silent in prompts; agents then learn
|
|
49
49
|
* about it only when the user mentions it.
|
|
50
50
|
*/
|
|
51
|
-
announceToAgent?: boolean
|
|
51
|
+
announceToAgent?: Volatile<boolean>
|
|
52
52
|
/** Master switch for the plugin (browser half + host announcement). */
|
|
53
|
-
enabled?: boolean
|
|
53
|
+
enabled?: Volatile<boolean>
|
|
54
54
|
/** Prevent idle system sleep while sessions run or schedules are armed. */
|
|
55
|
-
preventIdleSleep?: boolean
|
|
56
|
-
/**
|
|
55
|
+
preventIdleSleep?: Volatile<boolean>
|
|
56
|
+
/**
|
|
57
|
+
* Canonical reverse-proxy Host authorities admitted with a server-side
|
|
58
|
+
* token. Deployment-level, so it stays an ordinary field: editing it reloads
|
|
59
|
+
* the row, which re-registers the routes with the new proxy access.
|
|
60
|
+
*/
|
|
57
61
|
trustedProxyHosts?: string[]
|
|
58
62
|
/** Environment variable whose value the authenticated proxy injects upstream. */
|
|
59
63
|
proxyTokenEnv?: string
|
|
@@ -65,15 +69,54 @@ export interface Config {
|
|
|
65
69
|
sessionDefaultPermission?: TaskPermission
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
/**
|
|
73
|
+
* The schema is left to inference rather than annotated with `z<Config>`: a
|
|
74
|
+
* volatile field's parsed output is a `Volatile` reference while its accepted
|
|
75
|
+
* input stays the plain value, so the two sides no longer share one shape and
|
|
76
|
+
* the annotation would reject the schema the Host must be given.
|
|
77
|
+
*/
|
|
78
|
+
export const Config = z.object({
|
|
79
|
+
announceToAgent: z.boolean().default(false).volatile(),
|
|
80
|
+
enabled: z.boolean().default(true).volatile(),
|
|
81
|
+
preventIdleSleep: z.boolean().default(false).volatile(),
|
|
72
82
|
trustedProxyHosts: z.array(z.string()).default([]),
|
|
73
83
|
proxyTokenEnv: z.string().min(1).default(DEFAULT_PROXY_TOKEN_ENV),
|
|
74
84
|
sessionDefaultPermission: z.union(TASK_PERMISSIONS).default(DEFAULT_SESSION_PERMISSION),
|
|
75
85
|
})
|
|
76
86
|
|
|
87
|
+
declare module '@deepseek-ai/cordis' {
|
|
88
|
+
interface Events {
|
|
89
|
+
/**
|
|
90
|
+
* Volatile config values were committed into the running fiber without a
|
|
91
|
+
* remount; dispatched to the owning fiber only. Spelled here because the
|
|
92
|
+
* Loader package is not a dependency of this plugin, with the Loader's own
|
|
93
|
+
* shape so the two declarations merge when a Host program carries both.
|
|
94
|
+
* @param paths - changed config paths as key arrays; every value is committed before dispatch.
|
|
95
|
+
* @mode emit
|
|
96
|
+
*/
|
|
97
|
+
'loader/volatile-update'(paths: readonly (readonly string[])[]): void
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Read one config field's current value.
|
|
103
|
+
*
|
|
104
|
+
* The Loader hands schema-volatile fields as stable references it commits in
|
|
105
|
+
* place, so a live value must be read at use time rather than captured when
|
|
106
|
+
* the plugin activates; a plain value (a programmatic mount, or a field the
|
|
107
|
+
* schema does not mark volatile) is returned as it stands.
|
|
108
|
+
* @param field - the config field as the Loader handed it.
|
|
109
|
+
* @param fallback - value to use when the field is absent.
|
|
110
|
+
* @returns the effective field value.
|
|
111
|
+
*/
|
|
112
|
+
export function readConfigField<T>(field: Volatile<T> | T | undefined, fallback: T): T {
|
|
113
|
+
if (field === undefined) return fallback
|
|
114
|
+
if (typeof field === 'object' && field !== null && typeof (field as { get?: unknown }).get === 'function') {
|
|
115
|
+
return (field as Volatile<T>).get() as T
|
|
116
|
+
}
|
|
117
|
+
return field as T
|
|
118
|
+
}
|
|
119
|
+
|
|
77
120
|
/** Resolve proxy access without ever placing the token value in plugin config. */
|
|
78
121
|
export function resolveProxyAccess(config: Config | undefined, env: NodeJS.ProcessEnv = process.env): { trustedProxyHosts: string[]; proxyToken?: string } {
|
|
79
122
|
const trustedProxyHosts = config?.trustedProxyHosts ?? []
|
|
@@ -107,16 +150,28 @@ export function resolveLlmRuntime(ctx: Context): LlmRuntime | undefined {
|
|
|
107
150
|
}
|
|
108
151
|
|
|
109
152
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
153
|
+
* Activate the board's host half: ledger, routes, cron scheduler, power
|
|
154
|
+
* inhibitor, and the model-facing announcement.
|
|
155
|
+
*
|
|
156
|
+
* The effective settings are the config the Host hands this row. The three
|
|
157
|
+
* fields the browser card edits are schema-volatile, so the Loader commits an
|
|
158
|
+
* edit into the running fiber's references instead of remounting the row —
|
|
159
|
+
* `sync` therefore reads them at use time and follows
|
|
160
|
+
* `loader/volatile-update`, the event the Loader emits once it has committed
|
|
161
|
+
* them. Every other field reloads the row, which re-runs this activation.
|
|
114
162
|
* @param ctx - the plugin context (systemPrompt injected).
|
|
115
163
|
* @param config - resolved plugin config (schema defaults applied by the loader).
|
|
116
164
|
*/
|
|
117
165
|
export const apply = mountOnce('@linxin666/dsh-client-ui-task-board', applyImpl)
|
|
118
166
|
|
|
119
167
|
function applyImpl(ctx: Context, config?: Config): void {
|
|
168
|
+
/** Current master switch (the browser half reads the same field from its own form). */
|
|
169
|
+
const enabled = (): boolean => readConfigField(config?.enabled, true)
|
|
170
|
+
/** Current announcement flag. */
|
|
171
|
+
const announceToAgent = (): boolean => readConfigField(config?.announceToAgent, DEFAULT_ANNOUNCE)
|
|
172
|
+
/** Current idle-sleep protection flag. */
|
|
173
|
+
const preventIdleSleep = (): boolean => readConfigField(config?.preventIdleSleep, false)
|
|
174
|
+
|
|
120
175
|
const host = new TaskBoardHostService(ctx.typertGateway, {
|
|
121
176
|
workspaceRegistry: ctx.workspaceRegistry,
|
|
122
177
|
sessionDefaultPermission: config?.sessionDefaultPermission ?? DEFAULT_SESSION_PERMISSION,
|
|
@@ -128,7 +183,9 @@ function applyImpl(ctx: Context, config?: Config): void {
|
|
|
128
183
|
},
|
|
129
184
|
},
|
|
130
185
|
})
|
|
131
|
-
|
|
186
|
+
// Configuration before start(): a disabled row must not take the first
|
|
187
|
+
// scheduler tick, which would roll schedules the board is not running.
|
|
188
|
+
host.setConfiguration(enabled(), preventIdleSleep())
|
|
132
189
|
host.start()
|
|
133
190
|
ctx.effect(() => {
|
|
134
191
|
const disposers: Array<() => void> = []
|
|
@@ -151,24 +208,27 @@ function applyImpl(ctx: Context, config?: Config): void {
|
|
|
151
208
|
host.dispose()
|
|
152
209
|
}
|
|
153
210
|
}, 'task-board: host ledger, scheduler, and routes')
|
|
154
|
-
|
|
155
|
-
// settings surface is served, the composition entry otherwise
|
|
156
|
-
// (installSection swaps it when the namespace registers).
|
|
157
|
-
let current: () => Config = () => config ?? {}
|
|
211
|
+
|
|
158
212
|
let disposeSection: (() => void) | undefined
|
|
213
|
+
let applied: { enabled: boolean; announceToAgent: boolean; preventIdleSleep: boolean } | undefined
|
|
159
214
|
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
//
|
|
215
|
+
// Apply the current values to the host service and the announcement. A
|
|
216
|
+
// commit that changes nothing visible is a no-op, so following a coarse
|
|
217
|
+
// invalidation cannot churn the system-prompt registry. The section is kept
|
|
218
|
+
// under one disposer: re-registering first tears the old one down so a
|
|
219
|
+
// duplicate-name registration never throws.
|
|
163
220
|
const sync = (): void => {
|
|
221
|
+
const next = { enabled: enabled(), announceToAgent: announceToAgent(), preventIdleSleep: preventIdleSleep() }
|
|
222
|
+
if (applied !== undefined && applied.enabled === next.enabled && applied.announceToAgent === next.announceToAgent && applied.preventIdleSleep === next.preventIdleSleep) {
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
applied = next
|
|
226
|
+
host.setConfiguration(next.enabled, next.preventIdleSleep)
|
|
164
227
|
if (disposeSection !== undefined) {
|
|
165
228
|
disposeSection()
|
|
166
229
|
disposeSection = undefined
|
|
167
230
|
}
|
|
168
|
-
|
|
169
|
-
host.setConfiguration(active, current().preventIdleSleep ?? false)
|
|
170
|
-
if (!active) return
|
|
171
|
-
if ((current().announceToAgent ?? DEFAULT_ANNOUNCE) === false) return
|
|
231
|
+
if (!next.enabled || !next.announceToAgent) return
|
|
172
232
|
disposeSection = ctx.systemPrompt.section({
|
|
173
233
|
name: 'plugin:task-board',
|
|
174
234
|
order: SECTION_ORDER,
|
|
@@ -176,24 +236,9 @@ function applyImpl(ctx: Context, config?: Config): void {
|
|
|
176
236
|
})
|
|
177
237
|
}
|
|
178
238
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
setSource: (source) => { current = source },
|
|
184
|
-
onChange: sync,
|
|
185
|
-
})
|
|
186
|
-
} else if (typeof settingsCtx.settings?.register === 'function') {
|
|
187
|
-
const scope = settingsCtx.settings.register(TASK_BOARD_SETTINGS_NAMESPACE, Config, { base: config ?? {} })
|
|
188
|
-
current = () => scope?.get?.() ?? (config ?? {})
|
|
189
|
-
scope?.watch?.(() => { sync() })
|
|
190
|
-
}
|
|
191
|
-
} catch {
|
|
192
|
-
// Defensive fallback against settings registration differences
|
|
193
|
-
}
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
// Initial registration from the composition entry (covers deployments with
|
|
197
|
-
// no settings service, whose installSection never fires its hooks).
|
|
239
|
+
// A settings edit of a volatile field is committed into the references this
|
|
240
|
+
// fiber already holds, with no remount and no second call to apply: the
|
|
241
|
+
// commit event is what makes the edit take effect without a restart.
|
|
242
|
+
ctx.on('loader/volatile-update', () => { sync() })
|
|
198
243
|
sync()
|
|
199
244
|
}
|