forge-openclaw-plugin 0.2.60 → 0.2.61

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.
Files changed (46) hide show
  1. package/README.md +93 -46
  2. package/dist/assets/{board-B1V3M__K.js → board-DThHV1D8.js} +1 -1
  3. package/dist/assets/index-7gvVCqnV.css +1 -0
  4. package/dist/assets/index-_Cn6Prym.js +90 -0
  5. package/dist/assets/{motion-CltSTItx.js → motion-BtTJtHCw.js} +1 -1
  6. package/dist/assets/{table-B-VrSFx8.js → table-Bnw6pcwN.js} +1 -1
  7. package/dist/assets/{ui-DUqM4jkt.js → ui-CnVxFkj0.js} +1 -1
  8. package/dist/assets/{vendor-C0otBhgu.js → vendor-BgZ3YrRd.js} +212 -207
  9. package/dist/gamification-previews/dark-fantasy-item-trophy-tasks-anvil-marathon.webp +0 -0
  10. package/dist/gamification-previews/dark-fantasy-item-trophy-xp-levels-the-first-heat.webp +0 -0
  11. package/dist/gamification-previews/dark-fantasy-item-unlock-streaks-molten-crown-fire.webp +0 -0
  12. package/dist/gamification-previews/dark-fantasy-mascot.webp +0 -0
  13. package/dist/gamification-previews/dramatic-smithie-item-trophy-tasks-anvil-marathon.webp +0 -0
  14. package/dist/gamification-previews/dramatic-smithie-item-trophy-xp-levels-the-first-heat.webp +0 -0
  15. package/dist/gamification-previews/dramatic-smithie-item-unlock-streaks-molten-crown-fire.webp +0 -0
  16. package/dist/gamification-previews/dramatic-smithie-mascot.webp +0 -0
  17. package/dist/gamification-previews/mind-locksmith-item-trophy-tasks-anvil-marathon.webp +0 -0
  18. package/dist/gamification-previews/mind-locksmith-item-trophy-xp-levels-the-first-heat.webp +0 -0
  19. package/dist/gamification-previews/mind-locksmith-item-unlock-streaks-molten-crown-fire.webp +0 -0
  20. package/dist/gamification-previews/mind-locksmith-mascot.webp +0 -0
  21. package/dist/index.html +7 -7
  22. package/dist/openclaw/parity.js +27 -0
  23. package/dist/openclaw/plugin-entry-shared.js +2 -2
  24. package/dist/openclaw/plugin-sdk-types.d.ts +2 -1
  25. package/dist/openclaw/routes.d.ts +4 -0
  26. package/dist/openclaw/routes.js +112 -3
  27. package/dist/openclaw/tools.js +32 -4
  28. package/dist/server/server/migrations/059_data_backup_retention.sql +2 -0
  29. package/dist/server/server/src/app.js +125 -43
  30. package/dist/server/server/src/data-management-types.js +2 -0
  31. package/dist/server/server/src/health.js +40 -0
  32. package/dist/server/server/src/openapi.js +398 -7
  33. package/dist/server/server/src/repositories/rewards.js +60 -0
  34. package/dist/server/server/src/services/data-management.js +32 -2
  35. package/dist/server/server/src/services/doctor.js +762 -0
  36. package/dist/server/server/src/services/gamification.js +75 -3
  37. package/dist/server/src/lib/api.js +9 -0
  38. package/dist/server/src/lib/gamification-catalog.js +1 -1
  39. package/openclaw.plugin.json +85 -3
  40. package/package.json +8 -4
  41. package/server/migrations/059_data_backup_retention.sql +2 -0
  42. package/skills/forge-openclaw/SKILL.md +38 -19
  43. package/skills/forge-openclaw/entity_conversation_playbooks.md +66 -8
  44. package/skills/forge-openclaw/psyche_entity_playbooks.md +23 -0
  45. package/dist/assets/index-BwKAPo98.css +0 -1
  46. package/dist/assets/index-Dy7c-dRY.js +0 -90
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-Dy7c-dRY.js"></script>
17
- <link rel="modulepreload" crossorigin href="/forge/assets/vendor-C0otBhgu.js">
18
- <link rel="modulepreload" crossorigin href="/forge/assets/board-B1V3M__K.js">
19
- <link rel="modulepreload" crossorigin href="/forge/assets/ui-DUqM4jkt.js">
20
- <link rel="modulepreload" crossorigin href="/forge/assets/motion-CltSTItx.js">
21
- <link rel="modulepreload" crossorigin href="/forge/assets/table-B-VrSFx8.js">
16
+ <script type="module" crossorigin src="/forge/assets/index-_Cn6Prym.js"></script>
17
+ <link rel="modulepreload" crossorigin href="/forge/assets/vendor-BgZ3YrRd.js">
18
+ <link rel="modulepreload" crossorigin href="/forge/assets/board-DThHV1D8.js">
19
+ <link rel="modulepreload" crossorigin href="/forge/assets/ui-CnVxFkj0.js">
20
+ <link rel="modulepreload" crossorigin href="/forge/assets/motion-BtTJtHCw.js">
21
+ <link rel="modulepreload" crossorigin href="/forge/assets/table-Bnw6pcwN.js">
22
22
  <link rel="stylesheet" crossorigin href="/forge/assets/vendor-DT3pnAKJ.css">
