@dennisrongo/dsh-todo 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/lib/index.js ADDED
@@ -0,0 +1,353 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
5
+ var __typeError = (msg) => {
6
+ throw TypeError(msg);
7
+ };
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
10
+ var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
11
+ var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
12
+ var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
13
+ var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
14
+ var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
15
+ var __runInitializers = (array, flags, self, value) => {
16
+ for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
17
+ return value;
18
+ };
19
+ var __decorateElement = (array, flags, name, decorators, target, extra) => {
20
+ var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
21
+ var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
22
+ var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
23
+ var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
24
+ return __privateGet(this, extra);
25
+ }, set [name](x) {
26
+ return __privateSet(this, extra, x);
27
+ } }, name));
28
+ k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
29
+ for (var i = decorators.length - 1; i >= 0; i--) {
30
+ ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
31
+ if (k) {
32
+ ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
33
+ if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
34
+ if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
35
+ }
36
+ it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
37
+ if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
38
+ else if (typeof it !== "object" || it === null) __typeError("Object expected");
39
+ else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
40
+ }
41
+ return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
42
+ };
43
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
44
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
45
+ var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
46
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
47
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
48
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
49
+
50
+ // src/index.ts
51
+ import { mkdirSync, readFileSync, renameSync, existsSync } from "node:fs";
52
+ import { DatabaseSync } from "node:sqlite";
53
+ import { isAbsolute, join, resolve } from "node:path";
54
+ import { Service } from "@deepseek-ai/cordis";
55
+ import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
56
+ import { z } from "zod";
57
+
58
+ // src/types.ts
59
+ var MAX_TEXT = 500;
60
+ var MAX_ITEMS = 1e3;
61
+
62
+ // src/index.ts
63
+ var todoItemSchema = z.object({
64
+ id: z.string().min(1),
65
+ text: z.string().max(MAX_TEXT),
66
+ done: z.boolean(),
67
+ createdAt: z.number(),
68
+ completedAt: z.number().optional(),
69
+ archivedAt: z.number().optional()
70
+ });
71
+ var todoListSchema = z.object({
72
+ items: z.array(todoItemSchema).max(MAX_ITEMS),
73
+ revision: z.number(),
74
+ updatedAt: z.number()
75
+ });
76
+ var todoDomainSpec = {
77
+ name: "dsh_todo",
78
+ version: 2
79
+ };
80
+ var DOT_DSH = ".dsh";
81
+ var DB_FILE = "todo.db";
82
+ var _replace_dec, _list_dec, _a, _init, _b;
83
+ var _TodoService = class _TodoService extends (_b = TypertRemoteService) {
84
+ /**
85
+ * @param ctx - host context carrying the workspace registry.
86
+ */
87
+ constructor(ctx) {
88
+ super(ctx, "dshTodo");
89
+ __runInitializers(_init, 5, this);
90
+ /** Open database handle per workspace id, kept for the service lifetime. */
91
+ __publicField(this, "dbs", /* @__PURE__ */ new Map());
92
+ /** Resolved workspace directory per workspace id. */
93
+ __publicField(this, "dirs", /* @__PURE__ */ new Map());
94
+ /** Per-workspace write chain, keyed by workspace id. */
95
+ __publicField(this, "tails", /* @__PURE__ */ new Map());
96
+ }
97
+ /**
98
+ * Migrate the legacy central JSON store into the per-workspace databases.
99
+ *
100
+ * The legacy layout kept one `<home>/storages/dsh_todo.json` keyed by opaque
101
+ * workspace uuid. That uuid maps to a directory through the same home's
102
+ * `storages/workspace.json`, so the lists CAN be carried over: each one is
103
+ * imported into `<workspace>/.dsh/todo.db` with its revision and timestamps
104
+ * preserved, and only then is the legacy file renamed `.migrated`.
105
+ *
106
+ * Runs once per harness home; a workspace whose db already has content is
107
+ * left untouched, so a half-finished migration never clobbers newer data.
108
+ */
109
+ async [(_a = Service.init, _list_dec = [Remote], _replace_dec = [Remote], _a)]() {
110
+ this.ctx.effect(() => () => this.close(), "dsh-todo: close workspace databases");
111
+ const home = process.env.DSH_HOME;
112
+ if (!home) return;
113
+ const legacyPath = join(home, "storages", "dsh_todo.json");
114
+ const registryPath = join(home, "storages", "workspace.json");
115
+ try {
116
+ if (!existsSync(legacyPath)) return;
117
+ const legacy = JSON.parse(readFileSync(legacyPath, "utf8"));
118
+ const records = legacy?.tables?.workspaces ?? {};
119
+ let mapping = {};
120
+ if (existsSync(registryPath)) {
121
+ mapping = JSON.parse(readFileSync(registryPath, "utf8"))?.tables?.workspaces ?? {};
122
+ }
123
+ let importedItems = 0;
124
+ let importedWorkspaces = 0;
125
+ let unmapped = 0;
126
+ for (const [uuid, record] of Object.entries(records)) {
127
+ const dir = mapping[uuid]?.path;
128
+ if (!dir || !isAbsolute(dir)) {
129
+ unmapped += Object.keys(record?.items ?? {}).length ? 1 : 0;
130
+ continue;
131
+ }
132
+ const db = this.openDb(dir);
133
+ const current = this.readList(db);
134
+ if (current.revision > 0) continue;
135
+ const items = sanitizeItems(record?.items);
136
+ this.writeList(db, items, (record?.revision ?? 0) + 1, record?.updatedAt ?? Date.now());
137
+ importedItems += items.length;
138
+ importedWorkspaces += 1;
139
+ }
140
+ const stamp = legacyPath.replace(/\.json$/, ".migrated");
141
+ if (!existsSync(stamp)) renameSync(legacyPath, stamp);
142
+ console.log(
143
+ `[dsh-todo] legacy store migrated: ${importedItems} item(s) into ${importedWorkspaces} workspace db(s)` + (unmapped > 0 ? `; ${unmapped} workspace(s) had no directory mapping and were skipped` : "") + ` -> ${stamp}`
144
+ );
145
+ } catch (err) {
146
+ console.warn(`[dsh-todo] legacy store migration deferred:`, err);
147
+ }
148
+ }
149
+ /**
150
+ * Resolve a workspace id to its canonical directory via the registry.
151
+ * @param workspaceId - the workspace to resolve.
152
+ * @returns the canonical workspace directory.
153
+ */
154
+ workspaceDir(workspaceId) {
155
+ if (typeof workspaceId !== "string" || workspaceId.length === 0) {
156
+ throw new Error("dsh-todo: workspaceId must be a non-empty string");
157
+ }
158
+ const cached = this.dirs.get(workspaceId);
159
+ if (cached) return cached;
160
+ const registry = this.ctx.workspaceRegistry;
161
+ const workspace = registry.list().find((w) => String(w.id) === workspaceId);
162
+ if (workspace === void 0) throw new Error(`dsh-todo: unknown workspace ${workspaceId}`);
163
+ const dir = resolve(workspace.path);
164
+ this.dirs.set(workspaceId, dir);
165
+ return dir;
166
+ }
167
+ /**
168
+ * Open (creating if needed) the workspace's database by directory. Handles
169
+ * are cached per resolved path; `mkdir -p` runs on every open because the
170
+ * `.dsh` directory is cheap to create and the workspace may have been
171
+ * cloned since.
172
+ */
173
+ openDb(dir) {
174
+ const resolved = resolve(dir);
175
+ const cached = this.dbs.get(resolved);
176
+ if (cached) return cached;
177
+ mkdirSync(join(resolved, DOT_DSH), { recursive: true });
178
+ const db = new DatabaseSync(join(resolved, DOT_DSH, DB_FILE));
179
+ db.exec(`
180
+ CREATE TABLE IF NOT EXISTS todo (
181
+ id TEXT PRIMARY KEY,
182
+ text TEXT NOT NULL,
183
+ done INTEGER NOT NULL DEFAULT 0,
184
+ created_at INTEGER NOT NULL,
185
+ completed_at INTEGER,
186
+ archived_at INTEGER,
187
+ position INTEGER NOT NULL
188
+ );
189
+ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
190
+ `);
191
+ this.dbs.set(resolved, db);
192
+ return db;
193
+ }
194
+ /** Resolve the workspace id, then open (or find) its database. */
195
+ db(workspaceId) {
196
+ return this.openDb(this.workspaceDir(workspaceId));
197
+ }
198
+ /** Guard: registry lookups happen on every call, so unknown ids fail loudly. */
199
+ requireDb(workspaceId) {
200
+ return this.db(workspaceId);
201
+ }
202
+ async list(request) {
203
+ const db = this.requireDb(request?.workspaceId);
204
+ return { list: this.readList(db) };
205
+ }
206
+ async replace(request) {
207
+ const workspaceId = request?.workspaceId;
208
+ if (typeof workspaceId !== "string" || workspaceId.length === 0) {
209
+ throw new Error("dsh-todo: workspaceId must be a non-empty string");
210
+ }
211
+ const items = sanitizeItems(request?.items);
212
+ const ifRevision = request?.ifRevision ?? null;
213
+ return this.enqueue(workspaceId, async () => {
214
+ const db = this.requireDb(workspaceId);
215
+ const current = this.readList(db);
216
+ const matches = ifRevision === null ? current.revision === 0 : ifRevision === current.revision;
217
+ if (!matches) return { ok: false, code: "revision-conflict", list: current };
218
+ this.writeList(db, items, current.revision + 1);
219
+ return { ok: true, list: this.readList(db) };
220
+ });
221
+ }
222
+ /** Queue one whole read/compare/write behind this workspace's prior write. */
223
+ async enqueue(workspaceId, run) {
224
+ const prior = this.tails.get(workspaceId) ?? Promise.resolve();
225
+ const next = prior.then(run, run);
226
+ const tail = next.then(
227
+ () => void 0,
228
+ () => void 0
229
+ );
230
+ this.tails.set(workspaceId, tail);
231
+ try {
232
+ return await next;
233
+ } finally {
234
+ if (this.tails.get(workspaceId) === tail) this.tails.delete(workspaceId);
235
+ }
236
+ }
237
+ /**
238
+ * Close every open database handle. Called on fiber teardown via the init
239
+ * effect; public because embedders (and tests) driving the service without
240
+ * a full cordis lifecycle need the same guarantee — on Windows an open
241
+ * handle keeps the file locked against deletion/backup.
242
+ */
243
+ close() {
244
+ for (const db of this.dbs.values()) {
245
+ try {
246
+ db.close();
247
+ } catch {
248
+ }
249
+ }
250
+ this.dbs.clear();
251
+ }
252
+ /** Read the whole list from the database, ordered by `position`. */
253
+ readList(db) {
254
+ const revision = Number(db.prepare(`SELECT value FROM meta WHERE key = 'revision'`).get()?.value ?? 0);
255
+ const updatedAt = Number(db.prepare(`SELECT value FROM meta WHERE key = 'updatedAt'`).get()?.value ?? 0);
256
+ const rows = db.prepare(
257
+ `SELECT id, text, done, created_at, completed_at, archived_at
258
+ FROM todo ORDER BY position ASC`
259
+ ).all();
260
+ const items = [];
261
+ for (const row of rows) {
262
+ const candidate = {
263
+ id: String(row.id),
264
+ text: String(row.text),
265
+ done: Number(row.done) === 1,
266
+ createdAt: Number(row.created_at),
267
+ ...row.completed_at !== null && row.completed_at !== void 0 ? { completedAt: Number(row.completed_at) } : {},
268
+ ...row.archived_at !== null && row.archived_at !== void 0 ? { archivedAt: Number(row.archived_at) } : {}
269
+ };
270
+ const parsed = todoItemSchema.safeParse(candidate);
271
+ if (parsed.success) items.push(parsed.data);
272
+ }
273
+ const list = { items, revision, updatedAt };
274
+ const check = todoListSchema.safeParse(list);
275
+ return check.success ? list : { items: [], revision, updatedAt };
276
+ }
277
+ /** Replace every row inside one transaction and stamp the meta tokens. */
278
+ writeList(db, items, revision, updatedAt = Date.now()) {
279
+ const now = updatedAt;
280
+ db.exec("BEGIN");
281
+ try {
282
+ db.prepare("DELETE FROM todo").run();
283
+ const insert = db.prepare(
284
+ `INSERT INTO todo (id, text, done, created_at, completed_at, archived_at, position)
285
+ VALUES (?, ?, ?, ?, ?, ?, ?)`
286
+ );
287
+ items.forEach((item, index) => {
288
+ insert.run(
289
+ item.id,
290
+ item.text,
291
+ item.done ? 1 : 0,
292
+ item.createdAt,
293
+ item.completedAt ?? null,
294
+ item.archivedAt ?? null,
295
+ index
296
+ );
297
+ });
298
+ db.prepare(`INSERT INTO meta (key, value) VALUES ('revision', ?)
299
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(String(revision));
300
+ db.prepare(`INSERT INTO meta (key, value) VALUES ('updatedAt', ?)
301
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(String(now));
302
+ db.exec("COMMIT");
303
+ } catch (err) {
304
+ db.exec("ROLLBACK");
305
+ throw err;
306
+ }
307
+ }
308
+ };
309
+ _init = __decoratorStart(_b);
310
+ __decorateElement(_init, 1, "list", _list_dec, _TodoService);
311
+ __decorateElement(_init, 1, "replace", _replace_dec, _TodoService);
312
+ __decoratorMetadata(_init, _TodoService);
313
+ __name(_TodoService, "TodoService");
314
+ // Per-fiber service grants: the workspace registry property is only readable
315
+ // when declared here (same contract dsh-git follows).
316
+ __publicField(_TodoService, "inject", ["workspaceRegistry"]);
317
+ var TodoService = _TodoService;
318
+ function sanitizeItems(value) {
319
+ if (!Array.isArray(value)) return [];
320
+ const out = [];
321
+ const seen = /* @__PURE__ */ new Set();
322
+ for (const entry of value) {
323
+ if (out.length >= MAX_ITEMS) break;
324
+ if (!entry || typeof entry !== "object") continue;
325
+ const e = entry;
326
+ if (typeof e.id !== "string" || e.id.length === 0) continue;
327
+ if (typeof e.text !== "string") continue;
328
+ if (seen.has(e.id)) continue;
329
+ seen.add(e.id);
330
+ const done = e.done === true;
331
+ const completedAt = typeof e.completedAt === "number" ? e.completedAt : void 0;
332
+ const archivedAt = typeof e.archivedAt === "number" ? e.archivedAt : void 0;
333
+ out.push({
334
+ id: e.id,
335
+ text: e.text.slice(0, MAX_TEXT),
336
+ done,
337
+ createdAt: typeof e.createdAt === "number" ? e.createdAt : 0,
338
+ // completedAt is meaningless on an open item; drop it rather than store a lie.
339
+ ...done && completedAt !== void 0 ? { completedAt } : {},
340
+ // archivedAt is the archived flag itself, so a non-numeric value must not
341
+ // survive as a truthy marker.
342
+ ...archivedAt !== void 0 ? { archivedAt } : {}
343
+ });
344
+ }
345
+ return out;
346
+ }
347
+ __name(sanitizeItems, "sanitizeItems");
348
+ var index_default = TodoService;
349
+ export {
350
+ TodoService,
351
+ index_default as default,
352
+ todoDomainSpec
353
+ };
@@ -0,0 +1,138 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/remote.ts
5
+ import { z } from "zod";
6
+ var todoItemSchema = z.object({
7
+ id: z.string(),
8
+ text: z.string(),
9
+ done: z.boolean(),
10
+ createdAt: z.number(),
11
+ completedAt: z.number().optional(),
12
+ // Must be carried explicitly: these are strict codecs, so a field the schema
13
+ // does not name would be stripped off the wire and archiving would not persist.
14
+ archivedAt: z.number().optional()
15
+ });
16
+ var todoListSchema = z.object({
17
+ items: z.array(todoItemSchema),
18
+ revision: z.number(),
19
+ updatedAt: z.number()
20
+ });
21
+ var listRequestSchema = z.object({ workspaceId: z.string() });
22
+ var listResultSchema = z.object({ list: todoListSchema });
23
+ var replaceRequestSchema = z.object({
24
+ workspaceId: z.string(),
25
+ items: z.array(todoItemSchema),
26
+ ifRevision: z.union([z.number(), z.literal(null)])
27
+ });
28
+ var replaceResultSchema = z.union([
29
+ z.object({ ok: z.literal(true), list: todoListSchema }),
30
+ z.object({
31
+ ok: z.literal(false),
32
+ code: z.literal("revision-conflict"),
33
+ list: todoListSchema
34
+ })
35
+ ]);
36
+ var PACKAGE = "@dennisrongo/dsh-todo";
37
+ function descriptor(method, request, result) {
38
+ return {
39
+ id: `${PACKAGE}#dshTodo/${method}`,
40
+ service: "dshTodo",
41
+ namespace: "dshTodo",
42
+ method,
43
+ invocation: { kind: "direct" },
44
+ parameters: [
45
+ {
46
+ name: "request",
47
+ // Must equal the host method's PARAMETER NAME: the host resolves this
48
+ // endpoint through SRC discovery, which reads parameter names off the
49
+ // function source.
50
+ wire: "request",
51
+ source: "json",
52
+ codec: {
53
+ mode: "strict",
54
+ typeSymbol: `${PACKAGE}/types#${method}Request`,
55
+ schema: request
56
+ }
57
+ }
58
+ ],
59
+ result: {
60
+ mode: "strict",
61
+ typeSymbol: `${PACKAGE}/types#${method}Result`,
62
+ schema: result
63
+ }
64
+ };
65
+ }
66
+ __name(descriptor, "descriptor");
67
+ var TODO_REMOTE = {
68
+ package: PACKAGE,
69
+ descriptors: [
70
+ descriptor("list", listRequestSchema, listResultSchema),
71
+ descriptor("replace", replaceRequestSchema, replaceResultSchema)
72
+ ]
73
+ };
74
+
75
+ // src/typert.host.ts
76
+ var PACKAGE2 = "@dennisrongo/dsh-todo";
77
+ var TYPERT = {
78
+ package: PACKAGE2,
79
+ face: "host",
80
+ schemas: [],
81
+ invocations: TODO_REMOTE.descriptors,
82
+ model: {
83
+ services: [
84
+ {
85
+ tags: [],
86
+ summary: "Per-workspace todo list owned by the host.",
87
+ description: "Durable owner of every workspace's todo list, stored as one SQLite database per project at <workspace>/.dsh/todo.db and resolved through workspaceRegistry.",
88
+ key: "dshTodo",
89
+ exportName: "TodoService",
90
+ members: [
91
+ {
92
+ kind: "method",
93
+ name: "list",
94
+ signature: "@Remote list(request: TodoListRequest): Promise<TodoListResult>",
95
+ summary: "Read one workspace's list."
96
+ },
97
+ {
98
+ kind: "method",
99
+ name: "replace",
100
+ signature: "@Remote replace(request: TodoReplaceRequest): Promise<TodoReplaceResult>",
101
+ summary: "Replace one workspace's list, guarded by the observed revision."
102
+ }
103
+ ],
104
+ types: [
105
+ {
106
+ name: "TodoItem",
107
+ declaration: "export interface TodoItem {\n id: string;\n text: string;\n done: boolean;\n createdAt: number;\n completedAt?: number;\n archivedAt?: number;\n}"
108
+ },
109
+ {
110
+ name: "TodoList",
111
+ declaration: "export interface TodoList {\n items: TodoItem[];\n revision: number;\n updatedAt: number;\n}"
112
+ },
113
+ {
114
+ name: "TodoListRequest",
115
+ declaration: "export interface TodoListRequest {\n workspaceId: string;\n}"
116
+ },
117
+ {
118
+ name: "TodoListResult",
119
+ declaration: "export interface TodoListResult {\n list: TodoList;\n}"
120
+ },
121
+ {
122
+ name: "TodoReplaceRequest",
123
+ declaration: "export interface TodoReplaceRequest {\n workspaceId: string;\n items: TodoItem[];\n ifRevision: number | null;\n}"
124
+ },
125
+ {
126
+ name: "TodoReplaceResult",
127
+ declaration: "export type TodoReplaceResult = { ok: true; list: TodoList } | { ok: false; code: 'revision-conflict'; list: TodoList };"
128
+ }
129
+ ]
130
+ }
131
+ ],
132
+ events: [],
133
+ objects: []
134
+ }
135
+ };
136
+ export {
137
+ TYPERT
138
+ };
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@dennisrongo/dsh-todo",
3
+ "version": "0.1.0",
4
+ "description": "Todo list for DeepSeek Harness (dsh) — a per-workspace task list persisted on disk by the host",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Dennis Rongo",
8
+ "main": "lib/index.js",
9
+ "exports": {
10
+ ".": {
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./client": {
14
+ "default": "./lib/client.js"
15
+ },
16
+ "./typert": {
17
+ "default": "./lib/typert.host.js"
18
+ },
19
+ "./package.json": "./package.json"
20
+ },
21
+ "files": [
22
+ "lib",
23
+ "!lib/client.body.cjs",
24
+ "!lib/*.test.mjs",
25
+ "README.md",
26
+ "cordis.patch.yml"
27
+ ],
28
+ "scripts": {
29
+ "build": "node build/build.mjs",
30
+ "typecheck": "tsc --noEmit",
31
+ "test": "node test/smoke.mjs"
32
+ },
33
+ "dsh": {
34
+ "bundle": {
35
+ "patch": "./cordis.patch.yml"
36
+ },
37
+ "client": {
38
+ "platform": "web",
39
+ "inject": [
40
+ "@deepseek-ai/dsh-client-runtime"
41
+ ],
42
+ "immediately": true
43
+ }
44
+ },
45
+ "pnpm": {
46
+ "onlyBuiltDependencies": [
47
+ "esbuild"
48
+ ]
49
+ },
50
+ "dependencies": {
51
+ "zod": "^4.4.3"
52
+ },
53
+ "peerDependencies": {
54
+ "@deepseek-ai/cordis": "^4.0.1",
55
+ "@deepseek-ai/dsh-storage-domain": "^0.1.1-rc.2",
56
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.1-rc.2"
57
+ },
58
+ "devDependencies": {
59
+ "@types/node": "^22.20.1",
60
+ "@types/react": "^18.3.0",
61
+ "esbuild": "^0.24.0",
62
+ "react": "^18.3.0",
63
+ "react-dom": "^18.3.0",
64
+ "typescript": "^5.6.0"
65
+ },
66
+ "repository": {
67
+ "type": "git",
68
+ "url": "git+https://github.com/dennisrongo/dsh-plugins.git",
69
+ "directory": "plugins/dsh-todo"
70
+ },
71
+ "homepage": "https://github.com/dennisrongo/dsh-plugins/tree/main/plugins/dsh-todo#readme",
72
+ "bugs": {
73
+ "url": "https://github.com/dennisrongo/dsh-plugins/issues"
74
+ }
75
+ }