forge-openclaw-plugin 0.2.98 → 0.2.100
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/activity-copy-Bj4h9OcF.js +1 -0
- package/dist/assets/activity-page-5oyCFOns.js +1 -0
- package/dist/assets/ai-surface-workspace-qgk_B57-.js +1 -0
- package/dist/assets/atlas-panel-rfH2qOez.js +1 -0
- package/dist/assets/{board-Ju0h0SeG.js → board-BkDRaMp6.js} +1 -1
- package/dist/assets/calendar-display-preferences-Cid-2RnL.js +1 -0
- package/dist/assets/calendar-page-Bo2iua-a.js +1 -0
- package/dist/assets/calendar-rules-DA1g3QUk.js +1 -0
- package/dist/assets/calendar-ui-Cy1XRwzV.js +1 -0
- package/dist/assets/calendar-week-toolbar-DU1Q4RYj.js +1 -0
- package/dist/assets/charts-P7EVhIog.js +36 -0
- package/dist/assets/companion-sync-lab-page-CosNknOK.js +1 -0
- package/dist/assets/daily-metrics-dashboard-LjuGAB3f.js +1 -0
- package/dist/assets/date-keys-Cj1G3TOn.js +1 -0
- package/dist/assets/entity-links-DwpxhW2H.js +1 -0
- package/dist/assets/entity-note-count-link-BmGDB572.js +1 -0
- package/dist/assets/entity-notes-surface-DgEgicaE.js +1 -0
- package/dist/assets/execution-board-CDRXQB85.js +1 -0
- package/dist/assets/faceted-token-search-CE1YauRd.js +1 -0
- package/dist/assets/flagship-signal-deck-DDds90Gl.js +1 -0
- package/dist/assets/floating-action-menu-CJkI2iFy.js +1 -0
- package/dist/assets/forms-BFlTgZ3W.js +1 -0
- package/dist/assets/goal-detail-page-cJvHaLMQ.js +1 -0
- package/dist/assets/goals-page-f_39hvUV.js +1 -0
- package/dist/assets/graph-BZV40eAE.css +1 -0
- package/dist/assets/graph-D6JLqDbD.js +318 -0
- package/dist/assets/habits-page-DKb96_mj.js +1 -0
- package/dist/assets/health-link-options-Cpx8w7uM.js +1 -0
- package/dist/assets/index-BHTUu_4M.js +19 -0
- package/dist/assets/index-CZbuZQjw.css +1 -0
- package/dist/assets/insight-flow-dialog-pzAzyayN.js +1 -0
- package/dist/assets/insights-page-Dc9oFltJ.js +8 -0
- package/dist/assets/kanban-page-JAxerYh6.js +1 -0
- package/dist/assets/knowledge-graph-page-UQ3skqEi.js +1 -0
- package/dist/assets/life-force-page-BGDbQuVh.js +1 -0
- package/dist/assets/life-force-workspace-B1fYSXRC.js +1 -0
- package/dist/assets/maps-B-YMMjus.css +1 -0
- package/dist/assets/maps-ClgJoCjz.js +803 -0
- package/dist/assets/metric-tile-DX6TclqM.js +1 -0
- package/dist/assets/{motion-DRPJkN3a.js → motion-BeD44FeG.js} +1 -1
- package/dist/assets/movement-page-6HP6nGJx.js +1 -0
- package/dist/assets/note-markdown-DiW2-5d3.js +3 -0
- package/dist/assets/note-tags-input-DDLXf54U.js +1 -0
- package/dist/assets/notes-page-BuguDjhz.js +1 -0
- package/dist/assets/open-in-graph-button-Cg5VrKsC.js +1 -0
- package/dist/assets/orbit-map-GD05-0oS.js +1 -0
- package/dist/assets/overview-page-DuOs2OCB.js +1 -0
- package/dist/assets/page-hero-CQWo1Mm_.js +1 -0
- package/dist/assets/pill-cluster-BJogDRDJ.js +1 -0
- package/dist/assets/preference-entity-handoff-button-D4WAs9pC.js +1 -0
- package/dist/assets/preferences-page-BaJTMU1I.js +1 -0
- package/dist/assets/project-collections-DvaX20q_.js +1 -0
- package/dist/assets/project-detail-page-drPIFZGb.js +1 -0
- package/dist/assets/project-management-hierarchy-page-BUbRXvny.js +1 -0
- package/dist/assets/project-management-section-nav-C2Ud8Zdd.js +1 -0
- package/dist/assets/projects-page-BGzEZUtg.js +1 -0
- package/dist/assets/psyche-behaviors-page-Dmm_Io9D.js +5 -0
- package/dist/assets/psyche-flashcards-page-BgNKJ6QJ.js +1 -0
- package/dist/assets/psyche-goal-map-page-DXJs98Vr.js +1 -0
- package/dist/assets/psyche-graph-CFgs_Bqc.js +1 -0
- package/dist/assets/psyche-metrics-page-zYTJDbyZ.js +1 -0
- package/dist/assets/psyche-mode-guide-page-XPgRfCOf.js +1 -0
- package/dist/assets/psyche-modes-page-B-GA8oRF.js +1 -0
- package/dist/assets/psyche-page--r6a3e1t.js +1 -0
- package/dist/assets/psyche-patterns-page-BM5-3bMm.js +5 -0
- package/dist/assets/psyche-questionnaire-builder-page-CJshQ-mg.js +1 -0
- package/dist/assets/psyche-questionnaire-detail-page-USmR5G5A.js +1 -0
- package/dist/assets/psyche-questionnaire-run-detail-page-D7iBCmTi.js +1 -0
- package/dist/assets/psyche-questionnaire-run-page-Cpil-kDh.js +1 -0
- package/dist/assets/psyche-questionnaires-page-C-_y3VwS.js +1 -0
- package/dist/assets/psyche-report-detail-page--dkSPRaj.js +3 -0
- package/dist/assets/psyche-reports-page-CUaOXmIN.js +1 -0
- package/dist/assets/psyche-schemas-HFmg37Wj.js +1 -0
- package/dist/assets/psyche-schemas-beliefs-page-BX6xaap3.js +9 -0
- package/dist/assets/psyche-screen-time-page-CAAI4mD7.js +1 -0
- package/dist/assets/psyche-self-observation-page-BZ6FLuwa.js +1 -0
- package/dist/assets/psyche-values-page-yEV6MGt8.js +5 -0
- package/dist/assets/query-cache-IQ8W-LNC.js +1 -0
- package/dist/assets/report-chain-fields-fZ8Xd4H6.js +1 -0
- package/dist/assets/rewards-page-C2HQjIAf.js +1 -0
- package/dist/assets/scheduling-rules-editor-BHOpHOrV.js +1 -0
- package/dist/assets/schema-badge-DyKbxb51.js +1 -0
- package/dist/assets/schema-visuals-D6nxjbYC.js +1 -0
- package/dist/assets/select-menu-BX-pZNqL.js +1 -0
- package/dist/assets/settings-agents-page-VuYXTiyc.js +6 -0
- package/dist/assets/settings-bin-page-BNzvYaOk.js +1 -0
- package/dist/assets/settings-calendar-page-CjSFB53S.js +5 -0
- package/dist/assets/settings-data-page-CGSlryuI.js +1 -0
- package/dist/assets/settings-logs-page-BTK5fine.js +1 -0
- package/dist/assets/settings-mobile-page-CRaObOGo.js +1 -0
- package/dist/assets/settings-models-page-DFshpYF8.js +1 -0
- package/dist/assets/settings-page-BP81Mb5R.js +1 -0
- package/dist/assets/settings-rewards-page-CDJ1PH2G.js +1 -0
- package/dist/assets/settings-section-nav-CCFm27r2.js +1 -0
- package/dist/assets/settings-users-page-TdUocFPa.js +1 -0
- package/dist/assets/settings-wiki-page-B2zX0QQG.js +1 -0
- package/dist/assets/sleep-page-cI1GMVzk.js +1 -0
- package/dist/assets/sports-page-06LTqp0V.js +1 -0
- package/dist/assets/state-B-4sS1xO.js +1 -0
- package/dist/assets/strategies-page-DXP9Kx8s.js +1 -0
- package/dist/assets/strategy-detail-page-D6mx_Mik.js +1 -0
- package/dist/assets/strategy-dialog-BvzomTaF.js +1 -0
- package/dist/assets/{table-DewbFlTh.js → table-WfAPUppN.js} +1 -1
- package/dist/assets/task-detail-page-BIWIggdp.js +1 -0
- package/dist/assets/timebox-planning-dialog-CaCnoslG.js +1 -0
- package/dist/assets/today-page-DO2mRPT2.js +1 -0
- package/dist/assets/training-load-page-CyZ0mlEr.js +1 -0
- package/dist/assets/{ui-C2IvSrAz.js → ui-C13Nbgas.js} +4 -4
- package/dist/assets/use-psyche-focus-target-C1C_XjYG.js +1 -0
- package/dist/assets/vendor-CRS-psbw.css +1 -0
- package/dist/assets/vendor-DHkYh85p.js +1052 -0
- package/dist/assets/vitals-page-BQvEjTc6.js +1 -0
- package/dist/assets/weekly-review-page-Tp6Q9CRj.js +1 -0
- package/dist/assets/weight-loss-page-BBzlhLVV.js +1 -0
- package/dist/assets/wiki-article-markdown-DQYohmW2.js +4 -0
- package/dist/assets/wiki-editor-page-Dem_3eZv.js +26 -0
- package/dist/assets/wiki-ingest-history-page-BxoOcCoJ.js +1 -0
- package/dist/assets/wiki-ingest-modal-DhguKk3J.js +1 -0
- package/dist/assets/wiki-page-BLRxVXkl.js +1 -0
- package/dist/assets/workbench-flow-page-DqMkCCTy.js +5 -0
- package/dist/assets/workbench-page-BWd02wPw.js +1 -0
- package/dist/assets/workout-detail-page-BD8u7GyL.js +2 -0
- package/dist/index.html +148 -9
- package/dist/openclaw/tools.js +340 -0
- package/dist/server/server/migrations/065_weight_loss_nutrition_insights.sql +236 -0
- package/dist/server/server/migrations/066_watch_action_receipts.sql +20 -0
- package/dist/server/server/src/app.js +266 -13
- package/dist/server/server/src/health-weight-loss.js +1378 -0
- package/dist/server/server/src/health.js +188 -35
- package/dist/server/server/src/openapi.js +449 -0
- package/dist/server/server/src/services/context.js +6 -7
- package/dist/server/server/src/services/doctor.js +39 -4
- package/dist/server/server/src/services/gamification.js +146 -34
- package/dist/server/server/src/watch-mobile.js +564 -4
- package/dist/server/server/src/web.js +18 -5
- package/dist/server/src/components/ui/info-tooltip.js +48 -3
- package/dist/server/src/lib/api.js +131 -0
- package/dist/server/src/lib/weight-loss-types.js +1 -0
- package/openclaw.plugin.json +14 -1
- package/package.json +1 -1
- package/server/migrations/065_weight_loss_nutrition_insights.sql +236 -0
- package/server/migrations/066_watch_action_receipts.sql +20 -0
- package/skills/forge-openclaw/SKILL.md +26 -5
- package/skills/forge-openclaw/entity_conversation_playbooks.md +134 -5
- package/skills/forge-openclaw/psyche_entity_playbooks.md +45 -0
- package/dist/assets/index-Cn5Wpwau.css +0 -1
- package/dist/assets/index-CwvGs8n4.js +0 -91
- package/dist/assets/vendor-B-Lq_OG3.css +0 -1
- package/dist/assets/vendor-DL2K5ayT.js +0 -2186
|
@@ -4101,6 +4101,10 @@ export function buildOpenApiDocument() {
|
|
|
4101
4101
|
"reviewShortcutRule",
|
|
4102
4102
|
"readModelWriteRule",
|
|
4103
4103
|
"psycheOpeningQuestionRule",
|
|
4104
|
+
"psycheHypothesisRule",
|
|
4105
|
+
"mixedIntentSequencingRule",
|
|
4106
|
+
"duplicateDisambiguationRule",
|
|
4107
|
+
"destructiveActionRule",
|
|
4104
4108
|
"followUpQuestionRule",
|
|
4105
4109
|
"antiDriftRule",
|
|
4106
4110
|
"duplicateCheckRoute",
|
|
@@ -4118,6 +4122,10 @@ export function buildOpenApiDocument() {
|
|
|
4118
4122
|
reviewShortcutRule: { type: "string" },
|
|
4119
4123
|
readModelWriteRule: { type: "string" },
|
|
4120
4124
|
psycheOpeningQuestionRule: { type: "string" },
|
|
4125
|
+
psycheHypothesisRule: { type: "string" },
|
|
4126
|
+
mixedIntentSequencingRule: { type: "string" },
|
|
4127
|
+
duplicateDisambiguationRule: { type: "string" },
|
|
4128
|
+
destructiveActionRule: { type: "string" },
|
|
4121
4129
|
followUpQuestionRule: { type: "string" },
|
|
4122
4130
|
antiDriftRule: { type: "string" },
|
|
4123
4131
|
duplicateCheckRoute: { type: "string" },
|
|
@@ -5216,6 +5224,139 @@ export function buildOpenApiDocument() {
|
|
|
5216
5224
|
targetModel: { type: "object", additionalProperties: true }
|
|
5217
5225
|
}
|
|
5218
5226
|
};
|
|
5227
|
+
const nutritionMealItemInput = {
|
|
5228
|
+
type: "object",
|
|
5229
|
+
additionalProperties: false,
|
|
5230
|
+
required: ["name", "quantity"],
|
|
5231
|
+
properties: {
|
|
5232
|
+
name: { type: "string" },
|
|
5233
|
+
brand: nullable({ type: "string" }),
|
|
5234
|
+
quantity: { type: "number" },
|
|
5235
|
+
unit: nullable({ type: "string" }),
|
|
5236
|
+
calories: nullable({ type: "number" }),
|
|
5237
|
+
proteinGrams: nullable({ type: "number" }),
|
|
5238
|
+
carbohydrateGrams: nullable({ type: "number" }),
|
|
5239
|
+
fatGrams: nullable({ type: "number" }),
|
|
5240
|
+
fiberGrams: nullable({ type: "number" }),
|
|
5241
|
+
sugarGrams: nullable({ type: "number" }),
|
|
5242
|
+
sodiumMg: nullable({ type: "number" }),
|
|
5243
|
+
potassiumMg: nullable({ type: "number" }),
|
|
5244
|
+
caffeineMg: nullable({ type: "number" }),
|
|
5245
|
+
alcoholGrams: nullable({ type: "number" }),
|
|
5246
|
+
tags: arrayOf({ type: "string" }),
|
|
5247
|
+
confidence: nullable({ type: "number" })
|
|
5248
|
+
}
|
|
5249
|
+
};
|
|
5250
|
+
const nutritionFoodLogInput = {
|
|
5251
|
+
type: "object",
|
|
5252
|
+
additionalProperties: false,
|
|
5253
|
+
required: ["items"],
|
|
5254
|
+
properties: {
|
|
5255
|
+
loggedAt: { type: "string", format: "date-time" },
|
|
5256
|
+
mealLabel: nullable({ type: "string" }),
|
|
5257
|
+
source: {
|
|
5258
|
+
type: "string",
|
|
5259
|
+
enum: ["manual", "search", "barcode", "chatgpt", "photo", "saved_meal"]
|
|
5260
|
+
},
|
|
5261
|
+
confirmationState: {
|
|
5262
|
+
type: "string",
|
|
5263
|
+
enum: ["candidate", "confirmed", "needs_review", "discarded"]
|
|
5264
|
+
},
|
|
5265
|
+
userId: { type: "string" },
|
|
5266
|
+
placeId: nullable({ type: "string" }),
|
|
5267
|
+
stayId: nullable({ type: "string" }),
|
|
5268
|
+
workoutId: nullable({ type: "string" }),
|
|
5269
|
+
sleepId: nullable({ type: "string" }),
|
|
5270
|
+
dayKey: nullable({ type: "string" }),
|
|
5271
|
+
imageRefs: arrayOf({ type: "string" }),
|
|
5272
|
+
parserProvenance: { type: "object", additionalProperties: true },
|
|
5273
|
+
satietyScore: nullable({ type: "number" }),
|
|
5274
|
+
hungerBefore: nullable({ type: "number" }),
|
|
5275
|
+
hungerAfter: nullable({ type: "number" }),
|
|
5276
|
+
cravingScore: nullable({ type: "number" }),
|
|
5277
|
+
enjoymentScore: nullable({ type: "number" }),
|
|
5278
|
+
socialContext: nullable({ type: "string" }),
|
|
5279
|
+
locationContext: nullable({ type: "string" }),
|
|
5280
|
+
notes: nullable({ type: "string" }),
|
|
5281
|
+
items: arrayOf(nutritionMealItemInput)
|
|
5282
|
+
}
|
|
5283
|
+
};
|
|
5284
|
+
const nutritionFoodLog = {
|
|
5285
|
+
type: "object",
|
|
5286
|
+
additionalProperties: true,
|
|
5287
|
+
required: ["id", "loggedAt", "items", "totals"],
|
|
5288
|
+
properties: {
|
|
5289
|
+
id: { type: "string" },
|
|
5290
|
+
userId: { type: "string" },
|
|
5291
|
+
loggedAt: { type: "string", format: "date-time" },
|
|
5292
|
+
mealLabel: nullable({ type: "string" }),
|
|
5293
|
+
source: { type: "string" },
|
|
5294
|
+
confirmationState: { type: "string" },
|
|
5295
|
+
totals: { type: "object", additionalProperties: { type: "number" } },
|
|
5296
|
+
items: arrayOf({ type: "object", additionalProperties: true })
|
|
5297
|
+
}
|
|
5298
|
+
};
|
|
5299
|
+
const nutritionFoodSearchResult = {
|
|
5300
|
+
type: "object",
|
|
5301
|
+
additionalProperties: true,
|
|
5302
|
+
required: ["id", "source", "name"],
|
|
5303
|
+
properties: {
|
|
5304
|
+
id: { type: "string" },
|
|
5305
|
+
source: { type: "string" },
|
|
5306
|
+
sourceId: nullable({ type: "string" }),
|
|
5307
|
+
name: { type: "string" },
|
|
5308
|
+
brand: nullable({ type: "string" }),
|
|
5309
|
+
barcode: nullable({ type: "string" }),
|
|
5310
|
+
servingLabel: nullable({ type: "string" }),
|
|
5311
|
+
servingGrams: nullable({ type: "number" }),
|
|
5312
|
+
calories: nullable({ type: "number" }),
|
|
5313
|
+
proteinGrams: nullable({ type: "number" }),
|
|
5314
|
+
carbohydrateGrams: nullable({ type: "number" }),
|
|
5315
|
+
fatGrams: nullable({ type: "number" }),
|
|
5316
|
+
fiberGrams: nullable({ type: "number" }),
|
|
5317
|
+
tags: arrayOf({ type: "string" })
|
|
5318
|
+
}
|
|
5319
|
+
};
|
|
5320
|
+
const weightLossViewData = {
|
|
5321
|
+
type: "object",
|
|
5322
|
+
additionalProperties: false,
|
|
5323
|
+
required: [
|
|
5324
|
+
"userId",
|
|
5325
|
+
"generatedAt",
|
|
5326
|
+
"target",
|
|
5327
|
+
"summary",
|
|
5328
|
+
"todayLedger",
|
|
5329
|
+
"recentMeals",
|
|
5330
|
+
"energyModel",
|
|
5331
|
+
"weightTrend",
|
|
5332
|
+
"foodQuality",
|
|
5333
|
+
"trainingFuel",
|
|
5334
|
+
"subjective",
|
|
5335
|
+
"gut",
|
|
5336
|
+
"hypotheses",
|
|
5337
|
+
"experiments",
|
|
5338
|
+
"dataQuality"
|
|
5339
|
+
],
|
|
5340
|
+
properties: {
|
|
5341
|
+
userId: { type: "string" },
|
|
5342
|
+
generatedAt: { type: "string", format: "date-time" },
|
|
5343
|
+
target: { type: "object", additionalProperties: true },
|
|
5344
|
+
summary: { type: "object", additionalProperties: true },
|
|
5345
|
+
todayLedger: { type: "object", additionalProperties: true },
|
|
5346
|
+
recentMeals: arrayOf(nutritionFoodLog),
|
|
5347
|
+
bodyCheckins: arrayOf({ type: "object", additionalProperties: true }),
|
|
5348
|
+
appearanceCheckins: arrayOf({ type: "object", additionalProperties: true }),
|
|
5349
|
+
energyModel: { type: "object", additionalProperties: true },
|
|
5350
|
+
weightTrend: { type: "object", additionalProperties: true },
|
|
5351
|
+
foodQuality: { type: "object", additionalProperties: true },
|
|
5352
|
+
trainingFuel: { type: "object", additionalProperties: true },
|
|
5353
|
+
subjective: { type: "object", additionalProperties: true },
|
|
5354
|
+
gut: { type: "object", additionalProperties: true },
|
|
5355
|
+
hypotheses: arrayOf({ type: "object", additionalProperties: true }),
|
|
5356
|
+
experiments: arrayOf({ type: "object", additionalProperties: true }),
|
|
5357
|
+
dataQuality: { type: "object", additionalProperties: true }
|
|
5358
|
+
}
|
|
5359
|
+
};
|
|
5219
5360
|
const document = {
|
|
5220
5361
|
openapi: "3.1.0",
|
|
5221
5362
|
info: {
|
|
@@ -5328,6 +5469,11 @@ export function buildOpenApiDocument() {
|
|
|
5328
5469
|
SleepViewData: sleepViewData,
|
|
5329
5470
|
FitnessViewData: fitnessViewData,
|
|
5330
5471
|
TrainingLoadViewData: trainingLoadViewData,
|
|
5472
|
+
NutritionMealItemInput: nutritionMealItemInput,
|
|
5473
|
+
NutritionFoodLogInput: nutritionFoodLogInput,
|
|
5474
|
+
NutritionFoodLog: nutritionFoodLog,
|
|
5475
|
+
NutritionFoodSearchResult: nutritionFoodSearchResult,
|
|
5476
|
+
WeightLossViewData: weightLossViewData,
|
|
5331
5477
|
PsycheMetricsViewData: psycheMetricsViewData,
|
|
5332
5478
|
PsycheOverviewPayload: psycheOverviewPayload,
|
|
5333
5479
|
Insight: insight,
|
|
@@ -5635,6 +5781,309 @@ export function buildOpenApiDocument() {
|
|
|
5635
5781
|
}
|
|
5636
5782
|
}
|
|
5637
5783
|
},
|
|
5784
|
+
"/api/v1/health/weight-loss": {
|
|
5785
|
+
get: {
|
|
5786
|
+
tags: ["Health"],
|
|
5787
|
+
summary: "Read the Forge nutrition, weight-loss, food-effect, and body insight surface",
|
|
5788
|
+
responses: {
|
|
5789
|
+
"200": jsonResponse({
|
|
5790
|
+
type: "object",
|
|
5791
|
+
required: ["weightLoss"],
|
|
5792
|
+
properties: {
|
|
5793
|
+
weightLoss: {
|
|
5794
|
+
$ref: "#/components/schemas/WeightLossViewData"
|
|
5795
|
+
}
|
|
5796
|
+
}
|
|
5797
|
+
}, "Weight loss overview")
|
|
5798
|
+
}
|
|
5799
|
+
}
|
|
5800
|
+
},
|
|
5801
|
+
"/api/v1/health/weight-loss/target": {
|
|
5802
|
+
patch: {
|
|
5803
|
+
tags: ["Health"],
|
|
5804
|
+
summary: "Update nutrition and weight-loss targets",
|
|
5805
|
+
requestBody: {
|
|
5806
|
+
content: {
|
|
5807
|
+
"application/json": {
|
|
5808
|
+
schema: { type: "object", additionalProperties: true }
|
|
5809
|
+
}
|
|
5810
|
+
}
|
|
5811
|
+
},
|
|
5812
|
+
responses: {
|
|
5813
|
+
"200": jsonResponse({
|
|
5814
|
+
type: "object",
|
|
5815
|
+
required: ["target"],
|
|
5816
|
+
properties: {
|
|
5817
|
+
target: { type: "object", additionalProperties: true }
|
|
5818
|
+
}
|
|
5819
|
+
}, "Updated nutrition target")
|
|
5820
|
+
}
|
|
5821
|
+
}
|
|
5822
|
+
},
|
|
5823
|
+
"/api/v1/health/weight-loss/foods/search": {
|
|
5824
|
+
post: {
|
|
5825
|
+
tags: ["Health"],
|
|
5826
|
+
summary: "Search nutrition foods across local and public catalogs",
|
|
5827
|
+
requestBody: {
|
|
5828
|
+
content: {
|
|
5829
|
+
"application/json": {
|
|
5830
|
+
schema: {
|
|
5831
|
+
type: "object",
|
|
5832
|
+
additionalProperties: false,
|
|
5833
|
+
required: ["query"],
|
|
5834
|
+
properties: {
|
|
5835
|
+
query: { type: "string" },
|
|
5836
|
+
limit: { type: "integer", minimum: 1, maximum: 30 }
|
|
5837
|
+
}
|
|
5838
|
+
}
|
|
5839
|
+
}
|
|
5840
|
+
}
|
|
5841
|
+
},
|
|
5842
|
+
responses: {
|
|
5843
|
+
"200": jsonResponse({
|
|
5844
|
+
type: "object",
|
|
5845
|
+
required: ["foods"],
|
|
5846
|
+
properties: {
|
|
5847
|
+
foods: arrayOf({
|
|
5848
|
+
$ref: "#/components/schemas/NutritionFoodSearchResult"
|
|
5849
|
+
})
|
|
5850
|
+
}
|
|
5851
|
+
}, "Nutrition food search results")
|
|
5852
|
+
}
|
|
5853
|
+
}
|
|
5854
|
+
},
|
|
5855
|
+
"/api/v1/health/weight-loss/foods/barcode": {
|
|
5856
|
+
post: {
|
|
5857
|
+
tags: ["Health"],
|
|
5858
|
+
summary: "Lookup one nutrition food by barcode",
|
|
5859
|
+
requestBody: {
|
|
5860
|
+
content: {
|
|
5861
|
+
"application/json": {
|
|
5862
|
+
schema: {
|
|
5863
|
+
type: "object",
|
|
5864
|
+
additionalProperties: false,
|
|
5865
|
+
required: ["barcode"],
|
|
5866
|
+
properties: { barcode: { type: "string" } }
|
|
5867
|
+
}
|
|
5868
|
+
}
|
|
5869
|
+
}
|
|
5870
|
+
},
|
|
5871
|
+
responses: {
|
|
5872
|
+
"200": jsonResponse({
|
|
5873
|
+
type: "object",
|
|
5874
|
+
required: ["food"],
|
|
5875
|
+
properties: {
|
|
5876
|
+
food: nullable({
|
|
5877
|
+
$ref: "#/components/schemas/NutritionFoodSearchResult"
|
|
5878
|
+
})
|
|
5879
|
+
}
|
|
5880
|
+
}, "Nutrition barcode lookup result")
|
|
5881
|
+
}
|
|
5882
|
+
}
|
|
5883
|
+
},
|
|
5884
|
+
"/api/v1/health/weight-loss/food-logs": {
|
|
5885
|
+
post: {
|
|
5886
|
+
tags: ["Health"],
|
|
5887
|
+
summary: "Create a nutrition food log",
|
|
5888
|
+
requestBody: {
|
|
5889
|
+
content: {
|
|
5890
|
+
"application/json": {
|
|
5891
|
+
schema: { $ref: "#/components/schemas/NutritionFoodLogInput" }
|
|
5892
|
+
}
|
|
5893
|
+
}
|
|
5894
|
+
},
|
|
5895
|
+
responses: {
|
|
5896
|
+
"201": jsonResponse({
|
|
5897
|
+
type: "object",
|
|
5898
|
+
required: ["log"],
|
|
5899
|
+
properties: {
|
|
5900
|
+
log: { $ref: "#/components/schemas/NutritionFoodLog" }
|
|
5901
|
+
}
|
|
5902
|
+
}, "Created nutrition food log")
|
|
5903
|
+
}
|
|
5904
|
+
}
|
|
5905
|
+
},
|
|
5906
|
+
"/api/v1/health/weight-loss/food-logs/{id}": {
|
|
5907
|
+
patch: {
|
|
5908
|
+
tags: ["Health"],
|
|
5909
|
+
summary: "Patch a nutrition food log",
|
|
5910
|
+
responses: {
|
|
5911
|
+
"200": jsonResponse({
|
|
5912
|
+
type: "object",
|
|
5913
|
+
required: ["log"],
|
|
5914
|
+
properties: {
|
|
5915
|
+
log: { $ref: "#/components/schemas/NutritionFoodLog" }
|
|
5916
|
+
}
|
|
5917
|
+
}, "Updated nutrition food log")
|
|
5918
|
+
}
|
|
5919
|
+
},
|
|
5920
|
+
delete: {
|
|
5921
|
+
tags: ["Health"],
|
|
5922
|
+
summary: "Delete a nutrition food log",
|
|
5923
|
+
responses: {
|
|
5924
|
+
"200": jsonResponse({
|
|
5925
|
+
type: "object",
|
|
5926
|
+
required: ["deleted"],
|
|
5927
|
+
properties: { deleted: { type: "boolean" } }
|
|
5928
|
+
}, "Deleted nutrition food log")
|
|
5929
|
+
}
|
|
5930
|
+
}
|
|
5931
|
+
},
|
|
5932
|
+
"/api/v1/health/weight-loss/parse": {
|
|
5933
|
+
post: {
|
|
5934
|
+
tags: ["Health"],
|
|
5935
|
+
summary: "Parse a food log through Forge's openai-codex ChatGPT subscription connection",
|
|
5936
|
+
requestBody: {
|
|
5937
|
+
content: {
|
|
5938
|
+
"application/json": {
|
|
5939
|
+
schema: {
|
|
5940
|
+
type: "object",
|
|
5941
|
+
additionalProperties: false,
|
|
5942
|
+
properties: {
|
|
5943
|
+
text: { type: "string" },
|
|
5944
|
+
imageDescription: { type: "string" },
|
|
5945
|
+
loggedAt: { type: "string", format: "date-time" },
|
|
5946
|
+
mealLabel: { type: "string" }
|
|
5947
|
+
}
|
|
5948
|
+
}
|
|
5949
|
+
}
|
|
5950
|
+
}
|
|
5951
|
+
},
|
|
5952
|
+
responses: {
|
|
5953
|
+
"201": jsonResponse({
|
|
5954
|
+
type: "object",
|
|
5955
|
+
required: [
|
|
5956
|
+
"candidate",
|
|
5957
|
+
"log",
|
|
5958
|
+
"clarificationQuestions",
|
|
5959
|
+
"uncertaintyReasons"
|
|
5960
|
+
],
|
|
5961
|
+
properties: {
|
|
5962
|
+
candidate: {
|
|
5963
|
+
$ref: "#/components/schemas/NutritionFoodLogInput"
|
|
5964
|
+
},
|
|
5965
|
+
log: nullable({
|
|
5966
|
+
$ref: "#/components/schemas/NutritionFoodLog"
|
|
5967
|
+
}),
|
|
5968
|
+
clarificationQuestions: arrayOf({ type: "string" }),
|
|
5969
|
+
uncertaintyReasons: arrayOf({ type: "string" })
|
|
5970
|
+
}
|
|
5971
|
+
}, "Parsed candidate nutrition food log")
|
|
5972
|
+
}
|
|
5973
|
+
}
|
|
5974
|
+
},
|
|
5975
|
+
"/api/v1/health/weight-loss/body-checkins": {
|
|
5976
|
+
post: {
|
|
5977
|
+
tags: ["Health"],
|
|
5978
|
+
summary: "Create a body-composition check-in",
|
|
5979
|
+
responses: {
|
|
5980
|
+
"201": jsonResponse({
|
|
5981
|
+
type: "object",
|
|
5982
|
+
required: ["checkin"],
|
|
5983
|
+
properties: {
|
|
5984
|
+
checkin: { type: "object", additionalProperties: true }
|
|
5985
|
+
}
|
|
5986
|
+
}, "Created body check-in")
|
|
5987
|
+
}
|
|
5988
|
+
}
|
|
5989
|
+
},
|
|
5990
|
+
"/api/v1/health/weight-loss/appearance-checkins": {
|
|
5991
|
+
post: {
|
|
5992
|
+
tags: ["Health"],
|
|
5993
|
+
summary: "Create an aesthetic appearance check-in",
|
|
5994
|
+
responses: {
|
|
5995
|
+
"201": jsonResponse({
|
|
5996
|
+
type: "object",
|
|
5997
|
+
required: ["checkin"],
|
|
5998
|
+
properties: {
|
|
5999
|
+
checkin: { type: "object", additionalProperties: true }
|
|
6000
|
+
}
|
|
6001
|
+
}, "Created appearance check-in")
|
|
6002
|
+
}
|
|
6003
|
+
}
|
|
6004
|
+
},
|
|
6005
|
+
"/api/v1/health/weight-loss/subjective-checkins": {
|
|
6006
|
+
post: {
|
|
6007
|
+
tags: ["Health"],
|
|
6008
|
+
summary: "Create a subjective food-effect check-in",
|
|
6009
|
+
responses: {
|
|
6010
|
+
"201": jsonResponse({
|
|
6011
|
+
type: "object",
|
|
6012
|
+
required: ["checkin"],
|
|
6013
|
+
properties: {
|
|
6014
|
+
checkin: { type: "object", additionalProperties: true }
|
|
6015
|
+
}
|
|
6016
|
+
}, "Created subjective check-in")
|
|
6017
|
+
}
|
|
6018
|
+
}
|
|
6019
|
+
},
|
|
6020
|
+
"/api/v1/health/weight-loss/gut-checkins": {
|
|
6021
|
+
post: {
|
|
6022
|
+
tags: ["Health"],
|
|
6023
|
+
summary: "Create a gut-health food-effect check-in",
|
|
6024
|
+
responses: {
|
|
6025
|
+
"201": jsonResponse({
|
|
6026
|
+
type: "object",
|
|
6027
|
+
required: ["checkin"],
|
|
6028
|
+
properties: {
|
|
6029
|
+
checkin: { type: "object", additionalProperties: true }
|
|
6030
|
+
}
|
|
6031
|
+
}, "Created gut check-in")
|
|
6032
|
+
}
|
|
6033
|
+
}
|
|
6034
|
+
},
|
|
6035
|
+
"/api/v1/health/weight-loss/patterns": {
|
|
6036
|
+
get: {
|
|
6037
|
+
tags: ["Health"],
|
|
6038
|
+
summary: "Read current nutrition hypotheses and experiments",
|
|
6039
|
+
responses: {
|
|
6040
|
+
"200": jsonResponse({
|
|
6041
|
+
type: "object",
|
|
6042
|
+
required: ["hypotheses", "experiments"],
|
|
6043
|
+
properties: {
|
|
6044
|
+
hypotheses: arrayOf({
|
|
6045
|
+
type: "object",
|
|
6046
|
+
additionalProperties: true
|
|
6047
|
+
}),
|
|
6048
|
+
experiments: arrayOf({
|
|
6049
|
+
type: "object",
|
|
6050
|
+
additionalProperties: true
|
|
6051
|
+
})
|
|
6052
|
+
}
|
|
6053
|
+
}, "Nutrition pattern candidates")
|
|
6054
|
+
}
|
|
6055
|
+
}
|
|
6056
|
+
},
|
|
6057
|
+
"/api/v1/health/weight-loss/experiments": {
|
|
6058
|
+
post: {
|
|
6059
|
+
tags: ["Health"],
|
|
6060
|
+
summary: "Create a nutrition N-of-1 experiment",
|
|
6061
|
+
responses: {
|
|
6062
|
+
"201": jsonResponse({
|
|
6063
|
+
type: "object",
|
|
6064
|
+
required: ["experiment"],
|
|
6065
|
+
properties: {
|
|
6066
|
+
experiment: { type: "object", additionalProperties: true }
|
|
6067
|
+
}
|
|
6068
|
+
}, "Created nutrition experiment")
|
|
6069
|
+
}
|
|
6070
|
+
}
|
|
6071
|
+
},
|
|
6072
|
+
"/api/v1/health/weight-loss/experiments/{id}": {
|
|
6073
|
+
patch: {
|
|
6074
|
+
tags: ["Health"],
|
|
6075
|
+
summary: "Patch a nutrition N-of-1 experiment",
|
|
6076
|
+
responses: {
|
|
6077
|
+
"200": jsonResponse({
|
|
6078
|
+
type: "object",
|
|
6079
|
+
required: ["experiment"],
|
|
6080
|
+
properties: {
|
|
6081
|
+
experiment: { type: "object", additionalProperties: true }
|
|
6082
|
+
}
|
|
6083
|
+
}, "Updated nutrition experiment")
|
|
6084
|
+
}
|
|
6085
|
+
}
|
|
6086
|
+
},
|
|
5638
6087
|
"/api/v1/health/workouts": {
|
|
5639
6088
|
post: {
|
|
5640
6089
|
summary: "Create one manual workout session",
|
|
@@ -2,7 +2,6 @@ import { listActivityEvents } from "../repositories/activity-events.js";
|
|
|
2
2
|
import { listRewardLedger } from "../repositories/rewards.js";
|
|
3
3
|
import { listTags, listTagsByIds } from "../repositories/tags.js";
|
|
4
4
|
import { getDashboard } from "./dashboard.js";
|
|
5
|
-
import { buildAchievementSignals, buildGamificationProfile, buildMilestoneRewards } from "./gamification.js";
|
|
6
5
|
import { overviewContextSchema, riskContextSchema, todayContextSchema } from "../types.js";
|
|
7
6
|
function priorityWeight(task) {
|
|
8
7
|
switch (task.priority) {
|
|
@@ -105,7 +104,7 @@ function buildDomainBalance(goals, tasks) {
|
|
|
105
104
|
return [...domainRows.values()].sort((left, right) => right.completedPoints - left.completedPoints);
|
|
106
105
|
}
|
|
107
106
|
export function getOverviewContext(now = new Date(), options = {}) {
|
|
108
|
-
const dashboard = getDashboard(options);
|
|
107
|
+
const dashboard = options.dashboard ?? getDashboard({ userIds: options.userIds });
|
|
109
108
|
const focusTasks = dashboard.tasks.filter((task) => task.status === "focus" || task.status === "in_progress").length;
|
|
110
109
|
const overdueTasks = dashboard.tasks.filter((task) => task.status !== "done" && task.dueDate !== null && task.dueDate < now.toISOString().slice(0, 10)).length;
|
|
111
110
|
const dueHabits = dashboard.habits.filter((habit) => habit.dueToday).slice(0, 6);
|
|
@@ -129,17 +128,17 @@ export function getOverviewContext(now = new Date(), options = {}) {
|
|
|
129
128
|
topTasks: sortStrategicTasks(tasks.filter((task) => task.status !== "done")).slice(0, 6),
|
|
130
129
|
dueHabits,
|
|
131
130
|
recentEvidence: listActivityEvents({ limit: 12, userIds: options.userIds }),
|
|
132
|
-
achievements:
|
|
131
|
+
achievements: dashboard.achievements,
|
|
133
132
|
domainBalance: buildDomainBalance(goals, tasks),
|
|
134
133
|
neglectedGoals: buildNeglectedGoals(goals, tasks, now)
|
|
135
134
|
});
|
|
136
135
|
}
|
|
137
136
|
export function getTodayContext(now = new Date(), options = {}) {
|
|
138
|
-
const dashboard = getDashboard(options);
|
|
137
|
+
const dashboard = options.dashboard ?? getDashboard({ userIds: options.userIds });
|
|
139
138
|
const goals = dashboard.goals;
|
|
140
139
|
const tasks = dashboard.tasks;
|
|
141
140
|
const habits = dashboard.habits;
|
|
142
|
-
const gamification =
|
|
141
|
+
const gamification = dashboard.gamification;
|
|
143
142
|
const inProgressTasks = sortStrategicTasks(tasks.filter((task) => task.status === "in_progress")).slice(0, 4);
|
|
144
143
|
const readyTasks = sortStrategicTasks(tasks.filter((task) => task.status === "focus" || task.status === "backlog")).slice(0, 4);
|
|
145
144
|
const deferredTasks = sortStrategicTasks(tasks.filter((task) => task.status === "blocked")).slice(0, 4);
|
|
@@ -193,7 +192,7 @@ export function getTodayContext(now = new Date(), options = {}) {
|
|
|
193
192
|
}
|
|
194
193
|
],
|
|
195
194
|
dueHabits,
|
|
196
|
-
milestoneRewards:
|
|
195
|
+
milestoneRewards: dashboard.milestoneRewards,
|
|
197
196
|
recentHabitRewards: listRewardLedger({ entityType: "habit", limit: 8 }),
|
|
198
197
|
momentum: {
|
|
199
198
|
streakDays: gamification.streakDays,
|
|
@@ -207,7 +206,7 @@ export function getTodayContext(now = new Date(), options = {}) {
|
|
|
207
206
|
});
|
|
208
207
|
}
|
|
209
208
|
export function getRiskContext(now = new Date(), options = {}) {
|
|
210
|
-
const dashboard = getDashboard(options);
|
|
209
|
+
const dashboard = options.dashboard ?? getDashboard({ userIds: options.userIds });
|
|
211
210
|
const tasks = dashboard.tasks;
|
|
212
211
|
const goals = dashboard.goals;
|
|
213
212
|
const overdueTasks = sortStrategicTasks(tasks.filter((task) => task.status !== "done" && task.dueDate !== null && task.dueDate < now.toISOString().slice(0, 10))).slice(0, 8);
|
|
@@ -464,7 +464,39 @@ function strategyJsonChecks() {
|
|
|
464
464
|
function rewardAndGamificationChecks(settings) {
|
|
465
465
|
const checks = [];
|
|
466
466
|
const catalogItemIds = new Set(GAMIFICATION_CATALOG.map((item) => item.id));
|
|
467
|
-
const
|
|
467
|
+
const equipmentValueFields = [
|
|
468
|
+
{
|
|
469
|
+
column: "selected_mascot_skin",
|
|
470
|
+
unlockType: "mascot_skin",
|
|
471
|
+
payloadKey: "mascotSkin"
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
column: "selected_hud_treatment",
|
|
475
|
+
unlockType: "hud_treatment",
|
|
476
|
+
payloadKey: "hudTreatment"
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
column: "selected_streak_effect",
|
|
480
|
+
unlockType: "streak_effect",
|
|
481
|
+
payloadKey: "streakEffect"
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
column: "selected_trophy_shelf",
|
|
485
|
+
unlockType: "trophy_shelf",
|
|
486
|
+
payloadKey: "trophyShelf"
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
column: "selected_celebration_variant",
|
|
490
|
+
unlockType: "celebration_variant",
|
|
491
|
+
payloadKey: "celebrationVariant"
|
|
492
|
+
}
|
|
493
|
+
];
|
|
494
|
+
const equipmentValuesByColumn = new Map(equipmentValueFields.map((field) => [
|
|
495
|
+
field.column,
|
|
496
|
+
new Set(GAMIFICATION_CATALOG.filter((item) => item.kind === "unlock" &&
|
|
497
|
+
item.unlockType === field.unlockType &&
|
|
498
|
+
typeof item.rewardPayload[field.payloadKey] === "string").map((item) => item.rewardPayload[field.payloadKey]))
|
|
499
|
+
]));
|
|
468
500
|
checks.push(safeCountCheck({
|
|
469
501
|
id: "rewards.rules.missing",
|
|
470
502
|
group: "Rewards",
|
|
@@ -532,7 +564,10 @@ function rewardAndGamificationChecks(settings) {
|
|
|
532
564
|
: [];
|
|
533
565
|
const staleEquipment = equipmentRows.reduce((count, row) => {
|
|
534
566
|
return (count +
|
|
535
|
-
Object.
|
|
567
|
+
Object.entries(row).filter(([column, value]) => typeof value === "string" &&
|
|
568
|
+
!(equipmentValuesByColumn
|
|
569
|
+
.get(column)
|
|
570
|
+
?.has(value) ?? false)).length);
|
|
536
571
|
}, 0);
|
|
537
572
|
checks.push(check({
|
|
538
573
|
id: "rewards.gamification.equipment",
|
|
@@ -541,8 +576,8 @@ function rewardAndGamificationChecks(settings) {
|
|
|
541
576
|
passed: staleEquipment === 0,
|
|
542
577
|
severity: "warning",
|
|
543
578
|
summary: staleEquipment === 0
|
|
544
|
-
? "Selected gamification equipment
|
|
545
|
-
: `${staleEquipment} selected equipment
|
|
579
|
+
? "Selected gamification equipment values match current unlock reward payloads."
|
|
580
|
+
: `${staleEquipment} selected equipment value${staleEquipment === 1 ? "" : "s"} point to removed catalog rewards.`,
|
|
546
581
|
affectedCount: staleEquipment
|
|
547
582
|
}));
|
|
548
583
|
return checks.concat(check({
|