@zaclinley/dsh-mailharbor 0.22.2 → 0.24.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 CHANGED
@@ -2951,7 +2951,17 @@ var ProjectStore = class {
2951
2951
  parsed = null;
2952
2952
  }
2953
2953
  const projects = parsed !== null && typeof parsed === "object" && Array.isArray(parsed.projects) ? parsed.projects : [];
2954
- this.cache = { version: 1, projects: projects.filter(isProject) };
2954
+ this.cache = {
2955
+ version: 2,
2956
+ projects: projects.filter(isProject).map((project) => ({
2957
+ ...project,
2958
+ status: project.status ?? "active",
2959
+ notes: Array.isArray(project.notes) ? project.notes : [],
2960
+ milestones: Array.isArray(project.milestones) ? project.milestones : [],
2961
+ activity: Array.isArray(project.activity) ? project.activity : [],
2962
+ sessionIds: Array.isArray(project.sessionIds) ? project.sessionIds : project.sessionId === "" ? [] : [project.sessionId]
2963
+ }))
2964
+ };
2955
2965
  return this.cache;
2956
2966
  }
2957
2967
  flush() {
@@ -3006,6 +3016,89 @@ var ProjectStore = class {
3006
3016
  this.flush();
3007
3017
  return project;
3008
3018
  }
3019
+ addNote(id, body, createdAt) {
3020
+ const project = this.get(id);
3021
+ if (project === void 0) return void 0;
3022
+ const note = {
3023
+ id: `note_${randomUUID2().slice(0, 8)}`,
3024
+ body,
3025
+ createdAt,
3026
+ taskIds: []
3027
+ };
3028
+ project.notes.unshift(note);
3029
+ project.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3030
+ this.flush();
3031
+ return project;
3032
+ }
3033
+ updateNote(id, noteId, body, createdAt) {
3034
+ const project = this.get(id);
3035
+ const note = project?.notes.find((item) => item.id === noteId);
3036
+ if (project === void 0 || note === void 0) return void 0;
3037
+ note.body = body;
3038
+ note.createdAt = createdAt;
3039
+ note.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3040
+ project.updatedAt = note.updatedAt;
3041
+ this.flush();
3042
+ return project;
3043
+ }
3044
+ removeNote(id, noteId) {
3045
+ const project = this.get(id);
3046
+ if (project === void 0) return void 0;
3047
+ project.notes = project.notes.filter((item) => item.id !== noteId);
3048
+ project.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3049
+ this.flush();
3050
+ return project;
3051
+ }
3052
+ linkNoteTasks(id, noteId, taskIds) {
3053
+ const project = this.get(id);
3054
+ const note = project?.notes.find((item) => item.id === noteId);
3055
+ if (project === void 0 || note === void 0) return void 0;
3056
+ note.taskIds = [.../* @__PURE__ */ new Set([...note.taskIds, ...taskIds])];
3057
+ project.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3058
+ this.flush();
3059
+ return project;
3060
+ }
3061
+ addMilestone(id, title, due) {
3062
+ const project = this.get(id);
3063
+ if (project === void 0) return void 0;
3064
+ project.milestones.push({
3065
+ id: `ms_${randomUUID2().slice(0, 8)}`,
3066
+ title,
3067
+ due,
3068
+ done: false
3069
+ });
3070
+ project.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3071
+ this.flush();
3072
+ return project;
3073
+ }
3074
+ updateMilestone(id, milestoneId, patch) {
3075
+ const project = this.get(id);
3076
+ const milestone = project?.milestones.find((item) => item.id === milestoneId);
3077
+ if (project === void 0 || milestone === void 0) return void 0;
3078
+ if (patch.title !== void 0) milestone.title = patch.title;
3079
+ if (patch.due !== void 0) milestone.due = patch.due;
3080
+ if (patch.done !== void 0) milestone.done = patch.done;
3081
+ project.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3082
+ this.flush();
3083
+ return project;
3084
+ }
3085
+ removeMilestone(id, milestoneId) {
3086
+ const project = this.get(id);
3087
+ if (project === void 0) return void 0;
3088
+ project.milestones = project.milestones.filter((item) => item.id !== milestoneId);
3089
+ project.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3090
+ this.flush();
3091
+ return project;
3092
+ }
3093
+ addActivity(id, text2, at = (/* @__PURE__ */ new Date()).toISOString()) {
3094
+ const project = this.get(id);
3095
+ if (project === void 0) return void 0;
3096
+ project.activity.unshift({ id: `act_${randomUUID2().slice(0, 8)}`, at, text: text2 });
3097
+ if (project.activity.length > 100) project.activity.length = 100;
3098
+ project.updatedAt = at;
3099
+ this.flush();
3100
+ return project;
3101
+ }
3009
3102
  remove(id) {
3010
3103
  const data = this.load();
3011
3104
  const index = data.projects.findIndex((project) => project.id === id);
@@ -3066,35 +3159,18 @@ async function createProject(options) {
3066
3159
  } else {
3067
3160
  warnings.push("\u5F53\u524D DSH \u7EC4\u5408\u6CA1\u6709 workspaceRegistry\uFF0C\u9879\u76EE\u76EE\u5F55\u5DF2\u521B\u5EFA\u4F46\u6CA1\u6709\u6CE8\u518C\u5230\u4FA7\u680F");
3068
3161
  }
3069
- let sessionId = "";
3070
- if (options.sessions !== void 0) {
3071
- try {
3072
- sessionId = randomUUID2();
3073
- const session = options.sessions.create(sessionId, { meta: { cwd: path } });
3074
- session.append(
3075
- "user/message",
3076
- {
3077
- role: "user",
3078
- content: [{ type: "text", text: `\u6211\u521B\u5EFA\u4E86\u4E00\u4E2A\u90AE\u4EF6\u9879\u76EE\u300C${name2}\u300D\u3002
3079
-
3080
- ${brief}` }],
3081
- source: { kind: "user" }
3082
- },
3083
- { surfaceOp: "append" }
3084
- );
3085
- } catch (error) {
3086
- warnings.push(`\u4F1A\u8BDD\u521B\u5EFA\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`);
3087
- sessionId = "";
3088
- }
3089
- } else {
3090
- warnings.push("\u5F53\u524D DSH \u7EC4\u5408\u6CA1\u6709 sessions \u670D\u52A1\uFF0C\u672A\u521B\u5EFA\u9879\u76EE\u4F1A\u8BDD");
3091
- }
3162
+ const sessionId = "";
3092
3163
  const project = {
3093
3164
  id,
3094
3165
  name: name2,
3166
+ status: "active",
3167
+ notes: [],
3168
+ milestones: [],
3169
+ activity: [],
3095
3170
  path,
3096
3171
  workspaceId,
3097
3172
  sessionId,
3173
+ sessionIds: sessionId === "" ? [] : [sessionId],
3098
3174
  accountIds: [input.accountId],
3099
3175
  messages: input.message === void 0 ? [] : [input.message],
3100
3176
  threads: input.thread === void 0 ? [] : [input.thread],
@@ -3152,7 +3228,7 @@ ${conversation.trim()}
3152
3228
  }
3153
3229
 
3154
3230
  // src/host/version.ts
3155
- var VERSION = "0.22.2";
3231
+ var VERSION = "0.24.0";
3156
3232
 
3157
3233
  // src/host/api.ts
3158
3234
  init_smtp();
@@ -3977,10 +4053,12 @@ function buildRouter(runtime) {
3977
4053
  title: String(payload.title ?? ""),
3978
4054
  notes: typeof payload.notes === "string" ? payload.notes : "",
3979
4055
  due: typeof payload.due === "string" ? payload.due : "",
3980
- status: typeof payload.status === "string" ? payload.status : "NEEDS-ACTION",
4056
+ status: typeof payload.status === "string" ? payload.status : "TODO",
3981
4057
  priority: Number(payload.priority ?? 0),
3982
4058
  owner: typeof payload.owner === "string" ? payload.owner : "",
3983
4059
  projectId: typeof payload.projectId === "string" ? payload.projectId : null,
4060
+ parentId: typeof payload.parentId === "string" && payload.parentId !== "" ? payload.parentId : null,
4061
+ waitingOn: typeof payload.waitingOn === "string" ? payload.waitingOn : "",
3984
4062
  messageKey: typeof payload.messageKey === "string" ? payload.messageKey : "",
3985
4063
  messageSubject: typeof payload.messageSubject === "string" ? payload.messageSubject : ""
3986
4064
  });
@@ -3995,7 +4073,9 @@ function buildRouter(runtime) {
3995
4073
  ...typeof payload.status === "string" ? { status: payload.status } : {},
3996
4074
  ...typeof payload.priority === "number" ? { priority: payload.priority } : {},
3997
4075
  ...typeof payload.owner === "string" ? { owner: payload.owner } : {},
3998
- ...payload.projectId === null || typeof payload.projectId === "string" ? { projectId: payload.projectId } : {}
4076
+ ...payload.projectId === null || typeof payload.projectId === "string" ? { projectId: payload.projectId } : {},
4077
+ ...payload.parentId === null || typeof payload.parentId === "string" ? { parentId: payload.parentId } : {},
4078
+ ...typeof payload.waitingOn === "string" ? { waitingOn: payload.waitingOn } : {}
3999
4079
  });
