forge-openclaw-plugin 0.2.60 → 0.2.61
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 +93 -46
- package/dist/assets/{board-B1V3M__K.js → board-DThHV1D8.js} +1 -1
- package/dist/assets/index-7gvVCqnV.css +1 -0
- package/dist/assets/index-_Cn6Prym.js +90 -0
- package/dist/assets/{motion-CltSTItx.js → motion-BtTJtHCw.js} +1 -1
- package/dist/assets/{table-B-VrSFx8.js → table-Bnw6pcwN.js} +1 -1
- package/dist/assets/{ui-DUqM4jkt.js → ui-CnVxFkj0.js} +1 -1
- package/dist/assets/{vendor-C0otBhgu.js → vendor-BgZ3YrRd.js} +212 -207
- package/dist/gamification-previews/dark-fantasy-item-trophy-tasks-anvil-marathon.webp +0 -0
- package/dist/gamification-previews/dark-fantasy-item-trophy-xp-levels-the-first-heat.webp +0 -0
- package/dist/gamification-previews/dark-fantasy-item-unlock-streaks-molten-crown-fire.webp +0 -0
- package/dist/gamification-previews/dark-fantasy-mascot.webp +0 -0
- package/dist/gamification-previews/dramatic-smithie-item-trophy-tasks-anvil-marathon.webp +0 -0
- package/dist/gamification-previews/dramatic-smithie-item-trophy-xp-levels-the-first-heat.webp +0 -0
- package/dist/gamification-previews/dramatic-smithie-item-unlock-streaks-molten-crown-fire.webp +0 -0
- package/dist/gamification-previews/dramatic-smithie-mascot.webp +0 -0
- package/dist/gamification-previews/mind-locksmith-item-trophy-tasks-anvil-marathon.webp +0 -0
- package/dist/gamification-previews/mind-locksmith-item-trophy-xp-levels-the-first-heat.webp +0 -0
- package/dist/gamification-previews/mind-locksmith-item-unlock-streaks-molten-crown-fire.webp +0 -0
- package/dist/gamification-previews/mind-locksmith-mascot.webp +0 -0
- package/dist/index.html +7 -7
- package/dist/openclaw/parity.js +27 -0
- package/dist/openclaw/plugin-entry-shared.js +2 -2
- package/dist/openclaw/plugin-sdk-types.d.ts +2 -1
- package/dist/openclaw/routes.d.ts +4 -0
- package/dist/openclaw/routes.js +112 -3
- package/dist/openclaw/tools.js +32 -4
- package/dist/server/server/migrations/059_data_backup_retention.sql +2 -0
- package/dist/server/server/src/app.js +125 -43
- package/dist/server/server/src/data-management-types.js +2 -0
- package/dist/server/server/src/health.js +40 -0
- package/dist/server/server/src/openapi.js +398 -7
- package/dist/server/server/src/repositories/rewards.js +60 -0
- package/dist/server/server/src/services/data-management.js +32 -2
- package/dist/server/server/src/services/doctor.js +762 -0
- package/dist/server/server/src/services/gamification.js +75 -3
- package/dist/server/src/lib/api.js +9 -0
- package/dist/server/src/lib/gamification-catalog.js +1 -1
- package/openclaw.plugin.json +85 -3
- package/package.json +8 -4
- package/server/migrations/059_data_backup_retention.sql +2 -0
- package/skills/forge-openclaw/SKILL.md +38 -19
- package/skills/forge-openclaw/entity_conversation_playbooks.md +66 -8
- package/skills/forge-openclaw/psyche_entity_playbooks.md +23 -0
- package/dist/assets/index-BwKAPo98.css +0 -1
- package/dist/assets/index-Dy7c-dRY.js +0 -90
|
@@ -28,6 +28,13 @@ const HTTP_METHODS = new Set([
|
|
|
28
28
|
"options",
|
|
29
29
|
"head"
|
|
30
30
|
]);
|
|
31
|
+
const CALENDAR_PROVIDER_VALUES = [
|
|
32
|
+
"google",
|
|
33
|
+
"apple",
|
|
34
|
+
"microsoft",
|
|
35
|
+
"caldav",
|
|
36
|
+
"macos_local"
|
|
37
|
+
];
|
|
31
38
|
const API_TAGS = [
|
|
32
39
|
{
|
|
33
40
|
name: "Meta",
|
|
@@ -710,7 +717,7 @@ export function buildOpenApiDocument() {
|
|
|
710
717
|
],
|
|
711
718
|
properties: {
|
|
712
719
|
id: { type: "string" },
|
|
713
|
-
provider: { type: "string", enum:
|
|
720
|
+
provider: { type: "string", enum: CALENDAR_PROVIDER_VALUES },
|
|
714
721
|
label: { type: "string" },
|
|
715
722
|
accountLabel: { type: "string" },
|
|
716
723
|
status: {
|
|
@@ -725,6 +732,134 @@ export function buildOpenApiDocument() {
|
|
|
725
732
|
updatedAt: { type: "string", format: "date-time" }
|
|
726
733
|
}
|
|
727
734
|
};
|
|
735
|
+
const calendarConnectionMutationInput = {
|
|
736
|
+
type: "object",
|
|
737
|
+
additionalProperties: true,
|
|
738
|
+
required: ["provider", "label"],
|
|
739
|
+
properties: {
|
|
740
|
+
provider: { type: "string", enum: CALENDAR_PROVIDER_VALUES },
|
|
741
|
+
label: { type: "string" },
|
|
742
|
+
username: { type: "string" },
|
|
743
|
+
password: { type: "string" },
|
|
744
|
+
serverUrl: { type: "string" },
|
|
745
|
+
authSessionId: { type: "string" },
|
|
746
|
+
sourceId: { type: "string" },
|
|
747
|
+
selectedCalendarUrls: arrayOf({ type: "string" }),
|
|
748
|
+
forgeCalendarUrl: nullable({ type: "string" }),
|
|
749
|
+
createForgeCalendar: { type: "boolean" },
|
|
750
|
+
replaceConnectionIds: arrayOf({ type: "string" })
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
const calendarConnectionPatchInput = {
|
|
754
|
+
type: "object",
|
|
755
|
+
additionalProperties: false,
|
|
756
|
+
properties: {
|
|
757
|
+
label: { type: "string" },
|
|
758
|
+
selectedCalendarUrls: arrayOf({ type: "string" })
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
const calendarDiscoveryInput = {
|
|
762
|
+
type: "object",
|
|
763
|
+
additionalProperties: true,
|
|
764
|
+
required: ["provider"],
|
|
765
|
+
properties: {
|
|
766
|
+
provider: { type: "string", enum: ["apple", "caldav"] },
|
|
767
|
+
serverUrl: { type: "string" },
|
|
768
|
+
username: { type: "string" },
|
|
769
|
+
password: { type: "string" }
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
const calendarDiscoveryCalendar = {
|
|
773
|
+
type: "object",
|
|
774
|
+
additionalProperties: true,
|
|
775
|
+
required: [
|
|
776
|
+
"url",
|
|
777
|
+
"displayName",
|
|
778
|
+
"description",
|
|
779
|
+
"color",
|
|
780
|
+
"timezone",
|
|
781
|
+
"isPrimary",
|
|
782
|
+
"canWrite",
|
|
783
|
+
"selectedByDefault",
|
|
784
|
+
"isForgeCandidate"
|
|
785
|
+
],
|
|
786
|
+
properties: {
|
|
787
|
+
url: { type: "string" },
|
|
788
|
+
displayName: { type: "string" },
|
|
789
|
+
dedupedName: { type: "string" },
|
|
790
|
+
description: { type: "string" },
|
|
791
|
+
color: { type: "string" },
|
|
792
|
+
timezone: { type: "string" },
|
|
793
|
+
isPrimary: { type: "boolean" },
|
|
794
|
+
canWrite: { type: "boolean" },
|
|
795
|
+
selectedByDefault: { type: "boolean" },
|
|
796
|
+
isForgeCandidate: { type: "boolean" },
|
|
797
|
+
sourceId: nullable({ type: "string" }),
|
|
798
|
+
sourceTitle: nullable({ type: "string" }),
|
|
799
|
+
sourceType: nullable({ type: "string" }),
|
|
800
|
+
calendarType: nullable({ type: "string" }),
|
|
801
|
+
hostCalendarId: nullable({ type: "string" }),
|
|
802
|
+
canonicalKey: nullable({ type: "string" })
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
const calendarDiscoveryPayload = {
|
|
806
|
+
type: "object",
|
|
807
|
+
additionalProperties: true,
|
|
808
|
+
required: [
|
|
809
|
+
"provider",
|
|
810
|
+
"accountLabel",
|
|
811
|
+
"serverUrl",
|
|
812
|
+
"principalUrl",
|
|
813
|
+
"homeUrl",
|
|
814
|
+
"calendars"
|
|
815
|
+
],
|
|
816
|
+
properties: {
|
|
817
|
+
provider: { type: "string", enum: CALENDAR_PROVIDER_VALUES },
|
|
818
|
+
accountLabel: { type: "string" },
|
|
819
|
+
serverUrl: { type: "string" },
|
|
820
|
+
principalUrl: nullable({ type: "string" }),
|
|
821
|
+
homeUrl: nullable({ type: "string" }),
|
|
822
|
+
calendars: arrayOf(calendarDiscoveryCalendar)
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
const macOSLocalCalendarDiscoveryPayload = {
|
|
826
|
+
type: "object",
|
|
827
|
+
additionalProperties: true,
|
|
828
|
+
required: ["status", "requestedAt", "sources"],
|
|
829
|
+
properties: {
|
|
830
|
+
status: {
|
|
831
|
+
type: "string",
|
|
832
|
+
enum: [
|
|
833
|
+
"not_determined",
|
|
834
|
+
"denied",
|
|
835
|
+
"restricted",
|
|
836
|
+
"full_access",
|
|
837
|
+
"unavailable"
|
|
838
|
+
]
|
|
839
|
+
},
|
|
840
|
+
requestedAt: { type: "string", format: "date-time" },
|
|
841
|
+
sources: arrayOf({
|
|
842
|
+
type: "object",
|
|
843
|
+
additionalProperties: true,
|
|
844
|
+
required: [
|
|
845
|
+
"sourceId",
|
|
846
|
+
"sourceTitle",
|
|
847
|
+
"sourceType",
|
|
848
|
+
"accountLabel",
|
|
849
|
+
"accountIdentityKey",
|
|
850
|
+
"calendars"
|
|
851
|
+
],
|
|
852
|
+
properties: {
|
|
853
|
+
sourceId: { type: "string" },
|
|
854
|
+
sourceTitle: { type: "string" },
|
|
855
|
+
sourceType: { type: "string" },
|
|
856
|
+
accountLabel: { type: "string" },
|
|
857
|
+
accountIdentityKey: { type: "string" },
|
|
858
|
+
calendars: arrayOf(calendarDiscoveryCalendar)
|
|
859
|
+
}
|
|
860
|
+
})
|
|
861
|
+
}
|
|
862
|
+
};
|
|
728
863
|
const calendarResource = {
|
|
729
864
|
type: "object",
|
|
730
865
|
additionalProperties: false,
|
|
@@ -781,7 +916,7 @@ export function buildOpenApiDocument() {
|
|
|
781
916
|
],
|
|
782
917
|
properties: {
|
|
783
918
|
id: { type: "string" },
|
|
784
|
-
provider: { type: "string", enum:
|
|
919
|
+
provider: { type: "string", enum: CALENDAR_PROVIDER_VALUES },
|
|
785
920
|
connectionId: nullable({ type: "string" }),
|
|
786
921
|
calendarId: nullable({ type: "string" }),
|
|
787
922
|
remoteCalendarId: nullable({ type: "string" }),
|
|
@@ -3218,6 +3353,95 @@ export function buildOpenApiDocument() {
|
|
|
3218
3353
|
agentTokens: arrayOf({ $ref: "#/components/schemas/AgentTokenSummary" })
|
|
3219
3354
|
}
|
|
3220
3355
|
};
|
|
3356
|
+
const doctorFixProposal = {
|
|
3357
|
+
type: "object",
|
|
3358
|
+
additionalProperties: false,
|
|
3359
|
+
required: ["id", "kind", "title", "description", "requiresConfirmation"],
|
|
3360
|
+
properties: {
|
|
3361
|
+
id: { type: "string" },
|
|
3362
|
+
kind: { type: "string", enum: ["manual", "safe_auto_fix"] },
|
|
3363
|
+
title: { type: "string" },
|
|
3364
|
+
description: { type: "string" },
|
|
3365
|
+
requiresConfirmation: { type: "boolean" }
|
|
3366
|
+
}
|
|
3367
|
+
};
|
|
3368
|
+
const doctorCheck = {
|
|
3369
|
+
type: "object",
|
|
3370
|
+
additionalProperties: false,
|
|
3371
|
+
required: [
|
|
3372
|
+
"id",
|
|
3373
|
+
"group",
|
|
3374
|
+
"title",
|
|
3375
|
+
"status",
|
|
3376
|
+
"severity",
|
|
3377
|
+
"summary",
|
|
3378
|
+
"evidence",
|
|
3379
|
+
"affectedCount"
|
|
3380
|
+
],
|
|
3381
|
+
properties: {
|
|
3382
|
+
id: { type: "string" },
|
|
3383
|
+
group: { type: "string" },
|
|
3384
|
+
title: { type: "string" },
|
|
3385
|
+
status: { type: "string", enum: ["pass", "warn", "fail", "skipped"] },
|
|
3386
|
+
severity: { type: "string", enum: ["info", "warning", "error"] },
|
|
3387
|
+
summary: { type: "string" },
|
|
3388
|
+
evidence: arrayOf({ type: "string" }),
|
|
3389
|
+
affectedCount: { type: "integer" },
|
|
3390
|
+
fix: { $ref: "#/components/schemas/DoctorFixProposal" }
|
|
3391
|
+
}
|
|
3392
|
+
};
|
|
3393
|
+
const forgeDoctorReport = {
|
|
3394
|
+
type: "object",
|
|
3395
|
+
additionalProperties: true,
|
|
3396
|
+
required: [
|
|
3397
|
+
"ok",
|
|
3398
|
+
"now",
|
|
3399
|
+
"integrity",
|
|
3400
|
+
"runtime",
|
|
3401
|
+
"health",
|
|
3402
|
+
"settingsFile",
|
|
3403
|
+
"settingsSummary",
|
|
3404
|
+
"checks",
|
|
3405
|
+
"issues",
|
|
3406
|
+
"fixProposals",
|
|
3407
|
+
"warnings"
|
|
3408
|
+
],
|
|
3409
|
+
properties: {
|
|
3410
|
+
ok: { type: "boolean" },
|
|
3411
|
+
now: { type: "string", format: "date-time" },
|
|
3412
|
+
integrity: {
|
|
3413
|
+
type: "object",
|
|
3414
|
+
additionalProperties: true,
|
|
3415
|
+
required: ["score", "status", "headline", "lastCheckedAt"],
|
|
3416
|
+
properties: {
|
|
3417
|
+
score: { type: "integer" },
|
|
3418
|
+
status: { type: "string", enum: ["healthy", "warning", "critical"] },
|
|
3419
|
+
headline: { type: "string" },
|
|
3420
|
+
lastCheckedAt: { type: "string", format: "date-time" }
|
|
3421
|
+
}
|
|
3422
|
+
},
|
|
3423
|
+
runtime: { type: "object", additionalProperties: true },
|
|
3424
|
+
health: { type: "object", additionalProperties: true },
|
|
3425
|
+
settingsFile: { type: "object", additionalProperties: true },
|
|
3426
|
+
settingsSummary: { type: "object", additionalProperties: true },
|
|
3427
|
+
checks: arrayOf({ $ref: "#/components/schemas/DoctorCheck" }),
|
|
3428
|
+
issues: arrayOf({ $ref: "#/components/schemas/DoctorCheck" }),
|
|
3429
|
+
fixProposals: arrayOf({
|
|
3430
|
+
$ref: "#/components/schemas/DoctorFixProposal"
|
|
3431
|
+
}),
|
|
3432
|
+
warnings: arrayOf({ type: "string" })
|
|
3433
|
+
}
|
|
3434
|
+
};
|
|
3435
|
+
const doctorFixResult = {
|
|
3436
|
+
type: "object",
|
|
3437
|
+
additionalProperties: false,
|
|
3438
|
+
required: ["fixId", "status", "summary"],
|
|
3439
|
+
properties: {
|
|
3440
|
+
fixId: { type: "string" },
|
|
3441
|
+
status: { type: "string", enum: ["applied", "skipped", "failed"] },
|
|
3442
|
+
summary: { type: "string" }
|
|
3443
|
+
}
|
|
3444
|
+
};
|
|
3221
3445
|
const agentOnboardingPayload = {
|
|
3222
3446
|
type: "object",
|
|
3223
3447
|
additionalProperties: false,
|
|
@@ -3527,6 +3751,7 @@ export function buildOpenApiDocument() {
|
|
|
3527
3751
|
additionalProperties: false,
|
|
3528
3752
|
required: [
|
|
3529
3753
|
"classification",
|
|
3754
|
+
"aliases",
|
|
3530
3755
|
"summary",
|
|
3531
3756
|
"readRoutes",
|
|
3532
3757
|
"writeRoutes",
|
|
@@ -3538,6 +3763,7 @@ export function buildOpenApiDocument() {
|
|
|
3538
3763
|
type: "string",
|
|
3539
3764
|
enum: ["specialized_domain_surface"]
|
|
3540
3765
|
},
|
|
3766
|
+
aliases: arrayOf({ type: "string" }),
|
|
3541
3767
|
summary: { type: "string" },
|
|
3542
3768
|
readRoutes: {
|
|
3543
3769
|
type: "object",
|
|
@@ -4674,6 +4900,11 @@ export function buildOpenApiDocument() {
|
|
|
4674
4900
|
Project: project,
|
|
4675
4901
|
CalendarSchedulingRules: calendarSchedulingRules,
|
|
4676
4902
|
CalendarConnection: calendarConnection,
|
|
4903
|
+
CalendarConnectionMutationInput: calendarConnectionMutationInput,
|
|
4904
|
+
CalendarConnectionPatchInput: calendarConnectionPatchInput,
|
|
4905
|
+
CalendarDiscoveryInput: calendarDiscoveryInput,
|
|
4906
|
+
CalendarDiscoveryPayload: calendarDiscoveryPayload,
|
|
4907
|
+
MacOSLocalCalendarDiscoveryPayload: macOSLocalCalendarDiscoveryPayload,
|
|
4677
4908
|
CalendarResource: calendarResource,
|
|
4678
4909
|
CalendarEventSource: calendarEventSource,
|
|
4679
4910
|
CalendarEventLink: calendarEventLink,
|
|
@@ -4703,6 +4934,10 @@ export function buildOpenApiDocument() {
|
|
|
4703
4934
|
InsightsPayload: insightsPayload,
|
|
4704
4935
|
WeeklyReviewPayload: weeklyReviewPayload,
|
|
4705
4936
|
SettingsPayload: settingsPayload,
|
|
4937
|
+
DoctorFixProposal: doctorFixProposal,
|
|
4938
|
+
DoctorCheck: doctorCheck,
|
|
4939
|
+
ForgeDoctorReport: forgeDoctorReport,
|
|
4940
|
+
DoctorFixResult: doctorFixResult,
|
|
4706
4941
|
ExecutionSettings: executionSettings,
|
|
4707
4942
|
TaskRunClaimInput: taskRunClaimInput,
|
|
4708
4943
|
TaskRunHeartbeatInput: taskRunHeartbeatInput,
|
|
@@ -4812,6 +5047,52 @@ export function buildOpenApiDocument() {
|
|
|
4812
5047
|
}
|
|
4813
5048
|
}
|
|
4814
5049
|
},
|
|
5050
|
+
"/api/v1/doctor": {
|
|
5051
|
+
get: {
|
|
5052
|
+
summary: "Run Forge Doctor diagnostics for runtime, settings, storage, entities, hierarchy, rewards, and fix proposals",
|
|
5053
|
+
responses: {
|
|
5054
|
+
"200": jsonResponse({
|
|
5055
|
+
type: "object",
|
|
5056
|
+
required: ["doctor"],
|
|
5057
|
+
properties: {
|
|
5058
|
+
doctor: { $ref: "#/components/schemas/ForgeDoctorReport" }
|
|
5059
|
+
}
|
|
5060
|
+
}, "Forge Doctor report")
|
|
5061
|
+
}
|
|
5062
|
+
}
|
|
5063
|
+
},
|
|
5064
|
+
"/api/v1/doctor/fixes": {
|
|
5065
|
+
post: {
|
|
5066
|
+
summary: "Apply explicitly requested safe Forge Doctor fixes",
|
|
5067
|
+
requestBody: {
|
|
5068
|
+
required: true,
|
|
5069
|
+
content: {
|
|
5070
|
+
"application/json": {
|
|
5071
|
+
schema: {
|
|
5072
|
+
type: "object",
|
|
5073
|
+
additionalProperties: false,
|
|
5074
|
+
properties: {
|
|
5075
|
+
fixIds: arrayOf({ type: "string" }),
|
|
5076
|
+
applyAllSafe: { type: "boolean" }
|
|
5077
|
+
}
|
|
5078
|
+
}
|
|
5079
|
+
}
|
|
5080
|
+
}
|
|
5081
|
+
},
|
|
5082
|
+
responses: {
|
|
5083
|
+
"200": jsonResponse({
|
|
5084
|
+
type: "object",
|
|
5085
|
+
required: ["results", "doctor"],
|
|
5086
|
+
properties: {
|
|
5087
|
+
results: arrayOf({
|
|
5088
|
+
$ref: "#/components/schemas/DoctorFixResult"
|
|
5089
|
+
}),
|
|
5090
|
+
doctor: { $ref: "#/components/schemas/ForgeDoctorReport" }
|
|
5091
|
+
}
|
|
5092
|
+
}, "Forge Doctor fix result")
|
|
5093
|
+
}
|
|
5094
|
+
}
|
|
5095
|
+
},
|
|
4815
5096
|
"/api/v1/health/sleep": {
|
|
4816
5097
|
get: {
|
|
4817
5098
|
summary: "Read the Forge sleep overview surface",
|
|
@@ -7555,6 +7836,50 @@ export function buildOpenApiDocument() {
|
|
|
7555
7836
|
}
|
|
7556
7837
|
}
|
|
7557
7838
|
},
|
|
7839
|
+
"/api/v1/calendar/macos-local/discovery": {
|
|
7840
|
+
get: {
|
|
7841
|
+
summary: "Discover calendars already configured on this Mac through EventKit",
|
|
7842
|
+
responses: {
|
|
7843
|
+
"200": jsonResponse({
|
|
7844
|
+
type: "object",
|
|
7845
|
+
required: ["discovery"],
|
|
7846
|
+
properties: {
|
|
7847
|
+
discovery: {
|
|
7848
|
+
$ref: "#/components/schemas/MacOSLocalCalendarDiscoveryPayload"
|
|
7849
|
+
}
|
|
7850
|
+
}
|
|
7851
|
+
}, "macOS local calendar discovery"),
|
|
7852
|
+
default: { $ref: "#/components/responses/Error" }
|
|
7853
|
+
}
|
|
7854
|
+
}
|
|
7855
|
+
},
|
|
7856
|
+
"/api/v1/calendar/discovery": {
|
|
7857
|
+
post: {
|
|
7858
|
+
summary: "Discover Apple or custom CalDAV calendars before creating a connection",
|
|
7859
|
+
requestBody: {
|
|
7860
|
+
required: true,
|
|
7861
|
+
content: {
|
|
7862
|
+
"application/json": {
|
|
7863
|
+
schema: {
|
|
7864
|
+
$ref: "#/components/schemas/CalendarDiscoveryInput"
|
|
7865
|
+
}
|
|
7866
|
+
}
|
|
7867
|
+
}
|
|
7868
|
+
},
|
|
7869
|
+
responses: {
|
|
7870
|
+
"200": jsonResponse({
|
|
7871
|
+
type: "object",
|
|
7872
|
+
required: ["discovery"],
|
|
7873
|
+
properties: {
|
|
7874
|
+
discovery: {
|
|
7875
|
+
$ref: "#/components/schemas/CalendarDiscoveryPayload"
|
|
7876
|
+
}
|
|
7877
|
+
}
|
|
7878
|
+
}, "Calendar discovery"),
|
|
7879
|
+
default: { $ref: "#/components/responses/Error" }
|
|
7880
|
+
}
|
|
7881
|
+
}
|
|
7882
|
+
},
|
|
7558
7883
|
"/api/v1/calendar/connections": {
|
|
7559
7884
|
get: {
|
|
7560
7885
|
summary: "List connected calendar providers",
|
|
@@ -7573,10 +7898,7 @@ export function buildOpenApiDocument() {
|
|
|
7573
7898
|
"connectionHelp"
|
|
7574
7899
|
],
|
|
7575
7900
|
properties: {
|
|
7576
|
-
provider: {
|
|
7577
|
-
type: "string",
|
|
7578
|
-
enum: ["google", "apple", "caldav"]
|
|
7579
|
-
},
|
|
7901
|
+
provider: { type: "string", enum: CALENDAR_PROVIDER_VALUES },
|
|
7580
7902
|
label: { type: "string" },
|
|
7581
7903
|
supportsDedicatedForgeCalendar: { type: "boolean" },
|
|
7582
7904
|
connectionHelp: { type: "string" }
|
|
@@ -7591,8 +7913,18 @@ export function buildOpenApiDocument() {
|
|
|
7591
7913
|
}
|
|
7592
7914
|
},
|
|
7593
7915
|
post: {
|
|
7594
|
-
summary: "Create a Google, Apple, or custom CalDAV calendar connection",
|
|
7916
|
+
summary: "Create a Google, Apple, Exchange Online, local Mac, or custom CalDAV calendar connection",
|
|
7595
7917
|
description: "Forge first discovers the writable calendars for the account, then stores the chosen mirrored calendars and either reuses the existing shared Forge write target or saves a new one when needed.",
|
|
7918
|
+
requestBody: {
|
|
7919
|
+
required: true,
|
|
7920
|
+
content: {
|
|
7921
|
+
"application/json": {
|
|
7922
|
+
schema: {
|
|
7923
|
+
$ref: "#/components/schemas/CalendarConnectionMutationInput"
|
|
7924
|
+
}
|
|
7925
|
+
}
|
|
7926
|
+
}
|
|
7927
|
+
},
|
|
7596
7928
|
responses: {
|
|
7597
7929
|
"201": jsonResponse({
|
|
7598
7930
|
type: "object",
|
|
@@ -7607,6 +7939,65 @@ export function buildOpenApiDocument() {
|
|
|
7607
7939
|
}
|
|
7608
7940
|
}
|
|
7609
7941
|
},
|
|
7942
|
+
"/api/v1/calendar/connections/{id}": {
|
|
7943
|
+
patch: {
|
|
7944
|
+
summary: "Update one calendar connection label or selected mirrored calendars",
|
|
7945
|
+
requestBody: {
|
|
7946
|
+
required: true,
|
|
7947
|
+
content: {
|
|
7948
|
+
"application/json": {
|
|
7949
|
+
schema: {
|
|
7950
|
+
$ref: "#/components/schemas/CalendarConnectionPatchInput"
|
|
7951
|
+
}
|
|
7952
|
+
}
|
|
7953
|
+
}
|
|
7954
|
+
},
|
|
7955
|
+
responses: {
|
|
7956
|
+
"200": jsonResponse({
|
|
7957
|
+
type: "object",
|
|
7958
|
+
required: ["connection"],
|
|
7959
|
+
properties: {
|
|
7960
|
+
connection: {
|
|
7961
|
+
$ref: "#/components/schemas/CalendarConnection"
|
|
7962
|
+
}
|
|
7963
|
+
}
|
|
7964
|
+
}, "Updated calendar connection"),
|
|
7965
|
+
default: { $ref: "#/components/responses/Error" }
|
|
7966
|
+
}
|
|
7967
|
+
},
|
|
7968
|
+
delete: {
|
|
7969
|
+
summary: "Delete one calendar connection and stop mirroring it",
|
|
7970
|
+
responses: {
|
|
7971
|
+
"200": jsonResponse({
|
|
7972
|
+
type: "object",
|
|
7973
|
+
required: ["connection"],
|
|
7974
|
+
properties: {
|
|
7975
|
+
connection: {
|
|
7976
|
+
$ref: "#/components/schemas/CalendarConnection"
|
|
7977
|
+
}
|
|
7978
|
+
}
|
|
7979
|
+
}, "Deleted calendar connection"),
|
|
7980
|
+
default: { $ref: "#/components/responses/Error" }
|
|
7981
|
+
}
|
|
7982
|
+
}
|
|
7983
|
+
},
|
|
7984
|
+
"/api/v1/calendar/connections/{id}/discovery": {
|
|
7985
|
+
get: {
|
|
7986
|
+
summary: "Rediscover available calendars for an existing calendar connection",
|
|
7987
|
+
responses: {
|
|
7988
|
+
"200": jsonResponse({
|
|
7989
|
+
type: "object",
|
|
7990
|
+
required: ["discovery"],
|
|
7991
|
+
properties: {
|
|
7992
|
+
discovery: {
|
|
7993
|
+
$ref: "#/components/schemas/CalendarDiscoveryPayload"
|
|
7994
|
+
}
|
|
7995
|
+
}
|
|
7996
|
+
}, "Calendar connection discovery"),
|
|
7997
|
+
default: { $ref: "#/components/responses/Error" }
|
|
7998
|
+
}
|
|
7999
|
+
}
|
|
8000
|
+
},
|
|
7610
8001
|
"/api/v1/calendar/connections/{id}/sync": {
|
|
7611
8002
|
post: {
|
|
7612
8003
|
summary: "Sync one connected calendar provider",
|
|
@@ -27,6 +27,14 @@ const DEFAULT_RULES = [
|
|
|
27
27
|
description: "Award a small XP bounty for each ten credited minutes of active work.",
|
|
28
28
|
config: { fixedXp: 4, intervalMinutes: 10 }
|
|
29
29
|
},
|
|
30
|
+
{
|
|
31
|
+
id: "reward_rule_entity_created",
|
|
32
|
+
family: "consistency",
|
|
33
|
+
code: "entity_created",
|
|
34
|
+
title: "Forge entity created",
|
|
35
|
+
description: "Award a small activity bounty when the user creates a real Forge entity.",
|
|
36
|
+
config: { fixedXp: 2 }
|
|
37
|
+
},
|
|
30
38
|
{
|
|
31
39
|
id: "reward_rule_task_run_completion",
|
|
32
40
|
family: "completion",
|
|
@@ -673,6 +681,58 @@ export function recordTaskRunProgressRewards(taskRunId, taskId, actor, source, c
|
|
|
673
681
|
}
|
|
674
682
|
return rewards;
|
|
675
683
|
}
|
|
684
|
+
export function recordEntityCreationReward(input) {
|
|
685
|
+
ensureDefaultRewardRules();
|
|
686
|
+
const reversibleGroup = `entity_created:${input.entityType}:${input.entityId}`;
|
|
687
|
+
const existing = getDatabase()
|
|
688
|
+
.prepare(`SELECT
|
|
689
|
+
id, rule_id, event_log_id, entity_type, entity_id, actor, source,
|
|
690
|
+
delta_xp, reason_title, reason_summary, reversible_group,
|
|
691
|
+
reversed_by_reward_id, metadata_json, created_at
|
|
692
|
+
FROM reward_ledger
|
|
693
|
+
WHERE reversible_group = ?
|
|
694
|
+
ORDER BY created_at ASC
|
|
695
|
+
LIMIT 1`)
|
|
696
|
+
.get(reversibleGroup);
|
|
697
|
+
if (existing) {
|
|
698
|
+
return mapLedger(existing);
|
|
699
|
+
}
|
|
700
|
+
const createdAtDate = Number.isNaN(Date.parse(input.createdAt))
|
|
701
|
+
? new Date()
|
|
702
|
+
: new Date(input.createdAt);
|
|
703
|
+
const rule = getRuleByCode("entity_created");
|
|
704
|
+
const readableType = input.entityType.replaceAll("_", " ");
|
|
705
|
+
const title = input.title?.trim() || readableType;
|
|
706
|
+
const eventLog = recordEventLog({
|
|
707
|
+
eventKind: "reward.entity_created",
|
|
708
|
+
entityType: input.entityType,
|
|
709
|
+
entityId: input.entityId,
|
|
710
|
+
actor: input.actor ?? null,
|
|
711
|
+
source: input.source ?? "system",
|
|
712
|
+
metadata: {
|
|
713
|
+
entityType: input.entityType,
|
|
714
|
+
entityId: input.entityId,
|
|
715
|
+
title,
|
|
716
|
+
createdAt: input.createdAt
|
|
717
|
+
}
|
|
718
|
+
}, createdAtDate);
|
|
719
|
+
return insertLedgerEvent({
|
|
720
|
+
ruleId: rule?.id ?? null,
|
|
721
|
+
eventLogId: eventLog.id,
|
|
722
|
+
entityType: input.entityType,
|
|
723
|
+
entityId: input.entityId,
|
|
724
|
+
actor: input.actor ?? null,
|
|
725
|
+
source: input.source ?? "system",
|
|
726
|
+
deltaXp: Number(rule?.config.fixedXp ?? 2),
|
|
727
|
+
reasonTitle: `Created ${readableType}: ${title}`,
|
|
728
|
+
reasonSummary: "Small Forge activity XP for creating something concrete.",
|
|
729
|
+
reversibleGroup,
|
|
730
|
+
metadata: {
|
|
731
|
+
entityType: input.entityType,
|
|
732
|
+
title
|
|
733
|
+
}
|
|
734
|
+
}, createdAtDate);
|
|
735
|
+
}
|
|
676
736
|
export function recordWorkAdjustmentReward(input) {
|
|
677
737
|
const { rule, intervalMinutes, intervalSeconds, fixedXp } = getTaskRunProgressRewardCadence();
|
|
678
738
|
const entityType = workAdjustmentEntityTypeSchema.parse(input.entityType);
|
|
@@ -84,12 +84,13 @@ function ensureDataManagementSettingsRow() {
|
|
|
84
84
|
preferred_data_root,
|
|
85
85
|
backup_directory,
|
|
86
86
|
backup_frequency_hours,
|
|
87
|
+
backup_retention_days,
|
|
87
88
|
auto_repair_enabled,
|
|
88
89
|
last_auto_backup_at,
|
|
89
90
|
last_manual_backup_at,
|
|
90
91
|
created_at,
|
|
91
92
|
updated_at
|
|
92
|
-
) VALUES (1, ?, ?, NULL, 1, NULL, NULL, ?, ?)`)
|
|
93
|
+
) VALUES (1, ?, ?, NULL, 30, 1, NULL, NULL, ?, ?)`)
|
|
93
94
|
.run(dataRoot, backupDirectory, now, now);
|
|
94
95
|
}
|
|
95
96
|
function readDataManagementSettingsRow() {
|
|
@@ -99,6 +100,7 @@ function readDataManagementSettingsRow() {
|
|
|
99
100
|
preferred_data_root,
|
|
100
101
|
backup_directory,
|
|
101
102
|
backup_frequency_hours,
|
|
103
|
+
backup_retention_days,
|
|
102
104
|
auto_repair_enabled,
|
|
103
105
|
last_auto_backup_at,
|
|
104
106
|
last_manual_backup_at,
|
|
@@ -124,12 +126,13 @@ function writeDataManagementSettingsRow(patch) {
|
|
|
124
126
|
SET preferred_data_root = ?,
|
|
125
127
|
backup_directory = ?,
|
|
126
128
|
backup_frequency_hours = ?,
|
|
129
|
+
backup_retention_days = ?,
|
|
127
130
|
auto_repair_enabled = ?,
|
|
128
131
|
last_auto_backup_at = ?,
|
|
129
132
|
last_manual_backup_at = ?,
|
|
130
133
|
updated_at = ?
|
|
131
134
|
WHERE id = 1`)
|
|
132
|
-
.run(next.preferred_data_root, next.backup_directory, next.backup_frequency_hours, next.auto_repair_enabled, next.last_auto_backup_at, next.last_manual_backup_at, next.updated_at);
|
|
135
|
+
.run(next.preferred_data_root, next.backup_directory, next.backup_frequency_hours, next.backup_retention_days, next.auto_repair_enabled, next.last_auto_backup_at, next.last_manual_backup_at, next.updated_at);
|
|
133
136
|
}
|
|
134
137
|
function resolveCurrentDataManagementSettings() {
|
|
135
138
|
const row = readDataManagementSettingsRow();
|
|
@@ -139,6 +142,7 @@ function resolveCurrentDataManagementSettings() {
|
|
|
139
142
|
preferredDataRoot,
|
|
140
143
|
backupDirectory,
|
|
141
144
|
backupFrequencyHours: row.backup_frequency_hours,
|
|
145
|
+
backupRetentionDays: row.backup_retention_days,
|
|
142
146
|
autoRepairEnabled: row.auto_repair_enabled === 1,
|
|
143
147
|
lastAutoBackupAt: row.last_auto_backup_at,
|
|
144
148
|
lastManualBackupAt: row.last_manual_backup_at
|
|
@@ -461,6 +465,7 @@ export async function createDataBackup(input = { note: "" }, options = {}) {
|
|
|
461
465
|
}
|
|
462
466
|
if (mode === "automatic") {
|
|
463
467
|
writeDataManagementSettingsRow({ last_auto_backup_at: createdAt });
|
|
468
|
+
await pruneExpiredAutomaticBackups(settings.backupDirectory, settings.backupRetentionDays);
|
|
464
469
|
}
|
|
465
470
|
return backup;
|
|
466
471
|
}
|
|
@@ -468,6 +473,27 @@ export async function createDataBackup(input = { note: "" }, options = {}) {
|
|
|
468
473
|
await rm(sqliteSnapshot.tempDir, { recursive: true, force: true });
|
|
469
474
|
}
|
|
470
475
|
}
|
|
476
|
+
async function pruneExpiredAutomaticBackups(backupDirectory, retentionDays) {
|
|
477
|
+
if (!retentionDays) {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
|
|
481
|
+
const backups = await listDataBackups();
|
|
482
|
+
for (const backup of backups) {
|
|
483
|
+
if (backup.mode !== "automatic") {
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
if (path.resolve(backup.backupDirectory) !== path.resolve(backupDirectory)) {
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
const createdAtMs = new Date(backup.createdAt).getTime();
|
|
490
|
+
if (!Number.isFinite(createdAtMs) || createdAtMs >= cutoff) {
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
await rm(backup.archivePath, { force: true });
|
|
494
|
+
await rm(backup.manifestPath, { force: true });
|
|
495
|
+
}
|
|
496
|
+
}
|
|
471
497
|
async function openDatabaseSnapshot(databasePath) {
|
|
472
498
|
const database = new DatabaseSync(databasePath);
|
|
473
499
|
database.exec("PRAGMA busy_timeout = 250;");
|
|
@@ -643,6 +669,7 @@ export async function switchDataRoot(input, options = {}) {
|
|
|
643
669
|
preferred_data_root: targetDataRoot,
|
|
644
670
|
backup_directory: nextBackupDirectory,
|
|
645
671
|
backup_frequency_hours: previousSettings.backupFrequencyHours,
|
|
672
|
+
backup_retention_days: previousSettings.backupRetentionDays,
|
|
646
673
|
auto_repair_enabled: previousSettings.autoRepairEnabled ? 1 : 0
|
|
647
674
|
});
|
|
648
675
|
await (options.persistPreferredDataRoot ?? writeMonorepoPreferredDataRoot)(targetDataRoot);
|
|
@@ -698,6 +725,9 @@ export async function updateDataManagementSettings(input) {
|
|
|
698
725
|
backup_frequency_hours: parsed.backupFrequencyHours !== undefined
|
|
699
726
|
? parsed.backupFrequencyHours
|
|
700
727
|
: undefined,
|
|
728
|
+
backup_retention_days: parsed.backupRetentionDays !== undefined
|
|
729
|
+
? parsed.backupRetentionDays
|
|
730
|
+
: undefined,
|
|
701
731
|
auto_repair_enabled: parsed.autoRepairEnabled !== undefined
|
|
702
732
|
? parsed.autoRepairEnabled
|
|
703
733
|
? 1
|