dsh-task-ledger 0.0.3 → 0.0.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.
@@ -0,0 +1,140 @@
1
+ import { getBoardSnapshot, getClosedCounts, getFilterOptions, queryClosed } from './board.js';
2
+ import { getTask, readTasks, readWorklog } from './ledger-read.js';
3
+ import { resolveWorkspaces } from './workspace.js';
4
+ /** API 前缀(唯一真源就在这一处)。 */
5
+ const API_PREFIX = '/api/task-ledger';
6
+ function sendJson(res, status, body) {
7
+ res.writeHead(status, { 'content-type': 'application/json; charset=utf-8' });
8
+ res.end(JSON.stringify(body));
9
+ }
10
+ /** URL query → LedgerFilter(只认白名单键;limit/months 非正数忽略)。 */
11
+ function filterFromQuery(url) {
12
+ const filter = {};
13
+ const str = (key) => {
14
+ const value = url.searchParams.get(key);
15
+ return value !== null && value !== '' ? value : null;
16
+ };
17
+ const int = (key) => {
18
+ const raw = str(key);
19
+ if (raw === null)
20
+ return undefined;
21
+ const value = Number.parseInt(raw, 10);
22
+ return Number.isFinite(value) && value > 0 ? value : undefined;
23
+ };
24
+ const status = str('status');
25
+ if (status !== null)
26
+ filter.status = status;
27
+ const owner = str('owner');
28
+ if (owner !== null)
29
+ filter.owner = owner;
30
+ const category = str('category');
31
+ if (category !== null)
32
+ filter.category = category;
33
+ const tag = str('tag');
34
+ if (tag !== null)
35
+ filter.tag = tag;
36
+ const since = str('since');
37
+ if (since !== null)
38
+ filter.since = since;
39
+ const until = str('until');
40
+ if (until !== null)
41
+ filter.until = until;
42
+ const limit = int('limit');
43
+ if (limit !== undefined)
44
+ filter.limit = limit;
45
+ const months = int('months');
46
+ if (months !== undefined)
47
+ filter.months = months;
48
+ return filter;
49
+ }
50
+ /** 每请求重新解析工作区(registry 可能刚变;existsSync × N 是微秒级,不缓存)。 */
51
+ function workspacesOf(deps) {
52
+ return resolveWorkspaces(deps.registry.list());
53
+ }
54
+ export function registerRoutes(webServer, deps) {
55
+ webServer.register({
56
+ kind: 'exact',
57
+ path: `${API_PREFIX}/workspaces`,
58
+ handler: (_req, res) => { sendJson(res, 200, { workspaces: workspacesOf(deps) }); },
59
+ });
60
+ // 首页一次拿全:在办快照 + 两个完成类统计。
61
+ webServer.register({
62
+ kind: 'exact',
63
+ path: `${API_PREFIX}/board`,
64
+ handler: async (_req, res) => {
65
+ try {
66
+ const workspaces = workspacesOf(deps);
67
+ const [snapshot, closed] = await Promise.all([getBoardSnapshot(workspaces), getClosedCounts(workspaces)]);
68
+ sendJson(res, 200, { ...snapshot, closed });
69
+ }
70
+ catch (error) {
71
+ sendJson(res, 500, { error: error instanceof Error ? error.message : String(error) });
72
+ }
73
+ },
74
+ });
75
+ webServer.register({
76
+ kind: 'exact',
77
+ path: `${API_PREFIX}/tasks`,
78
+ handler: async (req, res) => {
79
+ try {
80
+ const filter = filterFromQuery(new URL(req.url ?? '/', 'http://localhost'));
81
+ const perWorkspace = await Promise.all(workspacesOf(deps).map((workspace) => readTasks(workspace, filter)));
82
+ sendJson(res, 200, { tasks: perWorkspace.flat() });
83
+ }
84
+ catch (error) {
85
+ sendJson(res, 500, { error: error instanceof Error ? error.message : String(error) });
86
+ }
87
+ },
88
+ });
89
+ // 任务详情 + 过程文档正文(query 传 ws/id,不做路径段解析——任务号含中文和连字符)。
90
+ webServer.register({
91
+ kind: 'exact',
92
+ path: `${API_PREFIX}/task`,
93
+ handler: async (req, res) => {
94
+ try {
95
+ const url = new URL(req.url ?? '/', 'http://localhost');
96
+ const workspace = workspacesOf(deps).find((item) => item.id === url.searchParams.get('ws'));
97
+ const taskId = url.searchParams.get('id');
98
+ if (workspace === undefined || taskId === null) {
99
+ sendJson(res, 400, { error: '缺少 ws / id 参数' });
100
+ return;
101
+ }
102
+ const task = await getTask(workspace, taskId);
103
+ if (task === null) {
104
+ sendJson(res, 404, { error: '任务不存在' });
105
+ return;
106
+ }
107
+ sendJson(res, 200, { task, worklog: await readWorklog(workspace, task) });
108
+ }
109
+ catch (error) {
110
+ sendJson(res, 500, { error: error instanceof Error ? error.message : String(error) });
111
+ }
112
+ },
113
+ });
114
+ webServer.register({
115
+ kind: 'exact',
116
+ path: `${API_PREFIX}/history`,
117
+ handler: async (req, res) => {
118
+ try {
119
+ const filter = filterFromQuery(new URL(req.url ?? '/', 'http://localhost'));
120
+ sendJson(res, 200, { records: await queryClosed(workspacesOf(deps), filter) });
121
+ }
122
+ catch (error) {
123
+ sendJson(res, 500, { error: error instanceof Error ? error.message : String(error) });
124
+ }
125
+ },
126
+ });
127
+ webServer.register({
128
+ kind: 'exact',
129
+ path: `${API_PREFIX}/filters`,
130
+ handler: async (_req, res) => {
131
+ try {
132
+ sendJson(res, 200, await getFilterOptions(workspacesOf(deps)));
133
+ }
134
+ catch (error) {
135
+ sendJson(res, 500, { error: error instanceof Error ? error.message : String(error) });
136
+ }
137
+ },
138
+ });
139
+ // POST /sessions(按钮会话交接)归「任务按钮与会话交接」任务——写入路径 A/B 拍板后再接。
140
+ }
@@ -0,0 +1,23 @@
1
+ export type LedgerScriptResult<T> = {
2
+ ok: true;
3
+ data: T;
4
+ } | {
5
+ ok: false;
6
+ error: string;
7
+ };
8
+ /**
9
+ * 全插件唯一碰 Python 的地方:spawn python3 跑内嵌 skill 脚本,收 stdout JSON。
10
+ * 不解析任何台账格式——解析真源只有 skill 脚本一份(2026-10-17 用户拍板)。
11
+ *
12
+ * 出错一律归一为 { ok:false, error }(人话,直接可上界面):
13
+ * 起不了 python3 / 超时(kill)/ 非零退出(stderr 原文)/ 输出不是 JSON。
14
+ *
15
+ * @param sub - 子命令(list / history / show…)
16
+ * @param args - 参数数组,由调用方自拼(含 --repo / --json / --months 等)
17
+ * @param opts.scriptPath - 测试用:覆盖脚本路径
18
+ * @param opts.timeoutMs - 覆盖超时
19
+ */
20
+ export declare function runLedgerScript<T = unknown>(sub: string, args: readonly string[], opts?: {
21
+ scriptPath?: string;
22
+ timeoutMs?: number;
23
+ }): Promise<LedgerScriptResult<T>>;
@@ -0,0 +1,59 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { fileURLToPath } from 'node:url';
3
+ /**
4
+ * 内嵌 skill 脚本的默认路径:dist/host/script.js → 包根 skill/scripts/progress.py。
5
+ * 构建时 scripts/copy-skill.mjs 把 packages/skill 拷到 packages/plugin/skill/。
6
+ */
7
+ const DEFAULT_SCRIPT_PATH = fileURLToPath(new URL('../../skill/scripts/progress.py', import.meta.url));
8
+ /** 单次脚本调用超时上限(ms)。实测单仓 5 万行全量 1.2s,10s 是宽松上限。 */
9
+ const DEFAULT_TIMEOUT_MS = 10_000;
10
+ /**
11
+ * 全插件唯一碰 Python 的地方:spawn python3 跑内嵌 skill 脚本,收 stdout JSON。
12
+ * 不解析任何台账格式——解析真源只有 skill 脚本一份(2026-10-17 用户拍板)。
13
+ *
14
+ * 出错一律归一为 { ok:false, error }(人话,直接可上界面):
15
+ * 起不了 python3 / 超时(kill)/ 非零退出(stderr 原文)/ 输出不是 JSON。
16
+ *
17
+ * @param sub - 子命令(list / history / show…)
18
+ * @param args - 参数数组,由调用方自拼(含 --repo / --json / --months 等)
19
+ * @param opts.scriptPath - 测试用:覆盖脚本路径
20
+ * @param opts.timeoutMs - 覆盖超时
21
+ */
22
+ export function runLedgerScript(sub, args, opts) {
23
+ const scriptPath = opts?.scriptPath ?? DEFAULT_SCRIPT_PATH;
24
+ const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
25
+ return new Promise((resolve) => {
26
+ const child = spawn('python3', [scriptPath, sub, ...args], { stdio: ['ignore', 'pipe', 'pipe'] });
27
+ let stdout = '';
28
+ let stderr = '';
29
+ let settled = false;
30
+ const timer = setTimeout(() => {
31
+ child.kill();
32
+ settle({ ok: false, error: `skill 脚本超时(>${timeoutMs}ms):${sub} ${args.join(' ')}` });
33
+ }, timeoutMs);
34
+ const settle = (result) => {
35
+ if (settled)
36
+ return;
37
+ settled = true;
38
+ clearTimeout(timer);
39
+ resolve(result);
40
+ };
41
+ child.stdout.on('data', (chunk) => { stdout += chunk.toString(); });
42
+ child.stderr.on('data', (chunk) => { stderr += chunk.toString(); });
43
+ child.on('error', (error) => {
44
+ settle({ ok: false, error: `起不了 python3(${error.message})——本插件需要 python3 运行内嵌 skill 脚本` });
45
+ });
46
+ child.on('close', (code) => {
47
+ if (code !== 0) {
48
+ settle({ ok: false, error: stderr.trim() || `skill 脚本退出码 ${String(code)}` });
49
+ return;
50
+ }
51
+ try {
52
+ settle({ ok: true, data: JSON.parse(stdout) });
53
+ }
54
+ catch {
55
+ settle({ ok: false, error: `skill 脚本输出不是 JSON:${stdout.slice(0, 200)}` });
56
+ }
57
+ });
58
+ });
59
+ }
@@ -0,0 +1,27 @@
1
+ /** 宿主 ctx.skills.register 的最小面(外部事实文档的结构面)。 */
2
+ export interface SkillRegistration {
3
+ name: string;
4
+ description: string;
5
+ content: string;
6
+ whenToUse?: string;
7
+ path?: string;
8
+ source?: string;
9
+ resourceBase?: {
10
+ kind: 'directory';
11
+ path: string;
12
+ };
13
+ metadata?: Record<string, unknown>;
14
+ }
15
+ /** 宿主 skills 服务的最小面。 */
16
+ export interface SkillsRegistry {
17
+ register(skill: SkillRegistration): () => void;
18
+ }
19
+ /**
20
+ * 组装注册体:读内嵌 SKILL.md → 取 name / description → 正文里的脚本路径占位符
21
+ * 替换为绝对路径。
22
+ *
23
+ * 相对资源解析的二选一(external/dsh-skill-registration.md §三)**未拍板**;
24
+ * 最小闭环选「插值绝对路径」——resourceBase 的消费者呈现未验证,不押注。
25
+ * 拍板后如改走 resourceBase,只动本函数,调用方无感。
26
+ */
27
+ export declare function buildSkillRegistration(skillDir?: string): SkillRegistration;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * 内嵌 skill 的注册体组装(任务 2「skill 注册最小闭环」)。
3
+ * 宿主事实(0.2.0-rc.2 npm pack 亲验):external/dsh-skill-registration.md。
4
+ */
5
+ import { readFileSync } from 'node:fs';
6
+ import { fileURLToPath } from 'node:url';
7
+ /** 内嵌 skill 目录:dist/host/skill-registration.js → 包根 skill/(copy-skill 产物)。 */
8
+ const SKILL_DIR = fileURLToPath(new URL('../../skill', import.meta.url));
9
+ /** SKILL.md 里指示脚本位置的占位符(注册时替换为绝对路径,模型据此找到脚本)。 */
10
+ const SKILL_DIR_TOKEN = '<本 SKILL.md 所在目录>';
11
+ /** frontmatter 最小解析:只取 name / description(不引 YAML 库;值去首尾引号)。 */
12
+ function parseFrontmatter(text) {
13
+ const match = /^---\n([\s\S]*?)\n---/.exec(text);
14
+ if (match === null)
15
+ throw new Error('SKILL.md 缺 frontmatter(--- 包裹的头)');
16
+ const field = (key) => {
17
+ const line = (match[1] ?? '').split('\n').find((item) => item.startsWith(`${key}:`));
18
+ if (line === undefined)
19
+ throw new Error(`SKILL.md frontmatter 缺 ${key}`);
20
+ return line.slice(key.length + 1).trim().replace(/^["']|["']$/g, '');
21
+ };
22
+ return { name: field('name'), description: field('description') };
23
+ }
24
+ /**
25
+ * 组装注册体:读内嵌 SKILL.md → 取 name / description → 正文里的脚本路径占位符
26
+ * 替换为绝对路径。
27
+ *
28
+ * 相对资源解析的二选一(external/dsh-skill-registration.md §三)**未拍板**;
29
+ * 最小闭环选「插值绝对路径」——resourceBase 的消费者呈现未验证,不押注。
30
+ * 拍板后如改走 resourceBase,只动本函数,调用方无感。
31
+ */
32
+ export function buildSkillRegistration(skillDir = SKILL_DIR) {
33
+ const text = readFileSync(`${skillDir}/SKILL.md`, 'utf-8');
34
+ const { name, description } = parseFrontmatter(text);
35
+ const content = text.split(SKILL_DIR_TOKEN).join(skillDir);
36
+ if (content.includes(SKILL_DIR_TOKEN)) {
37
+ throw new Error('占位符替换失败:SKILL.md 里仍残留脚本路径占位符');
38
+ }
39
+ if (!content.includes(`${skillDir}/scripts/progress.py`)) {
40
+ throw new Error('占位符替换异常:替换后找不到脚本路径');
41
+ }
42
+ return { name, description, content, path: `${skillDir}/SKILL.md` };
43
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * 后端方法共享类型。
3
+ * 宿主能力(workspaceRegistry / webServer 等)的结构面在 index.ts 与 routes.ts 本地声明——
4
+ * 不 import 官方包(参考插件同款纪律:宿主 rc 接口会无声变,结构化声明 + external/ 文档坐标
5
+ * 比绑死官方类型更稳)。
6
+ */
7
+ /** 一个活跃台账工作区:宿主 registry 条目 + 本地探测(ledger/progress.json 存在)。 */
8
+ export interface LedgerWorkspace {
9
+ /** 宿主工作区 id。 */
10
+ readonly id: string;
11
+ /** 宿主 title(允许重名)。 */
12
+ readonly title: string;
13
+ /** canonical 绝对路径(宿主已 realpath 归一;skill 脚本 --repo 直传它)。 */
14
+ readonly path: string;
15
+ }
16
+ /** progress.json 任务对象的原始字段(skill 脚本 list --json 的值形状;缺字段按可选处理)。 */
17
+ export interface TaskRecord {
18
+ title?: string;
19
+ status?: string;
20
+ creator?: string | null;
21
+ owner?: string | null;
22
+ created?: string | null;
23
+ started?: string | null;
24
+ finished?: string | null;
25
+ category?: string | null;
26
+ tags?: string[] | null;
27
+ worklog?: string | null;
28
+ }
29
+ /** 一条在办任务 = 原始字段 + 任务号 + 归属工作区(读取时打上)。 */
30
+ export interface TaskRow extends TaskRecord {
31
+ readonly id: string;
32
+ readonly workspace: LedgerWorkspace;
33
+ }
34
+ /** history 一条已结案记录(脚本 history --json 的 record 形状;字段名以脚本为准)。 */
35
+ export interface HistoryRecord {
36
+ /** 结案时间(首列)。 */
37
+ time?: string;
38
+ created?: string | null;
39
+ workpackage?: string;
40
+ category?: string | null;
41
+ tags?: string[] | null;
42
+ owner?: string;
43
+ /** 「完成了什么」列。 */
44
+ summary?: string;
45
+ /** 过程文档链接列。 */
46
+ log?: string | null;
47
+ /** 行所在历史文件(相对工作区,脚本打上)。 */
48
+ src?: string;
49
+ /** 归属工作区(读取时打上)。 */
50
+ readonly workspace?: LedgerWorkspace;
51
+ }
52
+ /** list / history 共用的过滤面(与 skill 脚本参数一一对应)。 */
53
+ export interface LedgerFilter {
54
+ /** 状态(open/doing/blocked;history 侧忽略)。 */
55
+ status?: string;
56
+ owner?: string;
57
+ category?: string;
58
+ tag?: string;
59
+ /** 创建时间起 / 止(YYYY-MM-DD [HH:mm[:ss]],脚本会补全)。 */
60
+ since?: string;
61
+ until?: string;
62
+ /** 列表分页大小(history 侧;script 的 --limit 在过滤 + 倒序后取前 N)。 */
63
+ limit?: number;
64
+ /** 只读最近 N 个月的历史文件(插件传 13;不传 = 脚本默认读全部)。 */
65
+ months?: number;
66
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * 后端方法共享类型。
3
+ * 宿主能力(workspaceRegistry / webServer 等)的结构面在 index.ts 与 routes.ts 本地声明——
4
+ * 不 import 官方包(参考插件同款纪律:宿主 rc 接口会无声变,结构化声明 + external/ 文档坐标
5
+ * 比绑死官方类型更稳)。
6
+ */
7
+ export {};
@@ -0,0 +1,13 @@
1
+ import type { LedgerWorkspace } from './types.js';
2
+ /** 宿主 workspaceRegistry.list() 条目的最小面(结构声明;事实与坐标见 external/dsh-workspace-registry.md)。 */
3
+ export interface RegistryWorkspace {
4
+ readonly id: string;
5
+ readonly title: string;
6
+ readonly path: string;
7
+ }
8
+ /**
9
+ * 工作区发现:宿主 registry 全量 → 逐个查 <path>/ledger/progress.json,只返回有的。
10
+ * 未初始化(有 ledger/ 无 progress.json)= 隐藏,后端无此概念(2026-10-17 拍板)。
11
+ * 补充源(registry 未收录目录)暂不做——形态待拍板,见 architecture §五。
12
+ */
13
+ export declare function resolveWorkspaces(registryList: readonly RegistryWorkspace[]): LedgerWorkspace[];
@@ -0,0 +1,16 @@
1
+ import { existsSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ /**
4
+ * 工作区发现:宿主 registry 全量 → 逐个查 <path>/ledger/progress.json,只返回有的。
5
+ * 未初始化(有 ledger/ 无 progress.json)= 隐藏,后端无此概念(2026-10-17 拍板)。
6
+ * 补充源(registry 未收录目录)暂不做——形态待拍板,见 architecture §五。
7
+ */
8
+ export function resolveWorkspaces(registryList) {
9
+ const found = [];
10
+ for (const workspace of registryList) {
11
+ if (existsSync(path.join(workspace.path, 'ledger', 'progress.json'))) {
12
+ found.push({ id: workspace.id, title: workspace.title, path: workspace.path });
13
+ }
14
+ }
15
+ return found;
16
+ }
@@ -0,0 +1,9 @@
1
+ export declare const name = "dsh-task-ledger";
2
+ /** 插件上下文的最小面(结构声明,不 import 官方包)。 */
3
+ interface PluginContext {
4
+ inject(deps: readonly string[], callback: (ctx: Record<string, unknown>) => void): void;
5
+ /** 组合内副作用:进入时 setup、离开时执行返回的清理函数(cordis 核心面)。 */
6
+ effect(setup: () => (() => void) | undefined): void;
7
+ }
8
+ export declare function apply(ctx: PluginContext): void;
9
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,42 @@
1
+ /**
2
+ * dsh-task-ledger host 半入口:装配层,不写业务逻辑。
3
+ *
4
+ * 已落地:数据面路由(任务 3,routes.ts)、内嵌 skill 全局注册(任务 2,
5
+ * skill-registration.ts)。入口 / 图标 / 设置页(任务 1)与按钮会话交接
6
+ * (任务 4)各自落地时再往本文件加装配,业务逻辑一律进 host/ 对应文件。
7
+ *
8
+ * 服务等待写在 apply 内的 ctx.inject(参考插件真机教训:顶层 inject 声明组合
9
+ * 满足不了的依赖会令 entry 一直 pending、卡死整个 dsh 启动);两条注入线
10
+ * 互相独立——skills 缺席不挡数据面,反之亦然。
11
+ */
12
+ import { registerRoutes } from './host/routes.js';
13
+ import { buildSkillRegistration } from './host/skill-registration.js';
14
+ export const name = 'dsh-task-ledger';
15
+ export function apply(ctx) {
16
+ // ── 数据面(任务 3) ──
17
+ ctx.inject(['workspaceRegistry', 'webServer'], (services) => {
18
+ const registry = services.workspaceRegistry;
19
+ const webServer = services.webServer;
20
+ if (registry === undefined || webServer === undefined) {
21
+ console.warn('[task-ledger] workspaceRegistry / webServer 未就位,数据面路由不注册');
22
+ return;
23
+ }
24
+ registerRoutes(webServer, { registry });
25
+ console.info('[task-ledger] 数据面路由已注册(/api/task-ledger/*)');
26
+ });
27
+ // ── skill 全局注册(任务 2) ──
28
+ // unscoped 注册落全局层,模型可 load;disposer 随插件 dispose 自动注销。
29
+ ctx.inject(['skills'], (services) => {
30
+ const skills = services.skills;
31
+ if (skills === undefined || typeof skills.register !== 'function') {
32
+ console.warn('[task-ledger] skills 服务未就位,内嵌 skill 不注册(数据面不受影响)');
33
+ return;
34
+ }
35
+ ctx.effect(() => {
36
+ const registration = buildSkillRegistration();
37
+ const dispose = skills.register(registration);
38
+ console.info(`[task-ledger] 已注册内嵌 skill:${registration.name}(正文 ${registration.content.length} 字)`);
39
+ return dispose;
40
+ });
41
+ });
42
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-task-ledger",
3
- "version": "0.0.3",
4
- "description": "Name reserved: upcoming dsh (DeepSeek Harness) plugin — task ledger board with the koc-ledger-progress skill bundled. First functional release in development.",
3
+ "version": "0.0.6",
4
+ "description": "Task ledger board for dsh (DeepSeek Harness): read-only board over task-ledger skill workspaces. The task-ledger skill is bundled — install the plugin, get the skill globally.",
5
5
  "keywords": [
6
6
  "deepseek",
7
7
  "harness",
@@ -10,12 +10,50 @@
10
10
  "task-ledger"
11
11
  ],
12
12
  "license": "MIT",
13
+ "type": "module",
14
+ "main": "dist/index.js",
15
+ "exports": {
16
+ ".": "./dist/index.js",
17
+ "./client": "./dist/client.js",
18
+ "./package.json": "./package.json"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "skill",
23
+ "cordis.patch.yml",
24
+ "README.md"
25
+ ],
26
+ "scripts": {
27
+ "build": "npm run build:host && npm run build:client",
28
+ "build:host": "tsc && node scripts/copy-skill.mjs",
29
+ "build:client": "tsc --noEmit -p tsconfig.client.json && tsdown --config tsdown.client.config.ts",
30
+ "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.client.json",
31
+ "smoke": "node scripts/smoke.mjs"
32
+ },
33
+ "engines": {
34
+ "node": ">=22.5"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^22.20.4",
38
+ "@types/react": "~18.3.1",
39
+ "tsdown": "^0.23.0",
40
+ "typescript": "^5.9.3"
41
+ },
13
42
  "repository": {
14
43
  "type": "git",
15
44
  "url": "git+https://github.com/cq-guojia/dsh-task-ledger.git"
16
45
  },
17
- "type": "module",
18
- "files": [
19
- "README.md"
20
- ]
46
+ "dsh": {
47
+ "bundle": {
48
+ "patch": "./cordis.patch.yml"
49
+ },
50
+ "client": {
51
+ "platform": "web",
52
+ "inject": [
53
+ "@deepseek-ai/dsh-client-locale",
54
+ "@deepseek-ai/dsh-client-ui-renderer",
55
+ "@deepseek-ai/dsh-client-ui-layout"
56
+ ]
57
+ }
58
+ }
21
59
  }