@zaclinley/dsh-mailharbor 0.23.0 → 0.25.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
@@ -2542,6 +2542,16 @@ var TASK_SYSTEM = `\u4F60\u662F\u4EFB\u52A1\u63D0\u53D6\u52A9\u624B\u3002\u4ECE\
2542
2542
  - \u622A\u6B62\u65F6\u95F4\u80FD\u786E\u5B9A\u624D\u586B\uFF0C\u5426\u5219\u7559\u7A7A
2543
2543
  - \u6CA1\u6709\u53EF\u6267\u884C\u4E8B\u9879\u65F6\u8F93\u51FA []
2544
2544
  - \u4E0D\u8981\u7F16\u9020\u90AE\u4EF6\u91CC\u6CA1\u6709\u7684\u4FE1\u606F`;
2545
+ var PROJECT_TASK_SYSTEM = `\u4F60\u662F\u9879\u76EE\u4EFB\u52A1\u62C6\u89E3\u52A9\u624B\u3002\u6839\u636E\u9879\u76EE\u8BB0\u5F55\u3001\u672A\u5B8C\u6210\u4EFB\u52A1\u548C\u90AE\u4EF6\u6458\u8981\uFF0C\u63D0\u51FA\u9700\u8981\u7528\u6237\u8DDF\u8FDB\u7684\u5177\u4F53\u4E8B\u9879\u3002
2546
+
2547
+ \u53EA\u8F93\u51FA\u4E00\u4E2A JSON \u6570\u7EC4\uFF0C\u4E0D\u8981\u8F93\u51FA\u89E3\u91CA\u3001Markdown \u6216\u4EE3\u7801\u5757\u6807\u8BB0\u3002\u6BCF\u4E2A\u5143\u7D20\uFF1A
2548
+ {"title": "\u4EFB\u52A1\u6807\u9898\uFF08\u52A8\u8BCD\u5F00\u5934\uFF0C\u5C3D\u91CF\u5177\u4F53\uFF09", "due": "YYYY-MM-DD \u6216\u7A7A\u5B57\u7B26\u4E32", "priority": 0 \u5230 9 \u7684\u6574\u6570, "owner": "\u76F8\u5173\u8054\u7CFB\u4EBA\u6216\u7A7A\u5B57\u7B26\u4E32", "notes": "\u4E00\u53E5\u4F9D\u636E"}
2549
+
2550
+ \u89C4\u5219\uFF1A
2551
+ - \u53EA\u63D0\u51FA\u6750\u6599\u91CC\u660E\u786E\u8981\u6C42\u3001\u6216\u660E\u663E\u9700\u8981\u8DDF\u8FDB\u7684\u4E8B\u9879
2552
+ - \u4E0D\u8981\u91CD\u590D\u5DF2\u7ECF\u5B58\u5728\u7684\u4EFB\u52A1
2553
+ - \u622A\u6B62\u65F6\u95F4\u4E0D\u786E\u5B9A\u5C31\u7559\u7A7A
2554
+ - \u6CA1\u6709\u53EF\u6267\u884C\u4E8B\u9879\u65F6\u8F93\u51FA []`;
2545
2555
  var STYLE_SYSTEM = `\u4F60\u662F\u5199\u4F5C\u98CE\u683C\u5206\u6790\u5E08\u3002\u9605\u8BFB\u7528\u6237\u6700\u8FD1\u53D1\u51FA\u7684\u90AE\u4EF6\uFF0C\u7528\u7B80\u4F53\u4E2D\u6587\u5199\u4E00\u6BB5 150-300 \u5B57\u7684\u5199\u4F5C\u98CE\u683C\u6863\u6848\uFF0C\u8986\u76D6\uFF1A
2546
2556
  \u5E38\u7528\u79F0\u547C\u3001\u5F00\u5934\u65B9\u5F0F\u3001\u7ED3\u5C3E\u4E0E\u7F72\u540D\u3001\u8BED\u6C14\uFF08\u6B63\u5F0F/\u4EB2\u5207/\u76F4\u63A5\uFF09\u3001\u53E5\u5B50\u957F\u77ED\u3001\u5E38\u7528\u8868\u8FBE\u6216\u53E3\u5934\u7985\u3002
2547
2557
  \u53EA\u8F93\u51FA\u6863\u6848\u6B63\u6587\uFF0C\u4E0D\u8981\u8F93\u51FA\u6807\u9898\u3001\u5217\u8868\u7B26\u53F7\u6216\u5206\u6790\u8FC7\u7A0B\u3002`;
