@elizaos/plugin-health 2.0.0-beta.1 → 2.0.3-beta.3

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.
Files changed (164) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +33 -27
  3. package/assets/hero.svg +67 -0
  4. package/package.json +69 -4
  5. package/dist/actions/index.d.ts +0 -20
  6. package/dist/actions/index.d.ts.map +0 -1
  7. package/dist/actions/index.js +0 -5
  8. package/dist/actions/index.js.map +0 -1
  9. package/dist/anchors/index.d.ts +0 -19
  10. package/dist/anchors/index.d.ts.map +0 -1
  11. package/dist/anchors/index.js +0 -9
  12. package/dist/anchors/index.js.map +0 -1
  13. package/dist/connectors/contract-stubs.d.ts +0 -112
  14. package/dist/connectors/contract-stubs.d.ts.map +0 -1
  15. package/dist/connectors/contract-stubs.js +0 -1
  16. package/dist/connectors/contract-stubs.js.map +0 -1
  17. package/dist/connectors/index.d.ts +0 -28
  18. package/dist/connectors/index.d.ts.map +0 -1
  19. package/dist/connectors/index.js +0 -202
  20. package/dist/connectors/index.js.map +0 -1
  21. package/dist/contracts/circadian-default.d.ts +0 -15
  22. package/dist/contracts/circadian-default.d.ts.map +0 -1
  23. package/dist/contracts/circadian-default.js +0 -30
  24. package/dist/contracts/circadian-default.js.map +0 -1
  25. package/dist/contracts/circadian.d.ts +0 -92
  26. package/dist/contracts/circadian.d.ts.map +0 -1
  27. package/dist/contracts/circadian.js +0 -14
  28. package/dist/contracts/circadian.js.map +0 -1
  29. package/dist/contracts/health.d.ts +0 -9
  30. package/dist/contracts/health.d.ts.map +0 -1
  31. package/dist/contracts/health.js +0 -21
  32. package/dist/contracts/health.js.map +0 -1
  33. package/dist/contracts/lifeops-connector-degradation.d.ts +0 -9
  34. package/dist/contracts/lifeops-connector-degradation.d.ts.map +0 -1
  35. package/dist/contracts/lifeops-connector-degradation.js +0 -17
  36. package/dist/contracts/lifeops-connector-degradation.js.map +0 -1
  37. package/dist/contracts/lifeops.d.ts +0 -3123
  38. package/dist/contracts/lifeops.d.ts.map +0 -1
  39. package/dist/contracts/lifeops.js +0 -635
  40. package/dist/contracts/lifeops.js.map +0 -1
  41. package/dist/contracts/permissions.d.ts +0 -39
  42. package/dist/contracts/permissions.d.ts.map +0 -1
  43. package/dist/contracts/permissions.js +0 -1
  44. package/dist/contracts/permissions.js.map +0 -1
  45. package/dist/default-packs/bedtime.d.ts +0 -14
  46. package/dist/default-packs/bedtime.d.ts.map +0 -1
  47. package/dist/default-packs/bedtime.js +0 -48
  48. package/dist/default-packs/bedtime.js.map +0 -1
  49. package/dist/default-packs/contract-stubs.d.ts +0 -161
  50. package/dist/default-packs/contract-stubs.d.ts.map +0 -1
  51. package/dist/default-packs/contract-stubs.js +0 -1
  52. package/dist/default-packs/contract-stubs.js.map +0 -1
  53. package/dist/default-packs/index.d.ts +0 -18
  54. package/dist/default-packs/index.d.ts.map +0 -1
  55. package/dist/default-packs/index.js +0 -39
  56. package/dist/default-packs/index.js.map +0 -1
  57. package/dist/default-packs/sleep-recap.d.ts +0 -14
  58. package/dist/default-packs/sleep-recap.d.ts.map +0 -1
  59. package/dist/default-packs/sleep-recap.js +0 -51
  60. package/dist/default-packs/sleep-recap.js.map +0 -1
  61. package/dist/default-packs/wake-up.d.ts +0 -14
  62. package/dist/default-packs/wake-up.d.ts.map +0 -1
  63. package/dist/default-packs/wake-up.js +0 -61
  64. package/dist/default-packs/wake-up.js.map +0 -1
  65. package/dist/health-bridge/health-bridge.d.ts +0 -57
  66. package/dist/health-bridge/health-bridge.d.ts.map +0 -1
  67. package/dist/health-bridge/health-bridge.js +0 -558
  68. package/dist/health-bridge/health-bridge.js.map +0 -1
  69. package/dist/health-bridge/health-connectors.d.ts +0 -23
  70. package/dist/health-bridge/health-connectors.d.ts.map +0 -1
  71. package/dist/health-bridge/health-connectors.js +0 -1018
  72. package/dist/health-bridge/health-connectors.js.map +0 -1
  73. package/dist/health-bridge/health-oauth.d.ts +0 -62
  74. package/dist/health-bridge/health-oauth.d.ts.map +0 -1
  75. package/dist/health-bridge/health-oauth.js +0 -432
  76. package/dist/health-bridge/health-oauth.js.map +0 -1
  77. package/dist/health-bridge/health-provider-registry.d.ts +0 -89
  78. package/dist/health-bridge/health-provider-registry.d.ts.map +0 -1
  79. package/dist/health-bridge/health-provider-registry.js +0 -141
  80. package/dist/health-bridge/health-provider-registry.js.map +0 -1
  81. package/dist/health-bridge/health-records.d.ts +0 -14
  82. package/dist/health-bridge/health-records.d.ts.map +0 -1
  83. package/dist/health-bridge/health-records.js +0 -45
  84. package/dist/health-bridge/health-records.js.map +0 -1
  85. package/dist/health-bridge/index.d.ts +0 -22
  86. package/dist/health-bridge/index.d.ts.map +0 -1
  87. package/dist/health-bridge/index.js +0 -7
  88. package/dist/health-bridge/index.js.map +0 -1
  89. package/dist/health-bridge/service-normalize-health.d.ts +0 -3
  90. package/dist/health-bridge/service-normalize-health.d.ts.map +0 -1
  91. package/dist/health-bridge/service-normalize-health.js +0 -96
  92. package/dist/health-bridge/service-normalize-health.js.map +0 -1
  93. package/dist/index.d.ts +0 -41
  94. package/dist/index.d.ts.map +0 -1
  95. package/dist/index.js +0 -62
  96. package/dist/index.js.map +0 -1
  97. package/dist/screen-time/index.d.ts +0 -23
  98. package/dist/screen-time/index.d.ts.map +0 -1
  99. package/dist/screen-time/index.js +0 -1
  100. package/dist/screen-time/index.js.map +0 -1
  101. package/dist/sleep/awake-probability.d.ts +0 -11
  102. package/dist/sleep/awake-probability.d.ts.map +0 -1
  103. package/dist/sleep/awake-probability.js +0 -163
  104. package/dist/sleep/awake-probability.js.map +0 -1
  105. package/dist/sleep/circadian-rules.d.ts +0 -45
  106. package/dist/sleep/circadian-rules.d.ts.map +0 -1
  107. package/dist/sleep/circadian-rules.js +0 -258
  108. package/dist/sleep/circadian-rules.js.map +0 -1
  109. package/dist/sleep/index.d.ts +0 -21
  110. package/dist/sleep/index.d.ts.map +0 -1
  111. package/dist/sleep/index.js +0 -11
  112. package/dist/sleep/index.js.map +0 -1
  113. package/dist/sleep/sleep-cycle-dispatch.d.ts +0 -75
  114. package/dist/sleep/sleep-cycle-dispatch.d.ts.map +0 -1
  115. package/dist/sleep/sleep-cycle-dispatch.js +0 -102
  116. package/dist/sleep/sleep-cycle-dispatch.js.map +0 -1
  117. package/dist/sleep/sleep-cycle.d.ts +0 -38
  118. package/dist/sleep/sleep-cycle.d.ts.map +0 -1
  119. package/dist/sleep/sleep-cycle.js +0 -418
  120. package/dist/sleep/sleep-cycle.js.map +0 -1
  121. package/dist/sleep/sleep-episode-store.d.ts +0 -25
  122. package/dist/sleep/sleep-episode-store.d.ts.map +0 -1
  123. package/dist/sleep/sleep-episode-store.js +0 -69
  124. package/dist/sleep/sleep-episode-store.js.map +0 -1
  125. package/dist/sleep/sleep-episode-types.d.ts +0 -38
  126. package/dist/sleep/sleep-episode-types.d.ts.map +0 -1
  127. package/dist/sleep/sleep-episode-types.js +0 -14
  128. package/dist/sleep/sleep-episode-types.js.map +0 -1
  129. package/dist/sleep/sleep-recap.d.ts +0 -19
  130. package/dist/sleep/sleep-recap.d.ts.map +0 -1
  131. package/dist/sleep/sleep-recap.js +0 -1
  132. package/dist/sleep/sleep-recap.js.map +0 -1
  133. package/dist/sleep/sleep-regularity.d.ts +0 -19
  134. package/dist/sleep/sleep-regularity.d.ts.map +0 -1
  135. package/dist/sleep/sleep-regularity.js +0 -242
  136. package/dist/sleep/sleep-regularity.js.map +0 -1
  137. package/dist/sleep/sleep-wake-events.d.ts +0 -58
  138. package/dist/sleep/sleep-wake-events.d.ts.map +0 -1
  139. package/dist/sleep/sleep-wake-events.js +0 -135
  140. package/dist/sleep/sleep-wake-events.js.map +0 -1
  141. package/dist/sleep/source-reliability.d.ts +0 -38
  142. package/dist/sleep/source-reliability.d.ts.map +0 -1
  143. package/dist/sleep/source-reliability.js +0 -62
  144. package/dist/sleep/source-reliability.js.map +0 -1
  145. package/dist/util/index.d.ts +0 -10
  146. package/dist/util/index.d.ts.map +0 -1
  147. package/dist/util/index.js +0 -3
  148. package/dist/util/index.js.map +0 -1
  149. package/dist/util/normalize.d.ts +0 -22
  150. package/dist/util/normalize.d.ts.map +0 -1
  151. package/dist/util/normalize.js +0 -62
  152. package/dist/util/normalize.js.map +0 -1
  153. package/dist/util/time-util.d.ts +0 -10
  154. package/dist/util/time-util.d.ts.map +0 -1
  155. package/dist/util/time-util.js +0 -14
  156. package/dist/util/time-util.js.map +0 -1
  157. package/dist/util/time.d.ts +0 -17
  158. package/dist/util/time.d.ts.map +0 -1
  159. package/dist/util/time.js +0 -152
  160. package/dist/util/time.js.map +0 -1
  161. package/dist/util/token-encryption.d.ts +0 -42
  162. package/dist/util/token-encryption.d.ts.map +0 -1
  163. package/dist/util/token-encryption.js +0 -96
  164. package/dist/util/token-encryption.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/sleep/source-reliability.ts"],"sourcesContent":["import type { LifeOpsActivitySignalSource } from \"../contracts/health.js\";\n\nexport type LifeOpsReliabilityKey =\n | { kind: \"manual_override\" }\n | { kind: \"mobile_health\"; permissionGranted: boolean }\n | { kind: \"desktop_power\"; transition: \"system\" | \"screen\" | \"session\" }\n | { kind: \"message_outbound\"; channel: LifeOpsMessageReliabilityChannel }\n | { kind: \"message_inbound\" }\n | { kind: \"status_activity\" }\n | { kind: \"desktop_idle\"; source: \"iokit_hid\" | \"cgevent\" }\n | { kind: \"browser_focus\" }\n | { kind: \"device_presence\"; transition: boolean }\n | { kind: \"mobile_device\"; source: \"capacitor\" | \"continuity_probe\" }\n | { kind: \"charging\" }\n | { kind: \"screen_time_summary\" }\n | { kind: \"prior_baseline\" };\n\nexport type LifeOpsMessageReliabilityChannel =\n | \"imessage\"\n | \"eliza_chat\"\n | \"gmail\"\n | \"x_dm\"\n | \"discord\"\n | \"telegram\"\n | \"signal\"\n | \"whatsapp\"\n | \"sms\";\n\nconst MESSAGE_CHANNEL_WEIGHTS: Record<\n LifeOpsMessageReliabilityChannel,\n number\n> = {\n imessage: 0.88,\n eliza_chat: 0.88,\n gmail: 0.8,\n x_dm: 0.8,\n discord: 0.8,\n telegram: 0.8,\n signal: 0.8,\n whatsapp: 0.8,\n sms: 0.8,\n};\n\nexport function resolveSourceReliability(key: LifeOpsReliabilityKey): number {\n switch (key.kind) {\n case \"manual_override\":\n return 1.0;\n case \"mobile_health\":\n return key.permissionGranted ? 0.95 : 0;\n case \"desktop_power\":\n return key.transition === \"system\"\n ? 0.92\n : key.transition === \"screen\"\n ? 0.92\n : 0.85;\n case \"message_outbound\":\n return MESSAGE_CHANNEL_WEIGHTS[key.channel];\n case \"message_inbound\":\n return 0.15;\n case \"status_activity\":\n return 0.6;\n case \"desktop_idle\":\n return key.source === \"iokit_hid\" ? 0.8 : 0.75;\n case \"browser_focus\":\n return 0.7;\n case \"device_presence\":\n return key.transition ? 0.7 : 0.3;\n case \"mobile_device\":\n return key.source === \"capacitor\" ? 0.7 : 0.5;\n case \"charging\":\n return 0.4;\n case \"screen_time_summary\":\n return 0.55;\n case \"prior_baseline\":\n return 0.4;\n }\n}\n\n/**\n * Default reliability key for each activity-signal source. The special-case\n * for `app_lifecycle` + `manual_override` platform is handled inline because\n * it's the only cross-axis override.\n */\nconst SOURCE_RELIABILITY_KEYS: Record<\n LifeOpsActivitySignalSource,\n LifeOpsReliabilityKey\n> = {\n app_lifecycle: { kind: \"device_presence\", transition: true },\n page_visibility: { kind: \"device_presence\", transition: true },\n desktop_power: { kind: \"desktop_power\", transition: \"system\" },\n desktop_interaction: { kind: \"desktop_idle\", source: \"iokit_hid\" },\n connector_activity: { kind: \"message_outbound\", channel: \"gmail\" },\n imessage_outbound: { kind: \"message_outbound\", channel: \"imessage\" },\n mobile_device: { kind: \"mobile_device\", source: \"capacitor\" },\n mobile_health: { kind: \"mobile_health\", permissionGranted: true },\n};\n\nexport function resolveActivitySignalReliability(\n source: LifeOpsActivitySignalSource,\n platform: string,\n): number {\n if (source === \"app_lifecycle\" && platform === \"manual_override\") {\n return resolveSourceReliability({ kind: \"manual_override\" });\n }\n return resolveSourceReliability(SOURCE_RELIABILITY_KEYS[source]);\n}\n"],"mappings":"AA4BA,MAAM,0BAGF;AAAA,EACF,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AAAA,EACT,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,KAAK;AACP;AAEO,SAAS,yBAAyB,KAAoC;AAC3E,UAAQ,IAAI,MAAM;AAAA,IAChB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,IAAI,oBAAoB,OAAO;AAAA,IACxC,KAAK;AACH,aAAO,IAAI,eAAe,WACtB,OACA,IAAI,eAAe,WACjB,OACA;AAAA,IACR,KAAK;AACH,aAAO,wBAAwB,IAAI,OAAO;AAAA,IAC5C,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,IAAI,WAAW,cAAc,MAAM;AAAA,IAC5C,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,IAAI,aAAa,MAAM;AAAA,IAChC,KAAK;AACH,aAAO,IAAI,WAAW,cAAc,MAAM;AAAA,IAC5C,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAOA,MAAM,0BAGF;AAAA,EACF,eAAe,EAAE,MAAM,mBAAmB,YAAY,KAAK;AAAA,EAC3D,iBAAiB,EAAE,MAAM,mBAAmB,YAAY,KAAK;AAAA,EAC7D,eAAe,EAAE,MAAM,iBAAiB,YAAY,SAAS;AAAA,EAC7D,qBAAqB,EAAE,MAAM,gBAAgB,QAAQ,YAAY;AAAA,EACjE,oBAAoB,EAAE,MAAM,oBAAoB,SAAS,QAAQ;AAAA,EACjE,mBAAmB,EAAE,MAAM,oBAAoB,SAAS,WAAW;AAAA,EACnE,eAAe,EAAE,MAAM,iBAAiB,QAAQ,YAAY;AAAA,EAC5D,eAAe,EAAE,MAAM,iBAAiB,mBAAmB,KAAK;AAClE;AAEO,SAAS,iCACd,QACA,UACQ;AACR,MAAI,WAAW,mBAAmB,aAAa,mBAAmB;AAChE,WAAO,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAAA,EAC7D;AACA,SAAO,yBAAyB,wBAAwB,MAAM,CAAC;AACjE;","names":[]}
