forge-openclaw-plugin 0.2.60 → 0.2.65

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 (59) hide show
  1. package/README.md +121 -51
  2. package/dist/assets/{board-B1V3M__K.js → board-DUwMfZvN.js} +1 -1
  3. package/dist/assets/index-B9VOpR7r.css +1 -0
  4. package/dist/assets/index-DoHjjze2.js +90 -0
  5. package/dist/assets/{motion-CltSTItx.js → motion-Crg3QyXD.js} +1 -1
  6. package/dist/assets/{table-B-VrSFx8.js → table-CTlDeYRs.js} +1 -1
  7. package/dist/assets/{ui-DUqM4jkt.js → ui-CJPaElbj.js} +1 -1
  8. package/dist/assets/{vendor-C0otBhgu.js → vendor-BdrT2htV.js} +217 -207
  9. package/dist/companion-iroh/darwin-arm64/forge-companion-iroh +0 -0
  10. package/dist/companion-iroh/darwin-x64/forge-companion-iroh +0 -0
  11. package/dist/companion-iroh/linux-x64/forge-companion-iroh +0 -0
  12. package/dist/companion-iroh-src/Cargo.lock +4559 -0
  13. package/dist/companion-iroh-src/Cargo.toml +37 -0
  14. package/dist/companion-iroh-src/src/lib.rs +279 -0
  15. package/dist/companion-iroh-src/src/main.rs +478 -0
  16. package/dist/companion-iroh-src/src/protocol.rs +129 -0
  17. package/dist/gamification-previews/dark-fantasy-item-trophy-tasks-anvil-marathon.webp +0 -0
  18. package/dist/gamification-previews/dark-fantasy-item-trophy-xp-levels-the-first-heat.webp +0 -0
  19. package/dist/gamification-previews/dark-fantasy-item-unlock-streaks-molten-crown-fire.webp +0 -0
  20. package/dist/gamification-previews/dark-fantasy-mascot.webp +0 -0
  21. package/dist/gamification-previews/dramatic-smithie-item-trophy-tasks-anvil-marathon.webp +0 -0
  22. package/dist/gamification-previews/dramatic-smithie-item-trophy-xp-levels-the-first-heat.webp +0 -0
  23. package/dist/gamification-previews/dramatic-smithie-item-unlock-streaks-molten-crown-fire.webp +0 -0
  24. package/dist/gamification-previews/dramatic-smithie-mascot.webp +0 -0
  25. package/dist/gamification-previews/mind-locksmith-item-trophy-tasks-anvil-marathon.webp +0 -0
  26. package/dist/gamification-previews/mind-locksmith-item-trophy-xp-levels-the-first-heat.webp +0 -0
  27. package/dist/gamification-previews/mind-locksmith-item-unlock-streaks-molten-crown-fire.webp +0 -0
  28. package/dist/gamification-previews/mind-locksmith-mascot.webp +0 -0
  29. package/dist/index.html +7 -7
  30. package/dist/openclaw/parity.js +27 -0
  31. package/dist/openclaw/plugin-entry-shared.js +2 -2
  32. package/dist/openclaw/plugin-sdk-types.d.ts +2 -1
  33. package/dist/openclaw/routes.d.ts +4 -0
  34. package/dist/openclaw/routes.js +112 -3
  35. package/dist/openclaw/tools.js +32 -4
  36. package/dist/server/server/migrations/059_data_backup_retention.sql +2 -0
  37. package/dist/server/server/src/app.js +288 -61
  38. package/dist/server/server/src/data-management-types.js +2 -0
  39. package/dist/server/server/src/discovery-advertiser.js +13 -0
  40. package/dist/server/server/src/health.js +58 -3
  41. package/dist/server/server/src/movement.js +16 -1
  42. package/dist/server/server/src/openapi.js +410 -9
  43. package/dist/server/server/src/repositories/rewards.js +60 -0
  44. package/dist/server/server/src/services/companion-iroh.js +425 -0
  45. package/dist/server/server/src/services/data-management.js +32 -2
  46. package/dist/server/server/src/services/doctor.js +762 -0
  47. package/dist/server/server/src/services/gamification.js +75 -3
  48. package/dist/server/server/src/services/life-force.js +166 -25
  49. package/dist/server/server/src/web.js +88 -12
  50. package/dist/server/src/lib/api.js +9 -0
  51. package/dist/server/src/lib/gamification-catalog.js +1 -1
  52. package/openclaw.plugin.json +85 -3
  53. package/package.json +10 -6
  54. package/server/migrations/059_data_backup_retention.sql +2 -0
  55. package/skills/forge-openclaw/SKILL.md +80 -19
  56. package/skills/forge-openclaw/entity_conversation_playbooks.md +283 -25
  57. package/skills/forge-openclaw/psyche_entity_playbooks.md +82 -0
  58. package/dist/assets/index-BwKAPo98.css +0 -1
  59. package/dist/assets/index-Dy7c-dRY.js +0 -90
