@linxin666/dsh-client-ui-task-board 0.1.15 → 0.1.17
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.i18n.yaml +2 -2
- package/README.md +8 -4
- package/README.zh.md +8 -4
- package/lib/client.js +499 -43
- package/lib/client.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/types/client/locales.d.ts +14 -0
- package/lib/types/client/settings-form.d.ts +62 -0
- package/lib/types/core/controller.d.ts +30 -1
- package/lib/types/core/execution.d.ts +49 -4
- package/lib/types/core/store.d.ts +1 -1
- package/lib/types/core/tasks.d.ts +27 -0
- package/lib/types/core/use-cases/task-update.d.ts +9 -4
- package/lib/types/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/client/PluginSettingsCard.tsx +7 -1
- package/src/client/board/NewTaskModal.tsx +68 -3
- package/src/client/board/TaskDetail.tsx +71 -1
- package/src/client/board.module.css +16 -0
- package/src/client/index.ts +68 -2
- package/src/client/locales.ts +28 -0
- package/src/client/settings-card.module.css +3 -0
- package/src/client/settings-form.ts +114 -12
- package/src/core/controller.ts +38 -2
- package/src/core/execution.ts +140 -12
- package/src/core/store.ts +15 -2
- package/src/core/tasks.ts +41 -0
- package/src/core/use-cases/task-update.ts +34 -5
- package/src/index.ts +1 -1
package/src/core/store.ts
CHANGED
|
@@ -14,8 +14,7 @@
|
|
|
14
14
|
* localStorage backend.
|
|
15
15
|
*/
|
|
16
16
|
import { isValidCron } from './schedule.ts'
|
|
17
|
-
import type
|
|
18
|
-
import { isTaskStatus } from './tasks.ts'
|
|
17
|
+
import { isTaskPermission, isTaskStatus, type ScheduleRule, type TaskRecord, type TaskPermission, type TaskStatus } from './tasks.ts'
|
|
19
18
|
|
|
20
19
|
/** Persistence seam for the task ledger. */
|
|
21
20
|
export interface TaskStore {
|
|
@@ -64,6 +63,9 @@ function isTaskRecordShape(value: unknown): value is Omit<TaskRecord, 'status'>
|
|
|
64
63
|
if (typeof record.prompt !== 'string') return false
|
|
65
64
|
if (typeof record.createdAt !== 'number') return false
|
|
66
65
|
if (typeof record.updatedAt !== 'number') return false
|
|
66
|
+
if (record.workspaceId !== undefined && typeof record.workspaceId !== 'string') return false
|
|
67
|
+
if (record.mode !== undefined && typeof record.mode !== 'string') return false
|
|
68
|
+
if (record.permission !== undefined && typeof record.permission !== 'string') return false
|
|
67
69
|
if (!Array.isArray(record.executions)) return false
|
|
68
70
|
for (const execution of record.executions) {
|
|
69
71
|
if (typeof execution !== 'object' || execution === null) return false
|
|
@@ -78,6 +80,11 @@ function isTaskRecordShape(value: unknown): value is Omit<TaskRecord, 'status'>
|
|
|
78
80
|
return true
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
/** Collapse a blank persisted target string to undefined (clears the pin). */
|
|
84
|
+
function normalizeTargetId(value: string | undefined): string | undefined {
|
|
85
|
+
return value !== undefined && value.trim() === '' ? undefined : value
|
|
86
|
+
}
|
|
87
|
+
|
|
81
88
|
/** A task record is structurally valid if it round-trips through the UI. */
|
|
82
89
|
export function isTaskRecord(value: unknown): value is TaskRecord {
|
|
83
90
|
return isTaskRecordShape(value) && isTaskStatus(value.status)
|
|
@@ -136,6 +143,12 @@ export function parseLedger(raw: string | null): TaskRecord[] {
|
|
|
136
143
|
// clear a malformed persisted rule rather than leave it in the row.
|
|
137
144
|
const task: TaskRecord = { ...row, status: normalizeStatus(row.status) }
|
|
138
145
|
task.schedule = normalizeSchedule(row.schedule)
|
|
146
|
+
// Execution targets are normalized like the schedule: blank strings
|
|
147
|
+
// clear the pin and unknown permission strings from a future version
|
|
148
|
+
// fall back to the session default instead of dropping the row.
|
|
149
|
+
task.workspaceId = normalizeTargetId(row.workspaceId)
|
|
150
|
+
task.mode = normalizeTargetId(row.mode)
|
|
151
|
+
task.permission = isTaskPermission(row.permission) ? row.permission as TaskPermission : undefined
|
|
139
152
|
tasks.push(task)
|
|
140
153
|
}
|
|
141
154
|
return tasks
|
package/src/core/tasks.ts
CHANGED
|
@@ -64,6 +64,32 @@ export interface TaskRecord {
|
|
|
64
64
|
executions: ExecutionRecord[]
|
|
65
65
|
/** Optional scheduled-run rule (absent on tasks without a schedule). */
|
|
66
66
|
schedule?: ScheduleRule
|
|
67
|
+
/**
|
|
68
|
+
* Workspace the execution must run in (a workspace-list id); absent means
|
|
69
|
+
* the recent-workspace fallback at execution time.
|
|
70
|
+
*/
|
|
71
|
+
workspaceId?: string
|
|
72
|
+
/**
|
|
73
|
+
* Agent preset the execution session must be composed from (an
|
|
74
|
+
* `agentPreset.list` id); absent means the deployment default.
|
|
75
|
+
*/
|
|
76
|
+
mode?: string
|
|
77
|
+
/**
|
|
78
|
+
* Permission preset applied to the execution session through the
|
|
79
|
+
* `/permission <id>` slash command; absent leaves the session default.
|
|
80
|
+
*/
|
|
81
|
+
permission?: TaskPermission
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Permission presets a task may pin on its execution session (the `/permission <id>` ids). */
|
|
85
|
+
export const TASK_PERMISSIONS = ['read-only', 'workspace-write', 'danger-full-access'] as const
|
|
86
|
+
|
|
87
|
+
/** One permission preset id. */
|
|
88
|
+
export type TaskPermission = typeof TASK_PERMISSIONS[number]
|
|
89
|
+
|
|
90
|
+
/** Whether an unknown value is a known permission preset id. */
|
|
91
|
+
export function isTaskPermission(value: unknown): value is TaskPermission {
|
|
92
|
+
return typeof value === 'string' && (TASK_PERMISSIONS as readonly string[]).includes(value)
|
|
67
93
|
}
|
|
68
94
|
|
|
69
95
|
/** Input for creating a task. */
|
|
@@ -71,6 +97,12 @@ export interface NewTaskInput {
|
|
|
71
97
|
title: string
|
|
72
98
|
description: string
|
|
73
99
|
prompt: string
|
|
100
|
+
/** Workspace the execution must run in; empty/absent = the recent workspace. */
|
|
101
|
+
workspaceId?: string
|
|
102
|
+
/** Agent preset the execution session must be composed from; empty/absent = deployment default. */
|
|
103
|
+
mode?: string
|
|
104
|
+
/** Permission preset applied to the execution session; absent = session default. */
|
|
105
|
+
permission?: TaskPermission
|
|
74
106
|
}
|
|
75
107
|
|
|
76
108
|
/** The five kanban columns, in display order. */
|
|
@@ -103,6 +135,12 @@ export function canMoveManually(_from: TaskStatus, to: TaskStatus): boolean {
|
|
|
103
135
|
return (MANUAL_STATUSES as readonly TaskStatus[]).includes(to)
|
|
104
136
|
}
|
|
105
137
|
|
|
138
|
+
/** Normalize one optional execution-target string: trim; blank collapses to undefined. */
|
|
139
|
+
function normalizeTargetId(value: string | undefined): string | undefined {
|
|
140
|
+
const trimmed = value?.trim()
|
|
141
|
+
return trimmed === undefined || trimmed === '' ? undefined : trimmed
|
|
142
|
+
}
|
|
143
|
+
|
|
106
144
|
/** Create a task from user input. */
|
|
107
145
|
export function createTask(input: NewTaskInput, now: number, id: string): TaskRecord {
|
|
108
146
|
return {
|
|
@@ -114,6 +152,9 @@ export function createTask(input: NewTaskInput, now: number, id: string): TaskRe
|
|
|
114
152
|
createdAt: now,
|
|
115
153
|
updatedAt: now,
|
|
116
154
|
executions: [],
|
|
155
|
+
workspaceId: normalizeTargetId(input.workspaceId),
|
|
156
|
+
mode: normalizeTargetId(input.mode),
|
|
157
|
+
permission: isTaskPermission(input.permission) ? input.permission : undefined,
|
|
117
158
|
}
|
|
118
159
|
}
|
|
119
160
|
|
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Update-task use case: apply an editable-field patch (title/description/
|
|
3
|
-
* prompt
|
|
4
|
-
* notify — the
|
|
3
|
+
* prompt plus the execution targets workspaceId/mode/permission) with a
|
|
4
|
+
* fresh updatedAt. Pure ledger transition (no persistence or notify — the
|
|
5
|
+
* controller orchestrates those).
|
|
6
|
+
*
|
|
7
|
+
* An explicit `undefined` in the patch clears the field (the task falls
|
|
8
|
+
* back to the runtime default); an unknown permission string is ignored so
|
|
9
|
+
* stale UI can never persist a value the execution service rejects.
|
|
5
10
|
*/
|
|
6
|
-
import type
|
|
11
|
+
import { isTaskPermission, type TaskRecord, type TaskPermission } from '../tasks.ts'
|
|
7
12
|
|
|
8
13
|
/** Editable fields on a task (the update patch surface). */
|
|
9
|
-
export type TaskUpdatePatch = Partial<Pick<TaskRecord, 'title' | 'description' | 'prompt'>>
|
|
14
|
+
export type TaskUpdatePatch = Partial<Pick<TaskRecord, 'title' | 'description' | 'prompt' | 'workspaceId' | 'mode' | 'permission'>>
|
|
15
|
+
|
|
16
|
+
/** Collapse a blank target string to undefined (clears the pin). */
|
|
17
|
+
function normalizeTargetId(value: string | undefined): string | undefined {
|
|
18
|
+
return value !== undefined && value.trim() === '' ? undefined : value
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Keep an unknown permission string from entering the ledger. */
|
|
22
|
+
function normalizePermission(
|
|
23
|
+
current: TaskPermission | undefined,
|
|
24
|
+
value: TaskPermission | undefined,
|
|
25
|
+
): TaskPermission | undefined {
|
|
26
|
+
if (value === undefined) return undefined
|
|
27
|
+
return isTaskPermission(value) ? value : current
|
|
28
|
+
}
|
|
10
29
|
|
|
11
30
|
/**
|
|
12
31
|
* Apply an update across the ledger. Tasks that do not match the id are left
|
|
@@ -22,5 +41,15 @@ export function applyUpdateTask(
|
|
|
22
41
|
patch: TaskUpdatePatch,
|
|
23
42
|
now: number,
|
|
24
43
|
): readonly TaskRecord[] {
|
|
25
|
-
return tasks.map(task =>
|
|
44
|
+
return tasks.map(task => {
|
|
45
|
+
if (task.id !== id) return task
|
|
46
|
+
const workspaceId = 'workspaceId' in patch ? normalizeTargetId(patch.workspaceId) : undefined
|
|
47
|
+
const mode = 'mode' in patch ? normalizeTargetId(patch.mode) : undefined
|
|
48
|
+
const permission = 'permission' in patch ? normalizePermission(task.permission, patch.permission) : undefined
|
|
49
|
+
const next: TaskRecord = { ...task, ...patch, updatedAt: now }
|
|
50
|
+
if (workspaceId !== undefined || 'workspaceId' in patch) next.workspaceId = workspaceId
|
|
51
|
+
if (mode !== undefined || 'mode' in patch) next.mode = mode
|
|
52
|
+
if (permission !== undefined || 'permission' in patch) next.permission = permission
|
|
53
|
+
return next
|
|
54
|
+
})
|
|
26
55
|
}
|
package/src/index.ts
CHANGED
|
@@ -22,7 +22,7 @@ const SECTION_ORDER = 200
|
|
|
22
22
|
export const inject = ['systemPrompt']
|
|
23
23
|
|
|
24
24
|
/** Model-facing announcement: plugin presence, capabilities, and limits. */
|
|
25
|
-
export const TASK_BOARD_GUIDANCE = '本机已安装 dsh-task-board 插件(DSH Web GUI 的任务看板):侧边栏「任务看板」入口;在 dsh-web-ui 插件全家桶仓库(packages/dsh-task-board)统一维护,经聚合包 web-ui-all 一键安装。能力:多列看板管理任务;任务可真实执行(驱动 agent
|
|
25
|
+
export const TASK_BOARD_GUIDANCE = '本机已安装 dsh-task-board 插件(DSH Web GUI 的任务看板):侧边栏「任务看板」入口;在 dsh-web-ui 插件全家桶仓库(packages/dsh-task-board)统一维护,经聚合包 web-ui-all 一键安装。能力:多列看板管理任务;任务可真实执行(驱动 agent 会话);任务可钉住执行目标——工作区 / 模式(agent 预设)/ 权限(read-only / workspace-write / danger-full-access),缺省用运行时默认;任务支持 5 段 cron 定时执行(如 0 23 * * *);数据存浏览器 localStorage(键 dsh.taskBoard.v1)。限制:定时调度在浏览器端,需 GUI 标签页打开,错过即跳过;执行消耗 API 额度。用户提到「任务看板 / 看板 / 定时任务」时即指本插件,请据此协作。'
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Settings namespace of the board's announcement capability — the section the
|