@@ -1,10 +0,0 @@
1
- /**
2
- * Pure-helper barrel for plugin-health.
3
- *
4
- * `time.ts` and `time-util.ts` are local copies of the same-named helpers
5
- * in `app-lifeops/src/lifeops/`. They are duplicated (not imported) so
6
- * plugin-health does not take a build-time dependency on app-lifeops.
7
- */
8
- export * from "./time.js";
9
- export * from "./time-util.js";
10
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/util/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC"}
@@ -1,3 +0,0 @@
1
- export * from "./time.js";
2
- export * from "./time-util.js";
3
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/util/index.ts"],"sourcesContent":["/**\n * Pure-helper barrel for plugin-health.\n *\n * `time.ts` and `time-util.ts` are local copies of the same-named helpers\n * in `app-lifeops/src/lifeops/`. They are duplicated (not imported) so\n * plugin-health does not take a build-time dependency on app-lifeops.\n */\n\nexport * from \"./time.js\";\nexport * from \"./time-util.js\";\n"],"mappings":"AAQA,cAAc;AACd,cAAc;","names":[]}
@@ -1,22 +0,0 @@
1
- /**
2
- * Minimal value-normalisation helpers used inside plugin-health.
3
- *
4
- * Reproduces only the helpers that the moved health-bridge / health-connectors
5
- * / service-normalize-health files actually call. Kept small and dependency-
6
- * free so plugin-health stays decoupled from app-lifeops' larger
7
- * `service-normalize.ts` family.
8
- *
9
- * Behaviour matches `app-lifeops/src/lifeops/service-normalize.ts` exactly.
10
- */
11
- export declare class HealthNormalizationError extends Error {
12
- readonly status: number;
13
- readonly code?: string;
14
- constructor(status: number, message: string, code?: string);
15
- }
16
- export declare function fail(status: number, message: string, code?: string): never;
17
- export declare function requireNonEmptyString(value: unknown, field: string): string;
18
- export declare function normalizeOptionalString(value: unknown): string | undefined;
19
- export declare function normalizeOptionalBoolean(value: unknown, _field: string): boolean | undefined;
20
- export declare function normalizeOptionalIsoString(value: unknown, field: string): string | undefined;
21
- export declare function normalizeOptionalFiniteNumber(value: unknown, field: string): number | null;
22
- //# sourceMappingURL=normalize.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/util/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,IAAI,CAAC,EAAE,MAAM,CAAC;gBAClB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;CAK3D;AAED,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAE1E;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAK3E;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAK1E;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,GACb,OAAO,GAAG,SAAS,CAMrB;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,GACZ,MAAM,GAAG,SAAS,CAWpB;AAED,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,GACZ,MAAM,GAAG,IAAI,CAQf"}
@@ -1,62 +0,0 @@
1
- class HealthNormalizationError extends Error {
2
- status;
3
- code;
4
- constructor(status, message, code) {
5
- super(message);
6
- this.status = status;
7
- if (code !== void 0) this.code = code;
8
- }
9
- }
10
- function fail(status, message, code) {
11
- throw new HealthNormalizationError(status, message, code);
12
- }
13
- function requireNonEmptyString(value, field) {
14
- if (typeof value !== "string" || value.trim().length === 0) {
15
- fail(400, `${field} must be a non-empty string`);
16
- }
17
- return value.trim();
18
- }
19
- function normalizeOptionalString(value) {
20
- if (value === void 0 || value === null) return void 0;
21
- if (typeof value !== "string") return void 0;
22
- const trimmed = value.trim();
23
- return trimmed.length > 0 ? trimmed : void 0;
24
- }
25
- function normalizeOptionalBoolean(value, _field) {
26
- if (value === void 0 || value === null) return void 0;
27
- if (typeof value === "boolean") return value;
28
- if (value === "true" || value === 1) return true;
29
- if (value === "false" || value === 0) return false;
30
- return void 0;
31
- }
32
- function normalizeOptionalIsoString(value, field) {
33
- if (value === void 0 || value === null) return void 0;
34
- if (typeof value !== "string") {
35
- fail(400, `${field} must be an ISO string`);
36
- }
37
- const trimmed = value.trim();
38
- if (trimmed.length === 0) return void 0;
39
- if (Number.isNaN(Date.parse(trimmed))) {
40
- fail(400, `${field} must be a valid ISO timestamp`);
41
- }
42
- return trimmed;
43
- }
44
- function normalizeOptionalFiniteNumber(value, field) {
45
- if (value === void 0 || value === null) return null;
46
- if (typeof value === "number" && Number.isFinite(value)) return value;
47
- if (typeof value === "string") {
48
- const parsed = Number(value);
49
- if (Number.isFinite(parsed)) return parsed;
50
- }
51
- fail(400, `${field} must be a finite number`);
52
- }
53
- export {
54
- HealthNormalizationError,
55
- fail,
56
- normalizeOptionalBoolean,
57
- normalizeOptionalFiniteNumber,
58
- normalizeOptionalIsoString,
59
- normalizeOptionalString,
60
- requireNonEmptyString
61
- };
62
- //# sourceMappingURL=normalize.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/util/normalize.ts"],"sourcesContent":["/**\n * Minimal value-normalisation helpers used inside plugin-health.\n *\n * Reproduces only the helpers that the moved health-bridge / health-connectors\n * / service-normalize-health files actually call. Kept small and dependency-\n * free so plugin-health stays decoupled from app-lifeops' larger\n * `service-normalize.ts` family.\n *\n * Behaviour matches `app-lifeops/src/lifeops/service-normalize.ts` exactly.\n */\n\nexport class HealthNormalizationError extends Error {\n public readonly status: number;\n public readonly code?: string;\n constructor(status: number, message: string, code?: string) {\n super(message);\n this.status = status;\n if (code !== undefined) this.code = code;\n }\n}\n\nexport function fail(status: number, message: string, code?: string): never {\n throw new HealthNormalizationError(status, message, code);\n}\n\nexport function requireNonEmptyString(value: unknown, field: string): string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n fail(400, `${field} must be a non-empty string`);\n }\n return (value as string).trim();\n}\n\nexport function normalizeOptionalString(value: unknown): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nexport function normalizeOptionalBoolean(\n value: unknown,\n _field: string,\n): boolean | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value === \"boolean\") return value;\n if (value === \"true\" || value === 1) return true;\n if (value === \"false\" || value === 0) return false;\n return undefined;\n}\n\nexport function normalizeOptionalIsoString(\n value: unknown,\n field: string,\n): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value !== \"string\") {\n fail(400, `${field} must be an ISO string`);\n }\n const trimmed = (value as string).trim();\n if (trimmed.length === 0) return undefined;\n if (Number.isNaN(Date.parse(trimmed))) {\n fail(400, `${field} must be a valid ISO timestamp`);\n }\n return trimmed;\n}\n\nexport function normalizeOptionalFiniteNumber(\n value: unknown,\n field: string,\n): number | null {\n if (value === undefined || value === null) return null;\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"string\") {\n const parsed = Number(value);\n if (Number.isFinite(parsed)) return parsed;\n }\n fail(400, `${field} must be a finite number`);\n}\n"],"mappings":"AAWO,MAAM,iCAAiC,MAAM;AAAA,EAClC;AAAA,EACA;AAAA,EAChB,YAAY,QAAgB,SAAiB,MAAe;AAC1D,UAAM,OAAO;AACb,SAAK,SAAS;AACd,QAAI,SAAS,OAAW,MAAK,OAAO;AAAA,EACtC;AACF;AAEO,SAAS,KAAK,QAAgB,SAAiB,MAAsB;AAC1E,QAAM,IAAI,yBAAyB,QAAQ,SAAS,IAAI;AAC1D;AAEO,SAAS,sBAAsB,OAAgB,OAAuB;AAC3E,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,WAAW,GAAG;AAC1D,SAAK,KAAK,GAAG,KAAK,6BAA6B;AAAA,EACjD;AACA,SAAQ,MAAiB,KAAK;AAChC;AAEO,SAAS,wBAAwB,OAAoC;AAC1E,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;AAEO,SAAS,yBACd,OACA,QACqB;AACrB,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,MAAI,OAAO,UAAU,UAAW,QAAO;AACvC,MAAI,UAAU,UAAU,UAAU,EAAG,QAAO;AAC5C,MAAI,UAAU,WAAW,UAAU,EAAG,QAAO;AAC7C,SAAO;AACT;AAEO,SAAS,2BACd,OACA,OACoB;AACpB,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,MAAI,OAAO,UAAU,UAAU;AAC7B,SAAK,KAAK,GAAG,KAAK,wBAAwB;AAAA,EAC5C;AACA,QAAM,UAAW,MAAiB,KAAK;AACvC,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,MAAI,OAAO,MAAM,KAAK,MAAM,OAAO,CAAC,GAAG;AACrC,SAAK,KAAK,GAAG,KAAK,gCAAgC;AAAA,EACpD;AACA,SAAO;AACT;AAEO,SAAS,8BACd,OACA,OACe;AACf,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAChE,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,SAAS,OAAO,KAAK;AAC3B,QAAI,OAAO,SAAS,MAAM,EAAG,QAAO;AAAA,EACtC;AACA,OAAK,KAAK,GAAG,KAAK,0BAA0B;AAC9C;","names":[]}
@@ -1,10 +0,0 @@
1
- /**
2
- * Tiny value-parsing helpers shared across the LifeOps inference layers.
3
- * Lives in its own file so modules that only need parsing don't transitively
4
- * pull in `getZonedDateParts` / timezone utilities.
5
- */
6
- /** Parse an ISO timestamp to milliseconds, returning null on any failure. */
7
- export declare function parseIsoMs(value: string | null | undefined): number | null;
8
- /** Clamp a 0-1 confidence value and round to two decimals. */
9
- export declare function roundConfidence(value: number): number;
10
- //# sourceMappingURL=time-util.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"time-util.d.ts","sourceRoot":"","sources":["../../src/util/time-util.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAI1E;AAED,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGrD"}
@@ -1,14 +0,0 @@
1
- function parseIsoMs(value) {
2
- if (typeof value !== "string" || value.trim().length === 0) return null;
3
- const parsed = Date.parse(value);
4
- return Number.isFinite(parsed) ? parsed : null;
5
- }
6
- function roundConfidence(value) {
7
- if (!Number.isFinite(value)) return 0;
8
- return Math.max(0, Math.min(1, Math.round(value * 100) / 100));
9
- }
10
- export {
11
- parseIsoMs,
12
- roundConfidence
13
- };
14
- //# sourceMappingURL=time-util.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/util/time-util.ts"],"sourcesContent":["/**\n * Tiny value-parsing helpers shared across the LifeOps inference layers.\n * Lives in its own file so modules that only need parsing don't transitively\n * pull in `getZonedDateParts` / timezone utilities.\n */\n\n/** Parse an ISO timestamp to milliseconds, returning null on any failure. */\nexport function parseIsoMs(value: string | null | undefined): number | null {\n if (typeof value !== \"string\" || value.trim().length === 0) return null;\n const parsed = Date.parse(value);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\n/** Clamp a 0-1 confidence value and round to two decimals. */\nexport function roundConfidence(value: number): number {\n if (!Number.isFinite(value)) return 0;\n return Math.max(0, Math.min(1, Math.round(value * 100) / 100));\n}\n"],"mappings":"AAOO,SAAS,WAAW,OAAiD;AAC1E,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,WAAW,EAAG,QAAO;AACnE,QAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,SAAO,OAAO,SAAS,MAAM,IAAI,SAAS;AAC5C;AAGO,SAAS,gBAAgB,OAAuB;AACrD,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC;AAC/D;","names":[]}
@@ -1,17 +0,0 @@
1
- export interface ZonedDateParts {
2
- year: number;
3
- month: number;
4
- day: number;
5
- hour: number;
6
- minute: number;
7
- second: number;
8
- }
9
- export declare function getZonedDateParts(date: Date, timeZone: string): ZonedDateParts;
10
- export declare function getTimeZoneOffsetMinutes(date: Date, timeZone: string): number;
11
- export declare function buildUtcDateFromLocalParts(timeZone: string, parts: ZonedDateParts): Date;
12
- export declare function formatInstantAsRfc3339InTimeZone(value: Date | string, timeZone: string): string;
13
- export declare function addDaysToLocalDate(dateOnly: Pick<ZonedDateParts, "year" | "month" | "day">, dayDelta: number): Pick<ZonedDateParts, "year" | "month" | "day">;
14
- export declare function getWeekdayForLocalDate(dateOnly: Pick<ZonedDateParts, "year" | "month" | "day">): number;
15
- export declare function getLocalDateKey(dateOnly: Pick<ZonedDateParts, "year" | "month" | "day">): string;
16
- export declare function addMinutes(date: Date, minutes: number): Date;
17
- //# sourceMappingURL=time.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../src/util/time.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAuCD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,GACf,cAAc,CAiBhB;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAa7E;AAyBD,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,cAAc,GACpB,IAAI,CAgBN;AAED,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,IAAI,GAAG,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,MAAM,CAmBR;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC,EACxD,QAAQ,EAAE,MAAM,GACf,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC,CAgBhD;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC,GACvD,MAAM,CAIR;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC,GACvD,MAAM,CAIR;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAE5D"}
package/dist/util/time.js DELETED
@@ -1,152 +0,0 @@
1
- const zonedFormatterCache = /* @__PURE__ */ new Map();
2
- const offsetFormatterCache = /* @__PURE__ */ new Map();
3
- function getZonedFormatter(timeZone) {
4
- const cacheKey = `parts:${timeZone}`;
5
- const cached = zonedFormatterCache.get(cacheKey);
6
- if (cached) return cached;
7
- const formatter = new Intl.DateTimeFormat("en-US", {
8
- timeZone,
9
- hour12: false,
10
- year: "numeric",
11
- month: "2-digit",
12
- day: "2-digit",
13
- hour: "2-digit",
14
- minute: "2-digit",
15
- second: "2-digit"
16
- });
17
- zonedFormatterCache.set(cacheKey, formatter);
18
- return formatter;
19
- }
20
- function getOffsetFormatter(timeZone) {
21
- const cacheKey = `offset:${timeZone}`;
22
- const cached = offsetFormatterCache.get(cacheKey);
23
- if (cached) return cached;
24
- const formatter = new Intl.DateTimeFormat("en-US", {
25
- timeZone,
26
- timeZoneName: "shortOffset",
27
- hour: "2-digit",
28
- minute: "2-digit",
29
- second: "2-digit",
30
- hour12: false
31
- });
32
- offsetFormatterCache.set(cacheKey, formatter);
33
- return formatter;
34
- }
35
- function getZonedDateParts(date, timeZone) {
36
- const parts = getZonedFormatter(timeZone).formatToParts(date);
37
- const read = (type) => {
38
- const part = parts.find((candidate) => candidate.type === type)?.value;
39
- if (!part) {
40
- throw new Error(`missing zoned date part: ${type}`);
41
- }
42
- return Number(part);
43
- };
44
- return {
45
- year: read("year"),
46
- month: read("month"),
47
- day: read("day"),
48
- hour: read("hour"),
49
- minute: read("minute"),
50
- second: read("second")
51
- };
52
- }
53
- function getTimeZoneOffsetMinutes(date, timeZone) {
54
- const parts = getOffsetFormatter(timeZone).formatToParts(date);
55
- const token = parts.find((part) => part.type === "timeZoneName")?.value?.trim() ?? "GMT";
56
- if (token === "GMT" || token === "UTC") return 0;
57
- const match = token.match(/^GMT([+-])(\d{1,2})(?::?(\d{2}))?$/i);
58
- if (!match) {
59
- throw new Error(`unsupported offset token: ${token}`);
60
- }
61
- const sign = match[1] === "+" ? 1 : -1;
62
- const hours = Number(match[2]);
63
- const minutes = Number(match[3] ?? "0");
64
- return sign * (hours * 60 + minutes);
65
- }
66
- function formatOffsetToken(offsetMinutes) {
67
- const sign = offsetMinutes >= 0 ? "+" : "-";
68
- const absolute = Math.abs(offsetMinutes);
69
- const hours = Math.trunc(absolute / 60).toString().padStart(2, "0");
70
- const minutes = Math.trunc(absolute % 60).toString().padStart(2, "0");
71
- return `${sign}${hours}:${minutes}`;
72
- }
73
- function localPartsToEpochMs(parts) {
74
- return Date.UTC(
75
- parts.year,
76
- parts.month - 1,
77
- parts.day,
78
- parts.hour,
79
- parts.minute,
80
- parts.second
81
- );
82
- }
83
- function buildUtcDateFromLocalParts(timeZone, parts) {
84
- const baseUtcMs = localPartsToEpochMs(parts);
85
- let candidate = new Date(baseUtcMs);
86
- for (let index = 0; index < 6; index += 1) {
87
- const offsetMinutes = getTimeZoneOffsetMinutes(candidate, timeZone);
88
- const adjusted = new Date(baseUtcMs - offsetMinutes * 6e4);
89
- const actualParts = getZonedDateParts(adjusted, timeZone);
90
- const deltaMinutes = Math.round(
91
- (localPartsToEpochMs(parts) - localPartsToEpochMs(actualParts)) / 6e4
92
- );
93
- if (deltaMinutes === 0) {
94
- return adjusted;
95
- }
96
- candidate = new Date(adjusted.getTime() + deltaMinutes * 6e4);
97
- }
98
- return candidate;
99
- }
100
- function formatInstantAsRfc3339InTimeZone(value, timeZone) {
101
- const date = value instanceof Date ? value : new Date(value);
102
- if (!Number.isFinite(date.getTime())) {
103
- throw new Error(
104
- `invalid datetime for timezone conversion: ${String(value)}`
105
- );
106
- }
107
- const parts = getZonedDateParts(date, timeZone);
108
- const offset = getTimeZoneOffsetMinutes(date, timeZone);
109
- return [
110
- `${parts.year.toString().padStart(4, "0")}-${parts.month.toString().padStart(2, "0")}-${parts.day.toString().padStart(2, "0")}`,
111
- `${parts.hour.toString().padStart(2, "0")}:${parts.minute.toString().padStart(2, "0")}:${parts.second.toString().padStart(2, "0")}`
112
- ].join("T") + formatOffsetToken(offset);
113
- }
114
- function addDaysToLocalDate(dateOnly, dayDelta) {
115
- const utcDate = new Date(
116
- Date.UTC(
117
- dateOnly.year,
118
- dateOnly.month - 1,
119
- dateOnly.day + dayDelta,
120
- 12,
121
- 0,
122
- 0
123
- )
124
- );
125
- return {
126
- year: utcDate.getUTCFullYear(),
127
- month: utcDate.getUTCMonth() + 1,
128
- day: utcDate.getUTCDate()
129
- };
130
- }
131
- function getWeekdayForLocalDate(dateOnly) {
132
- return new Date(
133
- Date.UTC(dateOnly.year, dateOnly.month - 1, dateOnly.day, 12, 0, 0)
134
- ).getUTCDay();
135
- }
136
- function getLocalDateKey(dateOnly) {
137
- return `${dateOnly.year.toString().padStart(4, "0")}-${dateOnly.month.toString().padStart(2, "0")}-${dateOnly.day.toString().padStart(2, "0")}`;
138
- }
139
- function addMinutes(date, minutes) {
140
- return new Date(date.getTime() + minutes * 6e4);
141
- }
142
- export {
143
- addDaysToLocalDate,
144
- addMinutes,
145
- buildUtcDateFromLocalParts,
146
- formatInstantAsRfc3339InTimeZone,
147
- getLocalDateKey,
148
- getTimeZoneOffsetMinutes,
149
- getWeekdayForLocalDate,
150
- getZonedDateParts
151
- };
152
- //# sourceMappingURL=time.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/util/time.ts"],"sourcesContent":["export interface ZonedDateParts {\n year: number;\n month: number;\n day: number;\n hour: number;\n minute: number;\n second: number;\n}\n\nconst zonedFormatterCache = new Map<string, Intl.DateTimeFormat>();\nconst offsetFormatterCache = new Map<string, Intl.DateTimeFormat>();\n\nfunction getZonedFormatter(timeZone: string): Intl.DateTimeFormat {\n const cacheKey = `parts:${timeZone}`;\n const cached = zonedFormatterCache.get(cacheKey);\n if (cached) return cached;\n const formatter = new Intl.DateTimeFormat(\"en-US\", {\n timeZone,\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n });\n zonedFormatterCache.set(cacheKey, formatter);\n return formatter;\n}\n\nfunction getOffsetFormatter(timeZone: string): Intl.DateTimeFormat {\n const cacheKey = `offset:${timeZone}`;\n const cached = offsetFormatterCache.get(cacheKey);\n if (cached) return cached;\n const formatter = new Intl.DateTimeFormat(\"en-US\", {\n timeZone,\n timeZoneName: \"shortOffset\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false,\n });\n offsetFormatterCache.set(cacheKey, formatter);\n return formatter;\n}\n\nexport function getZonedDateParts(\n date: Date,\n timeZone: string,\n): ZonedDateParts {\n const parts = getZonedFormatter(timeZone).formatToParts(date);\n const read = (type: Intl.DateTimeFormatPartTypes) => {\n const part = parts.find((candidate) => candidate.type === type)?.value;\n if (!part) {\n throw new Error(`missing zoned date part: ${type}`);\n }\n return Number(part);\n };\n return {\n year: read(\"year\"),\n month: read(\"month\"),\n day: read(\"day\"),\n hour: read(\"hour\"),\n minute: read(\"minute\"),\n second: read(\"second\"),\n };\n}\n\nexport function getTimeZoneOffsetMinutes(date: Date, timeZone: string): number {\n const parts = getOffsetFormatter(timeZone).formatToParts(date);\n const token =\n parts.find((part) => part.type === \"timeZoneName\")?.value?.trim() ?? \"GMT\";\n if (token === \"GMT\" || token === \"UTC\") return 0;\n const match = token.match(/^GMT([+-])(\\d{1,2})(?::?(\\d{2}))?$/i);\n if (!match) {\n throw new Error(`unsupported offset token: ${token}`);\n }\n const sign = match[1] === \"+\" ? 1 : -1;\n const hours = Number(match[2]);\n const minutes = Number(match[3] ?? \"0\");\n return sign * (hours * 60 + minutes);\n}\n\nfunction formatOffsetToken(offsetMinutes: number): string {\n const sign = offsetMinutes >= 0 ? \"+\" : \"-\";\n const absolute = Math.abs(offsetMinutes);\n const hours = Math.trunc(absolute / 60)\n .toString()\n .padStart(2, \"0\");\n const minutes = Math.trunc(absolute % 60)\n .toString()\n .padStart(2, \"0\");\n return `${sign}${hours}:${minutes}`;\n}\n\nfunction localPartsToEpochMs(parts: ZonedDateParts): number {\n return Date.UTC(\n parts.year,\n parts.month - 1,\n parts.day,\n parts.hour,\n parts.minute,\n parts.second,\n );\n}\n\nexport function buildUtcDateFromLocalParts(\n timeZone: string,\n parts: ZonedDateParts,\n): Date {\n const baseUtcMs = localPartsToEpochMs(parts);\n let candidate = new Date(baseUtcMs);\n for (let index = 0; index < 6; index += 1) {\n const offsetMinutes = getTimeZoneOffsetMinutes(candidate, timeZone);\n const adjusted = new Date(baseUtcMs - offsetMinutes * 60_000);\n const actualParts = getZonedDateParts(adjusted, timeZone);\n const deltaMinutes = Math.round(\n (localPartsToEpochMs(parts) - localPartsToEpochMs(actualParts)) / 60_000,\n );\n if (deltaMinutes === 0) {\n return adjusted;\n }\n candidate = new Date(adjusted.getTime() + deltaMinutes * 60_000);\n }\n return candidate;\n}\n\nexport function formatInstantAsRfc3339InTimeZone(\n value: Date | string,\n timeZone: string,\n): string {\n const date = value instanceof Date ? value : new Date(value);\n if (!Number.isFinite(date.getTime())) {\n throw new Error(\n `invalid datetime for timezone conversion: ${String(value)}`,\n );\n }\n const parts = getZonedDateParts(date, timeZone);\n const offset = getTimeZoneOffsetMinutes(date, timeZone);\n return (\n [\n `${parts.year.toString().padStart(4, \"0\")}-${parts.month\n .toString()\n .padStart(2, \"0\")}-${parts.day.toString().padStart(2, \"0\")}`,\n `${parts.hour.toString().padStart(2, \"0\")}:${parts.minute\n .toString()\n .padStart(2, \"0\")}:${parts.second.toString().padStart(2, \"0\")}`,\n ].join(\"T\") + formatOffsetToken(offset)\n );\n}\n\nexport function addDaysToLocalDate(\n dateOnly: Pick<ZonedDateParts, \"year\" | \"month\" | \"day\">,\n dayDelta: number,\n): Pick<ZonedDateParts, \"year\" | \"month\" | \"day\"> {\n const utcDate = new Date(\n Date.UTC(\n dateOnly.year,\n dateOnly.month - 1,\n dateOnly.day + dayDelta,\n 12,\n 0,\n 0,\n ),\n );\n return {\n year: utcDate.getUTCFullYear(),\n month: utcDate.getUTCMonth() + 1,\n day: utcDate.getUTCDate(),\n };\n}\n\nexport function getWeekdayForLocalDate(\n dateOnly: Pick<ZonedDateParts, \"year\" | \"month\" | \"day\">,\n): number {\n return new Date(\n Date.UTC(dateOnly.year, dateOnly.month - 1, dateOnly.day, 12, 0, 0),\n ).getUTCDay();\n}\n\nexport function getLocalDateKey(\n dateOnly: Pick<ZonedDateParts, \"year\" | \"month\" | \"day\">,\n): string {\n return `${dateOnly.year.toString().padStart(4, \"0\")}-${dateOnly.month\n .toString()\n .padStart(2, \"0\")}-${dateOnly.day.toString().padStart(2, \"0\")}`;\n}\n\nexport function addMinutes(date: Date, minutes: number): Date {\n return new Date(date.getTime() + minutes * 60_000);\n}\n"],"mappings":"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","names":[]}
@@ -1,42 +0,0 @@
1
- /**
2
- * AES-256-GCM encryption helpers for LifeOps connector tokens at rest.
3
- *
4
- * Connector tokens are encrypted at rest with AES-256-GCM using a symmetric
5
- * key sourced from one of:
6
- *
7
- * 1. `ELIZA_TOKEN_ENCRYPTION_KEY` env var (32 raw bytes, base64- or
8
- * hex-encoded). Preferred — operators who manage their own secret
9
- * management can inject the key directly.
10
- * 2. `<credentials-dir>/.encryption-key` file (mode 0600). Generated lazily
11
- * the first time we need to write a token and no env var is configured.
12
- * The agent reuses this key on subsequent boots.
13
- *
14
- * The on-disk format is intentionally minimal: encrypted blobs are JSON
15
- * objects with a top-level `__enc` discriminator.
16
- */
17
- declare const ENVELOPE_VERSION = 1;
18
- declare const ENVELOPE_DISCRIMINATOR: "__enc";
19
- export interface EncryptedTokenEnvelope {
20
- readonly [ENVELOPE_DISCRIMINATOR]: "aes-256-gcm";
21
- readonly v: typeof ENVELOPE_VERSION;
22
- readonly iv: string;
23
- readonly tag: string;
24
- readonly ct: string;
25
- }
26
- /**
27
- * Resolve the symmetric key for token encryption.
28
- *
29
- * Resolution order: env var (`ELIZA_TOKEN_ENCRYPTION_KEY`) → file at
30
- * `<credentialsDir>/.encryption-key`. The file is created (with mode 0600)
31
- * lazily on first call so existing deployments keep working without an env
32
- * var.
33
- */
34
- export declare function resolveTokenEncryptionKey(credentialsDir: string, env?: NodeJS.ProcessEnv): Buffer;
35
- export declare function encryptTokenPayload(plaintextJson: string, key: Buffer): EncryptedTokenEnvelope;
36
- export declare function decryptTokenEnvelope(envelope: EncryptedTokenEnvelope, key: Buffer): string;
37
- /**
38
- * Returns true when the parsed JSON value looks like an encrypted envelope.
39
- */
40
- export declare function isEncryptedTokenEnvelope(value: unknown): value is EncryptedTokenEnvelope;
41
- export {};
42
- //# sourceMappingURL=token-encryption.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"token-encryption.d.ts","sourceRoot":"","sources":["../../src/util/token-encryption.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAWH,QAAA,MAAM,gBAAgB,IAAI,CAAC;AAC3B,QAAA,MAAM,sBAAsB,EAAG,OAAgB,CAAC;AAEhD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,CAAC,sBAAsB,CAAC,EAAE,aAAa,CAAC;IACjD,QAAQ,CAAC,CAAC,EAAE,OAAO,gBAAgB,CAAC;IACpC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAiCD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,cAAc,EAAE,MAAM,EACtB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAMR;AAED,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,GACV,sBAAsB,CAuBxB;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,sBAAsB,EAChC,GAAG,EAAE,MAAM,GACV,MAAM,CAiBR;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,sBAAsB,CAMjC"}
@@ -1,96 +0,0 @@
1
- import crypto from "node:crypto";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- const KEY_ENV_VAR = "ELIZA_TOKEN_ENCRYPTION_KEY";
5
- const KEY_FILENAME = ".encryption-key";
6
- const KEY_BYTES = 32;
7
- const IV_BYTES = 12;
8
- const AUTH_TAG_BYTES = 16;
9
- const ENVELOPE_VERSION = 1;
10
- const ENVELOPE_DISCRIMINATOR = "__enc";
11
- function decodeKeyMaterial(raw) {
12
- const trimmed = raw.trim();
13
- if (/^[0-9a-fA-F]+$/.test(trimmed) && trimmed.length === KEY_BYTES * 2) {
14
- return Buffer.from(trimmed, "hex");
15
- }
16
- const buf = Buffer.from(trimmed, "base64");
17
- if (buf.length === KEY_BYTES) {
18
- return buf;
19
- }
20
- throw new Error(
21
- `${KEY_ENV_VAR} must decode to exactly ${KEY_BYTES} bytes (got ${buf.length})`
22
- );
23
- }
24
- function loadOrCreateKeyFile(credentialsDir) {
25
- const filePath = path.join(credentialsDir, KEY_FILENAME);
26
- if (fs.existsSync(filePath)) {
27
- const raw = fs.readFileSync(filePath, "utf8");
28
- return decodeKeyMaterial(raw);
29
- }
30
- fs.mkdirSync(credentialsDir, { recursive: true, mode: 448 });
31
- const key = crypto.randomBytes(KEY_BYTES);
32
- fs.writeFileSync(filePath, key.toString("base64"), {
33
- encoding: "utf8",
34
- mode: 384
35
- });
36
- return key;
37
- }
38
- function resolveTokenEncryptionKey(credentialsDir, env = process.env) {
39
- const fromEnv = env[KEY_ENV_VAR]?.trim();
40
- if (fromEnv) {
41
- return decodeKeyMaterial(fromEnv);
42
- }
43
- return loadOrCreateKeyFile(credentialsDir);
44
- }
45
- function encryptTokenPayload(plaintextJson, key) {
46
- if (key.length !== KEY_BYTES) {
47
- throw new Error(
48
- `Token encryption key must be ${KEY_BYTES} bytes (got ${key.length})`
49
- );
50
- }
51
- const iv = crypto.randomBytes(IV_BYTES);
52
- const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
53
- const ciphertext = Buffer.concat([
54
- cipher.update(plaintextJson, "utf8"),
55
- cipher.final()
56
- ]);
57
- const authTag = cipher.getAuthTag();
58
- if (authTag.length !== AUTH_TAG_BYTES) {
59
- throw new Error("AES-GCM auth tag had unexpected length");
60
- }
61
- return {
62
- [ENVELOPE_DISCRIMINATOR]: "aes-256-gcm",
63
- v: ENVELOPE_VERSION,
64
- iv: iv.toString("base64"),
65
- tag: authTag.toString("base64"),
66
- ct: ciphertext.toString("base64")
67
- };
68
- }
69
- function decryptTokenEnvelope(envelope, key) {
70
- if (envelope[ENVELOPE_DISCRIMINATOR] !== "aes-256-gcm") {
71
- throw new Error("Unsupported token envelope algorithm");
72
- }
73
- if (envelope.v !== ENVELOPE_VERSION) {
74
- throw new Error(`Unsupported token envelope version: ${envelope.v}`);
75
- }
76
- const iv = Buffer.from(envelope.iv, "base64");
77
- const tag = Buffer.from(envelope.tag, "base64");
78
- const ciphertext = Buffer.from(envelope.ct, "base64");
79
- const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
80
- decipher.setAuthTag(tag);
81
- const plaintext = Buffer.concat([
82
- decipher.update(ciphertext),
83
- decipher.final()
84
- ]);
85
- return plaintext.toString("utf8");
86
- }
87
- function isEncryptedTokenEnvelope(value) {
88
- return typeof value === "object" && value !== null && value[ENVELOPE_DISCRIMINATOR] === "aes-256-gcm";
89
- }
90
- export {
91
- decryptTokenEnvelope,
92
- encryptTokenPayload,
93
- isEncryptedTokenEnvelope,
94
- resolveTokenEncryptionKey
95
- };
96
- //# sourceMappingURL=token-encryption.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/util/token-encryption.ts"],"sourcesContent":["/**\n * AES-256-GCM encryption helpers for LifeOps connector tokens at rest.\n *\n * Connector tokens are encrypted at rest with AES-256-GCM using a symmetric\n * key sourced from one of:\n *\n * 1. `ELIZA_TOKEN_ENCRYPTION_KEY` env var (32 raw bytes, base64- or\n * hex-encoded). Preferred — operators who manage their own secret\n * management can inject the key directly.\n * 2. `<credentials-dir>/.encryption-key` file (mode 0600). Generated lazily\n * the first time we need to write a token and no env var is configured.\n * The agent reuses this key on subsequent boots.\n *\n * The on-disk format is intentionally minimal: encrypted blobs are JSON\n * objects with a top-level `__enc` discriminator.\n */\n\nimport crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nconst KEY_ENV_VAR = \"ELIZA_TOKEN_ENCRYPTION_KEY\";\nconst KEY_FILENAME = \".encryption-key\";\nconst KEY_BYTES = 32; // AES-256\nconst IV_BYTES = 12; // GCM standard nonce length\nconst AUTH_TAG_BYTES = 16;\nconst ENVELOPE_VERSION = 1;\nconst ENVELOPE_DISCRIMINATOR = \"__enc\" as const;\n\nexport interface EncryptedTokenEnvelope {\n readonly [ENVELOPE_DISCRIMINATOR]: \"aes-256-gcm\";\n readonly v: typeof ENVELOPE_VERSION;\n readonly iv: string;\n readonly tag: string;\n readonly ct: string;\n}\n\nfunction decodeKeyMaterial(raw: string): Buffer {\n const trimmed = raw.trim();\n // Hex first (deterministic length check).\n if (/^[0-9a-fA-F]+$/.test(trimmed) && trimmed.length === KEY_BYTES * 2) {\n return Buffer.from(trimmed, \"hex\");\n }\n // Otherwise assume base64 / base64url.\n const buf = Buffer.from(trimmed, \"base64\");\n if (buf.length === KEY_BYTES) {\n return buf;\n }\n throw new Error(\n `${KEY_ENV_VAR} must decode to exactly ${KEY_BYTES} bytes (got ${buf.length})`,\n );\n}\n\nfunction loadOrCreateKeyFile(credentialsDir: string): Buffer {\n const filePath = path.join(credentialsDir, KEY_FILENAME);\n if (fs.existsSync(filePath)) {\n const raw = fs.readFileSync(filePath, \"utf8\");\n return decodeKeyMaterial(raw);\n }\n fs.mkdirSync(credentialsDir, { recursive: true, mode: 0o700 });\n const key = crypto.randomBytes(KEY_BYTES);\n fs.writeFileSync(filePath, key.toString(\"base64\"), {\n encoding: \"utf8\",\n mode: 0o600,\n });\n return key;\n}\n\n/**\n * Resolve the symmetric key for token encryption.\n *\n * Resolution order: env var (`ELIZA_TOKEN_ENCRYPTION_KEY`) → file at\n * `<credentialsDir>/.encryption-key`. The file is created (with mode 0600)\n * lazily on first call so existing deployments keep working without an env\n * var.\n */\nexport function resolveTokenEncryptionKey(\n credentialsDir: string,\n env: NodeJS.ProcessEnv = process.env,\n): Buffer {\n const fromEnv = env[KEY_ENV_VAR]?.trim();\n if (fromEnv) {\n return decodeKeyMaterial(fromEnv);\n }\n return loadOrCreateKeyFile(credentialsDir);\n}\n\nexport function encryptTokenPayload(\n plaintextJson: string,\n key: Buffer,\n): EncryptedTokenEnvelope {\n if (key.length !== KEY_BYTES) {\n throw new Error(\n `Token encryption key must be ${KEY_BYTES} bytes (got ${key.length})`,\n );\n }\n const iv = crypto.randomBytes(IV_BYTES);\n const cipher = crypto.createCipheriv(\"aes-256-gcm\", key, iv);\n const ciphertext = Buffer.concat([\n cipher.update(plaintextJson, \"utf8\"),\n cipher.final(),\n ]);\n const authTag = cipher.getAuthTag();\n if (authTag.length !== AUTH_TAG_BYTES) {\n throw new Error(\"AES-GCM auth tag had unexpected length\");\n }\n return {\n [ENVELOPE_DISCRIMINATOR]: \"aes-256-gcm\",\n v: ENVELOPE_VERSION,\n iv: iv.toString(\"base64\"),\n tag: authTag.toString(\"base64\"),\n ct: ciphertext.toString(\"base64\"),\n };\n}\n\nexport function decryptTokenEnvelope(\n envelope: EncryptedTokenEnvelope,\n key: Buffer,\n): string {\n if (envelope[ENVELOPE_DISCRIMINATOR] !== \"aes-256-gcm\") {\n throw new Error(\"Unsupported token envelope algorithm\");\n }\n if (envelope.v !== ENVELOPE_VERSION) {\n throw new Error(`Unsupported token envelope version: ${envelope.v}`);\n }\n const iv = Buffer.from(envelope.iv, \"base64\");\n const tag = Buffer.from(envelope.tag, \"base64\");\n const ciphertext = Buffer.from(envelope.ct, \"base64\");\n const decipher = crypto.createDecipheriv(\"aes-256-gcm\", key, iv);\n decipher.setAuthTag(tag);\n const plaintext = Buffer.concat([\n decipher.update(ciphertext),\n decipher.final(),\n ]);\n return plaintext.toString(\"utf8\");\n}\n\n/**\n * Returns true when the parsed JSON value looks like an encrypted envelope.\n */\nexport function isEncryptedTokenEnvelope(\n value: unknown,\n): value is EncryptedTokenEnvelope {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (value as Record<string, unknown>)[ENVELOPE_DISCRIMINATOR] === \"aes-256-gcm\"\n );\n}\n"],"mappings":"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","names":[]}