forge-openclaw-plugin 0.2.73 → 0.2.78
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/dist/assets/{board-B0TuXl4u.js → board-DKxKOwax.js} +1 -1
- package/dist/assets/index-Chc9CWm5.css +1 -0
- package/dist/assets/{index-clNilMKr.js → index-DEoJdpz5.js} +56 -56
- package/dist/assets/{motion-Dmjq6HPm.js → motion-CM4AfIqo.js} +1 -1
- package/dist/assets/{table-CKKimYN1.js → table-BUeQ9wzR.js} +1 -1
- package/dist/assets/{ui-JBdCP1Qb.js → ui-3Wd4pVaA.js} +1 -1
- package/dist/assets/{vendor-fiXu5f59.js → vendor-BVU0cZC9.js} +243 -240
- package/dist/index.html +7 -7
- package/dist/server/server/src/app.js +48 -3
- package/dist/server/server/src/health.js +58 -0
- package/dist/server/server/src/openapi.js +2 -0
- package/dist/server/server/src/services/devrage.js +20 -6
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/forge-openclaw/SKILL.md +3 -2
- package/dist/assets/index-DtT3Y-Bj.css +0 -1
package/dist/index.html
CHANGED
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
/>
|
|
14
14
|
<link rel="icon" type="image/png" href="/forge/assets/favicon-BCHm9dUV.ico" />
|
|
15
15
|
<link rel="alternate icon" href="/forge/assets/favicon-BCHm9dUV.ico" />
|
|
16
|
-
<script type="module" crossorigin src="/forge/assets/index-
|
|
17
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/vendor-
|
|
18
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/board-
|
|
19
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/ui-
|
|
20
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/motion-
|
|
21
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/table-
|
|
16
|
+
<script type="module" crossorigin src="/forge/assets/index-DEoJdpz5.js"></script>
|
|
17
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/vendor-BVU0cZC9.js">
|
|
18
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/board-DKxKOwax.js">
|
|
19
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/ui-3Wd4pVaA.js">
|
|
20
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/motion-CM4AfIqo.js">
|
|
21
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/table-BUeQ9wzR.js">
|
|
22
22
|
<link rel="stylesheet" crossorigin href="/forge/assets/vendor-B-Lq_OG3.css">
|
|
23
|
-
<link rel="stylesheet" crossorigin href="/forge/assets/index-
|
|
23
|
+
<link rel="stylesheet" crossorigin href="/forge/assets/index-Chc9CWm5.css">
|
|
24
24
|
</head>
|
|
25
25
|
<body class="bg-canvas text-ink antialiased">
|
|
26
26
|
<div id="root"></div>
|
|
@@ -4637,6 +4637,31 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4637
4637
|
classification: "specialized_domain_surface",
|
|
4638
4638
|
aliases: ["movement", "Movement"],
|
|
4639
4639
|
summary: "Dedicated movement workspace API. Use these routes for stays, trips, time-in-place questions, visited places, trip detail, selection aggregates, user-defined overlays, and repair actions on already-recorded movement data.",
|
|
4640
|
+
routeKeys: [
|
|
4641
|
+
"day",
|
|
4642
|
+
"month",
|
|
4643
|
+
"allTime",
|
|
4644
|
+
"timeline",
|
|
4645
|
+
"places",
|
|
4646
|
+
"boxDetail",
|
|
4647
|
+
"tripDetail",
|
|
4648
|
+
"selection",
|
|
4649
|
+
"settings",
|
|
4650
|
+
"settingsUpdate",
|
|
4651
|
+
"placeCreate",
|
|
4652
|
+
"placeUpdate",
|
|
4653
|
+
"userBoxCreate",
|
|
4654
|
+
"userBoxPreflight",
|
|
4655
|
+
"userBoxUpdate",
|
|
4656
|
+
"userBoxDelete",
|
|
4657
|
+
"automaticBoxInvalidate",
|
|
4658
|
+
"stayUpdate",
|
|
4659
|
+
"stayDelete",
|
|
4660
|
+
"tripUpdate",
|
|
4661
|
+
"tripDelete",
|
|
4662
|
+
"tripPointUpdate",
|
|
4663
|
+
"tripPointDelete"
|
|
4664
|
+
],
|
|
4640
4665
|
routeSelectionQuestions: [
|
|
4641
4666
|
"Is the user asking for a day, month, all-time, timeline, place, trip detail, selected-span, or settings answer?",
|
|
4642
4667
|
"Is this a missing-gap overlay, a saved-overlay repair, or an edit to one already-recorded stay, trip, or trip point?",
|
|
@@ -4709,6 +4734,7 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4709
4734
|
classification: "specialized_domain_surface",
|
|
4710
4735
|
aliases: ["life_force", "life-force", "Life Force"],
|
|
4711
4736
|
summary: "Dedicated life-force API. Use it to read the current energy budget, drains, recommendations, and warnings, then patch only the parts that are meant to be user-controlled.",
|
|
4737
|
+
routeKeys: ["overview", "profile", "weekdayTemplate", "fatigueSignal"],
|
|
4712
4738
|
routeSelectionQuestions: [
|
|
4713
4739
|
"Is the user trying to understand the overview, change durable profile assumptions, change a weekday curve, or log a right-now fatigue signal?",
|
|
4714
4740
|
"Are they describing a repeatable weekly shape or a one-off current state?",
|
|
@@ -4742,6 +4768,7 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4742
4768
|
classification: "specialized_domain_surface",
|
|
4743
4769
|
aliases: ["lifeForce", "life-force", "Life Force"],
|
|
4744
4770
|
summary: "Alias for the dedicated Life Force API keyed to the entity-style name `life_force`. Use the same overview, profile, weekday-template, and fatigue-signal routes as `lifeForce`.",
|
|
4771
|
+
routeKeys: ["overview", "profile", "weekdayTemplate", "fatigueSignal"],
|
|
4745
4772
|
routeSelectionQuestions: [
|
|
4746
4773
|
"Is the user trying to understand the overview, change durable profile assumptions, change a weekday curve, or log a right-now fatigue signal?",
|
|
4747
4774
|
"Are they describing a repeatable weekly shape or a one-off current state?",
|
|
@@ -4776,6 +4803,24 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4776
4803
|
classification: "specialized_domain_surface",
|
|
4777
4804
|
aliases: ["workbench", "Workbench"],
|
|
4778
4805
|
summary: "Dedicated graph-flow API. Use it for flow catalog reads, flow CRUD, execution, run history, published outputs, node results, and latest successful node outputs.",
|
|
4806
|
+
routeKeys: [
|
|
4807
|
+
"listFlows",
|
|
4808
|
+
"flowById",
|
|
4809
|
+
"flowBySlug",
|
|
4810
|
+
"publishedOutput",
|
|
4811
|
+
"runs",
|
|
4812
|
+
"runDetail",
|
|
4813
|
+
"runNodes",
|
|
4814
|
+
"nodeResult",
|
|
4815
|
+
"latestNodeOutput",
|
|
4816
|
+
"boxCatalog",
|
|
4817
|
+
"createFlow",
|
|
4818
|
+
"updateFlow",
|
|
4819
|
+
"deleteFlow",
|
|
4820
|
+
"runFlow",
|
|
4821
|
+
"runByPayload",
|
|
4822
|
+
"chatFlow"
|
|
4823
|
+
],
|
|
4779
4824
|
routeSelectionQuestions: [
|
|
4780
4825
|
"Is the job flow discovery, flow creation, flow editing, flow deletion, execution, run history, published output, run detail, node result, latest node output, or flow chat follow-up?",
|
|
4781
4826
|
"Does the user need a stable public contract or one execution artifact?",
|
|
@@ -4891,8 +4936,8 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4891
4936
|
],
|
|
4892
4937
|
verifyCommands: [
|
|
4893
4938
|
`curl -s ${origin}/api/v1/health`,
|
|
4894
|
-
"openclaw plugins install ./projects/forge/openclaw-plugin",
|
|
4895
|
-
"openclaw plugins
|
|
4939
|
+
"openclaw plugins install --link --dangerously-force-unsafe-install ./projects/forge/openclaw-plugin",
|
|
4940
|
+
"openclaw plugins inspect forge-openclaw-plugin --runtime",
|
|
4896
4941
|
"openclaw gateway restart",
|
|
4897
4942
|
"openclaw forge onboarding",
|
|
4898
4943
|
"openclaw forge health"
|
|
@@ -4900,7 +4945,7 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4900
4945
|
configNotes: [
|
|
4901
4946
|
"Localhost and Tailscale targets can usually use the operator-session path without a long-lived token.",
|
|
4902
4947
|
"The operator-session route is /api/v1/auth/operator-session, so trusted local OpenClaw onboarding does not need a browser confirmation step.",
|
|
4903
|
-
"If your current OpenClaw build blocks the repo-local install because of the package scanner, keep the repo folder on plugins.load.paths and verify that plugins
|
|
4948
|
+
"If your current OpenClaw build blocks the repo-local install because of the package scanner, keep the repo folder on plugins.load.paths and verify that plugins inspect --runtime still points at the local Forge source path before continuing.",
|
|
4904
4949
|
"Use a distinct actor label such as Albert (claw) so OpenClaw-originated work stays obvious in Forge provenance.",
|
|
4905
4950
|
"Create each agent as a Forge bot user, then use userId or userIds in tool inputs whenever the agent should focus on one human, one bot, or a specific collaboration slice.",
|
|
4906
4951
|
"If you genuinely need a durable managed token, create it through /api/v1/settings/tokens instead of sending the operator into the Settings UI."
|
|
@@ -3569,8 +3569,64 @@ export function getSleepViewData(userIds) {
|
|
|
3569
3569
|
sessions: recentDisplay
|
|
3570
3570
|
};
|
|
3571
3571
|
}
|
|
3572
|
+
function pickVitalMetricValue(metrics, candidates) {
|
|
3573
|
+
for (const key of candidates) {
|
|
3574
|
+
const metric = metrics[key];
|
|
3575
|
+
if (!metric) {
|
|
3576
|
+
continue;
|
|
3577
|
+
}
|
|
3578
|
+
const value = vitalMetricPrimaryValue({
|
|
3579
|
+
aggregation: metric.aggregation,
|
|
3580
|
+
latest: metric.latest ?? null,
|
|
3581
|
+
average: metric.average ?? null,
|
|
3582
|
+
total: metric.total ?? null,
|
|
3583
|
+
maximum: metric.maximum ?? null
|
|
3584
|
+
});
|
|
3585
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
3586
|
+
return value;
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
return null;
|
|
3590
|
+
}
|
|
3591
|
+
function buildFitnessVitalsTrend(rows) {
|
|
3592
|
+
const byDate = new Map();
|
|
3593
|
+
for (const row of rows) {
|
|
3594
|
+
const metrics = safeJsonParse(row.metrics_json, {});
|
|
3595
|
+
const bucket = byDate.get(row.date_key) ?? {
|
|
3596
|
+
restingHeartRate: [],
|
|
3597
|
+
vo2Max: []
|
|
3598
|
+
};
|
|
3599
|
+
const restingHeartRate = pickVitalMetricValue(metrics, [
|
|
3600
|
+
"restingHeartRate",
|
|
3601
|
+
"resting_heart_rate"
|
|
3602
|
+
]);
|
|
3603
|
+
const vo2Max = pickVitalMetricValue(metrics, [
|
|
3604
|
+
"vo2Max",
|
|
3605
|
+
"vo2max",
|
|
3606
|
+
"vo2_max"
|
|
3607
|
+
]);
|
|
3608
|
+
if (restingHeartRate != null) {
|
|
3609
|
+
bucket.restingHeartRate.push(restingHeartRate);
|
|
3610
|
+
}
|
|
3611
|
+
if (vo2Max != null) {
|
|
3612
|
+
bucket.vo2Max.push(vo2Max);
|
|
3613
|
+
}
|
|
3614
|
+
byDate.set(row.date_key, bucket);
|
|
3615
|
+
}
|
|
3616
|
+
return [...byDate.entries()]
|
|
3617
|
+
.sort((left, right) => left[0].localeCompare(right[0]))
|
|
3618
|
+
.slice(-90)
|
|
3619
|
+
.map(([dateKey, values]) => ({
|
|
3620
|
+
dateKey,
|
|
3621
|
+
restingHeartRate: values.restingHeartRate.length > 0
|
|
3622
|
+
? round(average(values.restingHeartRate), 1)
|
|
3623
|
+
: null,
|
|
3624
|
+
vo2Max: values.vo2Max.length > 0 ? round(average(values.vo2Max), 2) : null
|
|
3625
|
+
}));
|
|
3626
|
+
}
|
|
3572
3627
|
export function getFitnessViewData(userIds) {
|
|
3573
3628
|
const workouts = listWorkoutRows(userIds).map(mapWorkoutSession);
|
|
3629
|
+
const vitalsTrend = buildFitnessVitalsTrend(listDailySummaryRows("vitals", userIds));
|
|
3574
3630
|
const recent = workouts.slice(0, 40);
|
|
3575
3631
|
const weekly = recent.filter((session) => Date.now() - Date.parse(session.startedAt) <= 7 * 24 * 60 * 60 * 1000);
|
|
3576
3632
|
const weeklyVolumeSeconds = weekly.reduce((sum, session) => sum + session.durationSeconds, 0);
|
|
@@ -3684,6 +3740,8 @@ export function getFitnessViewData(userIds) {
|
|
|
3684
3740
|
totalMinutes: metrics.totalMinutes,
|
|
3685
3741
|
energyKcal: metrics.energyKcal
|
|
3686
3742
|
})),
|
|
3743
|
+
vitalsTrend,
|
|
3744
|
+
analysisSessions: workouts,
|
|
3687
3745
|
sessions: recent
|
|
3688
3746
|
};
|
|
3689
3747
|
}
|
|
@@ -3754,6 +3754,7 @@ export function buildOpenApiDocument() {
|
|
|
3754
3754
|
"classification",
|
|
3755
3755
|
"aliases",
|
|
3756
3756
|
"summary",
|
|
3757
|
+
"routeKeys",
|
|
3757
3758
|
"methodRoutes",
|
|
3758
3759
|
"readRoutes",
|
|
3759
3760
|
"writeRoutes",
|
|
@@ -3767,6 +3768,7 @@ export function buildOpenApiDocument() {
|
|
|
3767
3768
|
},
|
|
3768
3769
|
aliases: arrayOf({ type: "string" }),
|
|
3769
3770
|
summary: { type: "string" },
|
|
3771
|
+
routeKeys: arrayOf({ type: "string" }),
|
|
3770
3772
|
methodRoutes: {
|
|
3771
3773
|
type: "object",
|
|
3772
3774
|
additionalProperties: { type: "string" }
|
|
@@ -5,6 +5,7 @@ import { psycheMetricsViewDataSchema } from "../psyche-types.js";
|
|
|
5
5
|
const SWEAR_COUNT_KEY = "swear_count";
|
|
6
6
|
const SWEARING_MESSAGE_PERCENT_KEY = "swearing_message_percent";
|
|
7
7
|
const DEFAULT_ROLE_FILTER = new Set(["user"]);
|
|
8
|
+
const DAILY_RESYNC_INTERVAL_MS = 60 * 60 * 1000;
|
|
8
9
|
const PSYCHE_METRIC_DEFINITIONS = {
|
|
9
10
|
[SWEAR_COUNT_KEY]: {
|
|
10
11
|
metric: "devrageSwearCount",
|
|
@@ -33,14 +34,27 @@ export async function syncDevrageMetricHistory(options = {}) {
|
|
|
33
34
|
}
|
|
34
35
|
export async function syncDevrageMetricHistoryIfNeeded() {
|
|
35
36
|
const state = getDevrageSyncState();
|
|
37
|
+
const nextSync = getNextDevrageMetricSync(state);
|
|
38
|
+
if (nextSync) {
|
|
39
|
+
await syncDevrageMetricHistory(nextSync);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function getNextDevrageMetricSync(state, now = new Date()) {
|
|
36
43
|
if (!state?.full_sync_completed_at) {
|
|
37
|
-
|
|
38
|
-
return;
|
|
44
|
+
return { forceFull: true };
|
|
39
45
|
}
|
|
40
|
-
const today = todayDateKey();
|
|
46
|
+
const today = todayDateKey(now);
|
|
41
47
|
if (state.last_synced_date_key !== today) {
|
|
42
|
-
|
|
48
|
+
return { dateKey: today };
|
|
49
|
+
}
|
|
50
|
+
const lastTodaySync = Date.parse(state.last_daily_sync_at ?? state.full_sync_completed_at ?? state.updated_at);
|
|
51
|
+
if (!Number.isFinite(lastTodaySync)) {
|
|
52
|
+
return { dateKey: today };
|
|
53
|
+
}
|
|
54
|
+
if (now.getTime() - lastTodaySync >= DAILY_RESYNC_INTERVAL_MS) {
|
|
55
|
+
return { dateKey: today };
|
|
43
56
|
}
|
|
57
|
+
return null;
|
|
44
58
|
}
|
|
45
59
|
export function getDevrageMetricPayload() {
|
|
46
60
|
const generatedAt = nowIso();
|
|
@@ -396,12 +410,12 @@ function stableId(prefix, ...parts) {
|
|
|
396
410
|
const digest = createHash("sha256").update(parts.join("\u0000")).digest("hex").slice(0, 20);
|
|
397
411
|
return `${prefix}_${digest}`;
|
|
398
412
|
}
|
|
399
|
-
function todayDateKey() {
|
|
413
|
+
function todayDateKey(date = new Date()) {
|
|
400
414
|
return new Intl.DateTimeFormat("en-CA", {
|
|
401
415
|
year: "numeric",
|
|
402
416
|
month: "2-digit",
|
|
403
417
|
day: "2-digit"
|
|
404
|
-
}).format(
|
|
418
|
+
}).format(date);
|
|
405
419
|
}
|
|
406
420
|
function nowIso() {
|
|
407
421
|
return new Date().toISOString();
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "forge-openclaw-plugin",
|
|
3
3
|
"name": "Forge",
|
|
4
4
|
"description": "Curated OpenClaw adapter for the Forge collaboration API, UI entrypoint, and localhost auto-start runtime.",
|
|
5
|
-
"version": "0.2.
|
|
5
|
+
"version": "0.2.78",
|
|
6
6
|
"activation": {
|
|
7
7
|
"onStartup": true,
|
|
8
8
|
"onCapabilities": [
|
package/package.json
CHANGED
|
@@ -89,8 +89,9 @@ guessing.
|
|
|
89
89
|
exact `/api/v1/*` route or OpenClaw `/forge/v1/*` mirror published in onboarding.
|
|
90
90
|
Life Force may be keyed as `lifeForce` and as the entity-style alias `life_force`;
|
|
91
91
|
both point to the same `/api/v1/life-force/*` route family.
|
|
92
|
-
- The live onboarding `methodRoutes` map and
|
|
93
|
-
include the exact route-key to method/path map. Use
|
|
92
|
+
- The live onboarding `routeKeys` list, `methodRoutes` map, and specialized
|
|
93
|
+
route-key tool schemas include the exact route-key to method/path map. Use
|
|
94
|
+
`routeKeys` for the allowed names and `methodRoutes` as the
|
|
94
95
|
route-key-to-`METHOD /api/v1/...` source of truth when checking specialized
|
|
95
96
|
methods, especially POST aggregate reads such as Movement `selection` and DELETE
|
|
96
97
|
repair paths. When a route key's exact path contains placeholders such as `:id`,
|