23
- <link rel="stylesheet" crossorigin href="/forge/assets/index-BwKAPo98.css">
23
+ <link rel="stylesheet" crossorigin href="/forge/assets/index-7gvVCqnV.css">
24
24
  </head>
25
25
  <body class="bg-canvas text-ink antialiased">
26
26
  <div id="root"></div>
@@ -12,6 +12,8 @@ export const FORGE_SUPPORTED_PLUGIN_API_ROUTES = [
12
12
  purpose: "operator_context"
13
13
  },
14
14
  { method: "GET", path: "/api/v1/agents/onboarding", purpose: "onboarding" },
15
+ { method: "GET", path: "/api/v1/doctor", purpose: "diagnostics" },
16
+ { method: "POST", path: "/api/v1/doctor/fixes", purpose: "diagnostics" },
15
17
  { method: "GET", path: "/api/v1/psyche/overview", purpose: "psyche" },
16
18
  { method: "GET", path: "/api/v1/metrics/xp", purpose: "xp" },
17
19
  { method: "GET", path: "/api/v1/reviews/weekly", purpose: "weekly_review" },
@@ -141,6 +143,16 @@ export const FORGE_SUPPORTED_PLUGIN_API_ROUTES = [
141
143
  purpose: "workbench"
142
144
  },
143
145
  { method: "GET", path: "/api/v1/calendar/overview", purpose: "calendar" },
