dsh-taskboard 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/LICENSE +201 -0
- package/README.md +169 -0
- package/cordis.patch.yml +12 -0
- package/lib/client.js +2085 -0
- package/lib/host/execution.js +189 -0
- package/lib/host/execution.js.map +1 -0
- package/lib/host/protocol-text.js +37 -0
- package/lib/host/protocol-text.js.map +1 -0
- package/lib/host/routes.js +369 -0
- package/lib/host/routes.js.map +1 -0
- package/lib/host/scheduler.js +91 -0
- package/lib/host/scheduler.js.map +1 -0
- package/lib/host/sdk.js +145 -0
- package/lib/host/sdk.js.map +1 -0
- package/lib/host/store.js +112 -0
- package/lib/host/store.js.map +1 -0
- package/lib/host/tools.js +620 -0
- package/lib/host/tools.js.map +1 -0
- package/lib/index.js +91 -0
- package/lib/index.js.map +1 -0
- package/lib/invariant.js +22 -0
- package/lib/invariant.js.map +1 -0
- package/lib/shared/api.js +9 -0
- package/lib/shared/api.js.map +1 -0
- package/lib/shared/protocol.js +279 -0
- package/lib/shared/protocol.js.map +1 -0
- package/package.json +74 -0
- package/src/client/api.ts +90 -0
- package/src/client/board/NewTaskModal.tsx +8 -0
- package/src/client/board/TaskBoard.tsx +184 -0
- package/src/client/board/TaskCard.tsx +61 -0
- package/src/client/board/TaskDetail.tsx +210 -0
- package/src/client/board/TaskFormModal.tsx +257 -0
- package/src/client/board-mount.tsx +92 -0
- package/src/client/controller.ts +241 -0
- package/src/client/index.ts +87 -0
- package/src/client/sidebar-entry.ts +165 -0
- package/src/client/styles.ts +391 -0
- package/src/host/execution.ts +244 -0
- package/src/host/protocol-text.ts +37 -0
- package/src/host/routes.ts +387 -0
- package/src/host/scheduler.ts +107 -0
- package/src/host/sdk.ts +200 -0
- package/src/host/store.ts +139 -0
- package/src/host/tools.ts +631 -0
- package/src/index.ts +124 -0
- package/src/invariant.ts +22 -0
- package/src/shared/api.ts +98 -0
- package/src/shared/protocol.ts +475 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host loader entry for dsh-taskboard.
|
|
3
|
+
*
|
|
4
|
+
* Wiring: the ledger store (one JSON file under the DSH home), the eight
|
|
5
|
+
* `taskboard_*` agent tools, the agent workflow-protocol system-prompt
|
|
6
|
+
* section, the /taskboard JSON+SSE routes (when a webServer is served),
|
|
7
|
+
* the host execution service (fresh in-project sessions, pinned models), and
|
|
8
|
+
* the host-side cron scheduler for scheduled tasks.
|
|
9
|
+
*
|
|
10
|
+
* Export shape follows the dsh-tool-todo lesson: a function/namespace plugin —
|
|
11
|
+
* `name` / `inject` / `apply`, NO default export.
|
|
12
|
+
*
|
|
13
|
+
* @module dsh-taskboard
|
|
14
|
+
*/
|
|
15
|
+
import type { Context } from '@deepseek-ai/cordis'
|
|
16
|
+
// Type-only module imports: they load the cordis Context augmentations
|
|
17
|
+
// (ctx.tools / ctx.systemPrompt / ctx.agents) and vanish at compile time —
|
|
18
|
+
// the built host half keeps ZERO runtime @deepseek-ai imports.
|
|
19
|
+
import type {} from '@deepseek-ai/dsh-tools'
|
|
20
|
+
import type {} from '@deepseek-ai/dsh-system-prompt'
|
|
21
|
+
import type {} from '@deepseek-ai/dsh-agent'
|
|
22
|
+
import { PROTOCOL_SECTION_NAME, PROTOCOL_SECTION_ORDER, TASKBOARD_PROTOCOL } from './host/protocol-text.ts'
|
|
23
|
+
import { ExecutionService, type EventsFace } from './host/execution.ts'
|
|
24
|
+
import { registerTaskboardRoutes } from './host/routes.ts'
|
|
25
|
+
import { SchedulerService } from './host/scheduler.ts'
|
|
26
|
+
import { dshHomePath } from './host/sdk.ts'
|
|
27
|
+
import { TaskStore } from './host/store.ts'
|
|
28
|
+
import { registerTaskboardTools, workspaceFace } from './host/tools.ts'
|
|
29
|
+
|
|
30
|
+
/** Ledger file name under the DSH home. */
|
|
31
|
+
export const LEDGER_FILE = 'dsh-taskboard.json'
|
|
32
|
+
|
|
33
|
+
/** Cordis plugin name. */
|
|
34
|
+
export const name = 'dsh-taskboard'
|
|
35
|
+
|
|
36
|
+
/** Required host services (tool registry + prompt assembly). */
|
|
37
|
+
export const inject = ['tools', 'systemPrompt']
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Mount the host half.
|
|
41
|
+
* @param ctx - the plugin context (tools + systemPrompt injected).
|
|
42
|
+
*/
|
|
43
|
+
export function apply(ctx: Context): void {
|
|
44
|
+
const store = new TaskStore({ file: dshHomePath(LEDGER_FILE) })
|
|
45
|
+
const now = () => Date.now()
|
|
46
|
+
|
|
47
|
+
// Agent workflow protocol (claim discipline, retry rules, done-gate).
|
|
48
|
+
const disposeSection = ctx.systemPrompt.section({
|
|
49
|
+
name: PROTOCOL_SECTION_NAME,
|
|
50
|
+
order: PROTOCOL_SECTION_ORDER,
|
|
51
|
+
text: TASKBOARD_PROTOCOL,
|
|
52
|
+
})
|
|
53
|
+
ctx.effect(() => disposeSection, 'dsh-taskboard: protocol section')
|
|
54
|
+
|
|
55
|
+
// Tools, routes, execution, and the scheduler all come up with the
|
|
56
|
+
// workspace registry (claim boundary + project execution need it).
|
|
57
|
+
ctx.inject(['workspaceRegistry'], (wsCtx: Context) => {
|
|
58
|
+
const disposers: Array<() => void> = []
|
|
59
|
+
disposers.push(...registerTaskboardTools(wsCtx, {
|
|
60
|
+
store,
|
|
61
|
+
workspaces: workspaceFace(wsCtx.workspaceRegistry),
|
|
62
|
+
now,
|
|
63
|
+
}))
|
|
64
|
+
|
|
65
|
+
// Settlement listener over the session event bus.
|
|
66
|
+
const events: EventsFace = {
|
|
67
|
+
onSessionEvent: (listener) => wsCtx.on('session/event', (session, event) => {
|
|
68
|
+
listener(session.id, event as { type: string; data?: unknown })
|
|
69
|
+
}),
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
wsCtx.inject(['agents'], (agentCtx: Context) => {
|
|
73
|
+
const execution = new ExecutionService({
|
|
74
|
+
store,
|
|
75
|
+
agents: {
|
|
76
|
+
create: (options): Promise<never> => agentCtx.agents.create(options as never) as Promise<never>,
|
|
77
|
+
},
|
|
78
|
+
workspaces: {
|
|
79
|
+
get: id => workspaceFace(wsCtx.workspaceRegistry).get(id),
|
|
80
|
+
attach: async (workspaceId, sessionId) => {
|
|
81
|
+
const ws = wsCtx.workspaceRegistry.get(workspaceId as never)
|
|
82
|
+
if (ws !== undefined) await ws.attachSession(sessionId as never)
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
events,
|
|
86
|
+
now,
|
|
87
|
+
defaultModel: () => {
|
|
88
|
+
try {
|
|
89
|
+
const selection = agentCtx.get('agentDefaultModel') as { currentSelection?: () => { provider: string; model: string } | undefined } | undefined
|
|
90
|
+
const read = selection?.currentSelection
|
|
91
|
+
return read === undefined ? undefined : read.call(selection)
|
|
92
|
+
} catch { return undefined }
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// /dsh-taskboard routes (the run action reaches the execution service).
|
|
97
|
+
let disposeRoutes: (() => void) | undefined
|
|
98
|
+
agentCtx.inject(['webServer'], (webCtx: Context) => {
|
|
99
|
+
disposeRoutes = registerTaskboardRoutes(webCtx, {
|
|
100
|
+
store,
|
|
101
|
+
workspaces: workspaceFace(wsCtx.workspaceRegistry),
|
|
102
|
+
now,
|
|
103
|
+
run: (taskId: string) => execution.run(taskId, 'manual'),
|
|
104
|
+
})
|
|
105
|
+
return () => disposeRoutes?.()
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
// Host-side cron scheduler: due scheduled tasks execute even with no
|
|
109
|
+
// browser open.
|
|
110
|
+
const scheduler = new SchedulerService({ store, execution, now })
|
|
111
|
+
scheduler.start()
|
|
112
|
+
disposers.push(() => scheduler.dispose())
|
|
113
|
+
|
|
114
|
+
return () => {
|
|
115
|
+
disposeRoutes?.()
|
|
116
|
+
for (const dispose of disposers.splice(0)) dispose()
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
return () => {
|
|
121
|
+
for (const dispose of disposers.splice(0)) dispose()
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
}
|
package/src/invariant.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invariant companion for dsh-taskboard.
|
|
3
|
+
*
|
|
4
|
+
* DSH convention (see dsh-base / dsh-workspace): packages may ship an
|
|
5
|
+
* `./invariant` export — a minimal companion plugin that reserves package
|
|
6
|
+
* ownership in compositions that load invariants without the full plugin.
|
|
7
|
+
* No behavior in P0; the real plugin carries everything.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Cordis plugin name. */
|
|
11
|
+
export const name = 'dsh-taskboard-invariant'
|
|
12
|
+
|
|
13
|
+
/** No services required. */
|
|
14
|
+
export const inject: string[] = []
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Register the invariant companion (no-op in P0).
|
|
18
|
+
* @param _ctx - the plugin context.
|
|
19
|
+
*/
|
|
20
|
+
export function apply(_ctx: unknown): void {
|
|
21
|
+
/* nothing to reserve yet */
|
|
22
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire contract for the /taskboard host routes: the JSON envelope,
|
|
3
|
+
* request/response shapes, and SSE event payloads shared by the host routes
|
|
4
|
+
* and the browser client.
|
|
5
|
+
*
|
|
6
|
+
* @module dsh-taskboard/shared/api
|
|
7
|
+
*/
|
|
8
|
+
import type { TaskLedger, TaskRecord, TaskSummary } from './protocol.ts'
|
|
9
|
+
|
|
10
|
+
export type { TaskRecord }
|
|
11
|
+
|
|
12
|
+
/** Route prefix on the shared DSH webserver (same origin as the GUI). */
|
|
13
|
+
export const ROUTE_PREFIX = '/dsh-taskboard'
|
|
14
|
+
|
|
15
|
+
/** SSE stream path (exact route; longest-prefix wins keep it disjoint). */
|
|
16
|
+
export const SSE_PATH = '/dsh-taskboard/events'
|
|
17
|
+
|
|
18
|
+
/** Stable error codes (mirror the tool-level codes plus HTTP mapping). */
|
|
19
|
+
export type ApiErrorCode =
|
|
20
|
+
| 'invalid_input'
|
|
21
|
+
| 'not_found'
|
|
22
|
+
| 'version_conflict'
|
|
23
|
+
| 'invalid_transition'
|
|
24
|
+
| 'forbidden'
|
|
25
|
+
| 'internal'
|
|
26
|
+
|
|
27
|
+
/** Success envelope. */
|
|
28
|
+
export type ApiOk<T> = { ok: true; value: T }
|
|
29
|
+
|
|
30
|
+
/** Failure envelope. */
|
|
31
|
+
export type ApiFail = { ok: false; error: { code: ApiErrorCode; message: string } }
|
|
32
|
+
|
|
33
|
+
/** The envelope either way. */
|
|
34
|
+
export type ApiResult<T> = ApiOk<T> | ApiFail
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// payloads
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
/** Full-state response (the reconnect baseline after an SSE gap). */
|
|
41
|
+
export type StateResponse = TaskLedger
|
|
42
|
+
|
|
43
|
+
/** Workspace listing for the UI pickers. */
|
|
44
|
+
export type WorkspaceView = { id: string; path: string; title: string; sessionCount: number }
|
|
45
|
+
|
|
46
|
+
/** Create-task request body (actor is always the GUI user). */
|
|
47
|
+
export type CreateTaskBody = {
|
|
48
|
+
title: string
|
|
49
|
+
workspaceId: string
|
|
50
|
+
urgency: string
|
|
51
|
+
description?: string
|
|
52
|
+
prompt?: string
|
|
53
|
+
execution?: { mode?: string; cron?: string }
|
|
54
|
+
model?: { provider: string; model: string }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Update-task request body (ifVersion mandatory). */
|
|
58
|
+
export type UpdateTaskBody = {
|
|
59
|
+
ifVersion: number
|
|
60
|
+
title?: string
|
|
61
|
+
description?: string
|
|
62
|
+
prompt?: string
|
|
63
|
+
urgency?: string
|
|
64
|
+
blocked?: boolean
|
|
65
|
+
/** Rebind the task to another project (GUI owner surface only). */
|
|
66
|
+
workspaceId?: string
|
|
67
|
+
execution?: { mode?: string; cron?: string }
|
|
68
|
+
model?: { provider: string; model: string } | null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Move-task request body (ifVersion mandatory; the user MAY move to done). */
|
|
72
|
+
export type MoveTaskBody = { ifVersion: number; status: string }
|
|
73
|
+
|
|
74
|
+
/** Comment request body. */
|
|
75
|
+
export type CommentBody = { body: string }
|
|
76
|
+
|
|
77
|
+
/** Delete request body (purge=true physically removes a trashed task). */
|
|
78
|
+
export type DeleteTaskBody = { ifVersion?: number; purge?: boolean }
|
|
79
|
+
|
|
80
|
+
/** Run request body (P3). */
|
|
81
|
+
export type RunTaskBody = Record<string, never>
|
|
82
|
+
|
|
83
|
+
/** One task (full record) response. */
|
|
84
|
+
export type TaskResponse = TaskRecord
|
|
85
|
+
|
|
86
|
+
/** Summary response used by list-ish endpoints. */
|
|
87
|
+
export type SummaryResponse = { tasks: TaskSummary[] }
|
|
88
|
+
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// SSE
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
/** Change frame pushed on every committed ledger mutation. */
|
|
94
|
+
export type ChangeEvent = {
|
|
95
|
+
revision: number
|
|
96
|
+
kind: 'task-created' | 'task-updated' | 'task-moved' | 'task-deleted' | 'comment-added' | 'execution-recorded'
|
|
97
|
+
tasks: TaskSummary[]
|
|
98
|
+
}
|