forge-openclaw-plugin 0.2.3 → 0.2.7
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 +114 -6
- package/dist/assets/board-CzgvdLO8.js +6 -0
- package/dist/assets/board-CzgvdLO8.js.map +1 -0
- package/dist/assets/favicon-BCHm9dUV.ico +0 -0
- package/dist/assets/index-8d_oM8fL.js +27 -0
- package/dist/assets/index-8d_oM8fL.js.map +1 -0
- package/dist/assets/index-D4A_bq8m.css +1 -0
- package/dist/assets/motion-STUd1O46.js +10 -0
- package/dist/assets/motion-STUd1O46.js.map +1 -0
- package/dist/assets/plus-jakarta-sans-latin-ext-wght-normal-DmpS2jIq.woff2 +0 -0
- package/dist/assets/plus-jakarta-sans-latin-wght-normal-eXO_dkmS.woff2 +0 -0
- package/dist/assets/plus-jakarta-sans-vietnamese-wght-normal-qRpaaN48.woff2 +0 -0
- package/dist/assets/sora-latin-ext-wght-normal-CawQDOvP.woff2 +0 -0
- package/dist/assets/sora-latin-wght-normal-DdqRvwsR.woff2 +0 -0
- package/dist/assets/space-grotesk-latin-500-normal-CNSSEhBt.woff +0 -0
- package/dist/assets/space-grotesk-latin-500-normal-lFbtlQH6.woff2 +0 -0
- package/dist/assets/space-grotesk-latin-700-normal-CwsQ-cCU.woff +0 -0
- package/dist/assets/space-grotesk-latin-700-normal-RjhwGPKo.woff2 +0 -0
- package/dist/assets/space-grotesk-latin-ext-500-normal-3dgZTiw9.woff +0 -0
- package/dist/assets/space-grotesk-latin-ext-500-normal-DUe3BAxM.woff2 +0 -0
- package/dist/assets/space-grotesk-latin-ext-700-normal-BQnZhY3m.woff2 +0 -0
- package/dist/assets/space-grotesk-latin-ext-700-normal-HVCqSBdx.woff +0 -0
- package/dist/assets/space-grotesk-vietnamese-500-normal-BTqKIpxg.woff +0 -0
- package/dist/assets/space-grotesk-vietnamese-500-normal-BmEvtly_.woff2 +0 -0
- package/dist/assets/space-grotesk-vietnamese-700-normal-DMty7AZE.woff2 +0 -0
- package/dist/assets/space-grotesk-vietnamese-700-normal-Duxec5Rn.woff +0 -0
- package/dist/assets/table-CtNlETLc.js +23 -0
- package/dist/assets/table-CtNlETLc.js.map +1 -0
- package/dist/assets/ui-ThzkR_oW.js +46 -0
- package/dist/assets/ui-ThzkR_oW.js.map +1 -0
- package/dist/assets/vendor-CRS-psbw.css +1 -0
- package/dist/assets/vendor-DyHAI6nk.js +423 -0
- package/dist/assets/vendor-DyHAI6nk.js.map +1 -0
- package/dist/assets/viz-BJuBCz_G.js +34 -0
- package/dist/assets/viz-BJuBCz_G.js.map +1 -0
- package/dist/favicon.ico +0 -0
- package/dist/favicon.png +0 -0
- package/dist/index.html +29 -0
- package/dist/openclaw/api-client.d.ts +8 -0
- package/dist/openclaw/api-client.js +31 -4
- package/dist/openclaw/local-runtime.d.ts +3 -0
- package/dist/openclaw/local-runtime.js +135 -0
- package/dist/openclaw/parity.d.ts +4 -4
- package/dist/openclaw/parity.js +23 -33
- package/dist/openclaw/plugin-entry-shared.d.ts +5 -3
- package/dist/openclaw/plugin-entry-shared.js +52 -10
- package/dist/openclaw/routes.d.ts +12 -3
- package/dist/openclaw/routes.js +156 -924
- package/dist/openclaw/tools.js +242 -1100
- package/dist/server/app.js +2450 -0
- package/dist/server/db.js +313 -0
- package/dist/server/e2e-server.js +20 -0
- package/dist/server/errors.js +15 -0
- package/dist/server/index.js +16 -0
- package/dist/server/managers/base.js +17 -0
- package/dist/server/managers/contracts.js +47 -0
- package/dist/server/managers/platform/api-gateway-manager.js +11 -0
- package/dist/server/managers/platform/audit-manager.js +15 -0
- package/dist/server/managers/platform/authentication-manager.js +56 -0
- package/dist/server/managers/platform/authorization-manager.js +56 -0
- package/dist/server/managers/platform/background-job-manager.js +10 -0
- package/dist/server/managers/platform/configuration-manager.js +33 -0
- package/dist/server/managers/platform/database-manager.js +14 -0
- package/dist/server/managers/platform/event-bus-manager.js +7 -0
- package/dist/server/managers/platform/external-service-manager.js +11 -0
- package/dist/server/managers/platform/health-manager.js +7 -0
- package/dist/server/managers/platform/migration-manager.js +8 -0
- package/dist/server/managers/platform/search-index-manager.js +4 -0
- package/dist/server/managers/platform/secrets-manager.js +19 -0
- package/dist/server/managers/platform/session-manager.js +121 -0
- package/dist/server/managers/platform/storage-manager.js +16 -0
- package/dist/server/managers/platform/token-manager.js +37 -0
- package/dist/server/managers/platform/transaction-manager.js +8 -0
- package/dist/server/managers/platform/trusted-network.js +39 -0
- package/dist/server/managers/runtime.js +56 -0
- package/dist/server/managers/type-guards.js +4 -0
- package/dist/server/openapi.js +3512 -0
- package/dist/server/psyche-types.js +395 -0
- package/dist/server/repositories/activity-events.js +157 -0
- package/dist/server/repositories/collaboration.js +497 -0
- package/dist/server/repositories/comments.js +176 -0
- package/dist/server/repositories/deleted-entities.js +192 -0
- package/dist/server/repositories/domains.js +30 -0
- package/dist/server/repositories/event-log.js +64 -0
- package/dist/server/repositories/goals.js +159 -0
- package/dist/server/repositories/projects.js +214 -0
- package/dist/server/repositories/psyche.js +1356 -0
- package/dist/server/repositories/rewards.js +675 -0
- package/dist/server/repositories/settings.js +399 -0
- package/dist/server/repositories/tags.js +160 -0
- package/dist/server/repositories/task-runs.js +488 -0
- package/dist/server/repositories/tasks.js +413 -0
- package/dist/server/services/context.js +214 -0
- package/dist/server/services/dashboard.js +170 -0
- package/dist/server/services/entity-crud.js +576 -0
- package/dist/server/services/gamification.js +215 -0
- package/dist/server/services/insights.js +91 -0
- package/dist/server/services/projects.js +75 -0
- package/dist/server/services/psyche.js +63 -0
- package/dist/server/services/relations.js +28 -0
- package/dist/server/services/reviews.js +88 -0
- package/dist/server/services/run-recovery.js +13 -0
- package/dist/server/services/tagging.js +49 -0
- package/dist/server/services/task-run-watchdog.js +92 -0
- package/dist/server/services/work-time.js +176 -0
- package/dist/server/types.js +999 -0
- package/dist/server/web.js +91 -0
- package/openclaw.plugin.json +22 -10
- package/package.json +17 -4
- package/server/migrations/001_core.sql +333 -0
- package/server/migrations/002_psyche.sql +241 -0
- package/server/migrations/003_timer_execution.sql +18 -0
- package/server/migrations/004_psyche_linked_entities.sql +5 -0
- package/server/migrations/005_adaptive_schemas.sql +157 -0
- package/server/migrations/006_psyche_auth_setting.sql +4 -0
- package/server/migrations/007_deleted_entities.sql +16 -0
- package/skills/forge-openclaw/SKILL.md +189 -275
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { reconcileExpiredTaskRuns } from "./run-recovery.js";
|
|
2
|
+
const DEFAULT_INTERVAL_MS = 15_000;
|
|
3
|
+
function createInitialStatus(intervalMs, limit) {
|
|
4
|
+
return {
|
|
5
|
+
intervalMs,
|
|
6
|
+
limit: limit ?? null,
|
|
7
|
+
running: false,
|
|
8
|
+
lastStartedAt: null,
|
|
9
|
+
lastCompletedAt: null,
|
|
10
|
+
lastSuccessfulAt: null,
|
|
11
|
+
lastErrorAt: null,
|
|
12
|
+
lastError: null,
|
|
13
|
+
lastRecovery: null,
|
|
14
|
+
totalRecoveredCount: 0,
|
|
15
|
+
consecutiveFailures: 0
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function createTaskRunWatchdog(options = {}) {
|
|
19
|
+
const intervalMs = Math.max(1_000, options.intervalMs ?? DEFAULT_INTERVAL_MS);
|
|
20
|
+
const now = options.now ?? (() => new Date());
|
|
21
|
+
const reconcile = options.reconcile ??
|
|
22
|
+
((input) => reconcileExpiredTaskRuns({
|
|
23
|
+
now: input.now,
|
|
24
|
+
limit: input.limit
|
|
25
|
+
}));
|
|
26
|
+
const status = createInitialStatus(intervalMs, options.limit);
|
|
27
|
+
let timer = null;
|
|
28
|
+
let inFlight = null;
|
|
29
|
+
const reconcileNow = async () => {
|
|
30
|
+
if (inFlight) {
|
|
31
|
+
return inFlight;
|
|
32
|
+
}
|
|
33
|
+
status.lastStartedAt = now().toISOString();
|
|
34
|
+
inFlight = Promise.resolve().then(() => reconcile({
|
|
35
|
+
now: now(),
|
|
36
|
+
limit: options.limit
|
|
37
|
+
}));
|
|
38
|
+
try {
|
|
39
|
+
const summary = await inFlight;
|
|
40
|
+
status.lastCompletedAt = summary.recoveredAt;
|
|
41
|
+
status.lastSuccessfulAt = summary.recoveredAt;
|
|
42
|
+
status.lastRecovery = summary;
|
|
43
|
+
status.totalRecoveredCount += summary.recoveredCount;
|
|
44
|
+
status.consecutiveFailures = 0;
|
|
45
|
+
status.lastError = null;
|
|
46
|
+
status.lastErrorAt = null;
|
|
47
|
+
return summary;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const errorAt = now().toISOString();
|
|
51
|
+
status.lastCompletedAt = errorAt;
|
|
52
|
+
status.lastErrorAt = errorAt;
|
|
53
|
+
status.lastError = error instanceof Error ? error.message : String(error);
|
|
54
|
+
status.consecutiveFailures += 1;
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
inFlight = null;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const start = async () => {
|
|
62
|
+
if (timer) {
|
|
63
|
+
return status.lastRecovery ?? reconcileNow();
|
|
64
|
+
}
|
|
65
|
+
status.running = true;
|
|
66
|
+
const summary = await reconcileNow();
|
|
67
|
+
timer = setInterval(() => {
|
|
68
|
+
void reconcileNow().catch(() => {
|
|
69
|
+
// Preserve error details in watchdog status without crashing the server loop.
|
|
70
|
+
});
|
|
71
|
+
}, intervalMs);
|
|
72
|
+
timer.unref?.();
|
|
73
|
+
return summary;
|
|
74
|
+
};
|
|
75
|
+
const stop = () => {
|
|
76
|
+
if (timer) {
|
|
77
|
+
clearInterval(timer);
|
|
78
|
+
timer = null;
|
|
79
|
+
}
|
|
80
|
+
status.running = false;
|
|
81
|
+
};
|
|
82
|
+
const getStatus = () => ({
|
|
83
|
+
...status,
|
|
84
|
+
running: timer !== null
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
start,
|
|
88
|
+
stop,
|
|
89
|
+
reconcileNow,
|
|
90
|
+
getStatus
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { getDatabase } from "../db.js";
|
|
2
|
+
function readTimeAccountingMode() {
|
|
3
|
+
try {
|
|
4
|
+
const row = getDatabase()
|
|
5
|
+
.prepare(`SELECT time_accounting_mode
|
|
6
|
+
FROM app_settings
|
|
7
|
+
WHERE id = 1`)
|
|
8
|
+
.get();
|
|
9
|
+
if (row?.time_accounting_mode === "split" ||
|
|
10
|
+
row?.time_accounting_mode === "parallel" ||
|
|
11
|
+
row?.time_accounting_mode === "primary_only") {
|
|
12
|
+
return row.time_accounting_mode;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return "split";
|
|
17
|
+
}
|
|
18
|
+
return "split";
|
|
19
|
+
}
|
|
20
|
+
function readTaskRunTimingRows() {
|
|
21
|
+
return getDatabase()
|
|
22
|
+
.prepare(`SELECT
|
|
23
|
+
id,
|
|
24
|
+
task_id,
|
|
25
|
+
actor,
|
|
26
|
+
status,
|
|
27
|
+
timer_mode,
|
|
28
|
+
planned_duration_seconds,
|
|
29
|
+
is_current,
|
|
30
|
+
claimed_at,
|
|
31
|
+
heartbeat_at,
|
|
32
|
+
lease_expires_at,
|
|
33
|
+
completed_at,
|
|
34
|
+
released_at,
|
|
35
|
+
timed_out_at,
|
|
36
|
+
updated_at
|
|
37
|
+
FROM task_runs
|
|
38
|
+
ORDER BY claimed_at ASC, id ASC`)
|
|
39
|
+
.all();
|
|
40
|
+
}
|
|
41
|
+
function roundCreditedSeconds(value) {
|
|
42
|
+
return Math.round(value * 100) / 100;
|
|
43
|
+
}
|
|
44
|
+
function getRunEndMs(row, nowMs) {
|
|
45
|
+
if (row.status === "active") {
|
|
46
|
+
return Math.max(Date.parse(row.claimed_at), Math.min(nowMs, Date.parse(row.lease_expires_at)));
|
|
47
|
+
}
|
|
48
|
+
const terminal = row.completed_at ??
|
|
49
|
+
row.released_at ??
|
|
50
|
+
row.timed_out_at ??
|
|
51
|
+
row.updated_at ??
|
|
52
|
+
row.lease_expires_at ??
|
|
53
|
+
row.heartbeat_at;
|
|
54
|
+
return Math.max(Date.parse(row.claimed_at), Date.parse(terminal));
|
|
55
|
+
}
|
|
56
|
+
function buildRunTimings(rows, now) {
|
|
57
|
+
const nowMs = now.getTime();
|
|
58
|
+
return rows
|
|
59
|
+
.map((row) => ({
|
|
60
|
+
row,
|
|
61
|
+
startMs: Date.parse(row.claimed_at),
|
|
62
|
+
endMs: getRunEndMs(row, nowMs)
|
|
63
|
+
}))
|
|
64
|
+
.filter((timing) => Number.isFinite(timing.startMs) && Number.isFinite(timing.endMs) && timing.endMs >= timing.startMs);
|
|
65
|
+
}
|
|
66
|
+
function computeCreditedSecondsByActor(timings, mode) {
|
|
67
|
+
const creditedMs = new Map();
|
|
68
|
+
const timingsByActor = new Map();
|
|
69
|
+
for (const timing of timings) {
|
|
70
|
+
const list = timingsByActor.get(timing.row.actor) ?? [];
|
|
71
|
+
list.push(timing);
|
|
72
|
+
timingsByActor.set(timing.row.actor, list);
|
|
73
|
+
creditedMs.set(timing.row.id, 0);
|
|
74
|
+
}
|
|
75
|
+
for (const actorTimings of timingsByActor.values()) {
|
|
76
|
+
const boundaries = [...new Set(actorTimings.flatMap((timing) => [timing.startMs, timing.endMs]))].sort((a, b) => a - b);
|
|
77
|
+
for (let index = 0; index < boundaries.length - 1; index += 1) {
|
|
78
|
+
const startMs = boundaries[index];
|
|
79
|
+
const endMs = boundaries[index + 1];
|
|
80
|
+
const sliceMs = endMs - startMs;
|
|
81
|
+
if (sliceMs <= 0) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const active = actorTimings.filter((timing) => timing.startMs < endMs && timing.endMs > startMs);
|
|
85
|
+
if (active.length === 0) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (mode === "parallel") {
|
|
89
|
+
for (const timing of active) {
|
|
90
|
+
creditedMs.set(timing.row.id, (creditedMs.get(timing.row.id) ?? 0) + sliceMs);
|
|
91
|
+
}
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (mode === "primary_only") {
|
|
95
|
+
const current = active.find((timing) => timing.row.is_current === 1);
|
|
96
|
+
if (current) {
|
|
97
|
+
creditedMs.set(current.row.id, (creditedMs.get(current.row.id) ?? 0) + sliceMs);
|
|
98
|
+
}
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const shareMs = sliceMs / active.length;
|
|
102
|
+
for (const timing of active) {
|
|
103
|
+
creditedMs.set(timing.row.id, (creditedMs.get(timing.row.id) ?? 0) + shareMs);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return new Map([...creditedMs.entries()].map(([runId, value]) => [runId, roundCreditedSeconds(value / 1000)]));
|
|
108
|
+
}
|
|
109
|
+
export function computeWorkTime(now = new Date()) {
|
|
110
|
+
const mode = readTimeAccountingMode();
|
|
111
|
+
const timings = buildRunTimings(readTaskRunTimingRows(), now);
|
|
112
|
+
const creditedByRunId = computeCreditedSecondsByActor(timings, mode);
|
|
113
|
+
const runMetrics = new Map();
|
|
114
|
+
const taskSummaries = new Map();
|
|
115
|
+
for (const timing of timings) {
|
|
116
|
+
const elapsedWallSeconds = Math.max(0, Math.floor((timing.endMs - timing.startMs) / 1000));
|
|
117
|
+
const creditedSeconds = creditedByRunId.get(timing.row.id) ?? 0;
|
|
118
|
+
const remainingSeconds = timing.row.timer_mode === "planned" && timing.row.planned_duration_seconds !== null
|
|
119
|
+
? Math.max(0, timing.row.planned_duration_seconds - elapsedWallSeconds)
|
|
120
|
+
: null;
|
|
121
|
+
const overtimeSeconds = timing.row.timer_mode === "planned" && timing.row.planned_duration_seconds !== null
|
|
122
|
+
? Math.max(0, elapsedWallSeconds - timing.row.planned_duration_seconds)
|
|
123
|
+
: 0;
|
|
124
|
+
const isCurrent = timing.row.is_current === 1 && timing.row.status === "active";
|
|
125
|
+
runMetrics.set(timing.row.id, {
|
|
126
|
+
elapsedWallSeconds,
|
|
127
|
+
creditedSeconds,
|
|
128
|
+
remainingSeconds,
|
|
129
|
+
overtimeSeconds,
|
|
130
|
+
isCurrent
|
|
131
|
+
});
|
|
132
|
+
const existing = taskSummaries.get(timing.row.task_id) ?? {
|
|
133
|
+
totalTrackedSeconds: 0,
|
|
134
|
+
totalCreditedSeconds: 0,
|
|
135
|
+
activeRunCount: 0,
|
|
136
|
+
hasCurrentRun: false,
|
|
137
|
+
currentRunId: null
|
|
138
|
+
};
|
|
139
|
+
taskSummaries.set(timing.row.task_id, {
|
|
140
|
+
totalTrackedSeconds: existing.totalTrackedSeconds + elapsedWallSeconds,
|
|
141
|
+
totalCreditedSeconds: roundCreditedSeconds(existing.totalCreditedSeconds + creditedSeconds),
|
|
142
|
+
activeRunCount: existing.activeRunCount + (timing.row.status === "active" ? 1 : 0),
|
|
143
|
+
hasCurrentRun: existing.hasCurrentRun || isCurrent,
|
|
144
|
+
currentRunId: isCurrent ? timing.row.id : existing.currentRunId
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
mode,
|
|
149
|
+
runMetrics,
|
|
150
|
+
taskSummaries
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
export function emptyTaskTimeSummary() {
|
|
154
|
+
return {
|
|
155
|
+
totalTrackedSeconds: 0,
|
|
156
|
+
totalCreditedSeconds: 0,
|
|
157
|
+
activeRunCount: 0,
|
|
158
|
+
hasCurrentRun: false,
|
|
159
|
+
currentRunId: null
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
export function sumTaskTimeSummaries(taskIds, summaries) {
|
|
163
|
+
return taskIds.reduce((accumulator, taskId) => {
|
|
164
|
+
const summary = summaries.get(taskId);
|
|
165
|
+
if (!summary) {
|
|
166
|
+
return accumulator;
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
totalTrackedSeconds: accumulator.totalTrackedSeconds + summary.totalTrackedSeconds,
|
|
170
|
+
totalCreditedSeconds: roundCreditedSeconds(accumulator.totalCreditedSeconds + summary.totalCreditedSeconds),
|
|
171
|
+
activeRunCount: accumulator.activeRunCount + summary.activeRunCount,
|
|
172
|
+
hasCurrentRun: accumulator.hasCurrentRun || summary.hasCurrentRun,
|
|
173
|
+
currentRunId: accumulator.currentRunId ?? summary.currentRunId
|
|
174
|
+
};
|
|
175
|
+
}, emptyTaskTimeSummary());
|
|
176
|
+
}
|