@@ -75,6 +75,7 @@ export const dataManagementSettingsSchema = z.object({
75
75
  preferredDataRoot: z.string(),
76
76
  backupDirectory: z.string(),
77
77
  backupFrequencyHours: z.number().int().positive().nullable(),
78
+ backupRetentionDays: z.number().int().positive().nullable(),
78
79
  autoRepairEnabled: z.boolean(),
79
80
  lastAutoBackupAt: z.string().nullable(),
80
81
  lastManualBackupAt: z.string().nullable()
@@ -89,6 +90,7 @@ export const dataManagementStateSchema = z.object({
89
90
  export const updateDataManagementSettingsSchema = z.object({
90
91
  backupDirectory: z.string().trim().optional(),
91
92
  backupFrequencyHours: z.number().int().positive().nullable().optional(),
93
+ backupRetentionDays: z.number().int().positive().nullable().optional(),
92
94
  autoRepairEnabled: z.boolean().optional()
93
95
  });
94
96
  export const createDataBackupSchema = z.object({
@@ -3,6 +3,7 @@ import os from "node:os";
3
3
  import { promisify } from "node:util";
4
4
  import { Bonjour } from "bonjour-service";
5
5
  import { logForgeDebug } from "./debug.js";
6
+ import { companionIrohApiBaseUrlFromNodeId, companionIrohUiBaseUrlFromNodeId, getCompanionIrohStatus } from "./services/companion-iroh.js";
6
7
  const execFileAsync = promisify(execFile);
7
8
  export async function startForgeDiscoveryAdvertiser(options) {
8
9
  if (options.enabled === false ||
@@ -15,6 +16,8 @@ export async function startForgeDiscoveryAdvertiser(options) {
15
16
  uiBaseUrl: options.tailscaleUiBaseUrl,
16
17
  basePath
17
18
  });
19
+ const irohTransport = getCompanionIrohStatus();
20
+ const irohNodeId = irohTransport.pairPayload?.node_id;
18
21
  const bonjour = new Bonjour({}, (error) => {
19
22
  logForgeDebug(`[forge-discovery] ignored mDNS advertisement error: ${formatDiscoveryError(error)}`);
20
23
  });
@@ -31,6 +34,16 @@ export async function startForgeDiscoveryAdvertiser(options) {
31
34
  tsApiBaseUrl: tailscaleTargets.apiBaseUrl ?? "",
32
35
  tsUiBaseUrl: tailscaleTargets.uiBaseUrl ?? "",
33
36
  tsDnsName: tailscaleTargets.dnsName ?? "",
37
+ irohApiBaseUrl: irohNodeId
38
+ ? companionIrohApiBaseUrlFromNodeId(irohNodeId)
39
+ : "",
40
+ irohUiBaseUrl: irohNodeId
41
+ ? companionIrohUiBaseUrlFromNodeId(irohNodeId)
42
+ : "",
43
+ irohProvider: irohNodeId ? "forge-companion-iroh" : "",
44
+ irohNodeId: irohNodeId ?? "",
45
+ irohRelay: irohTransport.pairPayload?.relay ?? "",
46
+ irohAlpn: irohTransport.alpn ?? "",
34
47
  watchReady: "1"
35
48
  }
36
49
  });
@@ -109,6 +109,10 @@ export const createCompanionPairingSessionSchema = z.object({
109
109
  label: z.string().trim().default("Forge Companion"),
110
110
  userId: z.string().trim().nullable().optional(),
111
111
  expiresInMinutes: z.coerce.number().int().min(5).max(24 * 60).default(30),
112
+ transportMode: z
113
+ .enum(["iroh", "manual-http"])
114
+ .default("iroh")
115
+ .transform((mode) => mode),
112
116
  capabilities: z
113
117
  .array(z.enum([
114
118
  "healthkit.sleep",
@@ -260,6 +264,16 @@ export const verifyCompanionPairingSchema = z.object({
260
264
  sourceDevice: z.string().trim().default("iPhone")
261
265
  })
262
266
  });
267
+ export const heartbeatCompanionPairingSchema = z.object({
268
+ sessionId: z.string().trim().min(1),
269
+ pairingToken: z.string().trim().min(1),
270
+ device: z.object({
271
+ name: z.string().trim().default("iPhone"),
272
+ platform: z.string().trim().default("ios"),
273
+ appVersion: z.string().trim().default(""),
274
+ sourceDevice: z.string().trim().default("iPhone")
275
+ })
276
+ });
263
277
  export const updateWorkoutMetadataSchema = z.object({
264
278
  subjectiveEffort: z.number().int().min(1).max(10).nullable().optional(),
265
279
  moodBefore: z.string().trim().optional(),
@@ -1461,8 +1475,17 @@ export function updateMobileCompanionSourceState(payload) {
1461
1475
  }
1462
1476
  });
1463
1477
  }
1464
- export function createCompanionPairingSession(baseApiUrl, input) {
1478
+ export function createCompanionPairingSession(pairingTransport, input) {
1465
1479
  const parsed = createCompanionPairingSessionSchema.parse(input);
1480
+ const resolvedTransport = typeof pairingTransport === "string"
1481
+ ? {
1482
+ apiBaseUrl: pairingTransport,
1483
+ uiBaseUrl: null,
1484
+ transportMode: "manual-http",
1485
+ transport: undefined
1486
+ }
1487
+ : pairingTransport;
1488
+ const baseApiUrl = resolvedTransport.apiBaseUrl;
1466
1489
  const now = new Date();
1467
1490
  const userId = parsed.userId ?? "user_operator";
1468
1491
  const serializedCapabilities = JSON.stringify(parsed.capabilities);
@@ -1474,10 +1497,9 @@ export function createCompanionPairingSession(baseApiUrl, input) {
1474
1497
  FROM companion_pairing_sessions
1475
1498
  WHERE user_id = ?
1476
1499
  AND label = ?
1477
- AND api_base_url = ?
1478
1500
  AND capability_flags_json = ?
1479
1501
  AND status = 'pending'`)
1480
- .all(userId, parsed.label, baseApiUrl, serializedCapabilities);
1502
+ .all(userId, parsed.label, serializedCapabilities);
1481
1503
  if (stalePendingRows.length > 0) {
1482
1504
  revokePairingRows(stalePendingRows, {
1483
1505
  actor: null,
@@ -1500,6 +1522,9 @@ export function createCompanionPairingSession(baseApiUrl, input) {
1500
1522
  const qrPayload = {
1501
1523
  kind: "forge-companion-pairing",
1502
1524
  apiBaseUrl: baseApiUrl,
1525
+ uiBaseUrl: resolvedTransport.uiBaseUrl ?? undefined,
1526
+ transportMode: resolvedTransport.transportMode ?? parsed.transportMode,
1527
+ transport: resolvedTransport.transport,
1503
1528
  sessionId: id,
1504
1529
  pairingToken,
1505
1530
  expiresAt,
@@ -1555,6 +1580,36 @@ export function verifyCompanionPairing(payload) {
1555
1580
  .get(pairing.id))
1556
1581
  };
1557
1582
  }
1583
+ export function heartbeatCompanionPairing(payload) {
1584
+ const parsed = heartbeatCompanionPairingSchema.parse(payload);
1585
+ const pairing = requireValidPairing(parsed.sessionId, parsed.pairingToken);
1586
+ const now = nowIso();
1587
+ const renewedExpiry = nextVerifiedCompanionPairingExpiry(new Date());
1588
+ const nextStatus = pairing.status === "revoked"
1589
+ ? pairing.status
1590
+ : pairing.status === "permission_denied"
1591
+ ? pairing.status
1592
+ : pairing.status === "stale"
1593
+ ? "paired"
1594
+ : pairing.status === "pending"
1595
+ ? "paired"
1596
+ : pairing.status;
1597
+ getDatabase()
1598
+ .prepare(`UPDATE companion_pairing_sessions
1599
+ SET status = ?, device_name = ?, platform = ?, app_version = ?,
1600
+ last_seen_at = ?, paired_at = COALESCE(paired_at, ?),
1601
+ expires_at = ?, updated_at = ?
1602
+ WHERE id = ?`)
1603
+ .run(nextStatus, parsed.device.name, parsed.device.platform, parsed.device.appVersion, now, now, renewedExpiry, now, pairing.id);
1604
+ ensurePairingSourceStates(getDatabase()
1605
+ .prepare(`SELECT * FROM companion_pairing_sessions WHERE id = ?`)
1606
+ .get(pairing.id));
1607
+ return {
1608
+ pairingSession: mapPairingSession(getDatabase()
1609
+ .prepare(`SELECT * FROM companion_pairing_sessions WHERE id = ?`)
1610
+ .get(pairing.id))
1611
+ };
1612
+ }
1558
1613
  export function requireValidPairing(sessionId, pairingToken) {
1559
1614
  const row = getDatabase()
1560
1615
  .prepare(`SELECT * FROM companion_pairing_sessions WHERE id = ?`)
@@ -233,11 +233,26 @@ export const movementSelectionAggregateSchema = z.object({
233
233
  });
234
234
  export const movementSettingsPatchSchema = movementSettingsInputSchema.partial();
235
235
  const MOVEMENT_TIMELINE_MAX_LIMIT = 360;
236
+ const queryStringArraySchema = z.preprocess((value) => {
237
+ if (value === undefined || value === null) {
238
+ return [];
239
+ }
240
+ const rawValues = Array.isArray(value) ? value : [value];
241
+ return rawValues.flatMap((item) => {
242
+ if (typeof item !== "string") {
243
+ return [];
244
+ }
245
+ return item
246
+ .split(",")
247
+ .map((part) => part.trim())
248
+ .filter(Boolean);
249
+ });
250
+ }, z.array(z.string().trim().min(1)));
236
251
  export const movementTimelineQuerySchema = z.object({
237
252
  before: z.string().trim().min(1).optional(),
238
253
  limit: z.coerce.number().int().min(1).max(MOVEMENT_TIMELINE_MAX_LIMIT).default(40),
239
254
  includeInvalid: z.coerce.boolean().default(false),
240
- userIds: z.array(z.string().trim().min(1)).default([])
255
+ userIds: queryStringArraySchema
241
256
  });
242
257
  export const movementStayPatchSchema = z.object({
243
258
  label: z.string().trim().optional(),