@zaclinley/dsh-mailharbor 0.22.1 → 0.23.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
@@ -1609,6 +1609,20 @@ var MailDatabase = class {
1609
1609
  return 0;
1610
1610
  }
1611
1611
  }
1612
+ /** Recover To/Cc from raw headers when the stored summary is empty. */
1613
+ recipientsFromRaw(raw) {
1614
+ const text2 = Buffer.from(raw.subarray(0, Math.min(raw.byteLength, 3e5))).toString("utf8");
1615
+ const separator = text2.search(/\r?\n\r?\n/);
1616
+ const head = (separator >= 0 ? text2.slice(0, separator) : text2).replace(/\r?\n[ \t]+/g, " ");
1617
+ const value = (name2) => {
1618
+ const match = new RegExp(`^${name2}:\\s*(.*)$`, "im").exec(head);
1619
+ return match === null ? "" : String(match[1] ?? "");
1620
+ };
1621
+ const to = parseAddressList(
1622
+ value("to") || value("delivered-to") || value("x-original-to") || value("apparently-to")
1623
+ );
1624
+ return { to, cc: parseAddressList(value("cc")) };
1625
+ }
1612
1626
  get(localId) {
1613
1627
  const db = this.open();
1614
1628
  const row = db.prepare(
@@ -1642,6 +1656,19 @@ var MailDatabase = class {
1642
1656
  size: Number(item.size ?? 0)
1643
1657
  }));
1644
1658
  const max = this.options.maxBodyChars ?? 4e5;
1659
+ let to = parseAddressList(String(row.to_addr ?? ""));
1660
+ let cc = parseAddressList(String(row.cc ?? ""));
1661
+ if (to.length === 0) {
1662
+ try {
1663
+ const raw = db.prepare("SELECT raw_eml FROM emails WHERE id = ?").get(localId)?.raw_eml;
1664
+ if (raw instanceof Uint8Array) {
1665
+ const recovered = this.recipientsFromRaw(raw);
1666
+ to = recovered.to;
1667
+ if (cc.length === 0) cc = recovered.cc;
1668
+ }
1669
+ } catch {
1670
+ }
1671
+ }
1645
1672
  const bodyText2 = String(row.body_text ?? "");
1646
1673
  const inlineImages = wantsInline ? rows.filter(
1647
1674
  (item) => String(item.content_type ?? "").toLowerCase().startsWith("image/")
@@ -1653,7 +1680,8 @@ var MailDatabase = class {
1653
1680
  })).filter((item) => item.data !== void 0) : [];
1654
1681
  return {
1655
1682
  ...rowToSummary(row),
1656
- cc: parseAddressList(String(row.cc ?? "")),
1683
+ to,
1684
+ cc,
1657
1685
  replyTo: [],
1658
1686
  text: truncate(bodyText2, max),
1659
1687
  html: bodyHtml === "" ? null : truncate(inlineCidImages(bodyHtml, inlineImages), max * 2),
@@ -2923,7 +2951,16 @@ var ProjectStore = class {
2923
2951
  parsed = null;
2924
2952
  }
2925
2953
  const projects = parsed !== null && typeof parsed === "object" && Array.isArray(parsed.projects) ? parsed.projects : [];
2926
- 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
+ sessionIds: Array.isArray(project.sessionIds) ? project.sessionIds : project.sessionId === "" ? [] : [project.sessionId]
2962
+ }))
2963
+ };
2927
2964
  return this.cache;
2928
2965
  }
