forge-openclaw-plugin 0.2.26 → 0.2.27
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/README.md +59 -3
- package/dist/assets/{board-ta0rUHOf.js → board-C6jCchjI.js} +2 -2
- package/dist/assets/{board-ta0rUHOf.js.map → board-C6jCchjI.js.map} +1 -1
- package/dist/assets/index-DVvS8iiU.css +1 -0
- package/dist/assets/index-zYB-9Dfo.js +85 -0
- package/dist/assets/index-zYB-9Dfo.js.map +1 -0
- package/dist/assets/knowledge-graph-layout.worker-DRvzPxhP.js +2 -0
- package/dist/assets/knowledge-graph-layout.worker-DRvzPxhP.js.map +1 -0
- package/dist/assets/{motion-fBKPB6yw.js → motion-DFHrH2rd.js} +2 -2
- package/dist/assets/{motion-fBKPB6yw.js.map → motion-DFHrH2rd.js.map} +1 -1
- package/dist/assets/{table-C-IGTQni.js → table-ZL7Di_u3.js} +2 -2
- package/dist/assets/{table-C-IGTQni.js.map → table-ZL7Di_u3.js.map} +1 -1
- package/dist/assets/{ui-DInOpaYF.js → ui-CKNPpz7q.js} +2 -2
- package/dist/assets/{ui-DInOpaYF.js.map → ui-CKNPpz7q.js.map} +1 -1
- package/dist/assets/vendor-DoNZuFhn.js +1247 -0
- package/dist/assets/vendor-DoNZuFhn.js.map +1 -0
- package/dist/index.html +7 -7
- package/dist/openclaw/local-runtime.js +16 -0
- package/dist/openclaw/routes.d.ts +27 -0
- package/dist/openclaw/routes.js +16 -12
- package/dist/server/server/migrations/037_workbench_public_inputs_and_run_inputs.sql +5 -0
- package/dist/server/server/migrations/038_data_management_settings.sql +11 -0
- package/dist/server/server/migrations/039_life_force_and_action_points.sql +114 -0
- package/dist/server/server/migrations/040_screen_time_domain.sql +89 -0
- package/dist/server/server/migrations/041_companion_source_states.sql +21 -0
- package/dist/server/server/migrations/042_movement_boxes.sql +47 -0
- package/dist/server/server/migrations/043_movement_box_overlap_overrides.sql +26 -0
- package/dist/server/server/src/app.js +1684 -117
- package/dist/server/server/src/connectors/box-registry.js +44 -9
- package/dist/server/server/src/data-management-types.js +107 -0
- package/dist/server/server/src/db.js +68 -4
- package/dist/server/server/src/demo-data.js +2 -2
- package/dist/server/server/src/health.js +702 -18
- package/dist/server/server/src/managers/platform/llm-manager.js +7 -4
- package/dist/server/server/src/managers/platform/mock-workbench-provider.js +149 -0
- package/dist/server/server/src/managers/platform/secrets-manager.js +18 -1
- package/dist/server/server/src/managers/runtime.js +9 -0
- package/dist/server/server/src/movement.js +1971 -112
- package/dist/server/server/src/openapi.js +489 -1
- package/dist/server/server/src/psyche-types.js +9 -1
- package/dist/server/server/src/repositories/activity-events.js +8 -0
- package/dist/server/server/src/repositories/ai-connectors.js +522 -74
- package/dist/server/server/src/repositories/habits.js +37 -1
- package/dist/server/server/src/repositories/model-settings.js +13 -3
- package/dist/server/server/src/repositories/notes.js +3 -0
- package/dist/server/server/src/repositories/settings.js +380 -18
- package/dist/server/server/src/repositories/tasks.js +170 -10
- package/dist/server/server/src/runtime-data-root.js +82 -0
- package/dist/server/server/src/screen-time.js +802 -0
- package/dist/server/server/src/services/data-management.js +788 -0
- package/dist/server/server/src/services/entity-crud.js +205 -2
- package/dist/server/server/src/services/knowledge-graph.js +1455 -0
- package/dist/server/server/src/services/life-force-model.js +197 -0
- package/dist/server/server/src/services/life-force.js +1270 -0
- package/dist/server/server/src/services/psyche-observation-calendar.js +383 -16
- package/dist/server/server/src/types.js +286 -13
- package/dist/server/server/src/web.js +228 -13
- package/dist/server/src/components/customization/utility-widgets.js +136 -27
- package/dist/server/src/components/ui/info-tooltip.js +25 -0
- package/dist/server/src/components/workbench-boxes/calendar/calendar-boxes.js +78 -0
- package/dist/server/src/components/workbench-boxes/goals/goals-boxes.js +62 -0
- package/dist/server/src/components/workbench-boxes/habits/habits-boxes.js +62 -0
- package/dist/server/src/components/workbench-boxes/health/health-boxes.js +63 -8
- package/dist/server/src/components/workbench-boxes/insights/insights-boxes.js +50 -0
- package/dist/server/src/components/workbench-boxes/kanban/kanban-boxes.js +62 -54
- package/dist/server/src/components/workbench-boxes/movement/movement-boxes.js +18 -8
- package/dist/server/src/components/workbench-boxes/notes/notes-boxes.js +56 -38
- package/dist/server/src/components/workbench-boxes/overview/overview-boxes.js +65 -0
- package/dist/server/src/components/workbench-boxes/preferences/preferences-boxes.js +78 -0
- package/dist/server/src/components/workbench-boxes/projects/projects-boxes.js +35 -30
- package/dist/server/src/components/workbench-boxes/psyche/psyche-boxes.js +88 -0
- package/dist/server/src/components/workbench-boxes/questionnaires/questionnaires-boxes.js +61 -0
- package/dist/server/src/components/workbench-boxes/review/review-boxes.js +53 -0
- package/dist/server/src/components/workbench-boxes/shared/define-workbench-box.js +3 -1
- package/dist/server/src/components/workbench-boxes/shared/generic-node-view.js +39 -3
- package/dist/server/src/components/workbench-boxes/strategies/strategies-boxes.js +62 -0
- package/dist/server/src/components/workbench-boxes/tasks/tasks-boxes.js +76 -0
- package/dist/server/src/components/workbench-boxes/today/today-boxes.js +47 -32
- package/dist/server/src/components/workbench-boxes/wiki/wiki-boxes.js +60 -0
- package/dist/server/src/lib/api.js +280 -21
- package/dist/server/src/lib/data-management-types.js +1 -0
- package/dist/server/src/lib/entity-visuals.js +279 -0
- package/dist/server/src/lib/knowledge-graph-types.js +276 -0
- package/dist/server/src/lib/knowledge-graph.js +470 -0
- package/dist/server/src/lib/schemas.js +4 -0
- package/dist/server/src/lib/snapshot-normalizer.js +43 -1
- package/dist/server/src/lib/workbench/contracts.js +229 -0
- package/dist/server/src/lib/workbench/nodes.js +200 -0
- package/dist/server/src/lib/workbench/registry.js +52 -5
- package/dist/server/src/lib/workbench/runtime.js +254 -38
- package/dist/server/src/lib/workbench/tool-catalog.js +68 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server/migrations/037_workbench_public_inputs_and_run_inputs.sql +5 -0
- package/server/migrations/038_data_management_settings.sql +11 -0
- package/server/migrations/039_life_force_and_action_points.sql +114 -0
- package/server/migrations/040_screen_time_domain.sql +89 -0
- package/server/migrations/041_companion_source_states.sql +21 -0
- package/server/migrations/042_movement_boxes.sql +47 -0
- package/server/migrations/043_movement_box_overlap_overrides.sql +26 -0
- package/skills/forge-openclaw/SKILL.md +24 -11
- package/skills/forge-openclaw/entity_conversation_playbooks.md +210 -34
- package/skills/forge-openclaw/psyche_entity_playbooks.md +113 -17
- package/dist/assets/index-Ro0ZF_az.css +0 -1
- package/dist/assets/index-ytlpSj23.js +0 -79
- package/dist/assets/index-ytlpSj23.js.map +0 -1
- package/dist/assets/vendor-lE3tZJcC.js +0 -876
- package/dist/assets/vendor-lE3tZJcC.js.map +0 -1
|
@@ -13,6 +13,8 @@ import { awardTaskCompletionReward, reverseLatestTaskCompletionReward } from "./
|
|
|
13
13
|
import { findUserByLabel, getDefaultUser, getUserById, resolveUserForMutation } from "./users.js";
|
|
14
14
|
import { assertTaskRelations } from "../services/relations.js";
|
|
15
15
|
import { computeWorkTime, emptyTaskTimeSummary } from "../services/work-time.js";
|
|
16
|
+
import { buildTaskLifeForceFields, getTaskCompletionRequirement, upsertTaskActionProfile } from "../services/life-force.js";
|
|
17
|
+
import { createWorkAdjustment } from "./work-adjustments.js";
|
|
16
18
|
import { calendarSchedulingRulesSchema, taskSchema } from "../types.js";
|
|
17
19
|
function readTaskTagIds(taskId) {
|
|
18
20
|
const rows = getDatabase()
|
|
@@ -21,7 +23,7 @@ function readTaskTagIds(taskId) {
|
|
|
21
23
|
return filterDeletedIds("tag", rows.map((row) => row.tag_id));
|
|
22
24
|
}
|
|
23
25
|
function mapTask(row, time = emptyTaskTimeSummary()) {
|
|
24
|
-
|
|
26
|
+
const task = taskSchema.parse(decorateOwnedEntity("task", {
|
|
25
27
|
id: row.id,
|
|
26
28
|
title: row.title,
|
|
27
29
|
description: row.description,
|
|
@@ -39,12 +41,20 @@ function mapTask(row, time = emptyTaskTimeSummary()) {
|
|
|
39
41
|
? null
|
|
40
42
|
: calendarSchedulingRulesSchema.parse(JSON.parse(row.scheduling_rules_json)),
|
|
41
43
|
sortOrder: row.sort_order,
|
|
44
|
+
resolutionKind: row.resolution_kind === "completed" || row.resolution_kind === "split"
|
|
45
|
+
? row.resolution_kind
|
|
46
|
+
: null,
|
|
47
|
+
splitParentTaskId: row.split_parent_task_id,
|
|
42
48
|
completedAt: row.completed_at,
|
|
43
49
|
createdAt: row.created_at,
|
|
44
50
|
updatedAt: row.updated_at,
|
|
45
51
|
tagIds: readTaskTagIds(row.id),
|
|
46
52
|
time
|
|
47
53
|
}));
|
|
54
|
+
return {
|
|
55
|
+
...task,
|
|
56
|
+
...buildTaskLifeForceFields(task, task.userId ?? undefined)
|
|
57
|
+
};
|
|
48
58
|
}
|
|
49
59
|
function replaceTaskTags(taskId, tagIds) {
|
|
50
60
|
const database = getDatabase();
|
|
@@ -193,12 +203,38 @@ function updateTaskRecord(current, input, activity) {
|
|
|
193
203
|
const movedColumns = nextStatus !== current.status;
|
|
194
204
|
const nextSort = input.sortOrder ??
|
|
195
205
|
(movedColumns ? nextSortOrder(nextStatus) : current.sortOrder);
|
|
206
|
+
if (current.status !== "done" &&
|
|
207
|
+
nextStatus === "done" &&
|
|
208
|
+
input.resolutionKind !== "split") {
|
|
209
|
+
const completionRequirement = getTaskCompletionRequirement(current, current.userId ?? undefined);
|
|
210
|
+
if (input.enforceTodayWorkLog === true &&
|
|
211
|
+
completionRequirement.requiresWorkLog &&
|
|
212
|
+
input.completedTodayWorkSeconds === undefined) {
|
|
213
|
+
throw new HttpError(409, "task_completion_work_log_required", "Log how long you worked on this task today before closing it.", {
|
|
214
|
+
taskId: current.id,
|
|
215
|
+
todayCreditedSeconds: completionRequirement.todayCreditedSeconds
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
if ((input.completedTodayWorkSeconds ?? 0) > 0) {
|
|
219
|
+
const appliedDeltaMinutes = Math.max(1, Math.round((input.completedTodayWorkSeconds ?? 0) / 60));
|
|
220
|
+
createWorkAdjustment({
|
|
221
|
+
entityType: "task",
|
|
222
|
+
entityId: current.id,
|
|
223
|
+
deltaMinutes: appliedDeltaMinutes,
|
|
224
|
+
appliedDeltaMinutes,
|
|
225
|
+
note: "Completion log for today"
|
|
226
|
+
}, {
|
|
227
|
+
actor: activity?.actor ?? null,
|
|
228
|
+
source: activity?.source ?? "ui"
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
196
232
|
const completedAt = normalizeCompletedAt(nextStatus, current.completedAt);
|
|
197
233
|
const updatedAt = new Date().toISOString();
|
|
198
234
|
getDatabase()
|
|
199
235
|
.prepare(`UPDATE tasks
|
|
200
236
|
SET title = ?, description = ?, status = ?, priority = ?, owner = ?, goal_id = ?, due_date = ?, effort = ?,
|
|
201
|
-
energy = ?, points = ?, planned_duration_seconds = ?, scheduling_rules_json = ?, sort_order = ?, completed_at = ?, updated_at = ?, project_id = ?
|
|
237
|
+
energy = ?, points = ?, planned_duration_seconds = ?, scheduling_rules_json = ?, sort_order = ?, resolution_kind = ?, split_parent_task_id = ?, completed_at = ?, updated_at = ?, project_id = ?
|
|
202
238
|
WHERE id = ?`)
|
|
203
239
|
.run(input.title ?? current.title, input.description ?? current.description, nextStatus, input.priority ?? current.priority, assignment.ownerLabel, nextGoalId, input.dueDate === undefined ? current.dueDate : input.dueDate, input.effort ?? current.effort, input.energy ?? current.energy, input.points ?? current.points, input.plannedDurationSeconds === undefined
|
|
204
240
|
? current.plannedDurationSeconds
|
|
@@ -208,10 +244,23 @@ function updateTaskRecord(current, input, activity) {
|
|
|
208
244
|
: JSON.stringify(current.schedulingRules)
|
|
209
245
|
: input.schedulingRules === null
|
|
210
246
|
? null
|
|
211
|
-
: JSON.stringify(input.schedulingRules), nextSort,
|
|
247
|
+
: JSON.stringify(input.schedulingRules), nextSort, input.resolutionKind === undefined ? current.resolutionKind : input.resolutionKind, input.splitParentTaskId === undefined
|
|
248
|
+
? current.splitParentTaskId
|
|
249
|
+
: input.splitParentTaskId, completedAt, updatedAt, nextProjectId, current.id);
|
|
212
250
|
replaceTaskTags(current.id, nextTagIds);
|
|
213
251
|
setEntityOwner("task", current.id, assignment.userId);
|
|
214
252
|
const updated = getTaskById(current.id);
|
|
253
|
+
if (updated &&
|
|
254
|
+
(input.actionCostBand !== undefined ||
|
|
255
|
+
input.plannedDurationSeconds !== undefined ||
|
|
256
|
+
input.title !== undefined)) {
|
|
257
|
+
upsertTaskActionProfile({
|
|
258
|
+
taskId: updated.id,
|
|
259
|
+
title: updated.title,
|
|
260
|
+
plannedDurationSeconds: updated.plannedDurationSeconds,
|
|
261
|
+
actionCostBand: input.actionCostBand ?? updated.actionPointSummary?.costBand ?? "standard"
|
|
262
|
+
});
|
|
263
|
+
}
|
|
215
264
|
if (updated && activity) {
|
|
216
265
|
const statusChanged = current.status !== updated.status;
|
|
217
266
|
const ownerChanged = current.owner !== updated.owner;
|
|
@@ -262,7 +311,9 @@ function updateTaskRecord(current, input, activity) {
|
|
|
262
311
|
pointsChanged
|
|
263
312
|
}
|
|
264
313
|
});
|
|
265
|
-
if (current.status !== "done" &&
|
|
314
|
+
if (current.status !== "done" &&
|
|
315
|
+
updated.status === "done" &&
|
|
316
|
+
updated.resolutionKind !== "split") {
|
|
266
317
|
awardTaskCompletionReward(updated, activity);
|
|
267
318
|
}
|
|
268
319
|
else if (current.status === "done" && updated.status !== "done") {
|
|
@@ -322,15 +373,21 @@ function insertTaskRecord(input, activity) {
|
|
|
322
373
|
getDatabase()
|
|
323
374
|
.prepare(`INSERT INTO tasks (
|
|
324
375
|
id, title, description, status, priority, owner, goal_id, project_id, due_date, effort, energy, points,
|
|
325
|
-
planned_duration_seconds, scheduling_rules_json, sort_order, completed_at, created_at, updated_at
|
|
376
|
+
planned_duration_seconds, scheduling_rules_json, sort_order, resolution_kind, split_parent_task_id, completed_at, created_at, updated_at
|
|
326
377
|
)
|
|
327
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
378
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
328
379
|
.run(id, input.title, input.description, input.status, input.priority, assignment.ownerLabel, relationState.goalId, relationState.projectId, input.dueDate, input.effort, input.energy, input.points, input.plannedDurationSeconds, input.schedulingRules === null
|
|
329
380
|
? null
|
|
330
|
-
: JSON.stringify(input.schedulingRules), sortOrder, completedAt, now, now);
|
|
381
|
+
: JSON.stringify(input.schedulingRules), sortOrder, input.status === "done" ? "completed" : null, null, completedAt, now, now);
|
|
331
382
|
setEntityOwner("task", id, assignment.userId);
|
|
332
383
|
replaceTaskTags(id, input.tagIds);
|
|
333
384
|
const task = getTaskById(id);
|
|
385
|
+
upsertTaskActionProfile({
|
|
386
|
+
taskId: task.id,
|
|
387
|
+
title: task.title,
|
|
388
|
+
plannedDurationSeconds: task.plannedDurationSeconds,
|
|
389
|
+
actionCostBand: input.actionCostBand
|
|
390
|
+
});
|
|
334
391
|
if (activity) {
|
|
335
392
|
recordActivityEvent({
|
|
336
393
|
entityType: "task",
|
|
@@ -350,7 +407,7 @@ function insertTaskRecord(input, activity) {
|
|
|
350
407
|
points: task.points
|
|
351
408
|
}
|
|
352
409
|
});
|
|
353
|
-
if (task.status === "done") {
|
|
410
|
+
if (task.status === "done" && task.resolutionKind !== "split") {
|
|
354
411
|
awardTaskCompletionReward(task, activity);
|
|
355
412
|
}
|
|
356
413
|
}
|
|
@@ -403,7 +460,7 @@ export function listTasks(filters = {}) {
|
|
|
403
460
|
}
|
|
404
461
|
const rows = getDatabase()
|
|
405
462
|
.prepare(`SELECT id, title, description, status, priority, owner, goal_id, project_id, due_date, effort, energy, points,
|
|
406
|
-
planned_duration_seconds, scheduling_rules_json, sort_order,
|
|
463
|
+
planned_duration_seconds, scheduling_rules_json, sort_order, resolution_kind, split_parent_task_id,
|
|
407
464
|
completed_at, created_at, updated_at
|
|
408
465
|
FROM tasks
|
|
409
466
|
${whereSql}
|
|
@@ -428,7 +485,7 @@ export function getTaskById(taskId) {
|
|
|
428
485
|
}
|
|
429
486
|
const row = getDatabase()
|
|
430
487
|
.prepare(`SELECT id, title, description, status, priority, owner, goal_id, project_id, due_date, effort, energy, points,
|
|
431
|
-
planned_duration_seconds, scheduling_rules_json, sort_order,
|
|
488
|
+
planned_duration_seconds, scheduling_rules_json, sort_order, resolution_kind, split_parent_task_id,
|
|
432
489
|
completed_at, created_at, updated_at
|
|
433
490
|
FROM tasks
|
|
434
491
|
WHERE id = ?`)
|
|
@@ -520,3 +577,106 @@ export function deleteTask(taskId, activity) {
|
|
|
520
577
|
return current;
|
|
521
578
|
});
|
|
522
579
|
}
|
|
580
|
+
export function splitTask(taskId, input, activity) {
|
|
581
|
+
const current = getTaskById(taskId);
|
|
582
|
+
if (!current) {
|
|
583
|
+
return undefined;
|
|
584
|
+
}
|
|
585
|
+
return runInTransaction(() => {
|
|
586
|
+
const remainingRatio = clampRatio(input.remainingRatio);
|
|
587
|
+
const remainingExpectedDurationSeconds = Math.max(60, current.actionPointSummary.expectedDurationSeconds -
|
|
588
|
+
current.time.totalCreditedSeconds);
|
|
589
|
+
const remainingAp = Math.max(1, current.actionPointSummary.remainingAp);
|
|
590
|
+
const firstChildDurationSeconds = Math.max(60, Math.round(remainingExpectedDurationSeconds * remainingRatio));
|
|
591
|
+
const secondChildDurationSeconds = Math.max(60, remainingExpectedDurationSeconds - firstChildDurationSeconds);
|
|
592
|
+
const firstChildTotalAp = Math.max(1, Math.round(remainingAp * remainingRatio));
|
|
593
|
+
const secondChildTotalAp = Math.max(1, remainingAp - firstChildTotalAp);
|
|
594
|
+
const parent = updateTaskRecord(current, {
|
|
595
|
+
status: "done",
|
|
596
|
+
resolutionKind: "split"
|
|
597
|
+
}, activity);
|
|
598
|
+
if (!parent) {
|
|
599
|
+
throw new HttpError(500, "task_split_failed", "Could not mark the original task as split.");
|
|
600
|
+
}
|
|
601
|
+
const totalCostPoints = current.points;
|
|
602
|
+
const firstChild = insertTaskRecord({
|
|
603
|
+
title: input.firstTitle,
|
|
604
|
+
description: current.description,
|
|
605
|
+
status: "focus",
|
|
606
|
+
priority: current.priority,
|
|
607
|
+
owner: current.owner,
|
|
608
|
+
userId: current.userId ?? null,
|
|
609
|
+
goalId: current.goalId,
|
|
610
|
+
projectId: current.projectId,
|
|
611
|
+
dueDate: current.dueDate,
|
|
612
|
+
effort: current.effort,
|
|
613
|
+
energy: current.energy,
|
|
614
|
+
points: Math.max(5, Math.round(totalCostPoints * remainingRatio)),
|
|
615
|
+
plannedDurationSeconds: firstChildDurationSeconds,
|
|
616
|
+
schedulingRules: current.schedulingRules,
|
|
617
|
+
actionCostBand: current.actionPointSummary.costBand,
|
|
618
|
+
tagIds: current.tagIds,
|
|
619
|
+
notes: []
|
|
620
|
+
}, activity);
|
|
621
|
+
const secondChild = insertTaskRecord({
|
|
622
|
+
title: input.secondTitle,
|
|
623
|
+
description: current.description,
|
|
624
|
+
status: "focus",
|
|
625
|
+
priority: current.priority,
|
|
626
|
+
owner: current.owner,
|
|
627
|
+
userId: current.userId ?? null,
|
|
628
|
+
goalId: current.goalId,
|
|
629
|
+
projectId: current.projectId,
|
|
630
|
+
dueDate: current.dueDate,
|
|
631
|
+
effort: current.effort,
|
|
632
|
+
energy: current.energy,
|
|
633
|
+
points: Math.max(5, totalCostPoints - firstChild.points),
|
|
634
|
+
plannedDurationSeconds: secondChildDurationSeconds,
|
|
635
|
+
schedulingRules: current.schedulingRules,
|
|
636
|
+
actionCostBand: current.actionPointSummary.costBand,
|
|
637
|
+
tagIds: current.tagIds,
|
|
638
|
+
notes: []
|
|
639
|
+
}, activity);
|
|
640
|
+
upsertTaskActionProfile({
|
|
641
|
+
taskId: firstChild.id,
|
|
642
|
+
title: firstChild.title,
|
|
643
|
+
plannedDurationSeconds: firstChildDurationSeconds,
|
|
644
|
+
actionCostBand: current.actionPointSummary.costBand,
|
|
645
|
+
totalCostAp: firstChildTotalAp
|
|
646
|
+
});
|
|
647
|
+
upsertTaskActionProfile({
|
|
648
|
+
taskId: secondChild.id,
|
|
649
|
+
title: secondChild.title,
|
|
650
|
+
plannedDurationSeconds: secondChildDurationSeconds,
|
|
651
|
+
actionCostBand: current.actionPointSummary.costBand,
|
|
652
|
+
totalCostAp: secondChildTotalAp
|
|
653
|
+
});
|
|
654
|
+
updateTaskRecord(firstChild, { splitParentTaskId: current.id }, activity);
|
|
655
|
+
updateTaskRecord(secondChild, { splitParentTaskId: current.id }, activity);
|
|
656
|
+
if (activity) {
|
|
657
|
+
recordActivityEvent({
|
|
658
|
+
entityType: "task",
|
|
659
|
+
entityId: current.id,
|
|
660
|
+
eventType: "task_split",
|
|
661
|
+
title: `Task split: ${current.title}`,
|
|
662
|
+
description: `Remaining work was split into ${input.firstTitle} and ${input.secondTitle}.`,
|
|
663
|
+
actor: activity.actor ?? null,
|
|
664
|
+
source: activity.source,
|
|
665
|
+
metadata: {
|
|
666
|
+
firstChildTaskId: firstChild.id,
|
|
667
|
+
secondChildTaskId: secondChild.id
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
return {
|
|
672
|
+
parent: getTaskById(current.id),
|
|
673
|
+
children: [getTaskById(firstChild.id), getTaskById(secondChild.id)]
|
|
674
|
+
};
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
function clampRatio(value) {
|
|
678
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
679
|
+
return 0.5;
|
|
680
|
+
}
|
|
681
|
+
return Math.min(0.9, Math.max(0.1, value));
|
|
682
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
7
|
+
const monorepoRuntimePreferencePath = path.resolve(projectRoot, "..", "..", "data", "forge-runtime.json");
|
|
8
|
+
export function getMonorepoRuntimePreferencePath() {
|
|
9
|
+
return monorepoRuntimePreferencePath;
|
|
10
|
+
}
|
|
11
|
+
export async function readMonorepoPreferredDataRoot() {
|
|
12
|
+
if (!existsSync(monorepoRuntimePreferencePath)) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const raw = await readFile(monorepoRuntimePreferencePath, "utf8");
|
|
17
|
+
const parsed = JSON.parse(raw);
|
|
18
|
+
return typeof parsed.dataRoot === "string" && parsed.dataRoot.trim().length > 0
|
|
19
|
+
? path.resolve(parsed.dataRoot)
|
|
20
|
+
: null;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export async function writeMonorepoPreferredDataRoot(dataRoot) {
|
|
27
|
+
await mkdir(path.dirname(monorepoRuntimePreferencePath), { recursive: true });
|
|
28
|
+
await writeFile(monorepoRuntimePreferencePath, `${JSON.stringify({
|
|
29
|
+
dataRoot: path.resolve(dataRoot),
|
|
30
|
+
updatedAt: new Date().toISOString()
|
|
31
|
+
}, null, 2)}\n`, "utf8");
|
|
32
|
+
}
|
|
33
|
+
async function patchJsonFile(filePath, transform) {
|
|
34
|
+
let payload = {};
|
|
35
|
+
if (existsSync(filePath)) {
|
|
36
|
+
try {
|
|
37
|
+
const raw = await readFile(filePath, "utf8");
|
|
38
|
+
const parsed = JSON.parse(raw);
|
|
39
|
+
if (parsed && typeof parsed === "object") {
|
|
40
|
+
payload = parsed;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
payload = {};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const next = transform(payload);
|
|
48
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
49
|
+
await writeFile(filePath, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
50
|
+
}
|
|
51
|
+
export async function syncLocalAdapterDataRoots(dataRoot) {
|
|
52
|
+
const resolved = path.resolve(dataRoot);
|
|
53
|
+
const openClawConfigPath = path.join(os.homedir(), ".openclaw", "openclaw.json");
|
|
54
|
+
const hermesConfigPath = path.join(os.homedir(), ".hermes", "forge", "config.json");
|
|
55
|
+
await patchJsonFile(openClawConfigPath, (payload) => {
|
|
56
|
+
const plugins = payload.plugins && typeof payload.plugins === "object"
|
|
57
|
+
? { ...payload.plugins }
|
|
58
|
+
: {};
|
|
59
|
+
const entries = plugins.entries && typeof plugins.entries === "object"
|
|
60
|
+
? { ...plugins.entries }
|
|
61
|
+
: {};
|
|
62
|
+
const currentEntry = entries["forge-openclaw-plugin"] &&
|
|
63
|
+
typeof entries["forge-openclaw-plugin"] === "object"
|
|
64
|
+
? { ...entries["forge-openclaw-plugin"] }
|
|
65
|
+
: { enabled: true };
|
|
66
|
+
const currentConfig = currentEntry.config && typeof currentEntry.config === "object"
|
|
67
|
+
? { ...currentEntry.config }
|
|
68
|
+
: {};
|
|
69
|
+
currentConfig.dataRoot = resolved;
|
|
70
|
+
currentEntry.config = currentConfig;
|
|
71
|
+
entries["forge-openclaw-plugin"] = currentEntry;
|
|
72
|
+
plugins.entries = entries;
|
|
73
|
+
return {
|
|
74
|
+
...payload,
|
|
75
|
+
plugins
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
await patchJsonFile(hermesConfigPath, (payload) => ({
|
|
79
|
+
...payload,
|
|
80
|
+
dataRoot: resolved
|
|
81
|
+
}));
|
|
82
|
+
}
|