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
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { getDatabase } from "../db.js";
|
|
2
|
+
import { getFitnessViewData, getSleepViewData } from "../health.js";
|
|
3
|
+
import { listMovementPlaces } from "../movement.js";
|
|
4
|
+
import { createNote, listNotes } from "../repositories/notes.js";
|
|
5
|
+
import { updateTask } from "../repositories/tasks.js";
|
|
6
|
+
import { searchEntities } from "../services/entity-crud.js";
|
|
7
|
+
import { executeCommonWorkbenchTool, mapWorkbenchTools } from "../../../src/lib/workbench/runtime.js";
|
|
8
|
+
import { getWorkbenchNodeCatalog, getWorkbenchNodeDefinition } from "../../../src/lib/workbench/registry.js";
|
|
9
|
+
function createSnapshotForConnectorOutput(boxId) {
|
|
10
|
+
const outputId = boxId.replace(/^connector-output:/, "");
|
|
11
|
+
const rows = getDatabase()
|
|
12
|
+
.prepare(`SELECT id, title, published_outputs_json, last_run_json FROM ai_connectors ORDER BY updated_at DESC`)
|
|
13
|
+
.all();
|
|
14
|
+
for (const row of rows) {
|
|
15
|
+
const outputs = JSON.parse(row.published_outputs_json || "[]");
|
|
16
|
+
const output = outputs.find((entry) => entry.id === outputId);
|
|
17
|
+
if (!output) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const lastRun = row.last_run_json
|
|
21
|
+
? JSON.parse(row.last_run_json)
|
|
22
|
+
: null;
|
|
23
|
+
const published = lastRun?.result?.outputs?.[outputId] ?? null;
|
|
24
|
+
return {
|
|
25
|
+
boxId,
|
|
26
|
+
label: output.label,
|
|
27
|
+
capturedAt: new Date().toISOString(),
|
|
28
|
+
contentText: published?.text ?? `${row.title}\nNo published output has been generated yet.`,
|
|
29
|
+
contentJson: published?.json ?? null,
|
|
30
|
+
tools: []
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function createRuntimeContext(input) {
|
|
36
|
+
const services = {
|
|
37
|
+
entities: {
|
|
38
|
+
search: searchEntities
|
|
39
|
+
},
|
|
40
|
+
notes: {
|
|
41
|
+
create: createNote,
|
|
42
|
+
list: listNotes
|
|
43
|
+
},
|
|
44
|
+
movement: {
|
|
45
|
+
listPlaces: listMovementPlaces
|
|
46
|
+
},
|
|
47
|
+
health: {
|
|
48
|
+
getSleepViewData: getSleepViewData,
|
|
49
|
+
getFitnessViewData: getFitnessViewData
|
|
50
|
+
},
|
|
51
|
+
tasks: {
|
|
52
|
+
update: ((taskId, patch) => updateTask(taskId, patch, { source: "agent", actor: "Workbench" }))
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
return {
|
|
56
|
+
actor: input?.actor ?? { userIds: null, source: "api" },
|
|
57
|
+
services,
|
|
58
|
+
routeParams: input?.routeParams,
|
|
59
|
+
filters: input?.filters,
|
|
60
|
+
now: new Date().toISOString()
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function toCatalogEntry(definition) {
|
|
64
|
+
if (!definition) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const toPortDefinition = (port) => ({
|
|
68
|
+
key: port.key,
|
|
69
|
+
label: port.label,
|
|
70
|
+
kind: port.kind,
|
|
71
|
+
required: port.required ?? false,
|
|
72
|
+
expandableKeys: "expandableKeys" in port ? (port.expandableKeys ?? []) : []
|
|
73
|
+
});
|
|
74
|
+
return {
|
|
75
|
+
id: definition.id,
|
|
76
|
+
boxId: definition.id,
|
|
77
|
+
surfaceId: definition.surfaceId,
|
|
78
|
+
routePath: definition.routePath,
|
|
79
|
+
title: definition.title,
|
|
80
|
+
label: definition.title,
|
|
81
|
+
icon: definition.icon ?? null,
|
|
82
|
+
description: definition.description,
|
|
83
|
+
category: definition.category,
|
|
84
|
+
tags: definition.tags,
|
|
85
|
+
capabilityModes: [
|
|
86
|
+
"content",
|
|
87
|
+
...(definition.tools.length > 0 ? ["tool"] : [])
|
|
88
|
+
],
|
|
89
|
+
inputs: definition.inputs.map(toPortDefinition),
|
|
90
|
+
params: definition.params.map(toPortDefinition),
|
|
91
|
+
output: definition.output.map(toPortDefinition),
|
|
92
|
+
tools: definition.tools,
|
|
93
|
+
outputs: definition.output.map(toPortDefinition),
|
|
94
|
+
toolAdapters: definition.tools,
|
|
95
|
+
snapshotResolverKey: undefined
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export function listForgeBoxCatalog() {
|
|
99
|
+
return getWorkbenchNodeCatalog();
|
|
100
|
+
}
|
|
101
|
+
export function getForgeBoxCatalogEntry(boxId) {
|
|
102
|
+
return toCatalogEntry(getWorkbenchNodeDefinition(boxId));
|
|
103
|
+
}
|
|
104
|
+
export function buildConnectorOutputCatalogEntry(input) {
|
|
105
|
+
return {
|
|
106
|
+
id: `connector-output:${input.outputId}`,
|
|
107
|
+
boxId: `connector-output:${input.outputId}`,
|
|
108
|
+
surfaceId: null,
|
|
109
|
+
routePath: `/workbench/${input.connectorId}`,
|
|
110
|
+
title: `${input.title} output`,
|
|
111
|
+
label: `${input.title} output`,
|
|
112
|
+
icon: null,
|
|
113
|
+
description: "Published Workbench output.",
|
|
114
|
+
category: "Workbench outputs",
|
|
115
|
+
tags: ["workbench", "output"],
|
|
116
|
+
capabilityModes: ["content"],
|
|
117
|
+
inputs: [],
|
|
118
|
+
params: [],
|
|
119
|
+
output: [
|
|
120
|
+
{
|
|
121
|
+
key: "primary",
|
|
122
|
+
label: "Published output",
|
|
123
|
+
kind: "content",
|
|
124
|
+
required: false,
|
|
125
|
+
expandableKeys: []
|
|
126
|
+
}
|
|
127
|
+
],
|
|
128
|
+
tools: [],
|
|
129
|
+
outputs: [
|
|
130
|
+
{
|
|
131
|
+
key: "primary",
|
|
132
|
+
label: "Published output",
|
|
133
|
+
kind: "content",
|
|
134
|
+
required: false,
|
|
135
|
+
expandableKeys: []
|
|
136
|
+
}
|
|
137
|
+
],
|
|
138
|
+
toolAdapters: []
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
export function resolveForgeBoxSnapshot(boxId, contextInput, executionInput) {
|
|
142
|
+
if (boxId.startsWith("connector-output:")) {
|
|
143
|
+
return (createSnapshotForConnectorOutput(boxId) ?? {
|
|
144
|
+
boxId,
|
|
145
|
+
label: boxId,
|
|
146
|
+
capturedAt: new Date().toISOString(),
|
|
147
|
+
contentText: "This connector output has not been generated yet.",
|
|
148
|
+
contentJson: null,
|
|
149
|
+
tools: []
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
const definition = getWorkbenchNodeDefinition(boxId);
|
|
153
|
+
if (!definition) {
|
|
154
|
+
return {
|
|
155
|
+
boxId,
|
|
156
|
+
label: boxId,
|
|
157
|
+
capturedAt: new Date().toISOString(),
|
|
158
|
+
contentText: "This Workbench node is not registered.",
|
|
159
|
+
contentJson: null,
|
|
160
|
+
tools: []
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const execution = definition.execute({
|
|
164
|
+
nodeId: boxId,
|
|
165
|
+
definition,
|
|
166
|
+
inputs: executionInput?.inputs ?? {},
|
|
167
|
+
params: executionInput?.params ?? {},
|
|
168
|
+
context: createRuntimeContext(contextInput)
|
|
169
|
+
});
|
|
170
|
+
if (execution instanceof Promise) {
|
|
171
|
+
throw new Error("Workbench box execution must be synchronous for snapshot resolution.");
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
boxId: definition.id,
|
|
175
|
+
label: definition.title,
|
|
176
|
+
capturedAt: new Date().toISOString(),
|
|
177
|
+
contentText: execution.primaryText,
|
|
178
|
+
contentJson: execution.payload,
|
|
179
|
+
tools: mapWorkbenchTools(definition.tools)
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
export function executeForgeBoxTool(boxId, toolKey, args, contextInput) {
|
|
183
|
+
const definition = getWorkbenchNodeDefinition(boxId);
|
|
184
|
+
if (!definition) {
|
|
185
|
+
throw new Error(`Unknown Forge box: ${boxId}`);
|
|
186
|
+
}
|
|
187
|
+
return executeCommonWorkbenchTool(createRuntimeContext(contextInput), definition, toolKey, args);
|
|
188
|
+
}
|
|
@@ -3,6 +3,8 @@ import { mkdir, readdir, readFile } from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { DatabaseSync } from "node:sqlite";
|
|
6
|
+
import { logForgeDebug } from "./debug.js";
|
|
7
|
+
import { ensureQuestionnaireSeeds } from "./repositories/questionnaires.js";
|
|
6
8
|
function nowIso() {
|
|
7
9
|
return new Date().toISOString();
|
|
8
10
|
}
|
|
@@ -288,6 +290,7 @@ export async function initializeDatabase() {
|
|
|
288
290
|
await mkdir(getDataDir(), { recursive: true });
|
|
289
291
|
const database = getDatabase();
|
|
290
292
|
const migrationFiles = await listMigrationFiles();
|
|
293
|
+
const pendingMigrations = [];
|
|
291
294
|
database.exec(`
|
|
292
295
|
CREATE TABLE IF NOT EXISTS migrations (
|
|
293
296
|
id TEXT PRIMARY KEY,
|
|
@@ -302,6 +305,7 @@ export async function initializeDatabase() {
|
|
|
302
305
|
if (applied.has(file)) {
|
|
303
306
|
continue;
|
|
304
307
|
}
|
|
308
|
+
pendingMigrations.push(file);
|
|
305
309
|
const sql = await readFile(path.join(migrationsDir, file), "utf8");
|
|
306
310
|
database.exec("BEGIN");
|
|
307
311
|
try {
|
|
@@ -316,9 +320,11 @@ export async function initializeDatabase() {
|
|
|
316
320
|
throw error;
|
|
317
321
|
}
|
|
318
322
|
}
|
|
323
|
+
logForgeDebug(`[forge-db] initialized database path=${getDatabasePath()} applied_count=${appliedRows.length} pending_applied=${pendingMigrations.length} pending_list=${pendingMigrations.join(",") || "none"}`);
|
|
319
324
|
if (seedDemoDataEnabled) {
|
|
320
325
|
seedData();
|
|
321
326
|
}
|
|
327
|
+
ensureQuestionnaireSeeds();
|
|
322
328
|
}
|
|
323
329
|
export function configureDatabaseSeeding(enabled) {
|
|
324
330
|
seedDemoDataEnabled = enabled;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
function isTruthyFlag(value) {
|
|
2
|
+
if (!value) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
const normalized = value.trim().toLowerCase();
|
|
6
|
+
return (normalized === "1" ||
|
|
7
|
+
normalized === "true" ||
|
|
8
|
+
normalized === "yes" ||
|
|
9
|
+
normalized === "on");
|
|
10
|
+
}
|
|
11
|
+
export function isForgeDebugLoggingEnabled(env = process.env) {
|
|
12
|
+
return isTruthyFlag(env.FORGE_DEBUG_LOGS);
|
|
13
|
+
}
|
|
14
|
+
export function logForgeDebug(message, env = process.env) {
|
|
15
|
+
if (!isForgeDebugLoggingEnabled(env)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
console.info(message);
|
|
19
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
import { Bonjour } from "bonjour-service";
|
|
5
|
+
const execFileAsync = promisify(execFile);
|
|
6
|
+
export async function startForgeDiscoveryAdvertiser(options) {
|
|
7
|
+
if (options.enabled === false || process.env.FORGE_DISABLE_DISCOVERY_ADVERTISEMENT === "1") {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const basePath = normalizeBasePath(options.basePath);
|
|
11
|
+
const tailscaleTargets = await resolveTailscaleTargets({
|
|
12
|
+
apiBaseUrl: options.tailscaleApiBaseUrl,
|
|
13
|
+
uiBaseUrl: options.tailscaleUiBaseUrl,
|
|
14
|
+
basePath
|
|
15
|
+
});
|
|
16
|
+
const bonjour = new Bonjour();
|
|
17
|
+
const service = bonjour.publish({
|
|
18
|
+
name: buildServiceName(),
|
|
19
|
+
type: "forge",
|
|
20
|
+
protocol: "tcp",
|
|
21
|
+
port: options.port,
|
|
22
|
+
txt: {
|
|
23
|
+
apiPath: "/api/v1",
|
|
24
|
+
uiPath: basePath,
|
|
25
|
+
tsApiBaseUrl: tailscaleTargets.apiBaseUrl ?? "",
|
|
26
|
+
tsUiBaseUrl: tailscaleTargets.uiBaseUrl ?? "",
|
|
27
|
+
tsDnsName: tailscaleTargets.dnsName ?? "",
|
|
28
|
+
watchReady: "1"
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
if (typeof service.start === "function") {
|
|
32
|
+
service.start();
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
stop: () => {
|
|
36
|
+
if (typeof service.stop === "function") {
|
|
37
|
+
service.stop(() => {
|
|
38
|
+
bonjour.destroy();
|
|
39
|
+
});
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
bonjour.destroy();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function buildServiceName() {
|
|
47
|
+
const hostname = os.hostname().trim();
|
|
48
|
+
return hostname ? `Forge on ${hostname}` : "Forge";
|
|
49
|
+
}
|
|
50
|
+
function normalizeBasePath(value) {
|
|
51
|
+
if (!value || value === "/") {
|
|
52
|
+
return "/";
|
|
53
|
+
}
|
|
54
|
+
const withLeadingSlash = value.startsWith("/") ? value : `/${value}`;
|
|
55
|
+
return withLeadingSlash.endsWith("/") ? withLeadingSlash : `${withLeadingSlash}/`;
|
|
56
|
+
}
|
|
57
|
+
async function resolveTailscaleTargets(input) {
|
|
58
|
+
const explicitApi = normalizeHttpsUrl(input.apiBaseUrl);
|
|
59
|
+
const explicitUi = normalizeHttpsUrl(input.uiBaseUrl);
|
|
60
|
+
if (explicitApi || explicitUi) {
|
|
61
|
+
return {
|
|
62
|
+
apiBaseUrl: explicitApi,
|
|
63
|
+
uiBaseUrl: explicitUi,
|
|
64
|
+
dnsName: readDnsNameFromUrl(explicitApi ?? explicitUi)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const dnsName = await readTailscaleDnsName();
|
|
68
|
+
if (!dnsName) {
|
|
69
|
+
return { apiBaseUrl: null, uiBaseUrl: null, dnsName: null };
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
apiBaseUrl: `https://${dnsName}/api/v1`,
|
|
73
|
+
uiBaseUrl: `https://${dnsName}${input.basePath}`,
|
|
74
|
+
dnsName
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function normalizeHttpsUrl(value) {
|
|
78
|
+
const trimmed = value?.trim();
|
|
79
|
+
if (!trimmed) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const url = new URL(trimmed);
|
|
84
|
+
return url.protocol === "https:" ? url.toString().replace(/\/$/, "") : null;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function readDnsNameFromUrl(value) {
|
|
91
|
+
if (!value) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
return new URL(value).hostname;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function readTailscaleDnsName() {
|
|
102
|
+
try {
|
|
103
|
+
const { stdout } = await execFileAsync("tailscale", ["status", "--json"], {
|
|
104
|
+
timeout: 1_500,
|
|
105
|
+
env: process.env
|
|
106
|
+
});
|
|
107
|
+
const parsed = JSON.parse(stdout);
|
|
108
|
+
const dnsName = parsed.Self?.DNSName?.trim().replace(/\.$/, "");
|
|
109
|
+
return dnsName || null;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { getDatabase, runInTransaction } from "./db.js";
|
|
4
4
|
import { HttpError } from "./errors.js";
|
|
5
|
+
import { getMovementMobileBootstrap, ingestMovementSync, movementSyncPayloadSchema } from "./movement.js";
|
|
5
6
|
import { recordActivityEvent } from "./repositories/activity-events.js";
|
|
6
7
|
import { recordHabitGeneratedWorkoutReward } from "./repositories/rewards.js";
|
|
7
8
|
const healthLinkSchema = z.object({
|
|
@@ -119,7 +120,8 @@ export const mobileHealthSyncSchema = z.object({
|
|
|
119
120
|
links: z.array(healthLinkSchema).default([]),
|
|
120
121
|
annotations: workoutAnnotationSchema.partial().default({})
|
|
121
122
|
}))
|
|
122
|
-
.default([])
|
|
123
|
+
.default([]),
|
|
124
|
+
movement: movementSyncPayloadSchema.default({})
|
|
123
125
|
});
|
|
124
126
|
export const verifyCompanionPairingSchema = z.object({
|
|
125
127
|
sessionId: z.string().trim().min(1),
|
|
@@ -606,7 +608,7 @@ export function verifyCompanionPairing(payload) {
|
|
|
606
608
|
.get(pairing.id))
|
|
607
609
|
};
|
|
608
610
|
}
|
|
609
|
-
function requireValidPairing(sessionId, pairingToken) {
|
|
611
|
+
export function requireValidPairing(sessionId, pairingToken) {
|
|
610
612
|
const row = getDatabase()
|
|
611
613
|
.prepare(`SELECT * FROM companion_pairing_sessions WHERE id = ?`)
|
|
612
614
|
.get(sessionId);
|
|
@@ -838,6 +840,7 @@ export function ingestMobileHealthSync(payload) {
|
|
|
838
840
|
let createdCount = 0;
|
|
839
841
|
let updatedCount = 0;
|
|
840
842
|
let mergedCount = 0;
|
|
843
|
+
const movementSync = ingestMovementSync(pairing, parsed.movement);
|
|
841
844
|
for (const sleep of parsed.sleepSessions) {
|
|
842
845
|
const result = insertOrUpdateSleepSession(pairing, sleep);
|
|
843
846
|
if (result.mode === "created") {
|
|
@@ -879,8 +882,16 @@ export function ingestMobileHealthSync(payload) {
|
|
|
879
882
|
.run(runId, pairing.id, pairing.user_id, parsed.device.sourceDevice, JSON.stringify({
|
|
880
883
|
permissions: parsed.permissions,
|
|
881
884
|
sleepSessions: parsed.sleepSessions.length,
|
|
882
|
-
workouts: parsed.workouts.length
|
|
883
|
-
|
|
885
|
+
workouts: parsed.workouts.length,
|
|
886
|
+
movement: {
|
|
887
|
+
knownPlaces: parsed.movement.knownPlaces.length,
|
|
888
|
+
stays: parsed.movement.stays.length,
|
|
889
|
+
trips: parsed.movement.trips.length
|
|
890
|
+
}
|
|
891
|
+
}), parsed.sleepSessions.length +
|
|
892
|
+
parsed.workouts.length +
|
|
893
|
+
parsed.movement.stays.length +
|
|
894
|
+
parsed.movement.trips.length, createdCount + movementSync.createdCount, updatedCount + movementSync.updatedCount, mergedCount, now, now, now);
|
|
884
895
|
recordActivityEvent({
|
|
885
896
|
entityType: "system",
|
|
886
897
|
entityId: pairing.id,
|
|
@@ -892,8 +903,10 @@ export function ingestMobileHealthSync(payload) {
|
|
|
892
903
|
metadata: {
|
|
893
904
|
sleepSessions: parsed.sleepSessions.length,
|
|
894
905
|
workouts: parsed.workouts.length,
|
|
895
|
-
|
|
896
|
-
|
|
906
|
+
movementStays: parsed.movement.stays.length,
|
|
907
|
+
movementTrips: parsed.movement.trips.length,
|
|
908
|
+
createdCount: createdCount + movementSync.createdCount,
|
|
909
|
+
updatedCount: updatedCount + movementSync.updatedCount,
|
|
897
910
|
mergedCount
|
|
898
911
|
}
|
|
899
912
|
});
|
|
@@ -904,10 +917,14 @@ export function ingestMobileHealthSync(payload) {
|
|
|
904
917
|
imported: {
|
|
905
918
|
sleepSessions: parsed.sleepSessions.length,
|
|
906
919
|
workouts: parsed.workouts.length,
|
|
907
|
-
createdCount,
|
|
908
|
-
updatedCount,
|
|
909
|
-
mergedCount
|
|
910
|
-
|
|
920
|
+
createdCount: createdCount + movementSync.createdCount,
|
|
921
|
+
updatedCount: updatedCount + movementSync.updatedCount,
|
|
922
|
+
mergedCount,
|
|
923
|
+
movementStays: parsed.movement.stays.length,
|
|
924
|
+
movementTrips: parsed.movement.trips.length,
|
|
925
|
+
movementKnownPlaces: parsed.movement.knownPlaces.length
|
|
926
|
+
},
|
|
927
|
+
movement: getMovementMobileBootstrap(pairing)
|
|
911
928
|
};
|
|
912
929
|
});
|
|
913
930
|
}
|
|
@@ -916,6 +933,14 @@ export function getCompanionOverview(userIds) {
|
|
|
916
933
|
const importRuns = listHealthImportRunRows(userIds).map(mapHealthImportRun);
|
|
917
934
|
const sleepSessions = listSleepRows(userIds).map(mapSleepSession);
|
|
918
935
|
const workouts = listWorkoutRows(userIds).map(mapWorkoutSession);
|
|
936
|
+
const movementSummary = importRuns.reduce((totals, run) => {
|
|
937
|
+
const movement = safeJsonParse(JSON.stringify(run.payloadSummary.movement ?? {}), {}) ?? {};
|
|
938
|
+
return {
|
|
939
|
+
knownPlaces: totals.knownPlaces + (movement.knownPlaces ?? 0),
|
|
940
|
+
stays: totals.stays + (movement.stays ?? 0),
|
|
941
|
+
trips: totals.trips + (movement.trips ?? 0)
|
|
942
|
+
};
|
|
943
|
+
}, { knownPlaces: 0, stays: 0, trips: 0 });
|
|
919
944
|
const activePairings = pairings.filter((pairing) => pairing.status !== "revoked");
|
|
920
945
|
const recentPermissionStates = importRuns
|
|
921
946
|
.map((run) => safeJsonParse(JSON.stringify(run.payloadSummary), {}))
|
|
@@ -951,7 +976,10 @@ export function getCompanionOverview(userIds) {
|
|
|
951
976
|
}).length,
|
|
952
977
|
linkedWorkouts: workouts.filter((session) => session.links.length > 0).length,
|
|
953
978
|
habitGeneratedWorkouts: workouts.filter((session) => session.sourceType === "habit_generated").length,
|
|
954
|
-
reconciledWorkouts: workouts.filter((session) => session.reconciliationStatus === "merged").length
|
|
979
|
+
reconciledWorkouts: workouts.filter((session) => session.reconciliationStatus === "merged").length,
|
|
980
|
+
movementKnownPlaces: movementSummary.knownPlaces,
|
|
981
|
+
movementStays: movementSummary.stays,
|
|
982
|
+
movementTrips: movementSummary.trips
|
|
955
983
|
},
|
|
956
984
|
permissions: {
|
|
957
985
|
healthKitAuthorized: recentPermissionStates.some((state) => state.healthKitAuthorized === true),
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { buildServer } from "./app.js";
|
|
2
2
|
import { closeDatabase } from "./db.js";
|
|
3
|
+
import { startForgeDiscoveryAdvertiser } from "./discovery-advertiser.js";
|
|
3
4
|
const port = Number(process.env.PORT ?? 4317);
|
|
4
5
|
const host = process.env.HOST ?? "0.0.0.0";
|
|
6
|
+
const basePath = process.env.FORGE_BASE_PATH ?? "/forge/";
|
|
5
7
|
const app = await buildServer();
|
|
8
|
+
const discoveryAdvertiser = await startForgeDiscoveryAdvertiser({ port, basePath });
|
|
6
9
|
const close = async () => {
|
|
10
|
+
discoveryAdvertiser?.stop();
|
|
7
11
|
await app.close();
|
|
8
12
|
closeDatabase();
|
|
9
13
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AbstractManager } from "../base.js";
|
|
2
|
-
import {
|
|
2
|
+
import { refreshOpenAICodexToken } from "@mariozechner/pi-ai/oauth";
|
|
3
|
+
import { readEncryptedSecret, storeEncryptedSecret } from "../../repositories/calendar.js";
|
|
3
4
|
function emitDiagnostic(logger, input) {
|
|
4
5
|
logger?.(input);
|
|
5
6
|
}
|
|
@@ -32,7 +33,7 @@ export class LlmManager extends AbstractManager {
|
|
|
32
33
|
});
|
|
33
34
|
return null;
|
|
34
35
|
}
|
|
35
|
-
const apiKey = this.readApiKey(profile.secretId);
|
|
36
|
+
const apiKey = await this.readApiKey(profile.secretId);
|
|
36
37
|
if (!apiKey) {
|
|
37
38
|
emitDiagnostic(logger, {
|
|
38
39
|
level: "error",
|
|
@@ -72,7 +73,7 @@ export class LlmManager extends AbstractManager {
|
|
|
72
73
|
});
|
|
73
74
|
throw new Error("Unsupported LLM provider.");
|
|
74
75
|
}
|
|
75
|
-
const apiKey = explicitApiKey?.trim() || this.readApiKey(profile.secretId);
|
|
76
|
+
const apiKey = explicitApiKey?.trim() || (await this.readApiKey(profile.secretId));
|
|
76
77
|
if (!apiKey) {
|
|
77
78
|
emitDiagnostic(logger, {
|
|
78
79
|
level: "error",
|
|
@@ -107,12 +108,29 @@ export class LlmManager extends AbstractManager {
|
|
|
107
108
|
outputPreview: result.outputPreview
|
|
108
109
|
};
|
|
109
110
|
}
|
|
111
|
+
async runTextPrompt(profile, input, logger) {
|
|
112
|
+
const provider = this.resolveProvider(profile.provider);
|
|
113
|
+
if (!provider?.runText) {
|
|
114
|
+
throw new Error("This LLM provider does not support text prompt execution.");
|
|
115
|
+
}
|
|
116
|
+
const apiKey = input.explicitApiKey?.trim() || (await this.readApiKey(profile.secretId));
|
|
117
|
+
if (!apiKey) {
|
|
118
|
+
throw new Error("Missing provider credential for prompt execution.");
|
|
119
|
+
}
|
|
120
|
+
return await provider.runText({
|
|
121
|
+
apiKey,
|
|
122
|
+
profile,
|
|
123
|
+
systemPrompt: input.systemPrompt,
|
|
124
|
+
prompt: input.prompt,
|
|
125
|
+
logger
|
|
126
|
+
});
|
|
127
|
+
}
|
|
110
128
|
resolveProvider(providerName) {
|
|
111
129
|
return (this.providers.get(providerName) ??
|
|
112
130
|
this.providers.get("openai-responses") ??
|
|
113
131
|
null);
|
|
114
132
|
}
|
|
115
|
-
readApiKey(secretId) {
|
|
133
|
+
async readApiKey(secretId) {
|
|
116
134
|
if (!secretId) {
|
|
117
135
|
return null;
|
|
118
136
|
}
|
|
@@ -121,6 +139,24 @@ export class LlmManager extends AbstractManager {
|
|
|
121
139
|
return null;
|
|
122
140
|
}
|
|
123
141
|
const payload = this.secretsManager.openJson(cipherText);
|
|
142
|
+
if (payload.kind === "oauth" &&
|
|
143
|
+
payload.provider === "openai-codex" &&
|
|
144
|
+
typeof payload.refresh === "string") {
|
|
145
|
+
let access = payload.access?.trim() || null;
|
|
146
|
+
const expires = typeof payload.expires === "number" ? payload.expires : Date.now();
|
|
147
|
+
if (!access || expires <= Date.now() + 60_000) {
|
|
148
|
+
const refreshed = await refreshOpenAICodexToken(payload.refresh);
|
|
149
|
+
const nextPayload = {
|
|
150
|
+
...payload,
|
|
151
|
+
access: refreshed.access,
|
|
152
|
+
refresh: refreshed.refresh,
|
|
153
|
+
expires: refreshed.expires
|
|
154
|
+
};
|
|
155
|
+
storeEncryptedSecret(secretId, this.secretsManager.sealJson(nextPayload), "Refreshed OpenAI Codex OAuth credential");
|
|
156
|
+
access = refreshed.access;
|
|
157
|
+
}
|
|
158
|
+
return access;
|
|
159
|
+
}
|
|
124
160
|
return payload.apiKey?.trim() || null;
|
|
125
161
|
}
|
|
126
162
|
}
|