2929
2966
  flush() {
@@ -2978,6 +3015,48 @@ var ProjectStore = class {
2978
3015
  this.flush();
2979
3016
  return project;
2980
3017
  }
3018
+ addNote(id, body, createdAt) {
3019
+ const project = this.get(id);
3020
+ if (project === void 0) return void 0;
3021
+ const note = {
3022
+ id: `note_${randomUUID2().slice(0, 8)}`,
3023
+ body,
3024
+ createdAt,
3025
+ taskIds: []
3026
+ };
3027
+ project.notes.unshift(note);
3028
+ project.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3029
+ this.flush();
3030
+ return project;
3031
+ }
3032
+ updateNote(id, noteId, body, createdAt) {
3033
+ const project = this.get(id);
3034
+ const note = project?.notes.find((item) => item.id === noteId);
3035
+ if (project === void 0 || note === void 0) return void 0;
3036
+ note.body = body;
3037
+ note.createdAt = createdAt;
3038
+ note.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3039
+ project.updatedAt = note.updatedAt;
3040
+ this.flush();
3041
+ return project;
3042
+ }
3043
+ removeNote(id, noteId) {
3044
+ const project = this.get(id);
3045
+ if (project === void 0) return void 0;
3046
+ project.notes = project.notes.filter((item) => item.id !== noteId);
3047
+ project.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3048
+ this.flush();
3049
+ return project;
3050
+ }
3051
+ linkNoteTasks(id, noteId, taskIds) {
3052
+ const project = this.get(id);
3053
+ const note = project?.notes.find((item) => item.id === noteId);
3054
+ if (project === void 0 || note === void 0) return void 0;
3055
+ note.taskIds = [.../* @__PURE__ */ new Set([...note.taskIds, ...taskIds])];
3056
+ project.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3057
+ this.flush();
3058
+ return project;
3059
+ }
2981
3060
  remove(id) {
2982
3061
  const data = this.load();
2983
3062
  const index = data.projects.findIndex((project) => project.id === id);
@@ -3038,35 +3117,17 @@ async function createProject(options) {
3038
3117
  } else {
3039
3118
  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");
3040
3119
  }
3041
- let sessionId = "";
3042
- if (options.sessions !== void 0) {
3043
- try {
3044
- sessionId = randomUUID2();
3045
- const session = options.sessions.create(sessionId, { meta: { cwd: path } });
3046
- session.append(
3047
- "user/message",
3048
- {
3049
- role: "user",
3050
- content: [{ type: "text", text: `\u6211\u521B\u5EFA\u4E86\u4E00\u4E2A\u90AE\u4EF6\u9879\u76EE\u300C${name2}\u300D\u3002
3051
-
3052
- ${brief}` }],
3053
- source: { kind: "user" }
3054
- },
3055
- { surfaceOp: "append" }
3056
- );
3057
- } catch (error) {
3058
- warnings.push(`\u4F1A\u8BDD\u521B\u5EFA\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`);
3059
- sessionId = "";
3060
- }
3061
- } else {
3062
- warnings.push("\u5F53\u524D DSH \u7EC4\u5408\u6CA1\u6709 sessions \u670D\u52A1\uFF0C\u672A\u521B\u5EFA\u9879\u76EE\u4F1A\u8BDD");
3063
- }
3120
+ const sessionId = "";
3064
3121
  const project = {
3065
3122
  id,
3066
3123
  name: name2,
3124
+ status: "active",
3125
+ notes: [],
3126
+ milestones: [],
3067
3127
  path,
3068
3128
  workspaceId,
3069
3129
  sessionId,
3130
+ sessionIds: sessionId === "" ? [] : [sessionId],
3070
3131
  accountIds: [input.accountId],
3071
3132
  messages: input.message === void 0 ? [] : [input.message],
3072
3133
  threads: input.thread === void 0 ? [] : [input.thread],
@@ -3124,7 +3185,7 @@ ${conversation.trim()}
3124
3185
  }
3125
3186
 
3126
3187
  // src/host/version.ts
3127
- var VERSION = "0.22.1";
3188
+ var VERSION = "0.23.0";
3128
3189
 
3129
3190
  // src/host/api.ts
3130
3191
  init_smtp();
@@ -3949,10 +4010,12 @@ function buildRouter(runtime) {
3949
4010
  title: String(payload.title ?? ""),
3950
4011
  notes: typeof payload.notes === "string" ? payload.notes : "",
3951
4012
  due: typeof payload.due === "string" ? payload.due : "",
3952
- status: typeof payload.status === "string" ? payload.status : "NEEDS-ACTION",
4013
+ status: typeof payload.status === "string" ? payload.status : "TODO",
3953
4014
  priority: Number(payload.priority ?? 0),
3954
4015
  owner: typeof payload.owner === "string" ? payload.owner : "",
3955
4016
  projectId: typeof payload.projectId === "string" ? payload.projectId : null,
4017
+ parentId: typeof payload.parentId === "string" && payload.parentId !== "" ? payload.parentId : null,
4018
+ waitingOn: typeof payload.waitingOn === "string" ? payload.waitingOn : "",
3956
4019
  messageKey: typeof payload.messageKey === "string" ? payload.messageKey : "",
3957
4020
  messageSubject: typeof payload.messageSubject === "string" ? payload.messageSubject : ""
3958
4021
  });
@@ -3967,7 +4030,9 @@ function buildRouter(runtime) {
3967
4030
  ...typeof payload.status === "string" ? { status: payload.status } : {},
3968
4031
  ...typeof payload.priority === "number" ? { priority: payload.priority } : {},
3969
4032
  ...typeof payload.owner === "string" ? { owner: payload.owner } : {},
3970
- ...payload.projectId === null || typeof payload.projectId === "string" ? { projectId: payload.projectId } : {}
4033
+ ...payload.projectId === null || typeof payload.projectId === "string" ? { projectId: payload.projectId } : {},
4034
+ ...payload.parentId === null || typeof payload.parentId === "string" ? { parentId: payload.parentId } : {},
4035
+ ...typeof payload.waitingOn === "string" ? { waitingOn: payload.waitingOn } : {}
3971
4036
  });