@@ -2752,6 +2762,11 @@ ${clip(item, 4e3)}`).join("\n\n");
2752
2762
  const text2 = await this.complete(TASK_SYSTEM, contextOf(message), signal);
2753
2763
  return parseTaskJson(text2);
2754
2764
  }
2765
+ /** Propose tasks for a project from its notes, open tasks and mail summary. */
2766
+ async proposeProjectTasks(context, signal) {
2767
+ const text2 = await this.complete(PROJECT_TASK_SYSTEM, clip(context, 3e4), signal);
2768
+ return parseTaskJson(text2);
2769
+ }
2755
2770
  /** Compose-body assistance: draft from points, polish, or shorten. */
2756
2771
  async compose(account, options, signal) {
2757
2772
  const style = this.style(account)?.profile ?? "";
@@ -2958,6 +2973,7 @@ var ProjectStore = class {
2958
2973
  status: project.status ?? "active",
2959
2974
  notes: Array.isArray(project.notes) ? project.notes : [],
2960
2975
  milestones: Array.isArray(project.milestones) ? project.milestones : [],
2976
+ activity: Array.isArray(project.activity) ? project.activity : [],
2961
2977
  sessionIds: Array.isArray(project.sessionIds) ? project.sessionIds : project.sessionId === "" ? [] : [project.sessionId]
2962
2978
  }))
2963
2979
  };
@@ -3057,6 +3073,47 @@ var ProjectStore = class {
3057
3073
  this.flush();
3058
3074
  return project;
3059
3075
  }
3076
+ addMilestone(id, title, due) {
3077
+ const project = this.get(id);
3078
+ if (project === void 0) return void 0;
3079
+ project.milestones.push({
3080
+ id: `ms_${randomUUID2().slice(0, 8)}`,
3081
+ title,
3082
+ due,
3083
+ done: false
3084
+ });
3085
+ project.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3086
+ this.flush();
3087
+ return project;
3088
+ }
3089
+ updateMilestone(id, milestoneId, patch) {
3090
+ const project = this.get(id);
3091
+ const milestone = project?.milestones.find((item) => item.id === milestoneId);
3092
+ if (project === void 0 || milestone === void 0) return void 0;
3093
+ if (patch.title !== void 0) milestone.title = patch.title;
3094
+ if (patch.due !== void 0) milestone.due = patch.due;
3095
+ if (patch.done !== void 0) milestone.done = patch.done;
3096
+ project.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3097
+ this.flush();
3098
+ return project;
3099
+ }
3100
+ removeMilestone(id, milestoneId) {
3101
+ const project = this.get(id);
3102
+ if (project === void 0) return void 0;
3103
+ project.milestones = project.milestones.filter((item) => item.id !== milestoneId);
3104
+ project.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3105
+ this.flush();
3106
+ return project;
3107
+ }
3108
+ addActivity(id, text2, at = (/* @__PURE__ */ new Date()).toISOString()) {
3109
+ const project = this.get(id);
3110
+ if (project === void 0) return void 0;
3111
+ project.activity.unshift({ id: `act_${randomUUID2().slice(0, 8)}`, at, text: text2 });
3112
+ if (project.activity.length > 100) project.activity.length = 100;
3113
+ project.updatedAt = at;
3114
+ this.flush();
3115
+ return project;
3116
+ }
3060
3117
  remove(id) {
3061
3118
  const data = this.load();
3062
3119
  const index = data.projects.findIndex((project) => project.id === id);
@@ -3124,6 +3181,7 @@ async function createProject(options) {
3124
3181
  status: "active",
3125
3182
  notes: [],
3126
3183
  milestones: [],
3184
+ activity: [],
3127
3185
  path,
3128
3186
  workspaceId,
3129
3187
  sessionId,
@@ -3185,7 +3243,7 @@ ${conversation.trim()}
3185
3243
  }
3186
3244
 
3187
3245
  // src/host/version.ts
3188
- var VERSION = "0.23.0";
3246
+ var VERSION = "0.25.0";
3189
3247
 
3190
3248
  // src/host/api.ts
3191
3249
  init_smtp();
@@ -4058,6 +4116,32 @@ function buildRouter(runtime) {
4058
4116
  throw aiHttpError(error);
4059
4117
  }
4060
4118
  });
4119
+ router.post("/ai/propose/tasks", async ({ body }) => {
4120
+ const payload = await body();
4121
+ const account = accountFromPayload(runtime, payload);
4122
+ const projectId = typeof payload.projectId === "string" ? payload.projectId : "";
4123
+ const project = projectId === "" ? void 0 : runtime.projects().get(projectId);
4124
+ if (projectId !== "" && project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4125
+ const notes = Array.isArray(payload.noteIds) ? payload.noteIds.map((id) => project?.notes.find((note) => note.id === id)).filter((note) => note !== void 0) : project?.notes.slice(0, 30) ?? [];
4126
+ const text2 = typeof payload.text === "string" ? payload.text : "";
4127
+ if (notes.length === 0 && text2.trim() === "") throw new HttpError(400, "\u6CA1\u6709\u53EF\u5206\u6790\u7684\u8BB0\u5F55");
4128
+ const tasks = project === void 0 ? [] : runtime.tasks().list({ accountId: account.id, projectId: project.id });
4129
+ const context = [
4130
+ project === void 0 ? "" : `\u9879\u76EE\uFF1A${project.name}\uFF08${project.status}\uFF09`,
4131
+ notes.length === 0 ? "" : `\u9879\u76EE\u8BB0\u5F55\uFF1A
4132
+ ${notes.map((note) => `- ${note.createdAt.slice(0, 10)} ${note.body}`).join("\n")}`,
4133
+ tasks.length === 0 ? "" : `\u5DF2\u6709\u4EFB\u52A1\uFF1A
4134
+ ${tasks.map((task) => `- ${task.title}`).join("\n")}`,
4135
+ text2.trim() === "" ? "" : `\u8865\u5145\u5185\u5BB9\uFF1A
4136
+ ${text2}`
4137
+ ].filter((line2) => line2 !== "").join("\n\n");
4138
+ try {
4139
+ const suggestions = await runtime.ai().proposeProjectTasks(context);
4140
+ return { suggestions };
4141
+ } catch (error) {
4142
+ throw aiHttpError(error);
4143
+ }
4144
+ });
4061
4145
  router.post("/projects/:id/attachment", async ({ params, body }) => {
4062
4146
  const payload = await body();
4063
4147
  const account = accountFromPayload(runtime, payload);
@@ -4168,7 +4252,87 @@ function buildRouter(runtime) {
4168
4252
  }
4169
4253
  const project = runtime.projects().update(String(params.id ?? ""), patch);
4170
4254
  if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4171
- return { project: projectView(project) };
4255
+ if (typeof patch.status === "string") {
4256
+ const labels = {
4257
+ active: "\u8FDB\u884C\u4E2D",
4258
+ paused: "\u6682\u505C",
4259
+ done: "\u5DF2\u5B8C\u6210",
4260
+ archived: "\u5DF2\u5F52\u6863"
4261
+ };
4262
+ runtime.projects().addActivity(project.id, `\u9879\u76EE\u72B6\u6001\u6539\u4E3A\u300C${labels[patch.status] ?? patch.status}\u300D`);
4263
+ }
4264
+ return { project: projectView(runtime.projects().get(project.id) ?? project) };
4265
+ });
4266
+ router.post("/projects/:id/milestones", async ({ params, body }) => {
4267
+ const payload = await body();
4268
+ const project = runtime.projects().get(String(params.id ?? ""));
4269
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4270
+ const title = String(payload.title ?? "").trim();
4271
+ if (title === "") throw new HttpError(400, "\u91CC\u7A0B\u7891\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
4272
+ const updated = runtime.projects().addMilestone(project.id, title, typeof payload.due === "string" ? payload.due : "");
4273
+ if (updated === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4274
+ runtime.projects().addActivity(project.id, `\u65B0\u589E\u91CC\u7A0B\u7891\u300C${title}\u300D`);
4275
+ return { project: projectView(runtime.projects().get(project.id) ?? updated) };
4276
+ });
4277
+ router.patch("/projects/:id/milestones/:milestoneId", async ({ params, body }) => {
4278
+ const payload = await body();
4279
+ const project = runtime.projects().get(String(params.id ?? ""));
4280
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4281
+ const milestone = project.milestones.find((item) => item.id === String(params.milestoneId ?? ""));
4282
+ if (milestone === void 0) throw new HttpError(404, "\u91CC\u7A0B\u7891\u4E0D\u5B58\u5728");
4283
+ const patch = {};
4284
+ if (typeof payload.title === "string") patch.title = payload.title.trim();
4285
+ if (typeof payload.due === "string") patch.due = payload.due;
4286
+ if (typeof payload.done === "boolean") patch.done = payload.done;
4287
+ const updated = runtime.projects().updateMilestone(project.id, milestone.id, patch);
4288
+ if (updated === void 0) throw new HttpError(404, "\u91CC\u7A0B\u7891\u4E0D\u5B58\u5728");
4289
+ if (patch.done !== void 0) {
4290
+ runtime.projects().addActivity(
4291
+ project.id,
4292
+ `\u91CC\u7A0B\u7891\u300C${patch.title ?? milestone.title}\u300D${patch.done ? "\u5DF2\u5B8C\u6210" : "\u91CD\u65B0\u6253\u5F00"}`
4293
+ );
4294
+ }
4295
+ return { project: projectView(runtime.projects().get(project.id) ?? updated) };
4296
+ });
4297
+ router.delete("/projects/:id/milestones/:milestoneId", ({ params }) => {
4298
+ const project = runtime.projects().get(String(params.id ?? ""));
4299
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4300
+ const milestone = project.milestones.find((item) => item.id === String(params.milestoneId ?? ""));
4301
+ const updated = runtime.projects().removeMilestone(project.id, String(params.milestoneId ?? ""));
4302
+ if (updated === void 0) throw new HttpError(404, "\u91CC\u7A0B\u7891\u4E0D\u5B58\u5728");
4303
+ runtime.projects().addActivity(project.id, `\u5220\u9664\u91CC\u7A0B\u7891\u300C${milestone?.title ?? ""}\u300D`);
4304
+ return { project: projectView(runtime.projects().get(project.id) ?? updated) };
4305
+ });
4306
+ router.post("/projects/:id/session", async ({ params, body }) => {
4307
+ const payload = await body();
4308
+ const project = runtime.projects().get(String(params.id ?? ""));
4309
+ if (project === void 0) throw new HttpError(404, "\u9879\u76EE\u4E0D\u5B58\u5728");
4310
+ if (runtime.sessions === void 0) throw new HttpError(400, "\u5F53\u524D DSH \u7EC4\u5408\u6CA1\u6709\u4F1A\u8BDD\u670D\u52A1");
4311
+ const purpose = typeof payload.purpose === "string" ? payload.purpose : "";
4312
+ const sessionId = randomUUID3();
4313
+ const session = runtime.sessions.create(sessionId, { meta: { cwd: project.path } });
4314
+ const notes = project.notes.slice(0, 20).map((note) => `- ${note.createdAt.slice(0, 10)} ${note.body}`).join("\n");
4315
+ const accountId = project.accountIds[0];
4316
+ const tasks = accountId === void 0 ? [] : runtime.tasks().list({ accountId, projectId: project.id }).filter((task) => task.status !== "COMPLETED" && task.status !== "CANCELLED").map(
4317
+ (task) => `- [${task.status}] ${task.title}${task.waitingOn === "" ? "" : `\uFF08\u7B49\u5F85\uFF1A${task.waitingOn}\uFF09`}`
4318
+ ).join("\n");
4319
+ const context = [
4320
+ `\u9879\u76EE\uFF1A${project.name}\uFF08${project.status}\uFF09`,
4321
+ purpose === "" ? "" : `\u8FD9\u6B21\u4F1A\u8BDD\u7684\u76EE\u7684\uFF1A${purpose}`,
4322
+ notes === "" ? "" : `\u6700\u8FD1\u8BB0\u5F55\uFF1A
4323
+ ${notes}`,
4324
+ tasks === "" ? "" : `\u672A\u5B8C\u6210\u4EFB\u52A1\uFF1A
4325
+ ${tasks}`,
4326
+ "\u8BF7\u57FA\u4E8E\u4EE5\u4E0A\u4E0A\u4E0B\u6587\u534F\u52A9\u6574\u7406\u8D44\u6599\u6216\u5904\u7406\u6587\u4EF6\uFF1B\u9700\u8981\u521B\u5EFA\u4EFB\u52A1\u65F6\u5148\u7ED9\u51FA\u63D0\u8BAE\uFF0C\u7B49\u6211\u786E\u8BA4\u3002"
4327
+ ].filter((line2) => line2 !== "").join("\n\n");
4328
+ session.append(
4329
+ "user/message",
4330
+ { role: "user", content: [{ type: "text", text: context }], source: { kind: "user" } },
4331
+ { surfaceOp: "append" }
4332
+ );
4333
+ runtime.projects().update(project.id, { sessionIds: [...project.sessionIds, sessionId] });
4334
+ runtime.projects().addActivity(project.id, "\u521B\u5EFA\u9879\u76EE\u4F1A\u8BDD");
4335
+ return { sessionId, project: projectView(runtime.projects().get(project.id) ?? project) };
4172
4336
  });
4173
4337
  router.post("/projects/:id/notes", async ({ params, body }) => {
4174
4338
  const payload = await body();
@@ -4178,7 +4342,10 @@ function buildRouter(runtime) {
4178
4342
  if (text2 === "") throw new HttpError(400, "\u8BB0\u5F55\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A");
4179
4343
  const createdAt = typeof payload.createdAt === "string" && payload.createdAt !== "" ? payload.createdAt : (/* @__PURE__ */ new Date()).toISOString();
4180
4344
  const updated = runtime.projects().addNote(project.id, text2, createdAt);
4181
- return { project: updated === void 0 ? null : projectView(updated) };
4345
+ runtime.projects().addActivity(project.id, "\u65B0\u589E\u4E00\u6761\u9879\u76EE\u8BB0\u5F55");
4346
+ return {
4347
+ project: updated === void 0 ? null : projectView(runtime.projects().get(project.id) ?? updated)
4348
+ };
4182
4349
  });
4183
4350
  router.patch("/projects/:id/notes/:noteId", async ({ params, body }) => {
4184
4351
  const payload = await body();
@@ -4222,6 +4389,7 @@ ${note.body}`,
4222
4389
  runtime.projects().linkNoteTasks(project.id, note.id, [task.id]);
4223
4390
  created.push(taskView(task));
4224
4391
  }
4392
+ runtime.projects().addActivity(project.id, `\u5C06 ${String(created.length)} \u6761\u8BB0\u5F55\u8F6C\u4E3A\u4EFB\u52A1`);
4225
4393
  return {
4226
4394
  tasks: created,
4227
4395
  project: projectView(runtime.projects().get(project.id) ?? project)
@@ -4499,6 +4667,7 @@ function projectView(project) {
4499
4667
  status: project.status,
4500
4668
  notes: project.notes.map((note) => ({ ...note, taskIds: [...note.taskIds] })),
4501
4669
  milestones: project.milestones.map((milestone) => ({ ...milestone })),
4670
+ activity: project.activity.map((event) => ({ ...event })),
4502
4671
  path: project.path,
4503
4672
  workspaceId: project.workspaceId,
4504
4673
  sessionId: project.sessionId,