forge-openclaw-plugin 0.2.24 → 0.2.26
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 +13 -0
- package/dist/assets/{board-_C6oMy5w.js → board-ta0rUHOf.js} +3 -3
- package/dist/assets/{board-_C6oMy5w.js.map → board-ta0rUHOf.js.map} +1 -1
- package/dist/assets/index-Ro0ZF_az.css +1 -0
- package/dist/assets/index-ytlpSj23.js +79 -0
- package/dist/assets/index-ytlpSj23.js.map +1 -0
- package/dist/assets/{motion-D4sZgCHd.js → motion-fBKPB6yw.js} +3 -3
- package/dist/assets/motion-fBKPB6yw.js.map +1 -0
- package/dist/assets/{table-BWzTaky1.js → table-C-IGTQni.js} +2 -2
- package/dist/assets/{table-BWzTaky1.js.map → table-C-IGTQni.js.map} +1 -1
- package/dist/assets/{ui-BzK4azQb.js → ui-DInOpaYF.js} +2 -2
- package/dist/assets/{ui-BzK4azQb.js.map → ui-DInOpaYF.js.map} +1 -1
- package/dist/assets/vendor-lE3tZJcC.js +876 -0
- package/dist/assets/vendor-lE3tZJcC.js.map +1 -0
- package/dist/index.html +7 -8
- package/dist/openclaw/local-runtime.d.ts +3 -1
- package/dist/openclaw/local-runtime.js +51 -15
- package/dist/openclaw/parity.d.ts +1 -1
- package/dist/openclaw/parity.js +29 -0
- package/dist/openclaw/plugin-entry-shared.d.ts +1 -0
- package/dist/openclaw/plugin-entry-shared.js +31 -6
- package/dist/openclaw/plugin-sdk-types.d.ts +29 -0
- package/dist/openclaw/routes.js +236 -0
- package/dist/openclaw/session-bootstrap.d.ts +78 -0
- package/dist/openclaw/session-bootstrap.js +240 -0
- package/dist/openclaw/tools.js +279 -6
- package/dist/server/server/migrations/001_core.sql +411 -0
- package/dist/server/server/migrations/002_psyche.sql +392 -0
- package/dist/server/server/migrations/003_habits.sql +30 -0
- package/dist/server/server/migrations/004_habit_links.sql +8 -0
- package/dist/server/server/migrations/005_habit_psyche_links.sql +24 -0
- package/dist/server/server/migrations/006_work_adjustments.sql +14 -0
- package/dist/server/server/migrations/007_weekly_review_closures.sql +17 -0
- package/dist/server/server/migrations/008_calendar_execution.sql +147 -0
- package/dist/server/server/migrations/009_true_calendar_events.sql +195 -0
- package/dist/server/server/migrations/010_calendar_selection_state.sql +6 -0
- package/dist/server/server/migrations/011_calendar_timezone_backfill.sql +11 -0
- package/dist/server/server/migrations/012_work_block_ranges.sql +7 -0
- package/dist/server/server/migrations/013_microsoft_local_auth_settings.sql +8 -0
- package/dist/server/server/migrations/014_note_tags_and_ephemeral.sql +8 -0
- package/dist/server/server/migrations/015_multi_user_and_strategies.sql +244 -0
- package/dist/server/server/migrations/016_health_companion.sql +158 -0
- package/dist/server/server/migrations/016_strategy_contracts_and_user_graph.sql +22 -0
- package/dist/server/server/migrations/017_preferences.sql +131 -0
- package/dist/server/server/migrations/018_preference_catalogs.sql +31 -0
- package/dist/server/server/migrations/019_wiki_memory.sql +255 -0
- package/dist/server/server/migrations/020_wiki_page_hierarchy.sql +11 -0
- package/dist/server/server/migrations/021_hide_evidence_from_wiki_index.sql +3 -0
- package/dist/server/server/migrations/022_wiki_ingest_background.sql +85 -0
- package/dist/server/server/migrations/023_diagnostic_logs.sql +28 -0
- package/dist/server/server/migrations/024_questionnaires.sql +96 -0
- package/dist/server/server/migrations/025_ai_model_connections.sql +26 -0
- package/dist/server/server/migrations/026_custom_theme_settings.sql +2 -0
- package/dist/server/server/migrations/027_ai_processors.sql +31 -0
- package/dist/server/server/migrations/028_movement_domain.sql +136 -0
- package/dist/server/server/migrations/029_watch_micro_capture.sql +23 -0
- package/dist/server/server/migrations/030_surface_layouts.sql +5 -0
- package/dist/server/server/migrations/031_ai_processor_runtime_upgrades.sql +10 -0
- package/dist/server/server/migrations/032_ai_connectors.sql +44 -0
- package/dist/server/server/migrations/033_movement_trip_point_sync.sql +36 -0
- package/dist/server/server/migrations/034_movement_segment_sync.sql +49 -0
- package/dist/server/server/migrations/035_google_local_auth_settings.sql +2 -0
- package/dist/server/server/migrations/036_google_local_auth_client_secret.sql +2 -0
- package/dist/server/{app.js → server/src/app.js} +992 -25
- package/dist/server/server/src/connectors/box-registry.js +188 -0
- package/dist/server/{db.js → server/src/db.js} +6 -0
- package/dist/server/server/src/debug.js +19 -0
- package/dist/server/server/src/discovery-advertiser.js +114 -0
- package/dist/server/{health.js → server/src/health.js} +39 -11
- package/dist/server/{index.js → server/src/index.js} +4 -0
- package/dist/server/{managers → server/src/managers}/platform/llm-manager.js +40 -4
- package/dist/server/{managers → server/src/managers}/platform/openai-responses-provider.js +129 -19
- package/dist/server/server/src/movement.js +2935 -0
- package/dist/server/{openapi.js → server/src/openapi.js} +628 -5
- package/dist/server/{psyche-types.js → server/src/psyche-types.js} +15 -1
- package/dist/server/server/src/questionnaire-flow.js +552 -0
- package/dist/server/server/src/questionnaire-seeds.js +853 -0
- package/dist/server/server/src/questionnaire-types.js +340 -0
- package/dist/server/server/src/repositories/ai-connectors.js +1207 -0
- package/dist/server/server/src/repositories/ai-processors.js +547 -0
- package/dist/server/{repositories → server/src/repositories}/calendar.js +1 -1
- package/dist/server/{repositories → server/src/repositories}/entity-ownership.js +9 -1
- package/dist/server/{repositories → server/src/repositories}/habits.js +69 -5
- package/dist/server/server/src/repositories/model-settings.js +216 -0
- package/dist/server/{repositories → server/src/repositories}/notes.js +57 -15
- package/dist/server/{repositories → server/src/repositories}/preferences.js +124 -0
- package/dist/server/server/src/repositories/questionnaires.js +1338 -0
- package/dist/server/{repositories → server/src/repositories}/settings.js +156 -12
- package/dist/server/server/src/repositories/surface-layouts.js +76 -0
- package/dist/server/{repositories → server/src/repositories}/wiki-memory.js +5 -1
- package/dist/server/{services → server/src/services}/calendar-runtime.js +775 -58
- package/dist/server/{services → server/src/services}/entity-crud.js +81 -2
- package/dist/server/server/src/services/google-calendar-oauth-config.js +176 -0
- package/dist/server/server/src/services/openai-codex-oauth.js +153 -0
- package/dist/server/server/src/services/psyche-observation-calendar.js +46 -0
- package/dist/server/{types.js → server/src/types.js} +621 -14
- package/dist/server/server/src/watch-mobile.js +562 -0
- package/dist/server/{web.js → server/src/web.js} +30 -4
- package/dist/server/src/components/customization/utility-widgets.js +330 -0
- package/dist/server/src/components/workbench-boxes/health/health-boxes.js +92 -0
- package/dist/server/src/components/workbench-boxes/kanban/kanban-boxes.js +128 -0
- package/dist/server/src/components/workbench-boxes/movement/movement-boxes.js +37 -0
- package/dist/server/src/components/workbench-boxes/notes/notes-boxes.js +114 -0
- package/dist/server/src/components/workbench-boxes/projects/projects-boxes.js +57 -0
- package/dist/server/src/components/workbench-boxes/shared/define-workbench-box.js +4 -0
- package/dist/server/src/components/workbench-boxes/shared/generic-node-view.js +13 -0
- package/dist/server/src/components/workbench-boxes/today/today-boxes.js +63 -0
- package/dist/server/src/lib/api-error.js +37 -0
- package/dist/server/src/lib/api.js +1859 -0
- package/dist/server/src/lib/calendar-name-deduper.js +144 -0
- package/dist/server/src/lib/diagnostics.js +67 -0
- package/dist/server/src/lib/psyche-types.js +1 -0
- package/dist/server/src/lib/questionnaire-types.js +1 -0
- package/dist/server/src/lib/runtime-paths.js +24 -0
- package/dist/server/src/lib/schemas.js +234 -0
- package/dist/server/src/lib/snapshot-normalizer.js +374 -0
- package/dist/server/src/lib/theme-system.js +319 -0
- package/dist/server/src/lib/types.js +1 -0
- package/dist/server/src/lib/utils.js +22 -0
- package/dist/server/src/lib/workbench/boxes.js +16 -0
- package/dist/server/src/lib/workbench/nodes.js +15 -0
- package/dist/server/src/lib/workbench/registry.js +73 -0
- package/dist/server/src/lib/workbench/runtime.js +181 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +6 -1
- package/server/index.js +68 -0
- package/server/migrations/024_questionnaires.sql +96 -0
- package/server/migrations/025_ai_model_connections.sql +26 -0
- package/server/migrations/026_custom_theme_settings.sql +2 -0
- package/server/migrations/027_ai_processors.sql +31 -0
- package/server/migrations/028_movement_domain.sql +136 -0
- package/server/migrations/029_watch_micro_capture.sql +23 -0
- package/server/migrations/030_surface_layouts.sql +5 -0
- package/server/migrations/031_ai_processor_runtime_upgrades.sql +10 -0
- package/server/migrations/032_ai_connectors.sql +44 -0
- package/server/migrations/033_movement_trip_point_sync.sql +36 -0
- package/server/migrations/034_movement_segment_sync.sql +49 -0
- package/server/migrations/035_google_local_auth_settings.sql +2 -0
- package/server/migrations/036_google_local_auth_client_secret.sql +2 -0
- package/skills/forge-openclaw/SKILL.md +15 -1
- package/skills/forge-openclaw/entity_conversation_playbooks.md +523 -87
- package/skills/forge-openclaw/psyche_entity_playbooks.md +331 -221
- package/dist/assets/index-DTCwBWAs.js +0 -65
- package/dist/assets/index-DTCwBWAs.js.map +0 -1
- package/dist/assets/index-DttXlAgi.css +0 -1
- package/dist/assets/motion-D4sZgCHd.js.map +0 -1
- package/dist/assets/vendor-De38P6YR.js +0 -729
- package/dist/assets/vendor-De38P6YR.js.map +0 -1
- package/dist/assets/viz-C6hfyqzu.js +0 -34
- package/dist/assets/viz-C6hfyqzu.js.map +0 -1
- package/skills/forge-openclaw/cron_jobs.md +0 -395
- /package/dist/server/{demo-data.js → server/src/demo-data.js} +0 -0
- /package/dist/server/{e2e-server.js → server/src/e2e-server.js} +0 -0
- /package/dist/server/{errors.js → server/src/errors.js} +0 -0
- /package/dist/server/{managers → server/src/managers}/base.js +0 -0
- /package/dist/server/{managers → server/src/managers}/contracts.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/api-gateway-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/audit-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/authentication-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/authorization-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/background-job-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/configuration-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/database-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/event-bus-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/external-service-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/health-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/migration-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/search-index-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/secrets-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/session-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/storage-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/token-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/transaction-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/trusted-network.js +0 -0
- /package/dist/server/{managers → server/src/managers}/runtime.js +0 -0
- /package/dist/server/{managers → server/src/managers}/type-guards.js +0 -0
- /package/dist/server/{preferences-seeds.js → server/src/preferences-seeds.js} +0 -0
- /package/dist/server/{preferences-types.js → server/src/preferences-types.js} +0 -0
- /package/dist/server/{repositories → server/src/repositories}/activity-events.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/collaboration.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/deleted-entities.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/diagnostic-logs.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/domains.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/event-log.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/goals.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/projects.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/psyche.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/rewards.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/strategies.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/tags.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/task-runs.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/tasks.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/users.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/weekly-reviews.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/work-adjustments.js +0 -0
- /package/dist/server/{seed-demo.js → server/src/seed-demo.js} +0 -0
- /package/dist/server/{services → server/src/services}/context.js +0 -0
- /package/dist/server/{services → server/src/services}/dashboard.js +0 -0
- /package/dist/server/{services → server/src/services}/gamification.js +0 -0
- /package/dist/server/{services → server/src/services}/insights.js +0 -0
- /package/dist/server/{services → server/src/services}/projects.js +0 -0
- /package/dist/server/{services → server/src/services}/psyche.js +0 -0
- /package/dist/server/{services → server/src/services}/relations.js +0 -0
- /package/dist/server/{services → server/src/services}/reviews.js +0 -0
- /package/dist/server/{services → server/src/services}/run-recovery.js +0 -0
- /package/dist/server/{services → server/src/services}/tagging.js +0 -0
- /package/dist/server/{services → server/src/services}/task-run-watchdog.js +0 -0
- /package/dist/server/{services → server/src/services}/work-time.js +0 -0
|
@@ -3,16 +3,20 @@ import { createInsight, deleteInsight, getInsightById, listInsights, updateInsig
|
|
|
3
3
|
import { createCalendarEvent, createTaskTimebox, createWorkBlockTemplate, deleteCalendarEvent, deleteTaskTimebox, deleteWorkBlockTemplate, getCalendarEventById, getTaskTimeboxById, getWorkBlockTemplateById, listCalendarEvents, listTaskTimeboxes, listWorkBlockTemplates, updateCalendarEvent, updateTaskTimebox, updateWorkBlockTemplate } from "../repositories/calendar.js";
|
|
4
4
|
import { createNote, deleteNote, getNoteById, listNotes, unlinkNotesForEntity, updateNote } from "../repositories/notes.js";
|
|
5
5
|
import { clearEntityOwner, filterOwnedEntities } from "../repositories/entity-ownership.js";
|
|
6
|
+
import { createPreferenceCatalog, createPreferenceCatalogItem, createPreferenceContext, createPreferenceItem, deletePreferenceCatalog, deletePreferenceCatalogItem, deletePreferenceContext, deletePreferenceItem, getPreferenceCatalogById, getPreferenceCatalogItemById, getPreferenceContextById, getPreferenceItemById, listPreferenceCatalogItems, listPreferenceCatalogs, listPreferenceContexts, listPreferenceItems, updatePreferenceCatalog, updatePreferenceCatalogItem, updatePreferenceContext, updatePreferenceItem } from "../repositories/preferences.js";
|
|
6
7
|
import { createBehaviorPatternSchema, createBehaviorSchema, createBeliefEntrySchema, createEmotionDefinitionSchema, createEventTypeSchema, createModeGuideSessionSchema, createModeProfileSchema, createPsycheValueSchema, createTriggerReportSchema, updateBehaviorPatternSchema, updateBehaviorSchema, updateBeliefEntrySchema, updateEmotionDefinitionSchema, updateEventTypeSchema, updateModeGuideSessionSchema, updateModeProfileSchema, updatePsycheValueSchema, updateTriggerReportSchema } from "../psyche-types.js";
|
|
7
8
|
import { buildSettingsBinPayload, cascadeSoftDeleteAnchoredCollaboration, clearDeletedEntityRecord, getDeletedEntityRecord, listDeletedEntities, restoreAnchoredCollaboration, restoreDeletedEntityRecord, upsertDeletedEntityRecord } from "../repositories/deleted-entities.js";
|
|
8
9
|
import { createGoal, deleteGoal, getGoalById, listGoals, updateGoal } from "../repositories/goals.js";
|
|
9
10
|
import { createHabit, deleteHabit, getHabitById, listHabits, updateHabit } from "../repositories/habits.js";
|
|
11
|
+
import { createQuestionnaireInstrument, deleteQuestionnaireInstrument, getQuestionnaireInstrumentEntityById, listQuestionnaireInstrumentEntities, updateQuestionnaireInstrument, updateQuestionnaireInstrumentSchema } from "../repositories/questionnaires.js";
|
|
10
12
|
import { createBehavior, createBehaviorPattern, createBeliefEntry, createEmotionDefinition, createEventType, createModeGuideSession, createModeProfile, createPsycheValue, createTriggerReport, deleteBehavior, deleteBehaviorPattern, deleteBeliefEntry, deleteEmotionDefinition, deleteEventType, deleteModeGuideSession, deleteModeProfile, deletePsycheValue, deleteTriggerReport, getBehaviorById, getBehaviorPatternById, getBeliefEntryById, getEmotionDefinitionById, getEventTypeById, getModeGuideSessionById, getModeProfileById, getPsycheValueById, getTriggerReportById, listBehaviors, listBehaviorPatterns, listBeliefEntries, listEmotionDefinitions, listEventTypes, listModeGuideSessions, listModeProfiles, listPsycheValues, listTriggerReports, updateBehavior, updateBehaviorPattern, updateBeliefEntry, updateEmotionDefinition, updateEventType, updateModeGuideSession, updateModeProfile, updatePsycheValue, updateTriggerReport } from "../repositories/psyche.js";
|
|
11
13
|
import { createProject, deleteProject, getProjectById, listProjects, updateProject } from "../repositories/projects.js";
|
|
12
14
|
import { createStrategy, deleteStrategy, getStrategyById, listStrategies, updateStrategy } from "../repositories/strategies.js";
|
|
13
15
|
import { createTag, deleteTag, getTagById, listTags, updateTag } from "../repositories/tags.js";
|
|
14
16
|
import { createTask, deleteTask, getTaskById, listTasks, updateTask } from "../repositories/tasks.js";
|
|
15
17
|
import { createCalendarEventSchema, createGoalSchema, createHabitSchema, createInsightSchema, createNoteSchema, createProjectSchema, createStrategySchema, createTaskTimeboxSchema, createTagSchema, createTaskSchema, createWorkBlockTemplateSchema, updateCalendarEventSchema, updateGoalSchema, updateHabitSchema, updateInsightSchema, updateNoteSchema, updateProjectSchema, updateStrategySchema, updateTaskTimeboxSchema, updateTagSchema, updateTaskSchema, updateWorkBlockTemplateSchema } from "../types.js";
|
|
18
|
+
import { createPreferenceCatalogItemSchema, createPreferenceCatalogSchema, createPreferenceContextSchema, createPreferenceItemSchema, updatePreferenceCatalogItemSchema, updatePreferenceCatalogSchema, updatePreferenceContextSchema, updatePreferenceItemSchema } from "../preferences-types.js";
|
|
19
|
+
import { createQuestionnaireInstrumentSchema } from "../questionnaire-types.js";
|
|
16
20
|
const ENTITY_CALENDAR_LIST_RANGE = {
|
|
17
21
|
from: "1970-01-01T00:00:00.000Z",
|
|
18
22
|
to: "2100-01-01T00:00:00.000Z"
|
|
@@ -249,6 +253,62 @@ const CRUD_ENTITY_CAPABILITIES = {
|
|
|
249
253
|
create: (data, context) => createTriggerReport(data, context),
|
|
250
254
|
update: (id, patch, context) => updateTriggerReport(id, patch, context),
|
|
251
255
|
hardDelete: (id, context) => deleteTriggerReport(id, context)
|
|
256
|
+
},
|
|
257
|
+
preference_catalog: {
|
|
258
|
+
entityType: "preference_catalog",
|
|
259
|
+
routeBase: "/api/v1/preferences/catalogs",
|
|
260
|
+
deleteMode: "immediate",
|
|
261
|
+
inBin: false,
|
|
262
|
+
list: () => listPreferenceCatalogs(),
|
|
263
|
+
get: (id) => getPreferenceCatalogById(id),
|
|
264
|
+
create: (data) => createPreferenceCatalog(data),
|
|
265
|
+
update: (id, patch) => updatePreferenceCatalog(id, patch),
|
|
266
|
+
hardDelete: (id) => deletePreferenceCatalog(id)
|
|
267
|
+
},
|
|
268
|
+
preference_catalog_item: {
|
|
269
|
+
entityType: "preference_catalog_item",
|
|
270
|
+
routeBase: "/api/v1/preferences/catalog-items",
|
|
271
|
+
deleteMode: "immediate",
|
|
272
|
+
inBin: false,
|
|
273
|
+
list: () => listPreferenceCatalogItems(),
|
|
274
|
+
get: (id) => getPreferenceCatalogItemById(id),
|
|
275
|
+
create: (data) => createPreferenceCatalogItem(data),
|
|
276
|
+
update: (id, patch) => updatePreferenceCatalogItem(id, patch),
|
|
277
|
+
hardDelete: (id) => deletePreferenceCatalogItem(id)
|
|
278
|
+
},
|
|
279
|
+
preference_context: {
|
|
280
|
+
entityType: "preference_context",
|
|
281
|
+
routeBase: "/api/v1/preferences/contexts",
|
|
282
|
+
deleteMode: "immediate",
|
|
283
|
+
inBin: false,
|
|
284
|
+
list: () => listPreferenceContexts(),
|
|
285
|
+
get: (id) => getPreferenceContextById(id),
|
|
286
|
+
create: (data) => createPreferenceContext(data),
|
|
287
|
+
update: (id, patch) => updatePreferenceContext(id, patch),
|
|
288
|
+
hardDelete: (id) => deletePreferenceContext(id)
|
|
289
|
+
},
|
|
290
|
+
preference_item: {
|
|
291
|
+
entityType: "preference_item",
|
|
292
|
+
routeBase: "/api/v1/preferences/items",
|
|
293
|
+
deleteMode: "immediate",
|
|
294
|
+
inBin: false,
|
|
295
|
+
list: () => listPreferenceItems(),
|
|
296
|
+
get: (id) => getPreferenceItemById(id),
|
|
297
|
+
create: (data) => createPreferenceItem(data),
|
|
298
|
+
update: (id, patch) => updatePreferenceItem(id, patch),
|
|
299
|
+
hardDelete: (id) => deletePreferenceItem(id)
|
|
300
|
+
},
|
|
301
|
+
questionnaire_instrument: {
|
|
302
|
+
entityType: "questionnaire_instrument",
|
|
303
|
+
routeBase: "/api/v1/psyche/questionnaires",
|
|
304
|
+
deleteMode: "immediate",
|
|
305
|
+
inBin: false,
|
|
306
|
+
list: () => listQuestionnaireInstrumentEntities(),
|
|
307
|
+
get: (id) => getQuestionnaireInstrumentEntityById(id),
|
|
308
|
+
create: (data, context) => createQuestionnaireInstrument(data, context)
|
|
309
|
+
.instrument,
|
|
310
|
+
update: (id, patch, context) => updateQuestionnaireInstrument(id, patch, context),
|
|
311
|
+
hardDelete: (id, context) => deleteQuestionnaireInstrument(id, context)
|
|
252
312
|
}
|
|
253
313
|
};
|
|
254
314
|
export function getCrudEntityCapabilityMatrix() {
|
|
@@ -283,7 +343,12 @@ const CREATE_ENTITY_SCHEMAS = {
|
|
|
283
343
|
mode_guide_session: createModeGuideSessionSchema,
|
|
284
344
|
event_type: createEventTypeSchema,
|
|
285
345
|
emotion_definition: createEmotionDefinitionSchema,
|
|
286
|
-
trigger_report: createTriggerReportSchema
|
|
346
|
+
trigger_report: createTriggerReportSchema,
|
|
347
|
+
preference_catalog: createPreferenceCatalogSchema,
|
|
348
|
+
preference_catalog_item: createPreferenceCatalogItemSchema,
|
|
349
|
+
preference_context: createPreferenceContextSchema,
|
|
350
|
+
preference_item: createPreferenceItemSchema,
|
|
351
|
+
questionnaire_instrument: createQuestionnaireInstrumentSchema
|
|
287
352
|
};
|
|
288
353
|
const UPDATE_ENTITY_SCHEMAS = {
|
|
289
354
|
goal: updateGoalSchema,
|
|
@@ -305,7 +370,12 @@ const UPDATE_ENTITY_SCHEMAS = {
|
|
|
305
370
|
mode_guide_session: updateModeGuideSessionSchema,
|
|
306
371
|
event_type: updateEventTypeSchema,
|
|
307
372
|
emotion_definition: updateEmotionDefinitionSchema,
|
|
308
|
-
trigger_report: updateTriggerReportSchema
|
|
373
|
+
trigger_report: updateTriggerReportSchema,
|
|
374
|
+
preference_catalog: updatePreferenceCatalogSchema,
|
|
375
|
+
preference_catalog_item: updatePreferenceCatalogItemSchema,
|
|
376
|
+
preference_context: updatePreferenceContextSchema,
|
|
377
|
+
preference_item: updatePreferenceItemSchema,
|
|
378
|
+
questionnaire_instrument: updateQuestionnaireInstrumentSchema
|
|
309
379
|
};
|
|
310
380
|
function parseCreateInput(entityType, data) {
|
|
311
381
|
return CREATE_ENTITY_SCHEMAS[entityType].parse(data);
|
|
@@ -476,6 +546,15 @@ function matchesLinkedTo(entityType, entity, linkedTo) {
|
|
|
476
546
|
(linkedTo.entityType === "behavior" && Array.isArray(entity.linkedBehaviorIds) && entity.linkedBehaviorIds.includes(linkedTo.id)) ||
|
|
477
547
|
(linkedTo.entityType === "belief_entry" && Array.isArray(entity.linkedBeliefIds) && entity.linkedBeliefIds.includes(linkedTo.id)) ||
|
|
478
548
|
(linkedTo.entityType === "mode_profile" && Array.isArray(entity.linkedModeIds) && entity.linkedModeIds.includes(linkedTo.id)));
|
|
549
|
+
case "preference_catalog_item":
|
|
550
|
+
return linkedTo.entityType === "preference_catalog" && entity.catalogId === linkedTo.id;
|
|
551
|
+
case "preference_item":
|
|
552
|
+
return (typeof entity.sourceEntityType === "string" &&
|
|
553
|
+
typeof entity.sourceEntityId === "string" &&
|
|
554
|
+
entity.sourceEntityType === linkedTo.entityType &&
|
|
555
|
+
entity.sourceEntityId === linkedTo.id);
|
|
556
|
+
case "questionnaire_instrument":
|
|
557
|
+
return false;
|
|
479
558
|
default:
|
|
480
559
|
return false;
|
|
481
560
|
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { createDecipheriv, createHash } from "node:crypto";
|
|
2
|
+
const GOOGLE_CALLBACK_PATH = "/api/v1/calendar/oauth/google/callback";
|
|
3
|
+
const DEFAULT_APP_PORT = "4317";
|
|
4
|
+
const DEFAULT_APP_BASE_URL = `http://127.0.0.1:${DEFAULT_APP_PORT}`;
|
|
5
|
+
const PACKAGED_DEFAULT_GOOGLE_CREDENTIAL_KEY_MATERIAL = [
|
|
6
|
+
"forge",
|
|
7
|
+
"desktop",
|
|
8
|
+
"oauth",
|
|
9
|
+
"default",
|
|
10
|
+
"google",
|
|
11
|
+
"bundle",
|
|
12
|
+
"2026",
|
|
13
|
+
"local"
|
|
14
|
+
];
|
|
15
|
+
const PACKAGED_DEFAULT_GOOGLE_CLIENT_ID_ENCRYPTED = {
|
|
16
|
+
iv: "2fc1a6723312a10b7d176f13",
|
|
17
|
+
data: "9fbdf9dfcd1cc8674fa46150dd0f4678df8ce1ba52cdf0ceb4d4a76c9c5df01b3d8a21f194201c827524a6e06b0d4a55c7002edb3fa20b76b67f540e46ba28f506a73edaf6559a1d",
|
|
18
|
+
tag: "1965dfe1213ecd888e46ea59241d03a9"
|
|
19
|
+
};
|
|
20
|
+
const PACKAGED_DEFAULT_GOOGLE_CLIENT_SECRET_ENCRYPTED = {
|
|
21
|
+
iv: "5a6ca66625e0ff5559b34f99",
|
|
22
|
+
data: "b80dec231f029d92a13426ad3f0b851e20a5b35ec6691e517744fd65349144e75005f1",
|
|
23
|
+
tag: "9d2cfd8a153d91f60d8c628bbe1c2f25"
|
|
24
|
+
};
|
|
25
|
+
const DEFAULT_DEV_WEB_ORIGINS = [
|
|
26
|
+
"http://127.0.0.1:3027",
|
|
27
|
+
"http://localhost:3027"
|
|
28
|
+
];
|
|
29
|
+
function decryptPackagedGoogleOauthValue(payload) {
|
|
30
|
+
const key = createHash("sha256")
|
|
31
|
+
.update(PACKAGED_DEFAULT_GOOGLE_CREDENTIAL_KEY_MATERIAL.join(":"))
|
|
32
|
+
.digest();
|
|
33
|
+
const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(payload.iv, "hex"));
|
|
34
|
+
decipher.setAuthTag(Buffer.from(payload.tag, "hex"));
|
|
35
|
+
const decrypted = Buffer.concat([
|
|
36
|
+
decipher.update(Buffer.from(payload.data, "hex")),
|
|
37
|
+
decipher.final()
|
|
38
|
+
]);
|
|
39
|
+
return decrypted.toString("utf8");
|
|
40
|
+
}
|
|
41
|
+
const PACKAGED_DEFAULT_GOOGLE_CLIENT_ID = decryptPackagedGoogleOauthValue(PACKAGED_DEFAULT_GOOGLE_CLIENT_ID_ENCRYPTED);
|
|
42
|
+
const PACKAGED_DEFAULT_GOOGLE_CLIENT_SECRET = decryptPackagedGoogleOauthValue(PACKAGED_DEFAULT_GOOGLE_CLIENT_SECRET_ENCRYPTED);
|
|
43
|
+
function runtimeOriginFromEnv(env) {
|
|
44
|
+
const port = env.PORT?.trim() || DEFAULT_APP_PORT;
|
|
45
|
+
return `http://127.0.0.1:${port}`;
|
|
46
|
+
}
|
|
47
|
+
function normalizeOrigin(value, fieldLabel) {
|
|
48
|
+
let url;
|
|
49
|
+
try {
|
|
50
|
+
url = new URL(value);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
throw new Error(`${fieldLabel} must be a full URL.`);
|
|
54
|
+
}
|
|
55
|
+
if (url.protocol !== "http:") {
|
|
56
|
+
throw new Error(`${fieldLabel} must use http in local Forge mode.`);
|
|
57
|
+
}
|
|
58
|
+
if (url.pathname !== "/" && url.pathname !== "") {
|
|
59
|
+
throw new Error(`${fieldLabel} must not include a path.`);
|
|
60
|
+
}
|
|
61
|
+
return url.origin;
|
|
62
|
+
}
|
|
63
|
+
function isLoopbackHostname(hostname) {
|
|
64
|
+
return hostname === "127.0.0.1" || hostname === "localhost";
|
|
65
|
+
}
|
|
66
|
+
function normalizeLoopbackOrigin(value, fieldLabel) {
|
|
67
|
+
const origin = normalizeOrigin(value, fieldLabel);
|
|
68
|
+
const url = new URL(origin);
|
|
69
|
+
if (!isLoopbackHostname(url.hostname)) {
|
|
70
|
+
throw new Error(`${fieldLabel} must use localhost or 127.0.0.1 in local Forge mode.`);
|
|
71
|
+
}
|
|
72
|
+
return origin;
|
|
73
|
+
}
|
|
74
|
+
function normalizeRedirectUri(value, appBaseUrl) {
|
|
75
|
+
let url;
|
|
76
|
+
try {
|
|
77
|
+
url = new URL(value);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
throw new Error("GOOGLE_REDIRECT_URI must be a full URL.");
|
|
81
|
+
}
|
|
82
|
+
if (url.protocol !== "http:") {
|
|
83
|
+
throw new Error("GOOGLE_REDIRECT_URI must use http in local Forge mode.");
|
|
84
|
+
}
|
|
85
|
+
if (!isLoopbackHostname(url.hostname)) {
|
|
86
|
+
throw new Error("GOOGLE_REDIRECT_URI must use localhost or 127.0.0.1 in local Forge mode.");
|
|
87
|
+
}
|
|
88
|
+
if (url.pathname !== GOOGLE_CALLBACK_PATH) {
|
|
89
|
+
throw new Error(`GOOGLE_REDIRECT_URI must end with ${GOOGLE_CALLBACK_PATH}.`);
|
|
90
|
+
}
|
|
91
|
+
if (url.origin !== appBaseUrl) {
|
|
92
|
+
throw new Error(`GOOGLE_REDIRECT_URI must use the same origin as APP_BASE_URL (${appBaseUrl}).`);
|
|
93
|
+
}
|
|
94
|
+
return url.toString();
|
|
95
|
+
}
|
|
96
|
+
function normalizeAllowedOrigins(value, appBaseUrl) {
|
|
97
|
+
const rawValues = value && value.trim().length > 0
|
|
98
|
+
? value
|
|
99
|
+
.split(",")
|
|
100
|
+
.map((entry) => entry.trim())
|
|
101
|
+
.filter((entry) => entry.length > 0)
|
|
102
|
+
: [appBaseUrl, ...DEFAULT_DEV_WEB_ORIGINS];
|
|
103
|
+
const normalized = rawValues.map((entry) => normalizeLoopbackOrigin(entry, "GOOGLE_ALLOWED_ORIGINS"));
|
|
104
|
+
return Array.from(new Set(normalized));
|
|
105
|
+
}
|
|
106
|
+
export function getGoogleCalendarOauthCallbackPath() {
|
|
107
|
+
return GOOGLE_CALLBACK_PATH;
|
|
108
|
+
}
|
|
109
|
+
export function resolveGoogleCalendarOauthPublicConfig(env = process.env, overrides) {
|
|
110
|
+
const runtimeOrigin = runtimeOriginFromEnv(env);
|
|
111
|
+
const appBaseUrl = normalizeLoopbackOrigin(env.APP_BASE_URL?.trim() ||
|
|
112
|
+
env.APP_URL?.trim() ||
|
|
113
|
+
DEFAULT_APP_BASE_URL, env.APP_BASE_URL?.trim() ? "APP_BASE_URL" : "APP_URL");
|
|
114
|
+
const redirectUri = normalizeRedirectUri(env.GOOGLE_REDIRECT_URI?.trim() || `${appBaseUrl}${GOOGLE_CALLBACK_PATH}`, appBaseUrl);
|
|
115
|
+
const allowedOrigins = normalizeAllowedOrigins(env.GOOGLE_ALLOWED_ORIGINS, appBaseUrl);
|
|
116
|
+
const storedClientId = overrides?.clientId?.trim() || "";
|
|
117
|
+
const storedClientSecret = overrides?.clientSecret?.trim() || "";
|
|
118
|
+
const envClientId = env.GOOGLE_CLIENT_ID?.trim() || "";
|
|
119
|
+
const envClientSecret = env.GOOGLE_CLIENT_SECRET?.trim() || "";
|
|
120
|
+
const hasStoredOverride = storedClientId.length > 0 || storedClientSecret.length > 0;
|
|
121
|
+
const hasEnvOverride = envClientId.length > 0 || envClientSecret.length > 0;
|
|
122
|
+
const clientId = hasStoredOverride
|
|
123
|
+
? storedClientId
|
|
124
|
+
: hasEnvOverride
|
|
125
|
+
? envClientId
|
|
126
|
+
: PACKAGED_DEFAULT_GOOGLE_CLIENT_ID.trim();
|
|
127
|
+
const clientSecret = hasStoredOverride
|
|
128
|
+
? storedClientSecret
|
|
129
|
+
: hasEnvOverride
|
|
130
|
+
? envClientSecret
|
|
131
|
+
: PACKAGED_DEFAULT_GOOGLE_CLIENT_SECRET.trim();
|
|
132
|
+
const isConfigured = clientId.length > 0;
|
|
133
|
+
const hasIncompleteStoredOverride = storedClientId.length > 0 !== storedClientSecret.length > 0;
|
|
134
|
+
const setupMessage = hasIncompleteStoredOverride
|
|
135
|
+
? "Google OAuth override is incomplete for this Forge install. Save both the client ID and client secret together, or clear both fields to use the packaged default."
|
|
136
|
+
: isConfigured
|
|
137
|
+
? "Google Calendar sign-in is configured for local Forge. Open Forge on localhost or 127.0.0.1 on the same machine that is running Forge, because Google will redirect back to the local callback on that machine."
|
|
138
|
+
: "Google client ID is not set for this Forge install.";
|
|
139
|
+
return {
|
|
140
|
+
clientId,
|
|
141
|
+
clientSecret,
|
|
142
|
+
storedClientId,
|
|
143
|
+
storedClientSecret,
|
|
144
|
+
appBaseUrl,
|
|
145
|
+
redirectUri,
|
|
146
|
+
allowedOrigins,
|
|
147
|
+
usesPkce: true,
|
|
148
|
+
requiresServerClientSecret: false,
|
|
149
|
+
oauthClientType: "desktop_app",
|
|
150
|
+
authMode: "localhost_pkce",
|
|
151
|
+
isConfigured,
|
|
152
|
+
isReadyForPairing: isConfigured && !hasIncompleteStoredOverride,
|
|
153
|
+
isLocalOnly: true,
|
|
154
|
+
runtimeOrigin,
|
|
155
|
+
setupMessage
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
export function resolveGoogleCalendarOauthPrivateConfig(env = process.env, overrides) {
|
|
159
|
+
return resolveGoogleCalendarOauthPublicConfig(env, overrides);
|
|
160
|
+
}
|
|
161
|
+
export function isGoogleCalendarOriginAllowed(origin, allowedOrigins) {
|
|
162
|
+
try {
|
|
163
|
+
return allowedOrigins.includes(new URL(origin).origin);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
export function isGoogleCalendarLoopbackOrigin(origin) {
|
|
170
|
+
try {
|
|
171
|
+
return isLoopbackHostname(new URL(origin).hostname);
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { loginOpenAICodex } from "@mariozechner/pi-ai/oauth";
|
|
3
|
+
import { openAiCodexOauthSessionSchema } from "../types.js";
|
|
4
|
+
const sessions = new Map();
|
|
5
|
+
const SESSION_TTL_MS = 15 * 60 * 1000;
|
|
6
|
+
function createDeferred() {
|
|
7
|
+
let resolve;
|
|
8
|
+
let reject;
|
|
9
|
+
const promise = new Promise((nextResolve, nextReject) => {
|
|
10
|
+
resolve = nextResolve;
|
|
11
|
+
reject = nextReject;
|
|
12
|
+
});
|
|
13
|
+
return { promise, resolve, reject };
|
|
14
|
+
}
|
|
15
|
+
function updateSession(sessionId, patch) {
|
|
16
|
+
const existing = sessions.get(sessionId);
|
|
17
|
+
if (!existing) {
|
|
18
|
+
throw new Error(`Unknown OpenAI Codex OAuth session ${sessionId}`);
|
|
19
|
+
}
|
|
20
|
+
existing.publicSession = openAiCodexOauthSessionSchema.parse({
|
|
21
|
+
...existing.publicSession,
|
|
22
|
+
...patch
|
|
23
|
+
});
|
|
24
|
+
return existing.publicSession;
|
|
25
|
+
}
|
|
26
|
+
function requireRecord(sessionId) {
|
|
27
|
+
const record = sessions.get(sessionId);
|
|
28
|
+
if (!record) {
|
|
29
|
+
throw new Error(`Unknown OpenAI Codex OAuth session ${sessionId}`);
|
|
30
|
+
}
|
|
31
|
+
const expiresAt = new Date(record.publicSession.expiresAt).getTime();
|
|
32
|
+
if (Date.now() >= expiresAt && record.publicSession.status !== "authorized") {
|
|
33
|
+
record.publicSession = openAiCodexOauthSessionSchema.parse({
|
|
34
|
+
...record.publicSession,
|
|
35
|
+
status: "expired"
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return record;
|
|
39
|
+
}
|
|
40
|
+
function parseError(error) {
|
|
41
|
+
if (error instanceof Error) {
|
|
42
|
+
return error.message;
|
|
43
|
+
}
|
|
44
|
+
return String(error);
|
|
45
|
+
}
|
|
46
|
+
export async function startOpenAiCodexOauthSession(options = {}) {
|
|
47
|
+
const id = `ocx_${randomUUID().replaceAll("-", "").slice(0, 8)}`;
|
|
48
|
+
const now = new Date();
|
|
49
|
+
const record = {
|
|
50
|
+
publicSession: openAiCodexOauthSessionSchema.parse({
|
|
51
|
+
id,
|
|
52
|
+
status: "starting",
|
|
53
|
+
authUrl: null,
|
|
54
|
+
accountLabel: null,
|
|
55
|
+
error: null,
|
|
56
|
+
createdAt: now.toISOString(),
|
|
57
|
+
expiresAt: new Date(now.getTime() + SESSION_TTL_MS).toISOString(),
|
|
58
|
+
credentialExpiresAt: null
|
|
59
|
+
}),
|
|
60
|
+
manualInput: createDeferred(),
|
|
61
|
+
authReady: createDeferred(),
|
|
62
|
+
credentials: null
|
|
63
|
+
};
|
|
64
|
+
sessions.set(id, record);
|
|
65
|
+
const login = options.login ?? loginOpenAICodex;
|
|
66
|
+
void login({
|
|
67
|
+
onAuth: ({ url }) => {
|
|
68
|
+
updateSession(id, {
|
|
69
|
+
status: "awaiting_browser",
|
|
70
|
+
authUrl: url,
|
|
71
|
+
error: null
|
|
72
|
+
});
|
|
73
|
+
record.authReady.resolve();
|
|
74
|
+
},
|
|
75
|
+
onPrompt: async () => {
|
|
76
|
+
updateSession(id, {
|
|
77
|
+
status: "awaiting_manual_input"
|
|
78
|
+
});
|
|
79
|
+
record.authReady.resolve();
|
|
80
|
+
return await record.manualInput.promise;
|
|
81
|
+
},
|
|
82
|
+
onManualCodeInput: async () => {
|
|
83
|
+
updateSession(id, {
|
|
84
|
+
status: "awaiting_manual_input"
|
|
85
|
+
});
|
|
86
|
+
record.authReady.resolve();
|
|
87
|
+
return await record.manualInput.promise;
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
.then((credentials) => {
|
|
91
|
+
record.credentials = credentials;
|
|
92
|
+
updateSession(id, {
|
|
93
|
+
status: "authorized",
|
|
94
|
+
accountLabel: typeof credentials.accountId === "string"
|
|
95
|
+
? credentials.accountId
|
|
96
|
+
: null,
|
|
97
|
+
credentialExpiresAt: typeof credentials.expires === "number"
|
|
98
|
+
? new Date(credentials.expires).toISOString()
|
|
99
|
+
: null
|
|
100
|
+
});
|
|
101
|
+
})
|
|
102
|
+
.catch((error) => {
|
|
103
|
+
updateSession(id, {
|
|
104
|
+
status: "error",
|
|
105
|
+
error: parseError(error)
|
|
106
|
+
});
|
|
107
|
+
try {
|
|
108
|
+
record.authReady.resolve();
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
await Promise.race([
|
|
115
|
+
record.authReady.promise,
|
|
116
|
+
new Promise((resolve) => setTimeout(resolve, 250))
|
|
117
|
+
]);
|
|
118
|
+
return record.publicSession;
|
|
119
|
+
}
|
|
120
|
+
export function getOpenAiCodexOauthSession(sessionId) {
|
|
121
|
+
return requireRecord(sessionId).publicSession;
|
|
122
|
+
}
|
|
123
|
+
export function submitOpenAiCodexOauthManualInput(sessionId, codeOrUrl) {
|
|
124
|
+
const record = requireRecord(sessionId);
|
|
125
|
+
if (record.publicSession.status !== "awaiting_browser" &&
|
|
126
|
+
record.publicSession.status !== "awaiting_manual_input" &&
|
|
127
|
+
record.publicSession.status !== "starting") {
|
|
128
|
+
throw new Error("OpenAI Codex OAuth session is not waiting for input.");
|
|
129
|
+
}
|
|
130
|
+
record.manualInput.resolve(codeOrUrl);
|
|
131
|
+
return updateSession(sessionId, {
|
|
132
|
+
status: "awaiting_manual_input"
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
export function consumeOpenAiCodexOauthCredentials(sessionId) {
|
|
136
|
+
const record = requireRecord(sessionId);
|
|
137
|
+
if (record.publicSession.status !== "authorized" || !record.credentials) {
|
|
138
|
+
throw new Error("OpenAI Codex OAuth session is not authorized yet.");
|
|
139
|
+
}
|
|
140
|
+
updateSession(sessionId, {
|
|
141
|
+
status: "consumed"
|
|
142
|
+
});
|
|
143
|
+
return {
|
|
144
|
+
kind: "oauth",
|
|
145
|
+
provider: "openai-codex",
|
|
146
|
+
access: String(record.credentials.access),
|
|
147
|
+
refresh: String(record.credentials.refresh),
|
|
148
|
+
expires: Number(record.credentials.expires),
|
|
149
|
+
accountId: typeof record.credentials.accountId === "string"
|
|
150
|
+
? record.credentials.accountId
|
|
151
|
+
: ""
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { psycheObservationCalendarPayloadSchema } from "../psyche-types.js";
|
|
2
|
+
import { listNotesByObservedAtRange, resolveNoteObservedAt } from "../repositories/notes.js";
|
|
3
|
+
import { filterOwnedEntities } from "../repositories/entity-ownership.js";
|
|
4
|
+
import { listBehaviorPatterns, listTriggerReports } from "../repositories/psyche.js";
|
|
5
|
+
function collectAvailableTags(observations) {
|
|
6
|
+
const seen = new Set();
|
|
7
|
+
const tags = [];
|
|
8
|
+
for (const observation of observations) {
|
|
9
|
+
for (const tag of observation.note.tags ?? []) {
|
|
10
|
+
const normalized = tag.trim().toLowerCase();
|
|
11
|
+
if (!normalized || seen.has(normalized)) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
seen.add(normalized);
|
|
15
|
+
tags.push(tag);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return tags.sort((left, right) => left.localeCompare(right));
|
|
19
|
+
}
|
|
20
|
+
export function getPsycheObservationCalendar({ from, to, userIds }) {
|
|
21
|
+
const patterns = filterOwnedEntities("behavior_pattern", listBehaviorPatterns(), userIds);
|
|
22
|
+
const reports = filterOwnedEntities("trigger_report", listTriggerReports(200), userIds);
|
|
23
|
+
const notes = listNotesByObservedAtRange({ from, to, userIds, limit: 600 });
|
|
24
|
+
const patternsById = new Map(patterns.map((pattern) => [pattern.id, pattern]));
|
|
25
|
+
const reportsById = new Map(reports.map((report) => [report.id, report]));
|
|
26
|
+
const observations = notes.map((note) => ({
|
|
27
|
+
id: note.id,
|
|
28
|
+
observedAt: resolveNoteObservedAt(note),
|
|
29
|
+
note,
|
|
30
|
+
linkedPatterns: note.links
|
|
31
|
+
.filter((link) => link.entityType === "behavior_pattern")
|
|
32
|
+
.map((link) => patternsById.get(link.entityId) ?? null)
|
|
33
|
+
.filter((pattern) => pattern !== null),
|
|
34
|
+
linkedReports: note.links
|
|
35
|
+
.filter((link) => link.entityType === "trigger_report")
|
|
36
|
+
.map((link) => reportsById.get(link.entityId) ?? null)
|
|
37
|
+
.filter((report) => report !== null)
|
|
38
|
+
}));
|
|
39
|
+
return psycheObservationCalendarPayloadSchema.parse({
|
|
40
|
+
generatedAt: new Date().toISOString(),
|
|
41
|
+
from,
|
|
42
|
+
to,
|
|
43
|
+
observations,
|
|
44
|
+
availableTags: collectAvailableTags(observations)
|
|
45
|
+
});
|
|
46
|
+
}
|