3972
4037
  return { task: taskView(task) };
3973
4038
  });
@@ -4023,8 +4088,10 @@ function buildRouter(runtime) {
4023
4088
  for (const task of runtime.tasks().list({ accountId: account.id })) {
4024
4089
  const projectId = task.projectId ?? "";
4025
4090
  if (projectId === "") continue;
4026
- const entry = counts.get(projectId) ?? { total: 0, done: 0 };
4091
+ const entry = counts.get(projectId) ?? { total: 0, todo: 0, waiting: 0, done: 0 };
4027
4092
  entry.total += 1;
4093
+ if (task.status === "TODO" || task.status === "IN-PROCESS") entry.todo += 1;
4094
+ if (task.status === "WAITING") entry.waiting += 1;
4028
4095
  if (task.status === "COMPLETED") entry.done += 1;
4029
4096
  counts.set(projectId, entry);
4030
4097
  }
@@ -4032,7 +4099,13 @@ function buildRouter(runtime) {
4032
4099
  return {
4033
4100
  items: runtime.projects().list().map((project) => {
4034
4101
  const stat = counts.get(project.id);
4035
- return { ...projectView(project), taskTotal: stat?.total ?? 0, taskDone: stat?.done ?? 0 };
4102
+ return {
4103
+ ...projectView(project),
4104
+ taskTotal: stat?.total ?? 0,
4105
+ taskTodo: stat?.todo ?? 0,
4106
+ taskWaiting: stat?.waiting ?? 0,
4107
+ taskDone: stat?.done ?? 0
4108
+ };
4036
4109
  })
4037
4110
  };
4038
4111
  });
@@ -4082,12 +4155,78 @@ function buildRouter(runtime) {
4082
4155
  });
