forge-openclaw-plugin 0.2.25 → 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/dist/assets/{board-VmF4FAfr.js → board-ta0rUHOf.js} +3 -3
- package/dist/assets/{board-VmF4FAfr.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-DvkU14p-.js → motion-fBKPB6yw.js} +2 -2
- package/dist/assets/{motion-DvkU14p-.js.map → motion-fBKPB6yw.js.map} +1 -1
- package/dist/assets/{table-DgiPof9E.js → table-C-IGTQni.js} +2 -2
- package/dist/assets/{table-DgiPof9E.js.map → table-C-IGTQni.js.map} +1 -1
- package/dist/assets/{ui-nYfoC0Gq.js → ui-DInOpaYF.js} +2 -2
- package/dist/assets/{ui-nYfoC0Gq.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/plugin-entry-shared.js +24 -2
- package/dist/openclaw/plugin-sdk-types.d.ts +17 -0
- package/dist/openclaw/tools.js +0 -3
- 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} +242 -111
- package/dist/server/server/src/connectors/box-registry.js +188 -0
- package/dist/server/{db.js → server/src/db.js} +4 -0
- package/dist/server/server/src/debug.js +19 -0
- package/dist/server/{openapi.js → server/src/openapi.js} +2 -2
- package/dist/server/{repositories → server/src/repositories}/ai-connectors.js +286 -23
- package/dist/server/{repositories → server/src/repositories}/calendar.js +1 -1
- package/dist/server/{repositories → server/src/repositories}/settings.js +51 -3
- package/dist/server/{services → server/src/services}/calendar-runtime.js +775 -58
- package/dist/server/server/src/services/google-calendar-oauth-config.js +176 -0
- package/dist/server/{types.js → server/src/types.js} +137 -19
- package/dist/server/{web.js → server/src/web.js} +21 -2
- 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 +1 -1
- package/server/index.js +68 -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 +3 -0
- package/skills/forge-openclaw/entity_conversation_playbooks.md +213 -24
- package/skills/forge-openclaw/psyche_entity_playbooks.md +82 -3
- package/dist/assets/index-CFCKDIMH.js +0 -67
- package/dist/assets/index-CFCKDIMH.js.map +0 -1
- package/dist/assets/index-ZPY6U1TU.css +0 -1
- package/dist/assets/vendor-D9PTEPSB.js +0 -824
- package/dist/assets/vendor-D9PTEPSB.js.map +0 -1
- package/dist/assets/viz-Cqb6s--o.js +0 -34
- package/dist/assets/viz-Cqb6s--o.js.map +0 -1
- package/dist/server/connectors/box-registry.js +0 -257
- /package/dist/server/{demo-data.js → server/src/demo-data.js} +0 -0
- /package/dist/server/{discovery-advertiser.js → server/src/discovery-advertiser.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/{health.js → server/src/health.js} +0 -0
- /package/dist/server/{index.js → server/src/index.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/llm-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/openai-responses-provider.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/{movement.js → server/src/movement.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/{psyche-types.js → server/src/psyche-types.js} +0 -0
- /package/dist/server/{questionnaire-flow.js → server/src/questionnaire-flow.js} +0 -0
- /package/dist/server/{questionnaire-seeds.js → server/src/questionnaire-seeds.js} +0 -0
- /package/dist/server/{questionnaire-types.js → server/src/questionnaire-types.js} +0 -0
- /package/dist/server/{repositories → server/src/repositories}/activity-events.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/ai-processors.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}/entity-ownership.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}/habits.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/model-settings.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/notes.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/preferences.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}/questionnaires.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}/surface-layouts.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}/wiki-memory.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}/entity-crud.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}/openai-codex-oauth.js +0 -0
- /package/dist/server/{services → server/src/services}/projects.js +0 -0
- /package/dist/server/{services → server/src/services}/psyche-observation-calendar.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
- /package/dist/server/{watch-mobile.js → server/src/watch-mobile.js} +0 -0
|
@@ -3,6 +3,7 @@ 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";
|
|
6
7
|
import { ensureQuestionnaireSeeds } from "./repositories/questionnaires.js";
|
|
7
8
|
function nowIso() {
|
|
8
9
|
return new Date().toISOString();
|
|
@@ -289,6 +290,7 @@ export async function initializeDatabase() {
|
|
|
289
290
|
await mkdir(getDataDir(), { recursive: true });
|
|
290
291
|
const database = getDatabase();
|
|
291
292
|
const migrationFiles = await listMigrationFiles();
|
|
293
|
+
const pendingMigrations = [];
|
|
292
294
|
database.exec(`
|
|
293
295
|
CREATE TABLE IF NOT EXISTS migrations (
|
|
294
296
|
id TEXT PRIMARY KEY,
|
|
@@ -303,6 +305,7 @@ export async function initializeDatabase() {
|
|
|
303
305
|
if (applied.has(file)) {
|
|
304
306
|
continue;
|
|
305
307
|
}
|
|
308
|
+
pendingMigrations.push(file);
|
|
306
309
|
const sql = await readFile(path.join(migrationsDir, file), "utf8");
|
|
307
310
|
database.exec("BEGIN");
|
|
308
311
|
try {
|
|
@@ -317,6 +320,7 @@ export async function initializeDatabase() {
|
|
|
317
320
|
throw error;
|
|
318
321
|
}
|
|
319
322
|
}
|
|
323
|
+
logForgeDebug(`[forge-db] initialized database path=${getDatabasePath()} applied_count=${appliedRows.length} pending_applied=${pendingMigrations.length} pending_list=${pendingMigrations.join(",") || "none"}`);
|
|
320
324
|
if (seedDemoDataEnabled) {
|
|
321
325
|
seedData();
|
|
322
326
|
}
|
|
@@ -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
|
+
}
|
|
@@ -1998,7 +1998,7 @@ export function buildOpenApiDocument() {
|
|
|
1998
1998
|
},
|
|
1999
1999
|
themePreference: {
|
|
2000
2000
|
type: "string",
|
|
2001
|
-
enum: ["obsidian", "solar", "aurora", "ember", "custom", "system"]
|
|
2001
|
+
enum: ["obsidian", "solar", "aurora", "ember", "paper", "dawn", "atelier", "custom", "system"]
|
|
2002
2002
|
},
|
|
2003
2003
|
customTheme: nullable({
|
|
2004
2004
|
type: "object",
|
|
@@ -2515,7 +2515,7 @@ export function buildOpenApiDocument() {
|
|
|
2515
2515
|
execution: { $ref: "#/components/schemas/ExecutionSettings" },
|
|
2516
2516
|
themePreference: {
|
|
2517
2517
|
type: "string",
|
|
2518
|
-
enum: ["obsidian", "solar", "aurora", "ember", "custom", "system"]
|
|
2518
|
+
enum: ["obsidian", "solar", "aurora", "ember", "paper", "dawn", "atelier", "custom", "system"]
|
|
2519
2519
|
},
|
|
2520
2520
|
customTheme: nullable({
|
|
2521
2521
|
type: "object",
|
|
@@ -193,17 +193,17 @@ function ensurePublishedOutputs(connectorId, graph) {
|
|
|
193
193
|
outputId: "primary"
|
|
194
194
|
})
|
|
195
195
|
].map((entry) => ({
|
|
196
|
-
id: entry.
|
|
196
|
+
id: entry.id.replace(/^connector-output:/, ""),
|
|
197
197
|
nodeId: "node_output",
|
|
198
|
-
label: entry.
|
|
199
|
-
apiPath: `/api/v1/
|
|
198
|
+
label: entry.title,
|
|
199
|
+
apiPath: `/api/v1/workbench/flows/${connectorId}/output`
|
|
200
200
|
}));
|
|
201
201
|
}
|
|
202
202
|
return outputNodes.map((node, index) => ({
|
|
203
203
|
id: `${connectorId}_out_${index + 1}`,
|
|
204
204
|
nodeId: node.id,
|
|
205
205
|
label: node.data.label || `Output ${index + 1}`,
|
|
206
|
-
apiPath: `/api/v1/
|
|
206
|
+
apiPath: `/api/v1/workbench/flows/${connectorId}/output`
|
|
207
207
|
}));
|
|
208
208
|
}
|
|
209
209
|
function mapRun(row) {
|
|
@@ -322,6 +322,75 @@ function tryParseStructuredAgentResponse(value) {
|
|
|
322
322
|
return null;
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
|
+
function tryParseJsonObject(value) {
|
|
326
|
+
try {
|
|
327
|
+
const parsed = JSON.parse(value);
|
|
328
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
329
|
+
return parsed;
|
|
330
|
+
}
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function coerceText(value) {
|
|
338
|
+
if (typeof value === "string") {
|
|
339
|
+
return value;
|
|
340
|
+
}
|
|
341
|
+
if (value == null) {
|
|
342
|
+
return "";
|
|
343
|
+
}
|
|
344
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
345
|
+
return String(value);
|
|
346
|
+
}
|
|
347
|
+
try {
|
|
348
|
+
return JSON.stringify(value);
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
return "";
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
function buildOutputMap(primaryText, primaryJson, outputKeys = []) {
|
|
355
|
+
const outputMap = {
|
|
356
|
+
primary: {
|
|
357
|
+
text: primaryText,
|
|
358
|
+
json: primaryJson
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
for (const key of outputKeys) {
|
|
362
|
+
if (!primaryJson || !(key in primaryJson)) {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
const value = primaryJson[key];
|
|
366
|
+
outputMap[key] = {
|
|
367
|
+
text: coerceText(value),
|
|
368
|
+
json: value && typeof value === "object" && !Array.isArray(value)
|
|
369
|
+
? value
|
|
370
|
+
: null
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
return outputMap;
|
|
374
|
+
}
|
|
375
|
+
function readOutputSelection(value, handle) {
|
|
376
|
+
if (!handle || handle === "primary") {
|
|
377
|
+
return { text: value.text, json: value.json };
|
|
378
|
+
}
|
|
379
|
+
const selected = value.outputMap[handle];
|
|
380
|
+
if (selected) {
|
|
381
|
+
return selected;
|
|
382
|
+
}
|
|
383
|
+
if (value.json && handle in value.json) {
|
|
384
|
+
const raw = value.json[handle];
|
|
385
|
+
return {
|
|
386
|
+
text: coerceText(raw),
|
|
387
|
+
json: raw && typeof raw === "object" && !Array.isArray(raw)
|
|
388
|
+
? raw
|
|
389
|
+
: null
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
return { text: value.text, json: value.json };
|
|
393
|
+
}
|
|
325
394
|
async function executeMachineTool(tool, args) {
|
|
326
395
|
if (tool === "machine_read_file") {
|
|
327
396
|
const targetPath = typeof args.path === "string" ? resolveAllowedPath(args.path) : null;
|
|
@@ -522,24 +591,35 @@ async function runModelNode(input) {
|
|
|
522
591
|
if (activeTools.length === 0) {
|
|
523
592
|
return {
|
|
524
593
|
text: rawText.trim(),
|
|
525
|
-
|
|
594
|
+
json: tryParseJsonObject(rawText.trim()),
|
|
595
|
+
conversationId,
|
|
596
|
+
logs: transcript
|
|
526
597
|
};
|
|
527
598
|
}
|
|
528
599
|
const structured = tryParseStructuredAgentResponse(rawText.trim());
|
|
529
600
|
if (!structured || structured.action === "final") {
|
|
530
601
|
return {
|
|
531
602
|
text: structured?.text?.trim() || rawText.trim(),
|
|
532
|
-
|
|
603
|
+
json: tryParseJsonObject(structured?.text?.trim() || rawText.trim()),
|
|
604
|
+
conversationId,
|
|
605
|
+
logs: transcript
|
|
533
606
|
};
|
|
534
607
|
}
|
|
535
608
|
const toolResult = structured.tool.startsWith("machine_")
|
|
536
609
|
? await executeMachineTool(structured.tool, structured.args)
|
|
537
|
-
: await executeForgeBoxTool(activeTools.find((tool) => tool.key === structured.tool)?.boxId ?? "", structured.tool, structured.args
|
|
610
|
+
: await executeForgeBoxTool(activeTools.find((tool) => tool.key === structured.tool)?.boxId ?? "", structured.tool, structured.args, {
|
|
611
|
+
actor: {
|
|
612
|
+
userIds: null,
|
|
613
|
+
source: "agent"
|
|
614
|
+
}
|
|
615
|
+
});
|
|
538
616
|
transcript.push(`Tool call ${structured.tool}: ${JSON.stringify(structured.args)}`, `Tool result: ${JSON.stringify(toolResult)}`);
|
|
539
617
|
}
|
|
540
618
|
return {
|
|
541
619
|
text: "Connector stopped after reaching the maximum tool step count.",
|
|
542
|
-
|
|
620
|
+
json: null,
|
|
621
|
+
conversationId,
|
|
622
|
+
logs: transcript
|
|
543
623
|
};
|
|
544
624
|
}
|
|
545
625
|
function validateConnectorGraph(graph) {
|
|
@@ -593,6 +673,27 @@ function buildOutputResult(connector, resolvedNodeValues) {
|
|
|
593
673
|
outputs
|
|
594
674
|
});
|
|
595
675
|
}
|
|
676
|
+
function parseValueLiteral(valueType, valueLiteral) {
|
|
677
|
+
if (valueType === "null") {
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
if (valueType === "boolean") {
|
|
681
|
+
return valueLiteral.trim().toLowerCase() === "true";
|
|
682
|
+
}
|
|
683
|
+
if (valueType === "number") {
|
|
684
|
+
const parsed = Number(valueLiteral);
|
|
685
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
686
|
+
}
|
|
687
|
+
if (valueType === "array" || valueType === "object") {
|
|
688
|
+
try {
|
|
689
|
+
return JSON.parse(valueLiteral || (valueType === "array" ? "[]" : "{}"));
|
|
690
|
+
}
|
|
691
|
+
catch {
|
|
692
|
+
return valueType === "array" ? [] : {};
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return valueLiteral;
|
|
696
|
+
}
|
|
596
697
|
function createConversationRecord(input) {
|
|
597
698
|
const now = new Date().toISOString();
|
|
598
699
|
return saveAiConnectorConversation(aiConnectorConversationSchema.parse({
|
|
@@ -615,6 +716,8 @@ async function executeConnector(connector, rawInput, services) {
|
|
|
615
716
|
incoming.set(edge.target, list);
|
|
616
717
|
}
|
|
617
718
|
const values = new Map();
|
|
719
|
+
const debugNodes = [];
|
|
720
|
+
const debugErrors = [];
|
|
618
721
|
const outputNodes = connector.graph.nodes.filter((node) => node.type === "output");
|
|
619
722
|
const activeConversation = parsedInput.conversationId
|
|
620
723
|
? getAiConnectorConversationById(parsedInput.conversationId)
|
|
@@ -628,18 +731,50 @@ async function executeConnector(connector, rawInput, services) {
|
|
|
628
731
|
if (!node) {
|
|
629
732
|
throw new Error(`Missing connector node ${nodeId}.`);
|
|
630
733
|
}
|
|
631
|
-
const
|
|
734
|
+
const upstreamEdges = incoming.get(nodeId) ?? [];
|
|
735
|
+
const upstream = await Promise.all(upstreamEdges.map(async (edge) => {
|
|
736
|
+
const upstreamValue = await evaluateNode(edge.source);
|
|
737
|
+
const selected = readOutputSelection(upstreamValue, edge.sourceHandle);
|
|
738
|
+
return {
|
|
739
|
+
edge,
|
|
740
|
+
sourceValue: upstreamValue,
|
|
741
|
+
selected
|
|
742
|
+
};
|
|
743
|
+
}));
|
|
632
744
|
let resolved;
|
|
633
|
-
if (node.type === "box_input") {
|
|
745
|
+
if (node.type === "box" || node.type === "box_input") {
|
|
634
746
|
const boxId = node.data.boxId?.trim() || "";
|
|
747
|
+
const resolvedInputs = Object.fromEntries(upstream.map(({ edge, selected }, index) => [
|
|
748
|
+
edge.targetHandle ?? edge.sourceHandle ?? `input_${index + 1}`,
|
|
749
|
+
selected.json ?? selected.text
|
|
750
|
+
]));
|
|
751
|
+
const resolvedParams = node.data.paramValues && typeof node.data.paramValues === "object"
|
|
752
|
+
? node.data.paramValues
|
|
753
|
+
: {};
|
|
635
754
|
const providedSnapshot = boxId ? parsedInput.boxSnapshots[boxId] : null;
|
|
636
755
|
const snapshot = providedSnapshot && typeof providedSnapshot === "object"
|
|
637
756
|
? {
|
|
638
|
-
...resolveForgeBoxSnapshot(boxId
|
|
757
|
+
...resolveForgeBoxSnapshot(boxId, {
|
|
758
|
+
actor: {
|
|
759
|
+
userIds: null,
|
|
760
|
+
source: "agent"
|
|
761
|
+
}
|
|
762
|
+
}, {
|
|
763
|
+
inputs: resolvedInputs,
|
|
764
|
+
params: resolvedParams
|
|
765
|
+
}),
|
|
639
766
|
contentJson: providedSnapshot
|
|
640
767
|
}
|
|
641
768
|
: boxId
|
|
642
|
-
? resolveForgeBoxSnapshot(boxId
|
|
769
|
+
? resolveForgeBoxSnapshot(boxId, {
|
|
770
|
+
actor: {
|
|
771
|
+
userIds: null,
|
|
772
|
+
source: "agent"
|
|
773
|
+
}
|
|
774
|
+
}, {
|
|
775
|
+
inputs: resolvedInputs,
|
|
776
|
+
params: resolvedParams
|
|
777
|
+
})
|
|
643
778
|
: {
|
|
644
779
|
boxId: "",
|
|
645
780
|
label: node.data.label,
|
|
@@ -648,6 +783,10 @@ async function executeConnector(connector, rawInput, services) {
|
|
|
648
783
|
contentJson: null,
|
|
649
784
|
tools: []
|
|
650
785
|
};
|
|
786
|
+
const outputKeys = [
|
|
787
|
+
...(node.data.outputs ?? []).map((port) => port.key),
|
|
788
|
+
...Object.keys(snapshot.contentJson ?? {})
|
|
789
|
+
];
|
|
651
790
|
resolved = {
|
|
652
791
|
text: snapshot.contentText,
|
|
653
792
|
json: snapshot.contentJson,
|
|
@@ -657,7 +796,28 @@ async function executeConnector(connector, rawInput, services) {
|
|
|
657
796
|
label: tool.label,
|
|
658
797
|
description: tool.description
|
|
659
798
|
})),
|
|
660
|
-
conversationId: null
|
|
799
|
+
conversationId: null,
|
|
800
|
+
outputMap: buildOutputMap(snapshot.contentText, snapshot.contentJson, outputKeys),
|
|
801
|
+
logs: []
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
else if (node.type === "value") {
|
|
805
|
+
const parsedValue = parseValueLiteral(node.data.valueType ?? "string", node.data.valueLiteral ?? "");
|
|
806
|
+
const jsonValue = parsedValue && typeof parsedValue === "object" && !Array.isArray(parsedValue)
|
|
807
|
+
? parsedValue
|
|
808
|
+
: null;
|
|
809
|
+
const textValue = parsedValue === null
|
|
810
|
+
? "null"
|
|
811
|
+
: typeof parsedValue === "string"
|
|
812
|
+
? parsedValue
|
|
813
|
+
: JSON.stringify(parsedValue, null, 2);
|
|
814
|
+
resolved = {
|
|
815
|
+
text: textValue,
|
|
816
|
+
json: jsonValue,
|
|
817
|
+
tools: [],
|
|
818
|
+
conversationId: null,
|
|
819
|
+
outputMap: buildOutputMap(textValue, jsonValue, ["primary"]),
|
|
820
|
+
logs: []
|
|
661
821
|
};
|
|
662
822
|
}
|
|
663
823
|
else if (node.type === "user_input") {
|
|
@@ -665,16 +825,76 @@ async function executeConnector(connector, rawInput, services) {
|
|
|
665
825
|
text: parsedInput.userInput || "",
|
|
666
826
|
json: Object.keys(parsedInput.context).length > 0 ? parsedInput.context : null,
|
|
667
827
|
tools: [],
|
|
668
|
-
conversationId: activeConversation?.id ?? null
|
|
828
|
+
conversationId: activeConversation?.id ?? null,
|
|
829
|
+
outputMap: buildOutputMap(parsedInput.userInput || "", Object.keys(parsedInput.context).length > 0 ? parsedInput.context : null, Object.keys(parsedInput.context ?? {})),
|
|
830
|
+
logs: []
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
else if (node.type === "merge") {
|
|
834
|
+
const mergedText = upstream
|
|
835
|
+
.map((entry) => entry.selected.text)
|
|
836
|
+
.filter(Boolean)
|
|
837
|
+
.join("\n\n");
|
|
838
|
+
const mergedJson = Object.assign({}, ...upstream
|
|
839
|
+
.map((entry) => entry.selected.json)
|
|
840
|
+
.filter((entry) => Boolean(entry) && typeof entry === "object"));
|
|
841
|
+
resolved = {
|
|
842
|
+
text: mergedText,
|
|
843
|
+
json: Object.keys(mergedJson).length > 0 ? mergedJson : null,
|
|
844
|
+
tools: upstream.flatMap((entry) => entry.sourceValue.tools),
|
|
845
|
+
conversationId: upstream.find((entry) => entry.sourceValue.conversationId)?.sourceValue
|
|
846
|
+
.conversationId ?? null,
|
|
847
|
+
outputMap: buildOutputMap(mergedText, Object.keys(mergedJson).length > 0 ? mergedJson : null, Object.keys(mergedJson)),
|
|
848
|
+
logs: []
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
else if (node.type === "template") {
|
|
852
|
+
const primary = upstream[0]?.selected ?? { text: "", json: null };
|
|
853
|
+
const rendered = (node.data.template ?? node.data.promptTemplate ?? "")
|
|
854
|
+
.replaceAll("{{input}}", primary.text)
|
|
855
|
+
.replaceAll("{{json}}", primary.json ? JSON.stringify(primary.json) : "");
|
|
856
|
+
resolved = {
|
|
857
|
+
text: rendered,
|
|
858
|
+
json: tryParseJsonObject(rendered),
|
|
859
|
+
tools: [],
|
|
860
|
+
conversationId: upstream.find((entry) => entry.sourceValue.conversationId)?.sourceValue
|
|
861
|
+
.conversationId ?? null,
|
|
862
|
+
outputMap: buildOutputMap(rendered, tryParseJsonObject(rendered)),
|
|
863
|
+
logs: []
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
else if (node.type === "pick_key") {
|
|
867
|
+
const primary = upstream[0]?.selected ?? { text: "", json: null };
|
|
868
|
+
const selectedKey = node.data.selectedKey?.trim() || "";
|
|
869
|
+
const selectedValue = primary.json && selectedKey in primary.json ? primary.json[selectedKey] : null;
|
|
870
|
+
const selectedJson = selectedValue &&
|
|
871
|
+
typeof selectedValue === "object" &&
|
|
872
|
+
!Array.isArray(selectedValue)
|
|
873
|
+
? selectedValue
|
|
874
|
+
: null;
|
|
875
|
+
resolved = {
|
|
876
|
+
text: coerceText(selectedValue),
|
|
877
|
+
json: selectedJson,
|
|
878
|
+
tools: [],
|
|
879
|
+
conversationId: upstream.find((entry) => entry.sourceValue.conversationId)?.sourceValue
|
|
880
|
+
.conversationId ?? null,
|
|
881
|
+
outputMap: buildOutputMap(coerceText(selectedValue), selectedJson),
|
|
882
|
+
logs: []
|
|
669
883
|
};
|
|
670
884
|
}
|
|
671
885
|
else if (node.type === "output") {
|
|
672
|
-
const mergedText = upstream
|
|
886
|
+
const mergedText = upstream
|
|
887
|
+
.map((entry) => entry.selected.text)
|
|
888
|
+
.filter(Boolean)
|
|
889
|
+
.join("\n\n");
|
|
673
890
|
resolved = {
|
|
674
891
|
text: mergedText,
|
|
675
|
-
json: upstream[0]?.json ?? null,
|
|
892
|
+
json: upstream[0]?.selected.json ?? null,
|
|
676
893
|
tools: [],
|
|
677
|
-
conversationId: upstream.find((entry) => entry.conversationId)?.
|
|
894
|
+
conversationId: upstream.find((entry) => entry.sourceValue.conversationId)?.sourceValue
|
|
895
|
+
.conversationId ?? null,
|
|
896
|
+
outputMap: buildOutputMap(mergedText, upstream[0]?.selected.json ?? null, Object.keys(upstream[0]?.selected.json ?? {})),
|
|
897
|
+
logs: []
|
|
678
898
|
};
|
|
679
899
|
}
|
|
680
900
|
else {
|
|
@@ -682,24 +902,67 @@ async function executeConnector(connector, rawInput, services) {
|
|
|
682
902
|
connector,
|
|
683
903
|
node,
|
|
684
904
|
userInput: parsedInput.userInput,
|
|
685
|
-
upstream
|
|
905
|
+
upstream: upstream.map((entry) => ({
|
|
906
|
+
text: entry.selected.text,
|
|
907
|
+
json: entry.selected.json,
|
|
908
|
+
tools: entry.sourceValue.tools,
|
|
909
|
+
conversationId: entry.sourceValue.conversationId,
|
|
910
|
+
outputMap: entry.sourceValue.outputMap,
|
|
911
|
+
logs: entry.sourceValue.logs
|
|
912
|
+
})),
|
|
686
913
|
services,
|
|
687
914
|
conversation: activeConversation
|
|
688
915
|
});
|
|
916
|
+
const outputKeys = (node.data.outputs ?? []).map((port) => port.key);
|
|
689
917
|
resolved = {
|
|
690
918
|
text: modelResult.text,
|
|
691
|
-
json:
|
|
919
|
+
json: modelResult.json,
|
|
692
920
|
tools: [],
|
|
693
|
-
conversationId: modelResult.conversationId
|
|
921
|
+
conversationId: modelResult.conversationId,
|
|
922
|
+
outputMap: buildOutputMap(modelResult.text, modelResult.json, outputKeys),
|
|
923
|
+
logs: modelResult.logs
|
|
694
924
|
};
|
|
695
925
|
}
|
|
696
926
|
values.set(nodeId, resolved);
|
|
927
|
+
debugNodes.push({
|
|
928
|
+
nodeId: node.id,
|
|
929
|
+
nodeType: node.type,
|
|
930
|
+
label: node.data.label,
|
|
931
|
+
input: upstream.map((entry) => ({
|
|
932
|
+
sourceNodeId: entry.edge.source,
|
|
933
|
+
sourceHandle: entry.edge.sourceHandle ?? null,
|
|
934
|
+
targetHandle: entry.edge.targetHandle ?? null,
|
|
935
|
+
text: entry.selected.text,
|
|
936
|
+
json: entry.selected.json
|
|
937
|
+
})),
|
|
938
|
+
output: {
|
|
939
|
+
text: resolved.text,
|
|
940
|
+
json: resolved.json
|
|
941
|
+
},
|
|
942
|
+
tools: resolved.tools.map((tool) => tool.key),
|
|
943
|
+
logs: resolved.logs,
|
|
944
|
+
error: null
|
|
945
|
+
});
|
|
697
946
|
return resolved;
|
|
698
947
|
};
|
|
699
|
-
|
|
700
|
-
|
|
948
|
+
try {
|
|
949
|
+
for (const outputNode of outputNodes) {
|
|
950
|
+
await evaluateNode(outputNode.id);
|
|
951
|
+
}
|
|
701
952
|
}
|
|
702
|
-
|
|
953
|
+
catch (error) {
|
|
954
|
+
debugErrors.push(error instanceof Error ? error.message : "Flow execution failed");
|
|
955
|
+
throw error;
|
|
956
|
+
}
|
|
957
|
+
const result = aiConnectorRunResultSchema.parse({
|
|
958
|
+
...buildOutputResult(connector, values),
|
|
959
|
+
debugTrace: parsedInput.debug
|
|
960
|
+
? {
|
|
961
|
+
nodes: debugNodes,
|
|
962
|
+
errors: debugErrors
|
|
963
|
+
}
|
|
964
|
+
: undefined
|
|
965
|
+
});
|
|
703
966
|
const conversationProviderNode = connector.graph.nodes.find((node) => node.type === "chat");
|
|
704
967
|
const resolvedConversationId = [...values.values()].find((entry) => entry.conversationId)?.conversationId ?? null;
|
|
705
968
|
const nextConversation = conversationProviderNode
|
|
@@ -1132,7 +1132,7 @@ export function getCalendarOverview(query) {
|
|
|
1132
1132
|
provider: "google",
|
|
1133
1133
|
label: "Google Calendar",
|
|
1134
1134
|
supportsDedicatedForgeCalendar: true,
|
|
1135
|
-
connectionHelp: "
|
|
1135
|
+
connectionHelp: "Forge uses a localhost Authorization Code + PKCE flow. Users sign in with Google from the same machine running Forge, Forge exchanges the code on the backend, and stores a per-user refresh token server-side."
|
|
1136
1136
|
},
|
|
1137
1137
|
{
|
|
1138
1138
|
provider: "apple",
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
2
2
|
import { getDatabase, runInTransaction } from "../db.js";
|
|
3
|
+
import { logForgeDebug } from "../debug.js";
|
|
3
4
|
import { recordActivityEvent } from "./activity-events.js";
|
|
4
5
|
import { recordEventLog } from "./event-log.js";
|
|
6
|
+
import { resolveGoogleCalendarOauthPublicConfig } from "../services/google-calendar-oauth-config.js";
|
|
5
7
|
import { buildConnectionAgentIdentity, FORGE_DEFAULT_AGENT_ID, listAiModelConnections, syncForgeManagedWikiProfile } from "./model-settings.js";
|
|
6
8
|
import { createAgentTokenSchema, agentIdentitySchema, customThemeSchema, settingsPayloadSchema, updateSettingsSchema } from "../types.js";
|
|
7
9
|
function boolFromInt(value) {
|
|
@@ -25,6 +27,12 @@ function normalizeMicrosoftRedirectUri(value) {
|
|
|
25
27
|
const trimmed = value?.trim();
|
|
26
28
|
return trimmed && trimmed.length > 0 ? trimmed : defaultMicrosoftRedirectUri();
|
|
27
29
|
}
|
|
30
|
+
function logCalendarSettingsDebug(message, details) {
|
|
31
|
+
const serialized = Object.entries(details)
|
|
32
|
+
.map(([key, value]) => `${key}=${JSON.stringify(value)}`)
|
|
33
|
+
.join(" ");
|
|
34
|
+
logForgeDebug(`[forge-calendar-settings] ${message} ${serialized}`);
|
|
35
|
+
}
|
|
28
36
|
function normalizeModelConnectionId(value) {
|
|
29
37
|
const trimmed = value?.trim();
|
|
30
38
|
return trimmed && trimmed.length > 0 ? trimmed : "";
|
|
@@ -137,7 +145,7 @@ function readSettingsRow() {
|
|
|
137
145
|
.prepare(`SELECT
|
|
138
146
|
operator_name, operator_email, operator_title, theme_preference, custom_theme_json, locale_preference,
|
|
139
147
|
goal_drift_alerts, daily_quest_reminders, achievement_celebrations, max_active_tasks, time_accounting_mode,
|
|
140
|
-
integrity_score, last_audit_at, psyche_auth_required, microsoft_client_id, microsoft_tenant_id, microsoft_redirect_uri,
|
|
148
|
+
integrity_score, last_audit_at, psyche_auth_required, google_client_id, google_client_secret, microsoft_client_id, microsoft_tenant_id, microsoft_redirect_uri,
|
|
141
149
|
forge_basic_chat_connection_id, forge_basic_chat_model, forge_wiki_connection_id, forge_wiki_model, created_at, updated_at
|
|
142
150
|
FROM app_settings
|
|
143
151
|
WHERE id = 1`)
|
|
@@ -213,6 +221,18 @@ export function isPsycheAuthRequired() {
|
|
|
213
221
|
export function getSettings() {
|
|
214
222
|
const row = readSettingsRow();
|
|
215
223
|
const connections = listAiModelConnections();
|
|
224
|
+
const googleConfig = resolveGoogleCalendarOauthPublicConfig(process.env, {
|
|
225
|
+
clientId: row.google_client_id,
|
|
226
|
+
clientSecret: row.google_client_secret
|
|
227
|
+
});
|
|
228
|
+
logCalendarSettingsDebug("get_settings", {
|
|
229
|
+
storedGoogleClientId: row.google_client_id,
|
|
230
|
+
storedGoogleClientSecret: row.google_client_secret.length > 0,
|
|
231
|
+
resolvedGoogleClientId: googleConfig.clientId,
|
|
232
|
+
resolvedGoogleClientSecret: googleConfig.clientSecret.length > 0,
|
|
233
|
+
googleIsConfigured: googleConfig.isConfigured,
|
|
234
|
+
googleRedirectUri: googleConfig.redirectUri
|
|
235
|
+
});
|
|
216
236
|
const microsoftClientId = row.microsoft_client_id?.trim() ?? "";
|
|
217
237
|
const microsoftTenantId = normalizeMicrosoftTenantId(row.microsoft_tenant_id);
|
|
218
238
|
const microsoftRedirectUri = normalizeMicrosoftRedirectUri(row.microsoft_redirect_uri);
|
|
@@ -248,6 +268,7 @@ export function getSettings() {
|
|
|
248
268
|
psycheAuthRequired: boolFromInt(row.psyche_auth_required)
|
|
249
269
|
},
|
|
250
270
|
calendarProviders: {
|
|
271
|
+
google: googleConfig,
|
|
251
272
|
microsoft: {
|
|
252
273
|
clientId: microsoftClientId,
|
|
253
274
|
tenantId: microsoftTenantId,
|
|
@@ -297,6 +318,20 @@ export function updateSettings(input, options = {}) {
|
|
|
297
318
|
return runInTransaction(() => {
|
|
298
319
|
const current = getSettings();
|
|
299
320
|
const now = new Date().toISOString();
|
|
321
|
+
const nextGoogleClientId = parsed.calendarProviders?.google?.clientId?.trim() ??
|
|
322
|
+
current.calendarProviders.google.storedClientId;
|
|
323
|
+
const nextGoogleClientSecret = parsed.calendarProviders?.google?.clientSecret?.trim() ??
|
|
324
|
+
current.calendarProviders.google.storedClientSecret;
|
|
325
|
+
logCalendarSettingsDebug("update_settings_requested", {
|
|
326
|
+
requestedGoogleClientId: parsed.calendarProviders?.google?.clientId ?? null,
|
|
327
|
+
requestedGoogleClientSecret: parsed.calendarProviders?.google?.clientSecret !== undefined
|
|
328
|
+
? parsed.calendarProviders.google.clientSecret.length > 0
|
|
329
|
+
: null,
|
|
330
|
+
currentGoogleClientId: current.calendarProviders.google.storedClientId,
|
|
331
|
+
currentGoogleClientSecret: current.calendarProviders.google.storedClientSecret.length > 0,
|
|
332
|
+
nextGoogleClientId,
|
|
333
|
+
nextGoogleClientSecret: nextGoogleClientSecret.length > 0
|
|
334
|
+
});
|
|
300
335
|
const next = {
|
|
301
336
|
profile: {
|
|
302
337
|
operatorName: parsed.profile?.operatorName ?? current.profile.operatorName,
|
|
@@ -317,6 +352,10 @@ export function updateSettings(input, options = {}) {
|
|
|
317
352
|
localePreference: parsed.localePreference ?? current.localePreference,
|
|
318
353
|
psycheAuthRequired: parsed.security?.psycheAuthRequired ?? current.security.psycheAuthRequired,
|
|
319
354
|
calendarProviders: {
|
|
355
|
+
google: resolveGoogleCalendarOauthPublicConfig(process.env, {
|
|
356
|
+
clientId: nextGoogleClientId,
|
|
357
|
+
clientSecret: nextGoogleClientSecret
|
|
358
|
+
}),
|
|
320
359
|
microsoft: {
|
|
321
360
|
clientId: parsed.calendarProviders?.microsoft?.clientId?.trim() ??
|
|
322
361
|
current.calendarProviders.microsoft.clientId,
|
|
@@ -350,10 +389,16 @@ export function updateSettings(input, options = {}) {
|
|
|
350
389
|
.prepare(`UPDATE app_settings
|
|
351
390
|
SET operator_name = ?, operator_email = ?, operator_title = ?, theme_preference = ?, custom_theme_json = ?, locale_preference = ?,
|
|
352
391
|
goal_drift_alerts = ?, daily_quest_reminders = ?, achievement_celebrations = ?, max_active_tasks = ?, time_accounting_mode = ?,
|
|
353
|
-
psyche_auth_required = ?, microsoft_client_id = ?, microsoft_tenant_id = ?, microsoft_redirect_uri = ?,
|
|
392
|
+
psyche_auth_required = ?, google_client_id = ?, google_client_secret = ?, microsoft_client_id = ?, microsoft_tenant_id = ?, microsoft_redirect_uri = ?,
|
|
354
393
|
forge_basic_chat_connection_id = ?, forge_basic_chat_model = ?, forge_wiki_connection_id = ?, forge_wiki_model = ?, updated_at = ?
|
|
355
394
|
WHERE id = 1`)
|
|
356
|
-
.run(next.profile.operatorName, next.profile.operatorEmail, next.profile.operatorTitle, next.themePreference, next.customTheme ? JSON.stringify(next.customTheme) : "", next.localePreference, toInt(next.notifications.goalDriftAlerts), toInt(next.notifications.dailyQuestReminders), toInt(next.notifications.achievementCelebrations), next.execution.maxActiveTasks, next.execution.timeAccountingMode, toInt(next.psycheAuthRequired), next.calendarProviders.microsoft.clientId, next.calendarProviders.microsoft.tenantId, next.calendarProviders.microsoft.redirectUri, next.modelSettings.forgeAgent.basicChat.connectionId, next.modelSettings.forgeAgent.basicChat.model, next.modelSettings.forgeAgent.wiki.connectionId, next.modelSettings.forgeAgent.wiki.model, now);
|
|
395
|
+
.run(next.profile.operatorName, next.profile.operatorEmail, next.profile.operatorTitle, next.themePreference, next.customTheme ? JSON.stringify(next.customTheme) : "", next.localePreference, toInt(next.notifications.goalDriftAlerts), toInt(next.notifications.dailyQuestReminders), toInt(next.notifications.achievementCelebrations), next.execution.maxActiveTasks, next.execution.timeAccountingMode, toInt(next.psycheAuthRequired), nextGoogleClientId, nextGoogleClientSecret, next.calendarProviders.microsoft.clientId, next.calendarProviders.microsoft.tenantId, next.calendarProviders.microsoft.redirectUri, next.modelSettings.forgeAgent.basicChat.connectionId, next.modelSettings.forgeAgent.basicChat.model, next.modelSettings.forgeAgent.wiki.connectionId, next.modelSettings.forgeAgent.wiki.model, now);
|
|
396
|
+
logCalendarSettingsDebug("update_settings_committed", {
|
|
397
|
+
persistedGoogleClientId: nextGoogleClientId,
|
|
398
|
+
persistedGoogleClientSecret: nextGoogleClientSecret.length > 0,
|
|
399
|
+
persistedMicrosoftClientId: next.calendarProviders.microsoft.clientId,
|
|
400
|
+
updatedAt: now
|
|
401
|
+
});
|
|
357
402
|
if (options.secrets) {
|
|
358
403
|
syncForgeManagedWikiProfile(options.secrets);
|
|
359
404
|
}
|
|
@@ -374,6 +419,9 @@ export function updateSettings(input, options = {}) {
|
|
|
374
419
|
dailyQuestReminders: next.notifications.dailyQuestReminders,
|
|
375
420
|
maxActiveTasks: next.execution.maxActiveTasks,
|
|
376
421
|
timeAccountingMode: next.execution.timeAccountingMode,
|
|
422
|
+
googleConfigured: next.calendarProviders.google.isConfigured,
|
|
423
|
+
googleAppBaseUrl: next.calendarProviders.google.appBaseUrl,
|
|
424
|
+
googleRedirectUri: next.calendarProviders.google.redirectUri,
|
|
377
425
|
microsoftConfigured: next.calendarProviders.microsoft.clientId.trim().length > 0,
|
|
378
426
|
microsoftTenantId: next.calendarProviders.microsoft.tenantId,
|
|
379
427
|
forgeBasicChatModel: next.modelSettings.forgeAgent.basicChat.model,
|