4000
4080
  return { task: taskView(task) };
4001
4081
  });
@@ -4051,8 +4131,10 @@ function buildRouter(runtime) {
4051
4131
  for (const task of runtime.tasks().list({ accountId: account.id })) {
4052
4132
  const projectId = task.projectId ?? "";
4053
4133
  if (projectId === "") continue;
4054
- const entry = counts.get(projectId) ?? { total: 0, done: 0 };
4134
+ const entry = counts.get(projectId) ?? { total: 0, todo: 0, waiting: 0, done: 0 };
4055
4135
  entry.total += 1;
4136
+ if (task.status === "TODO" || task.status === "IN-PROCESS") entry.todo += 1;
4137
+ if (task.status === "WAITING") entry.waiting += 1;
4056
4138
  if (task.status === "COMPLETED") entry.done += 1;
4057
4139
  counts.set(projectId, entry);
4058
4140
  }
@@ -4060,7 +4142,13 @@ function buildRouter(runtime) {
4060
4142
  return {
4061
4143
  items: runtime.projects().list().map((project) => {
4062
4144
  const stat = counts.get(project.id);
4063
- return { ...projectView(project), taskTotal: stat?.total ?? 0, taskDone: stat?.done ?? 0 };
4145
+ return {
4146
+ ...projectView(project),
4147
+ taskTotal: stat?.total ?? 0,
4148
+ taskTodo: stat?.todo ?? 0,
4149
+ taskWaiting: stat?.waiting ?? 0,
4150
+ taskDone: stat?.done ?? 0
4151
+ };
4064
4152
  })
4065
4153
  };
4066
4154
  });