4083
4156
  router.patch("/projects/:id", async ({ params, body }) => {
4084
4157
  const payload = await body();
4085
- const name2 = String(payload.name ?? "").trim();
4086
- if (name2 === "") throw new HttpError(400, "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
4087
- const project = runtime.projects().update(String(params.id ?? ""), { name: name2 });
4158
+ const patch = {};
4159
+ if (typeof payload.name === "string") {
4160
+ const name2 = payload.name.trim();
4161
+ if (name2 === "") throw new HttpError(400, "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
4162
+ patch.name = name2;
4163
+ }
4164
+ if (typeof payload.status === "string") {
4165
+ const allowed = ["active", "paused", "done", "archived"];
4166
+ if (!allowed.includes(payload.status)) throw new HttpError(400, "\u9879\u76EE\u72B6\u6001\u4E0D\u6B63\u786E");
4167
+ patch.status = payload.status;
4168
+ }
4169
+ const project = runtime.projects().update(String(params.id ?? ""), patch);
4170
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4171
+ return { project: projectView(project) };
4172
+ });
4173
+ router.post("/projects/:id/notes", async ({ params, body }) => {
4174
+ const payload = await body();
4175
+ const project = runtime.projects().get(String(params.id ?? ""));
4176
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4177
+ const text2 = String(payload.body ?? "").trim();
4178
+ if (text2 === "") throw new HttpError(400, "\u8BB0\u5F55\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A");
4179
+ const createdAt = typeof payload.createdAt === "string" && payload.createdAt !== "" ? payload.createdAt : (/* @__PURE__ */ new Date()).toISOString();
4180
+ const updated = runtime.projects().addNote(project.id, text2, createdAt);
4181
+ return { project: updated === void 0 ? null : projectView(updated) };
4182
+ });
4183
+ router.patch("/projects/:id/notes/:noteId", async ({ params, body }) => {
4184
+ const payload = await body();
4185
+ const project = runtime.projects().get(String(params.id ?? ""));
4088
4186
  if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4187
+ const text2 = String(payload.body ?? "").trim();
4188
+ if (text2 === "") throw new HttpError(400, "\u8BB0\u5F55\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A");
4189
+ const createdAt = typeof payload.createdAt === "string" && payload.createdAt !== "" ? payload.createdAt : (/* @__PURE__ */ new Date()).toISOString();
4190
+ const updated = runtime.projects().updateNote(project.id, String(params.noteId ?? ""), text2, createdAt);
4191
+ if (updated === void 0) throw new HttpError(404, "\u8BB0\u5F55\u4E0D\u5B58\u5728");
4192
+ return { project: projectView(updated) };
4193
+ });
4194
+ router.delete("/projects/:id/notes/:noteId", ({ params }) => {
4195
+ const project = runtime.projects().removeNote(String(params.id ?? ""), String(params.noteId ?? ""));
4196
+ if (project === void 0) throw new HttpError(404, "\u8BB0\u5F55\u4E0D\u5B58\u5728");
4089
4197
  return { project: projectView(project) };
4090
4198
  });
4199
+ router.post("/projects/:id/notes/convert", async ({ params, body }) => {
4200
+ const payload = await body();
4201
+ const project = runtime.projects().get(String(params.id ?? ""));
4202
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4203
+ const noteIds = Array.isArray(payload.noteIds) ? payload.noteIds.filter((id) => typeof id === "string") : [];
4204
+ if (noteIds.length === 0) throw new HttpError(400, "\u8BF7\u9009\u62E9\u8981\u8F6C\u6362\u7684\u8BB0\u5F55");
4205
+ const accountId = typeof payload.accountId === "string" && payload.accountId !== "" ? payload.accountId : project.accountIds[0] ?? "";
4206
+ if (accountId === "") throw new HttpError(400, "\u9879\u76EE\u6CA1\u6709\u5173\u8054\u8D26\u53F7");
4207
+ const created = [];
4208
+ for (const noteId of noteIds) {
4209
+ const note = project.notes.find((item) => item.id === noteId);
4210
+ if (note === void 0) continue;
4211
+ const first = note.body.split("\n").map((line2) => line2.trim()).find((line2) => line2 !== "") ?? "\u9879\u76EE\u8BB0\u5F55";
4212
+ const title = first.replace(/^#+\s*/, "").slice(0, 120);
4213
+ const task = runtime.tasks().create({
4214
+ accountId,
4215
+ title,
4216
+ notes: `\u6765\u81EA\u9879\u76EE\u8BB0\u5F55 ${note.createdAt.slice(0, 10)}
4217
+
4218
+ ${note.body}`,
4219
+ status: "TODO",
4220
+ projectId: project.id
4221
+ });
4222
+ runtime.projects().linkNoteTasks(project.id, note.id, [task.id]);
4223
+ created.push(taskView(task));
4224
+ }
4225
+ return {
4226
+ tasks: created,
4227
+ project: projectView(runtime.projects().get(project.id) ?? project)
4228
+ };
4229
+ });
4091
4230
  router.delete("/projects/:id", async ({ params, url }) => {
4092
4231
  const id = String(params.id ?? "");
4093
4232
  const project = runtime.projects().get(id);
@@ -4337,10 +4476,12 @@ function taskView(task) {
4337
4476
  id: task.id,
4338
4477
  accountId: task.accountId,
4339
4478
  projectId: task.projectId,
4479
+ parentId: task.parentId,
4340
4480
  title: task.title,
4341
4481
  notes: task.notes,
4342
4482
  due: task.due,
4343
4483
  status: task.status,
4484
+ waitingOn: task.waitingOn,
4344
4485
  priority: task.priority,
4345
4486
  owner: task.owner,
4346
4487
  messageKey: task.messageKey,
@@ -4355,9 +4496,13 @@ function projectView(project) {
4355
4496
  return {
4356
4497
  id: project.id,
4357
4498
  name: project.name,
4499
+ status: project.status,
4500
+ notes: project.notes.map((note) => ({ ...note, taskIds: [...note.taskIds] })),
4501
+ milestones: project.milestones.map((milestone) => ({ ...milestone })),
4358
4502
  path: project.path,
4359
4503
  workspaceId: project.workspaceId,
4360
4504
  sessionId: project.sessionId,
4505
+ sessionIds: [...project.sessionIds],
4361
4506
  accountIds: [...project.accountIds],
4362
4507
  messages: project.messages.map((message) => ({ ...message })),
4363
4508
  threads: project.threads.map((thread) => ({ ...thread })),
@@ -5011,7 +5156,7 @@ import { randomUUID as randomUUID5 } from "node:crypto";
5011
5156
  import { mkdirSync as mkdirSync9 } from "node:fs";
5012
5157
  import { dirname as dirname5 } from "node:path";
5013
5158
  import { DatabaseSync as DatabaseSync5 } from "node:sqlite";
5014
- var STATUSES = /* @__PURE__ */ new Set(["NEEDS-ACTION", "IN-PROCESS", "COMPLETED", "CANCELLED"]);
5159
+ var STATUSES = /* @__PURE__ */ new Set(["TODO", "IN-PROCESS", "WAITING", "COMPLETED", "CANCELLED"]);
5015
5160
  function now() {
5016
5161
  return (/* @__PURE__ */ new Date()).toISOString();
5017
5162
  }
@@ -5021,6 +5166,8 @@ function rowToTask(row) {
5021
5166
  id: String(row.id),
5022
5167
  accountId: String(row.account_id),
5023
5168
  projectId: row.project_id === null || row.project_id === void 0 ? null : String(row.project_id),
5169
+ parentId: row.parent_id === null || row.parent_id === void 0 ? null : String(row.parent_id),
5170
+ waitingOn: String(row.waiting_on ?? ""),
5024
5171
  calendarHref: row.calendar_href === null || row.calendar_href === void 0 ? null : String(row.calendar_href),
5025
5172
  href: row.href === null || row.href === void 0 ? null : String(row.href),
5026
5173
  etag: row.etag === null || row.etag === void 0 ? null : String(row.etag),
@@ -5041,8 +5188,9 @@ function rowToTask(row) {
5041
5188
  };
5042
5189
  }
5043
5190
  function statusOf(value) {
5044
- const text2 = String(value ?? "NEEDS-ACTION").toUpperCase();
5045
- return STATUSES.has(text2) ? text2 : "NEEDS-ACTION";
5191
+ const text2 = String(value ?? "TODO").toUpperCase();
5192
+ if (text2 === "NEEDS-ACTION") return "TODO";
5193
+ return STATUSES.has(text2) ? text2 : "TODO";
5046
5194
  }
5047
5195
  var TaskStore = class {
5048
5196
  file;
@@ -5067,6 +5215,8 @@ var TaskStore = class {
5067
5215
  id TEXT PRIMARY KEY,
5068
5216
  account_id TEXT NOT NULL,
5069
5217
  project_id TEXT,
5218
+ parent_id TEXT,
5219
+ waiting_on TEXT,
5070
5220
  calendar_href TEXT,
5071
5221
  href TEXT,
5072
5222
  etag TEXT,
@@ -5096,6 +5246,14 @@ var TaskStore = class {
5096
5246
  error TEXT
5097
5247
  );
5098
5248
  `);
5249
+ const columns = db.prepare("PRAGMA table_info(tasks)").all();
5250
+ if (!columns.some((column) => column.name === "parent_id")) {
5251
+ db.exec("ALTER TABLE tasks ADD COLUMN parent_id TEXT");
5252
+ }
5253
+ if (!columns.some((column) => column.name === "waiting_on")) {
5254
+ db.exec("ALTER TABLE tasks ADD COLUMN waiting_on TEXT");
5255
+ }
5256
+ db.exec("UPDATE tasks SET status = 'TODO' WHERE status = 'NEEDS-ACTION'");
5099
5257
  this.db = db;
5100
5258
  return db;
5101
5259
  }
@@ -5135,16 +5293,26 @@ var TaskStore = class {
5135
5293
  if (title === "") throw new Error("\u4EFB\u52A1\u6807\u9898\u4E0D\u80FD\u4E3A\u7A7A");
5136
5294
  if (title.length > 300) throw new Error("\u4EFB\u52A1\u6807\u9898\u8FC7\u957F");
5137
5295
  const status = statusOf(input.status);
5296
+ if (status === "WAITING" && String(input.waitingOn ?? "").trim() === "") {
5297
+ throw new Error("\u7B49\u5F85\u4E2D\u4EFB\u52A1\u9700\u8981\u586B\u5199\u7B49\u8C01/\u7B49\u4EC0\u4E48");
5298
+ }
5299
+ if (input.parentId !== void 0 && input.parentId !== null) {
5300
+ const parent = this.get(input.parentId);
5301
+ if (parent === void 0) throw new Error("\u7236\u4EFB\u52A1\u4E0D\u5B58\u5728");
5302
+ if (parent.parentId !== null) throw new Error("\u5B50\u4EFB\u52A1\u4E0B\u4E0D\u80FD\u518D\u5EFA\u5B50\u4EFB\u52A1");
5303
+ }
5138
5304
  const id = `task_${randomUUID5()}`;
5139
5305
  const stamp3 = now();
5140
5306
  this.open().prepare(
5141
- `INSERT INTO tasks (id, account_id, project_id, uid, title, notes, due, status, priority, owner,
5142
- message_key, message_subject, completed_at, dirty, deleted, created_at, updated_at)
5143
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, ?, ?)`
5307
+ `INSERT INTO tasks (id, account_id, project_id, parent_id, waiting_on, uid, title, notes, due, status,
5308
+ priority, owner, message_key, message_subject, completed_at, dirty, deleted, created_at, updated_at)
5309
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 0, ?, ?)`
5144
5310
  ).run(
5145
5311
  id,
5146
5312
  input.accountId,
5147
5313
  input.projectId ?? null,
5314
+ input.parentId ?? null,
5315
+ String(input.waitingOn ?? ""),
5148
5316
  `${randomUUID5()}@dsh-mailharbor`,
5149
5317
  title,
5150
5318
  String(input.notes ?? ""),
@@ -5173,15 +5341,27 @@ var TaskStore = class {
5173
5341
  priority: fields.priority === void 0 ? current.priority : Number(fields.priority),
5174
5342
  owner: fields.owner === void 0 ? current.owner : String(fields.owner ?? ""),
5175
5343
  projectId: fields.projectId === void 0 ? current.projectId : fields.projectId,
5344
+ parentId: fields.parentId === void 0 ? current.parentId : fields.parentId,
5345
+ waitingOn: fields.waitingOn === void 0 ? current.waitingOn : String(fields.waitingOn ?? ""),
5176
5346
  messageKey: fields.messageKey === void 0 ? current.messageKey : String(fields.messageKey),
5177
5347
  messageSubject: fields.messageSubject === void 0 ? current.messageSubject : String(fields.messageSubject)
5178
5348
  };
5179
5349
  if (next.title === "") throw new Error("\u4EFB\u52A1\u6807\u9898\u4E0D\u80FD\u4E3A\u7A7A");
5350
+ if (next.status === "WAITING" && next.waitingOn.trim() === "") {
5351
+ throw new Error("\u7B49\u5F85\u4E2D\u4EFB\u52A1\u9700\u8981\u586B\u5199\u7B49\u8C01/\u7B49\u4EC0\u4E48");
5352
+ }
5353
+ if (next.parentId !== null) {
5354
+ if (next.parentId === id) throw new Error("\u4EFB\u52A1\u4E0D\u80FD\u4F5C\u4E3A\u81EA\u5DF1\u7684\u5B50\u4EFB\u52A1");
5355
+ const parent = this.get(next.parentId);
5356
+ if (parent === void 0) throw new Error("\u7236\u4EFB\u52A1\u4E0D\u5B58\u5728");
5357
+ if (parent.parentId !== null) throw new Error("\u5B50\u4EFB\u52A1\u4E0B\u4E0D\u80FD\u518D\u5EFA\u5B50\u4EFB\u52A1");
5358
+ }
5180
5359
  const stamp3 = now();
5181
5360
  const completedAt = next.status === "COMPLETED" ? current.completedAt ?? stamp3 : null;
5182
5361
  this.open().prepare(
5183
5362
  `UPDATE tasks SET title = ?, notes = ?, due = ?, status = ?, priority = ?, owner = ?, project_id = ?,
5184
- message_key = ?, message_subject = ?, completed_at = ?, dirty = 1, updated_at = ? WHERE id = ?`
5363
+ parent_id = ?, waiting_on = ?, message_key = ?, message_subject = ?, completed_at = ?,
5364
+ dirty = 1, updated_at = ? WHERE id = ?`
5185
5365
  ).run(
5186
5366
  next.title,
5187
5367
  next.notes,
@@ -5190,6 +5370,8 @@ var TaskStore = class {
5190
5370
  Math.max(0, Math.min(9, Number(next.priority) || 0)),
5191
5371
  next.owner,
5192
5372
  next.projectId,
5373
+ next.parentId,
5374
+ next.waitingOn,
5193
5375
  next.messageKey,
5194
5376
  next.messageSubject,
5195
5377
  completedAt,