146
+ {
147
+ method: "GET",
148
+ path: "/api/v1/calendar/macos-local/discovery",
149
+ purpose: "calendar"
150
+ },
151
+ {
152
+ method: "POST",
153
+ path: "/api/v1/calendar/discovery",
154
+ purpose: "calendar"
155
+ },
144
156
  {
145
157
  method: "GET",
146
158
  path: "/api/v1/calendar/connections",
@@ -151,6 +163,21 @@ export const FORGE_SUPPORTED_PLUGIN_API_ROUTES = [
151
163
  path: "/api/v1/calendar/connections",
152
164
  purpose: "calendar"
153
165
  },
166
+ {
167
+ method: "PATCH",
168
+ path: "/api/v1/calendar/connections/:id",
169
+ purpose: "calendar"
170
+ },
171
+ {
172
+ method: "DELETE",
173
+ path: "/api/v1/calendar/connections/:id",
174
+ purpose: "calendar"
175
+ },
176
+ {
177
+ method: "GET",
178
+ path: "/api/v1/calendar/connections/:id/discovery",
179
+ purpose: "calendar"
180
+ },
154
181
  {
155
182
  method: "POST",
156
183
  path: "/api/v1/calendar/connections/:id/sync",
@@ -128,7 +128,7 @@ export const forgePluginConfigSchema = {
128
128
  dataRoot: {
129
129
  type: "string",
130
130
  default: "~/.forge",
131
- description: "Absolute path for the shared Forge data root. Defaults to ~/.forge so local OpenClaw, Hermes, and Codex installs converge on one runtime."
131
+ description: "Absolute path for the shared Forge data root. Defaults to ~/.forge. Explicit values override that default; use the same value across local adapters that should share one database."
132
132
  },
133
133
  apiToken: {
134
134
  type: "string",
@@ -167,7 +167,7 @@ export const forgePluginConfigSchema = {
167
167
  },
168
168
  dataRoot: {
169
169
  label: "Forge Data Root",
170
- help: "Absolute folder path for the shared Forge home. Local installs default to ~/.forge so OpenClaw, Hermes, and Codex automatically meet in one runtime.",
170
+ help: "Optional absolute folder path for Forge data. Defaults to ~/.forge. When set, Forge stores forge.sqlite directly in this folder; use the same value across adapters that should share one database.",
171
171
  placeholder: "~/.forge",
172
172
  advanced: true
173
173
  },
@@ -30,7 +30,8 @@ export type ForgeRegisteredTool = {
30
30
  export type ForgeCliProgram = {
31
31
  command(name: string): ForgeCliProgram;
32
32
  description(text: string): ForgeCliProgram;
33
- action(handler: () => Promise<void> | void): ForgeCliProgram;
33
+ option(flags: string, description: string): ForgeCliProgram;
34
+ action(handler: (options?: unknown) => Promise<void> | void): ForgeCliProgram;
34
35
  };
35
36
  export type ForgeCliRegistrarContext = {
36
37
  program: ForgeCliProgram;
@@ -59,5 +59,9 @@ export declare function runDoctor(config: ForgePluginConfig): Promise<{
59
59
  unexpectedMirrors: `${Uppercase<string>} ${string}`[];
60
60
  };
61
61
  }>;
62
+ export declare function applyDoctorFixes(config: ForgePluginConfig, input: {
63
+ fixIds?: string[];
64
+ applyAllSafe?: boolean;
65
+ }): Promise<unknown>;
62
66
  export declare function registerForgePluginCli(api: ForgePluginCliApi, config: ForgePluginConfig): void;
63
67
  export {};
@@ -124,6 +124,18 @@ export const FORGE_PLUGIN_ROUTE_GROUPS = [
124
124
  upstreamPath: "/api/v1/agents/onboarding",
125
125
  target: (_match, url) => passthroughSearch("/api/v1/agents/onboarding", url)
126
126
  }),
127
+ exact("/forge/v1/doctor", {
128
+ method: "GET",
129
+ upstreamPath: "/api/v1/doctor",
130
+ target: (_match, url) => passthroughSearch("/api/v1/doctor", url)
131
+ }),
132
+ exact("/forge/v1/doctor/fixes", {
133
+ method: "POST",
134
+ upstreamPath: "/api/v1/doctor/fixes",
135
+ requestBody: "json",
136
+ requiresToken: true,
137
+ target: (_match, url) => passthroughSearch("/api/v1/doctor/fixes", url)
138
+ }),
127
139
  exact("/forge/v1/users/directory", {
128
140
  method: "GET",
129
141
  upstreamPath: "/api/v1/users/directory",
@@ -492,6 +504,20 @@ export const FORGE_PLUGIN_ROUTE_GROUPS = [
492
504
  upstreamPath: "/api/v1/calendar/overview",
493
505
  target: (_match, url) => passthroughSearch("/api/v1/calendar/overview", url)
494
506
  },
507
+ {
508
+ method: "GET",
509
+ pattern: /^\/forge\/v1\/calendar\/macos-local\/discovery$/,
510
+ upstreamPath: "/api/v1/calendar/macos-local/discovery",
511
+ target: (_match, url) => passthroughSearch("/api/v1/calendar/macos-local/discovery", url)
512
+ },
513
+ {
514
+ method: "POST",
515
+ pattern: /^\/forge\/v1\/calendar\/discovery$/,
516
+ upstreamPath: "/api/v1/calendar/discovery",
517
+ requestBody: "json",
518
+ requiresToken: true,
519
+ target: (_match, url) => passthroughSearch("/api/v1/calendar/discovery", url)
520
+ },
495
521
  {
496
522
  method: "GET",
497
523
  pattern: /^\/forge\/v1\/calendar\/connections$/,
@@ -506,6 +532,28 @@ export const FORGE_PLUGIN_ROUTE_GROUPS = [
506
532
  requiresToken: true,
507
533
  target: (_match, url) => passthroughSearch("/api/v1/calendar/connections", url)
508
534
  },
535
+ {
536
+ method: "PATCH",
537
+ pattern: /^\/forge\/v1\/calendar\/connections\/([^/]+)$/,
538
+ upstreamPath: "/api/v1/calendar/connections/:id",
539
+ requestBody: "json",
540
+ requiresToken: true,
541
+ target: (match, url) => passthroughSearch(`/api/v1/calendar/connections/${match[1]}`, url)
542
+ },
543
+ {
544
+ method: "DELETE",
545
+ pattern: /^\/forge\/v1\/calendar\/connections\/([^/]+)$/,
546
+ upstreamPath: "/api/v1/calendar/connections/:id",
547
+ requiresToken: true,
548
+ target: (match, url) => passthroughSearch(`/api/v1/calendar/connections/${match[1]}`, url)
549
+ },
550
+ {
551
+ method: "GET",
552
+ pattern: /^\/forge\/v1\/calendar\/connections\/([^/]+)\/discovery$/,
553
+ upstreamPath: "/api/v1/calendar/connections/:id/discovery",
554
+ requiresToken: true,
555
+ target: (match, url) => passthroughSearch(`/api/v1/calendar/connections/${match[1]}/discovery`, url)
556
+ },
509
557
  {
510
558
  method: "POST",
511
559
  pattern: /^\/forge\/v1\/calendar\/connections\/([^/]+)\/sync$/,
@@ -1058,6 +1106,13 @@ async function runReadOnly(config, path) {
1058
1106
  path
1059
1107
  }));
1060
1108
  }
1109
+ async function runPost(config, path, body) {
1110
+ return expectForgeSuccess(await callConfiguredForgeApi(config, {
1111
+ method: "POST",
1112
+ path,
1113
+ body
1114
+ }));
1115
+ }
1061
1116
  export async function runRouteCheck(config) {
1062
1117
  const openapi = await runReadOnly(config, "/api/v1/openapi.json");
1063
1118
  const pathMap = typeof openapi === "object" &&
@@ -1138,6 +1193,39 @@ export async function runDoctor(config) {
1138
1193
  routeParity
1139
1194
  };
1140
1195
  }
1196
+ export async function applyDoctorFixes(config, input) {
1197
+ return runPost(config, "/api/v1/doctor/fixes", input);
1198
+ }
1199
+ function getSafeDoctorFixIds(doctor) {
1200
+ const issues = Array.isArray(doctor.issues) ? doctor.issues : [];
1201
+ return issues
1202
+ .map((issue) => typeof issue === "object" &&
1203
+ issue !== null &&
1204
+ "fix" in issue &&
1205
+ typeof issue.fix === "object" &&
1206
+ issue.fix !== null &&
1207
+ issue.fix.kind === "safe_auto_fix" &&
1208
+ typeof issue.fix.id === "string"
1209
+ ? issue.fix.id
1210
+ : null)
1211
+ .filter((fixId) => Boolean(fixId));
1212
+ }
1213
+ function formatDoctorSummary(doctor) {
1214
+ const integrity = typeof doctor.integrity === "object" && doctor.integrity !== null
1215
+ ? doctor.integrity
1216
+ : {};
1217
+ const issues = Array.isArray(doctor.issues) ? doctor.issues : [];
1218
+ const warnings = Array.isArray(doctor.warnings) ? doctor.warnings : [];
1219
+ const lines = [
1220
+ `Forge Doctor: ${doctor.ok === true ? "ok" : "attention needed"}`,
1221
+ `Integrity: ${typeof integrity.score === "number" ? integrity.score : "unknown"}%`,
1222
+ typeof integrity.headline === "string" ? integrity.headline : null,
1223
+ `Issues: ${issues.length}`,
1224
+ warnings.length > 0 ? "" : null,
1225
+ ...warnings.slice(0, 8).map((warning) => `- ${String(warning)}`)
1226
+ ].filter((line) => line !== null);
1227
+ return lines.join("\n");
1228
+ }
1141
1229
  export function registerForgePluginCli(api, config) {
1142
1230
  api.registerCli?.(({ program }) => {
1143
1231
  const command = program
@@ -1190,9 +1278,30 @@ export function registerForgePluginCli(api, config) {
1190
1278
  });
1191
1279
  command
1192
1280
  .command("doctor")
1193
- .description("Run plugin connectivity and curated route diagnostics")
1194
- .action(async () => {
1195
- console.log(JSON.stringify(await runDoctor(config), null, 2));
1281
+ .description("Run Forge Doctor diagnostics")
1282
+ .option("--json", "Print the full machine-readable Doctor payload")
1283
+ .option("--fix", "Apply safe Doctor fixes after diagnostics")
1284
+ .action(async (rawOptions) => {
1285
+ const options = (rawOptions ?? {});
1286
+ const doctor = (await runDoctor(config));
1287
+ if (options.fix) {
1288
+ const fixIds = getSafeDoctorFixIds(doctor);
1289
+ if (fixIds.length > 0) {
1290
+ const fixResult = await applyDoctorFixes(config, { fixIds });
1291
+ const refreshedDoctor = (await runDoctor(config));
1292
+ const payload = {
1293
+ doctor: refreshedDoctor,
1294
+ fixResult
1295
+ };
1296
+ console.log(options.json
1297
+ ? JSON.stringify(payload, null, 2)
1298
+ : `${formatDoctorSummary(refreshedDoctor)}\n\nApplied fixes: ${fixIds.join(", ")}`);
1299
+ return;
1300
+ }
1301
+ }
1302
+ console.log(options.json
1303
+ ? JSON.stringify(doctor, null, 2)
1304
+ : formatDoctorSummary(doctor));
1196
1305
  });
1197
1306
  command
1198
1307
  .command("route-check")
@@ -355,14 +355,25 @@ function withQueryParams(path, params, allowedKeys) {
355
355
  return search.size > 0 ? `${path}?${search.toString()}` : path;
356
356
  }
357
357
  function buildRouteKeySchema(routeSpecs) {
358
- return Type.Union(Object.keys(routeSpecs).map((routeKey) => Type.Literal(routeKey)));
358
+ const routeGuide = Object.entries(routeSpecs)
359
+ .map(([routeKey, spec]) => `${routeKey}: ${spec.method} ${spec.path}`)
360
+ .join("; ");
361
+ return Type.Union(Object.keys(routeSpecs).map((routeKey) => Type.Literal(routeKey)), {
362
+ description: `Dedicated route key. Exact routes: ${routeGuide}. For any :placeholder shown in a route, fill pathParams with that exact placeholder name; do not put raw paths or ids into routeKey.`
363
+ });
359
364
  }
360
365
  function specializedRouteParametersSchema(routeSpecs) {
361
366
  return Type.Object({
362
367
  routeKey: buildRouteKeySchema(routeSpecs),
363
- pathParams: Type.Optional(Type.Record(Type.String(), Type.String())),
364
- query: Type.Optional(Type.Record(Type.String(), Type.Any())),
365
- body: Type.Optional(Type.Any())
368
+ pathParams: Type.Optional(Type.Record(Type.String(), Type.String(), {
369
+ description: "Path parameters required by the selected route key. Use the exact :placeholder names shown in the routeKey description, such as id, weekday, slug, runId, nodeId, or pointId."
370
+ })),
371
+ query: Type.Optional(Type.Record(Type.String(), Type.Any(), {
372
+ description: "Optional query parameters for the selected dedicated route."
373
+ })),
374
+ body: Type.Optional(Type.Any({
375
+ description: "JSON body for POST, PATCH, and PUT route keys. Omit for GET and DELETE route keys."
376
+ }))
366
377
  });
367
378
  }
368
379
  function appendAnyQueryParams(path, query) {
@@ -471,6 +482,23 @@ export function registerForgePluginTools(api, config) {
471
482
  description: "Fetch the live Forge onboarding contract with the exact Forge tool list, batch payload rules, UI handoff rules, and verification guidance.",
472
483
  path: () => "/api/v1/agents/onboarding"
473
484
  });
485
+ registerReadTool(api, config, {
486
+ name: "forge_get_doctor",
487
+ label: "Forge Doctor",
488
+ description: "Run Forge Doctor diagnostics for runtime health, settings, SQLite storage, entity links, hierarchy consistency, rewards, gamification state, and proposed fixes.",
489
+ path: () => "/api/v1/doctor"
490
+ });
491
+ registerWriteTool(api, config, {
492
+ name: "forge_apply_doctor_fix",
493
+ label: "Apply Forge Doctor Fix",
494
+ description: "Apply explicitly approved Forge Doctor safe fixes. Do not call this unless the user has approved the specific fix id or asked for Doctor autofix.",
495
+ parameters: Type.Object({
496
+ fixIds: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
497
+ applyAllSafe: Type.Optional(Type.Boolean())
498
+ }),
499
+ method: "POST",
500
+ path: "/api/v1/doctor/fixes"
501
+ });
474
502
  registerSpecializedRouteTool(api, config, {
475
503
  name: "forge_call_movement_route",
476
504
  label: "Forge Movement Route",
@@ -0,0 +1,2 @@
1
+ ALTER TABLE data_management_settings
2
+ ADD COLUMN backup_retention_days INTEGER DEFAULT 30;