@@ -4110,12 +4198,131 @@ function buildRouter(runtime) {
4110
4198
  });
4111
4199
  router.patch("/projects/:id", async ({ params, body }) => {
4112
4200
  const payload = await body();
4113
- const name2 = String(payload.name ?? "").trim();
4114
- if (name2 === "") throw new HttpError(400, "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
4115
- const project = runtime.projects().update(String(params.id ?? ""), { name: name2 });
4201
+ const patch = {};
4202
+ if (typeof payload.name === "string") {
4203
+ const name2 = payload.name.trim();
4204
+ if (name2 === "") throw new HttpError(400, "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
4205
+ patch.name = name2;
4206
+ }
4207
+ if (typeof payload.status === "string") {
4208
+ const allowed = ["active", "paused", "done", "archived"];
4209
+ if (!allowed.includes(payload.status)) throw new HttpError(400, "\u9879\u76EE\u72B6\u6001\u4E0D\u6B63\u786E");
4210
+ patch.status = payload.status;
4211
+ }
4212
+ const project = runtime.projects().update(String(params.id ?? ""), patch);
4213
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4214
+ if (typeof patch.status === "string") {
4215
+ const labels = {
4216
+ active: "\u8FDB\u884C\u4E2D",
4217
+ paused: "\u6682\u505C",
4218
+ done: "\u5DF2\u5B8C\u6210",
4219
+ archived: "\u5DF2\u5F52\u6863"
4220
+ };
4221
+ runtime.projects().addActivity(project.id, `\u9879\u76EE\u72B6\u6001\u6539\u4E3A\u300C${labels[patch.status] ?? patch.status}\u300D`);
4222
+ }
4223
+ return { project: projectView(runtime.projects().get(project.id) ?? project) };
4224
+ });
4225
+ router.post("/projects/:id/milestones", async ({ params, body }) => {
4226
+ const payload = await body();
4227
+ const project = runtime.projects().get(String(params.id ?? ""));
4228
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4229
+ const title = String(payload.title ?? "").trim();
4230
+ if (title === "") throw new HttpError(400, "\u91CC\u7A0B\u7891\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
4231
+ const updated = runtime.projects().addMilestone(project.id, title, typeof payload.due === "string" ? payload.due : "");
4232
+ if (updated === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4233
+ runtime.projects().addActivity(project.id, `\u65B0\u589E\u91CC\u7A0B\u7891\u300C${title}\u300D`);
4234
+ return { project: projectView(runtime.projects().get(project.id) ?? updated) };
4235
+ });
4236
+ router.patch("/projects/:id/milestones/:milestoneId", async ({ params, body }) => {
4237
+ const payload = await body();
4238
+ const project = runtime.projects().get(String(params.id ?? ""));
4239
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4240
+ const milestone = project.milestones.find((item) => item.id === String(params.milestoneId ?? ""));
4241
+ if (milestone === void 0) throw new HttpError(404, "\u91CC\u7A0B\u7891\u4E0D\u5B58\u5728");
4242
+ const patch = {};
4243
+ if (typeof payload.title === "string") patch.title = payload.title.trim();
4244
+ if (typeof payload.due === "string") patch.due = payload.due;
4245
+ if (typeof payload.done === "boolean") patch.done = payload.done;
4246
+ const updated = runtime.projects().updateMilestone(project.id, milestone.id, patch);
4247
+ if (updated === void 0) throw new HttpError(404, "\u91CC\u7A0B\u7891\u4E0D\u5B58\u5728");
4248
+ if (patch.done !== void 0) {
4249
+ runtime.projects().addActivity(
4250
+ project.id,
4251
+ `\u91CC\u7A0B\u7891\u300C${patch.title ?? milestone.title}\u300D${patch.done ? "\u5DF2\u5B8C\u6210" : "\u91CD\u65B0\u6253\u5F00"}`
4252
+ );
4253
+ }
4254
+ return { project: projectView(runtime.projects().get(project.id) ?? updated) };
4255
+ });
4256
+ router.delete("/projects/:id/milestones/:milestoneId", ({ params }) => {
4257
+ const project = runtime.projects().get(String(params.id ?? ""));
4116
4258
  if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4259
+ const milestone = project.milestones.find((item) => item.id === String(params.milestoneId ?? ""));
4260
+ const updated = runtime.projects().removeMilestone(project.id, String(params.milestoneId ?? ""));
4261
+ if (updated === void 0) throw new HttpError(404, "\u91CC\u7A0B\u7891\u4E0D\u5B58\u5728");
4262
+ runtime.projects().addActivity(project.id, `\u5220\u9664\u91CC\u7A0B\u7891\u300C${milestone?.title ?? ""}\u300D`);
4263
+ return { project: projectView(runtime.projects().get(project.id) ?? updated) };
4264
+ });
4265
+ router.post("/projects/:id/notes", async ({ params, body }) => {
4266
+ const payload = await body();
4267
+ const project = runtime.projects().get(String(params.id ?? ""));
4268
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4269
+ const text2 = String(payload.body ?? "").trim();
4270
+ if (text2 === "") throw new HttpError(400, "\u8BB0\u5F55\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A");
4271
+ const createdAt = typeof payload.createdAt === "string" && payload.createdAt !== "" ? payload.createdAt : (/* @__PURE__ */ new Date()).toISOString();
4272
+ const updated = runtime.projects().addNote(project.id, text2, createdAt);
4273
+ runtime.projects().addActivity(project.id, "\u65B0\u589E\u4E00\u6761\u9879\u76EE\u8BB0\u5F55");
4274
+ return {
4275
+ project: updated === void 0 ? null : projectView(runtime.projects().get(project.id) ?? updated)
4276
+ };
4277
+ });
4278
+ router.patch("/projects/:id/notes/:noteId", async ({ params, body }) => {
4279
+ const payload = await body();
4280
+ const project = runtime.projects().get(String(params.id ?? ""));
4281
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4282
+ const text2 = String(payload.body ?? "").trim();
4283
+ if (text2 === "") throw new HttpError(400, "\u8BB0\u5F55\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A");
4284
+ const createdAt = typeof payload.createdAt === "string" && payload.createdAt !== "" ? payload.createdAt : (/* @__PURE__ */ new Date()).toISOString();
4285
+ const updated = runtime.projects().updateNote(project.id, String(params.noteId ?? ""), text2, createdAt);
4286
+ if (updated === void 0) throw new HttpError(404, "\u8BB0\u5F55\u4E0D\u5B58\u5728");
4287
+ return { project: projectView(updated) };
4288
+ });
4289
+ router.delete("/projects/:id/notes/:noteId", ({ params }) => {
4290
+ const project = runtime.projects().removeNote(String(params.id ?? ""), String(params.noteId ?? ""));
4291
+ if (project === void 0) throw new HttpError(404, "\u8BB0\u5F55\u4E0D\u5B58\u5728");
4117
4292
  return { project: projectView(project) };
4118
4293
  });
4294
+ router.post("/projects/:id/notes/convert", async ({ params, body }) => {
4295
+ const payload = await body();
4296
+ const project = runtime.projects().get(String(params.id ?? ""));
4297
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4298
+ const noteIds = Array.isArray(payload.noteIds) ? payload.noteIds.filter((id) => typeof id === "string") : [];
4299
+ if (noteIds.length === 0) throw new HttpError(400, "\u8BF7\u9009\u62E9\u8981\u8F6C\u6362\u7684\u8BB0\u5F55");
4300
+ const accountId = typeof payload.accountId === "string" && payload.accountId !== "" ? payload.accountId : project.accountIds[0] ?? "";
4301
+ if (accountId === "") throw new HttpError(400, "\u9879\u76EE\u6CA1\u6709\u5173\u8054\u8D26\u53F7");
4302
+ const created = [];
4303
+ for (const noteId of noteIds) {
4304
+ const note = project.notes.find((item) => item.id === noteId);
4305
+ if (note === void 0) continue;
4306
+ const first = note.body.split("\n").map((line2) => line2.trim()).find((line2) => line2 !== "") ?? "\u9879\u76EE\u8BB0\u5F55";
4307
+ const title = first.replace(/^#+\s*/, "").slice(0, 120);
4308
+ const task = runtime.tasks().create({
4309
+ accountId,
4310
+ title,
4311
+ notes: `\u6765\u81EA\u9879\u76EE\u8BB0\u5F55 ${note.createdAt.slice(0, 10)}
4312
+
4313
+ ${note.body}`,
4314
+ status: "TODO",
4315
+ projectId: project.id
4316
+ });
4317
+ runtime.projects().linkNoteTasks(project.id, note.id, [task.id]);
4318
+ created.push(taskView(task));
4319
+ }
4320
+ runtime.projects().addActivity(project.id, `\u5C06 ${String(created.length)} \u6761\u8BB0\u5F55\u8F6C\u4E3A\u4EFB\u52A1`);
4321
+ return {
4322
+ tasks: created,
4323
+ project: projectView(runtime.projects().get(project.id) ?? project)
4324
+ };
4325
+ });
4119
4326
  router.delete("/projects/:id", async ({ params, url }) => {
4120
4327
  const id = String(params.id ?? "");
4121
4328
  const project = runtime.projects().get(id);
@@ -4365,10 +4572,12 @@ function taskView(task) {
4365
4572
  id: task.id,
4366
4573
  accountId: task.accountId,
4367
4574
  projectId: task.projectId,
4575
+ parentId: task.parentId,
4368
4576
  title: task.title,
4369
4577
  notes: task.notes,
4370
4578
  due: task.due,
4371
4579
  status: task.status,
4580
+ waitingOn: task.waitingOn,
4372
4581
  priority: task.priority,
4373
4582
  owner: task.owner,
4374
4583
  messageKey: task.messageKey,
@@ -4383,9 +4592,14 @@ function projectView(project) {
4383
4592
  return {
4384
4593
  id: project.id,
4385
4594
  name: project.name,
4595
+ status: project.status,
4596
+ notes: project.notes.map((note) => ({ ...note, taskIds: [...note.taskIds] })),
4597
+ milestones: project.milestones.map((milestone) => ({ ...milestone })),
4598
+ activity: project.activity.map((event) => ({ ...event })),
4386
4599
  path: project.path,
4387
4600
  workspaceId: project.workspaceId,
4388
4601
  sessionId: project.sessionId,
4602
+ sessionIds: [...project.sessionIds],
4389
4603
  accountIds: [...project.accountIds],
4390
4604
  messages: project.messages.map((message) => ({ ...message })),
4391
4605
  threads: project.threads.map((thread) => ({ ...thread })),
@@ -5039,7 +5253,7 @@ import { randomUUID as randomUUID5 } from "node:crypto";
5039
5253
  import { mkdirSync as mkdirSync9 } from "node:fs";
5040
5254
  import { dirname as dirname5 } from "node:path";
5041
5255
  import { DatabaseSync as DatabaseSync5 } from "node:sqlite";
5042
- var STATUSES = /* @__PURE__ */ new Set(["NEEDS-ACTION", "IN-PROCESS", "COMPLETED", "CANCELLED"]);
5256
+ var STATUSES = /* @__PURE__ */ new Set(["TODO", "IN-PROCESS", "WAITING", "COMPLETED", "CANCELLED"]);
5043
5257
  function now() {
5044
5258
  return (/* @__PURE__ */ new Date()).toISOString();
5045
5259
  }
@@ -5049,6 +5263,8 @@ function rowToTask(row) {
5049
5263
  id: String(row.id),
5050
5264
  accountId: String(row.account_id),
5051
5265
  projectId: row.project_id === null || row.project_id === void 0 ? null : String(row.project_id),
5266
+ parentId: row.parent_id === null || row.parent_id === void 0 ? null : String(row.parent_id),
5267
+ waitingOn: String(row.waiting_on ?? ""),
5052
5268
  calendarHref: row.calendar_href === null || row.calendar_href === void 0 ? null : String(row.calendar_href),
5053
5269
  href: row.href === null || row.href === void 0 ? null : String(row.href),
5054
5270
  etag: row.etag === null || row.etag === void 0 ? null : String(row.etag),
@@ -5069,8 +5285,9 @@ function rowToTask(row) {
5069
5285
  };
5070
5286
  }
5071
5287
  function statusOf(value) {
5072
- const text2 = String(value ?? "NEEDS-ACTION").toUpperCase();
5073
- return STATUSES.has(text2) ? text2 : "NEEDS-ACTION";
5288
+ const text2 = String(value ?? "TODO").toUpperCase();
5289
+ if (text2 === "NEEDS-ACTION") return "TODO";
5290
+ return STATUSES.has(text2) ? text2 : "TODO";
5074
5291
  }
5075
5292
  var TaskStore = class {
5076
5293
  file;
@@ -5095,6 +5312,8 @@ var TaskStore = class {
5095
5312
  id TEXT PRIMARY KEY,
5096
5313
  account_id TEXT NOT NULL,
5097
5314
  project_id TEXT,
5315
+ parent_id TEXT,
5316
+ waiting_on TEXT,
5098
5317
  calendar_href TEXT,
5099
5318
  href TEXT,
5100
5319
  etag TEXT,
@@ -5124,6 +5343,14 @@ var TaskStore = class {
5124
5343
  error TEXT
5125
5344
  );
5126
5345
  `);
5346
+ const columns = db.prepare("PRAGMA table_info(tasks)").all();
5347
+ if (!columns.some((column) => column.name === "parent_id")) {
5348
+ db.exec("ALTER TABLE tasks ADD COLUMN parent_id TEXT");
5349
+ }
5350
+ if (!columns.some((column) => column.name === "waiting_on")) {
5351
+ db.exec("ALTER TABLE tasks ADD COLUMN waiting_on TEXT");
5352
+ }
5353
+ db.exec("UPDATE tasks SET status = 'TODO' WHERE status = 'NEEDS-ACTION'");
5127
5354
  this.db = db;
5128
5355
  return db;
5129
5356
  }
@@ -5163,16 +5390,26 @@ var TaskStore = class {
5163
5390
  if (title === "") throw new Error("\u4EFB\u52A1\u6807\u9898\u4E0D\u80FD\u4E3A\u7A7A");
5164
5391
  if (title.length > 300) throw new Error("\u4EFB\u52A1\u6807\u9898\u8FC7\u957F");
5165
5392
  const status = statusOf(input.status);
5393
+ if (status === "WAITING" && String(input.waitingOn ?? "").trim() === "") {
5394
+ throw new Error("\u7B49\u5F85\u4E2D\u4EFB\u52A1\u9700\u8981\u586B\u5199\u7B49\u8C01/\u7B49\u4EC0\u4E48");
5395
+ }
5396
+ if (input.parentId !== void 0 && input.parentId !== null) {
5397
+ const parent = this.get(input.parentId);
5398
+ if (parent === void 0) throw new Error("\u7236\u4EFB\u52A1\u4E0D\u5B58\u5728");
5399
+ if (parent.parentId !== null) throw new Error("\u5B50\u4EFB\u52A1\u4E0B\u4E0D\u80FD\u518D\u5EFA\u5B50\u4EFB\u52A1");
5400
+ }
5166
5401
  const id = `task_${randomUUID5()}`;
5167
5402
  const stamp3 = now();
5168
5403
  this.open().prepare(
5169
- `INSERT INTO tasks (id, account_id, project_id, uid, title, notes, due, status, priority, owner,
5170
- message_key, message_subject, completed_at, dirty, deleted, created_at, updated_at)
5171
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, ?, ?)`
5404
+ `INSERT INTO tasks (id, account_id, project_id, parent_id, waiting_on, uid, title, notes, due, status,
5405
+ priority, owner, message_key, message_subject, completed_at, dirty, deleted, created_at, updated_at)
5406
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, ?, ?)`
5172
5407
  ).run(
5173
5408
  id,
5174
5409
  input.accountId,
5175
5410
  input.projectId ?? null,
5411
+ input.parentId ?? null,
5412
+ String(input.waitingOn ?? ""),
5176
5413
  `${randomUUID5()}@dsh-mailharbor`,
5177
5414
  title,
5178
5415
  String(input.notes ?? ""),
@@ -5201,15 +5438,27 @@ var TaskStore = class {
5201
5438
  priority: fields.priority === void 0 ? current.priority : Number(fields.priority),
5202
5439
  owner: fields.owner === void 0 ? current.owner : String(fields.owner ?? ""),
5203
5440
  projectId: fields.projectId === void 0 ? current.projectId : fields.projectId,
5441
+ parentId: fields.parentId === void 0 ? current.parentId : fields.parentId,
5442
+ waitingOn: fields.waitingOn === void 0 ? current.waitingOn : String(fields.waitingOn ?? ""),
5204
5443
  messageKey: fields.messageKey === void 0 ? current.messageKey : String(fields.messageKey),
5205
5444
  messageSubject: fields.messageSubject === void 0 ? current.messageSubject : String(fields.messageSubject)
5206
5445
  };
5207
5446
  if (next.title === "") throw new Error("\u4EFB\u52A1\u6807\u9898\u4E0D\u80FD\u4E3A\u7A7A");
5447
+ if (next.status === "WAITING" && next.waitingOn.trim() === "") {
5448
+ throw new Error("\u7B49\u5F85\u4E2D\u4EFB\u52A1\u9700\u8981\u586B\u5199\u7B49\u8C01/\u7B49\u4EC0\u4E48");
5449
+ }
5450
+ if (next.parentId !== null) {
5451
+ if (next.parentId === id) throw new Error("\u4EFB\u52A1\u4E0D\u80FD\u4F5C\u4E3A\u81EA\u5DF1\u7684\u5B50\u4EFB\u52A1");
5452
+ const parent = this.get(next.parentId);
5453
+ if (parent === void 0) throw new Error("\u7236\u4EFB\u52A1\u4E0D\u5B58\u5728");
5454
+ if (parent.parentId !== null) throw new Error("\u5B50\u4EFB\u52A1\u4E0B\u4E0D\u80FD\u518D\u5EFA\u5B50\u4EFB\u52A1");
5455
+ }
5208
5456
  const stamp3 = now();
5209
5457
  const completedAt = next.status === "COMPLETED" ? current.completedAt ?? stamp3 : null;
5210
5458
  this.open().prepare(
5211
5459
  `UPDATE tasks SET title = ?, notes = ?, due = ?, status = ?, priority = ?, owner = ?, project_id = ?,
5212
- message_key = ?, message_subject = ?, completed_at = ?, dirty = 1, updated_at = ? WHERE id = ?`
5460
+ parent_id = ?, waiting_on = ?, message_key = ?, message_subject = ?, completed_at = ?,
5461
+ dirty = 1, updated_at = ? WHERE id = ?`
5213
5462
  ).run(
5214
5463
  next.title,
5215
5464
  next.notes,
@@ -5218,6 +5467,8 @@ var TaskStore = class {
5218
5467
  Math.max(0, Math.min(9, Number(next.priority) || 0)),
5219
5468
  next.owner,
5220
5469
  next.projectId,
5470
+ next.parentId,
5471
+ next.waitingOn,
5221
5472
  next.messageKey,
5222
5473
  next.messageSubject,
5223
5474
  completedAt,