@jungjaehoon/mama-os 0.13.3 → 0.14.2

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 (89) hide show
  1. package/README.md +1 -1
  2. package/dist/agent/agent-loop.d.ts +1 -1
  3. package/dist/agent/agent-loop.d.ts.map +1 -1
  4. package/dist/agent/agent-loop.js +9 -2
  5. package/dist/agent/agent-loop.js.map +1 -1
  6. package/dist/agent/codex-mcp-process.d.ts +1 -0
  7. package/dist/agent/codex-mcp-process.d.ts.map +1 -1
  8. package/dist/agent/codex-mcp-process.js +50 -1
  9. package/dist/agent/codex-mcp-process.js.map +1 -1
  10. package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
  11. package/dist/agent/gateway-tool-executor.js +101 -1
  12. package/dist/agent/gateway-tool-executor.js.map +1 -1
  13. package/dist/agent/types.d.ts +1 -1
  14. package/dist/agent/types.d.ts.map +1 -1
  15. package/dist/agent/types.js.map +1 -1
  16. package/dist/api/auth-middleware.d.ts +46 -0
  17. package/dist/api/auth-middleware.d.ts.map +1 -0
  18. package/dist/api/auth-middleware.js +206 -0
  19. package/dist/api/auth-middleware.js.map +1 -0
  20. package/dist/api/cron-handler.d.ts.map +1 -1
  21. package/dist/api/cron-handler.js +10 -0
  22. package/dist/api/cron-handler.js.map +1 -1
  23. package/dist/api/graph-api.d.ts.map +1 -1
  24. package/dist/api/graph-api.js +53 -57
  25. package/dist/api/graph-api.js.map +1 -1
  26. package/dist/api/index.d.ts +2 -2
  27. package/dist/api/index.d.ts.map +1 -1
  28. package/dist/api/index.js +32 -1
  29. package/dist/api/index.js.map +1 -1
  30. package/dist/api/token-handler.d.ts +4 -4
  31. package/dist/api/token-handler.d.ts.map +1 -1
  32. package/dist/api/token-handler.js.map +1 -1
  33. package/dist/auth/oauth-manager.d.ts.map +1 -1
  34. package/dist/auth/oauth-manager.js +16 -1
  35. package/dist/auth/oauth-manager.js.map +1 -1
  36. package/dist/cli/commands/start.d.ts.map +1 -1
  37. package/dist/cli/commands/start.js +244 -27
  38. package/dist/cli/commands/start.js.map +1 -1
  39. package/dist/gateways/attachment-utils.d.ts.map +1 -1
  40. package/dist/gateways/attachment-utils.js +292 -4
  41. package/dist/gateways/attachment-utils.js.map +1 -1
  42. package/dist/gateways/channel-history.d.ts +3 -3
  43. package/dist/gateways/channel-history.d.ts.map +1 -1
  44. package/dist/gateways/channel-history.js +38 -2
  45. package/dist/gateways/channel-history.js.map +1 -1
  46. package/dist/gateways/plugin-loader.d.ts.map +1 -1
  47. package/dist/gateways/plugin-loader.js +12 -0
  48. package/dist/gateways/plugin-loader.js.map +1 -1
  49. package/dist/gateways/session-store.d.ts +2 -2
  50. package/dist/gateways/session-store.d.ts.map +1 -1
  51. package/dist/gateways/session-store.js.map +1 -1
  52. package/dist/gateways/slack.d.ts.map +1 -1
  53. package/dist/gateways/slack.js +0 -2
  54. package/dist/gateways/slack.js.map +1 -1
  55. package/dist/multi-agent/swarm/swarm-db.d.ts +12 -12
  56. package/dist/multi-agent/swarm/swarm-db.d.ts.map +1 -1
  57. package/dist/multi-agent/swarm/swarm-db.js +2 -2
  58. package/dist/multi-agent/swarm/swarm-db.js.map +1 -1
  59. package/dist/multi-agent/swarm/swarm-manager.d.ts +2 -2
  60. package/dist/multi-agent/swarm/swarm-manager.d.ts.map +1 -1
  61. package/dist/multi-agent/swarm/swarm-manager.js.map +1 -1
  62. package/dist/multi-agent/swarm/swarm-task-runner.d.ts.map +1 -1
  63. package/dist/multi-agent/swarm/swarm-task-runner.js.map +1 -1
  64. package/dist/observability/metrics-store.d.ts.map +1 -1
  65. package/dist/observability/metrics-store.js +2 -2
  66. package/dist/observability/metrics-store.js.map +1 -1
  67. package/dist/scheduler/cron-scheduler.d.ts.map +1 -1
  68. package/dist/scheduler/cron-scheduler.js +7 -0
  69. package/dist/scheduler/cron-scheduler.js.map +1 -1
  70. package/dist/scheduler/heartbeat.d.ts +1 -0
  71. package/dist/scheduler/heartbeat.d.ts.map +1 -1
  72. package/dist/scheduler/heartbeat.js +9 -1
  73. package/dist/scheduler/heartbeat.js.map +1 -1
  74. package/dist/scheduler/schedule-store.d.ts +2 -2
  75. package/dist/scheduler/schedule-store.d.ts.map +1 -1
  76. package/dist/scheduler/schedule-store.js.map +1 -1
  77. package/dist/security/security-monitor.d.ts +34 -0
  78. package/dist/security/security-monitor.d.ts.map +1 -0
  79. package/dist/security/security-monitor.js +662 -0
  80. package/dist/security/security-monitor.js.map +1 -0
  81. package/dist/security/trusted-proxy.d.ts +9 -0
  82. package/dist/security/trusted-proxy.d.ts.map +1 -0
  83. package/dist/security/trusted-proxy.js +36 -0
  84. package/dist/security/trusted-proxy.js.map +1 -0
  85. package/dist/sqlite.d.ts +29 -0
  86. package/dist/sqlite.d.ts.map +1 -0
  87. package/dist/sqlite.js +124 -0
  88. package/dist/sqlite.js.map +1 -0
  89. package/package.json +5 -6
@@ -14,6 +14,7 @@ const promises_1 = require("node:fs/promises");
14
14
  const node_fs_1 = require("node:fs");
15
15
  const node_os_1 = require("node:os");
16
16
  const node_path_1 = require("node:path");
17
+ const node_crypto_1 = require("node:crypto");
17
18
  const types_js_1 = require("./types.js");
18
19
  /**
19
20
  * Anthropic OAuth constants (from OpenClaw analysis)
@@ -223,7 +224,21 @@ class OAuthManager {
223
224
  refreshToken: token.refreshToken,
224
225
  expiresAt: token.expiresAt,
225
226
  };
226
- await (0, promises_1.writeFile)(this.credentialsPath, JSON.stringify(data, null, 2), 'utf-8');
227
+ // Atomic write: write to temp file, then rename to prevent corruption on crash
228
+ const tmpPath = `${this.credentialsPath}.${(0, node_crypto_1.randomBytes)(4).toString('hex')}.tmp`;
229
+ await (0, promises_1.writeFile)(tmpPath, JSON.stringify(data, null, 2), 'utf-8');
230
+ try {
231
+ await (0, promises_1.rename)(tmpPath, this.credentialsPath);
232
+ }
233
+ catch (renameError) {
234
+ try {
235
+ await (0, promises_1.unlink)(tmpPath);
236
+ }
237
+ catch {
238
+ // Ignore cleanup failure and rethrow original rename error below.
239
+ }
240
+ throw renameError;
241
+ }
227
242
  }
228
243
  catch (error) {
229
244
  if (error instanceof types_js_1.OAuthError) {
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-manager.js","sourceRoot":"","sources":["../../src/auth/oauth-manager.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAEH,+CAAuD;AACvD,qCAAqC;AACrC,qCAAkC;AAClC,yCAAiC;AAYjC,yCAAwC;AAExC;;GAEG;AACH,MAAM,SAAS,GAAG,8CAA8C,CAAC;AACjE,MAAM,SAAS,GAAG,kDAAkD,CAAC;AAErE;;GAEG;AACH,MAAM,wBAAwB,GAAG,2BAA2B,CAAC;AAC7D,MAAM,oBAAoB,GAAG,MAAM,CAAC,CAAC,WAAW;AAChD,MAAM,yBAAyB,GAAG,OAAO,CAAC,CAAC,aAAa;AACxD,MAAM,gBAAgB,GAAG,OAAO,CAAC,CAAC,2CAA2C;AAE7E,MAAa,YAAY;IACN,eAAe,CAAS;IACxB,UAAU,CAAS;IACnB,eAAe,CAAS;IACxB,OAAO,CAAe;IAE/B,KAAK,GAAuB,IAAI,CAAC;IAEzC,YAAY,UAA+B,EAAE;QAC3C,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,IAAA,gBAAI,EAAC,IAAA,iBAAO,GAAE,EAAE,wBAAwB,CAAC,CAAC;QAC5F,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,oBAAoB,CAAC;QAC7D,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,yBAAyB,CAAC;QAC5E,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC;IAC1C,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,QAAQ;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,oBAAoB;QACpB,IAAI,IAAI,CAAC,KAAK,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAC9D,mDAAmD;YACnD,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;gBACnD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC;YACtC,CAAC;QACH,CAAC;QAED,6BAA6B;QAC7B,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;QAEjD,6BAA6B;QAC7B,IAAI,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7C,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;YACzE,MAAM,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC;YAE5C,IAAI,CAAC,KAAK,GAAG;gBACX,KAAK,EAAE,cAAc;gBACrB,QAAQ,EAAE,GAAG;gBACb,gBAAgB,EAAE,WAAW,CAAC,gBAAgB;gBAC9C,aAAa,EAAE,WAAW,CAAC,aAAa;aACzC,CAAC;YAEF,OAAO,cAAc,CAAC,WAAW,CAAC;QACpC,CAAC;QAED,iCAAiC;QACjC,IAAI,CAAC,KAAK,GAAG;YACX,KAAK,EAAE;gBACL,WAAW,EAAE,WAAW,CAAC,WAAW;gBACpC,YAAY,EAAE,WAAW,CAAC,YAAY;gBACtC,SAAS,EAAE,WAAW,CAAC,SAAS;aACjC;YACD,QAAQ,EAAE,GAAG;YACb,gBAAgB,EAAE,WAAW,CAAC,gBAAgB;YAC9C,aAAa,EAAE,WAAW,CAAC,aAAa;SACzC,CAAC;QAEF,OAAO,WAAW,CAAC,WAAW,CAAC;IACjC,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,SAAS;QACb,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;YACjD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,SAAS,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;YAEnE,OAAO;gBACL,KAAK,EAAE,WAAW,CAAC,SAAS,GAAG,GAAG;gBAClC,SAAS,EAAE,WAAW,CAAC,SAAS;gBAChC,SAAS,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gBACxC,YAAY,EAAE,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,SAAS,CAAC;gBACtD,gBAAgB,EAAE,WAAW,CAAC,gBAAgB;gBAC9C,aAAa,EAAE,WAAW,CAAC,aAAa;aACzC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,qBAAU,EAAE,CAAC;gBAChC,OAAO;oBACL,KAAK,EAAE,KAAK;oBACZ,SAAS,EAAE,IAAI;oBACf,SAAS,EAAE,IAAI;oBACf,YAAY,EAAE,KAAK;oBACnB,KAAK,EAAE,KAAK,CAAC,OAAO;iBACrB,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,YAAY;QAChB,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;QACjD,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;QACzE,MAAM,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC;QAE5C,IAAI,CAAC,KAAK,GAAG;YACX,KAAK,EAAE,cAAc;YACrB,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE;YACpB,gBAAgB,EAAE,WAAW,CAAC,gBAAgB;YAC9C,aAAa,EAAE,WAAW,CAAC,aAAa;SACzC,CAAC;QAEF,OAAO,cAAc,CAAC,WAAW,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,UAAU;QACR,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED;;OAEG;IACK,YAAY,CAAC,SAAiB;QACpC,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,SAAS,GAAG,IAAI,CAAC,eAAe,CAAC;IACxD,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,eAAe;QAC3B,IAAI,CAAC,IAAA,oBAAU,EAAC,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,qBAAU,CAClB,mGAAmG,IAAI,CAAC,eAAe,EAAE,EACzH,uBAAuB,CACxB,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAA,mBAAQ,EAAC,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;YAC9D,MAAM,IAAI,GAA0B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAExD,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;gBACxB,MAAM,IAAI,qBAAU,CAClB,yFAAyF,EACzF,qBAAqB,CACtB,CAAC;YACJ,CAAC;YAED,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC;YAEjC,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;gBAClE,MAAM,IAAI,qBAAU,CAClB,qFAAqF,EACrF,qBAAqB,CACtB,CAAC;YACJ,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,qBAAU,EAAE,CAAC;gBAChC,MAAM,KAAK,CAAC;YACd,CAAC;YACD,MAAM,IAAI,qBAAU,CAClB,oCAAoC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAC5F,qBAAqB,EACrB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC3C,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,YAAY,CAAC,YAAoB;QAC7C,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE;gBAC7C,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;iBACnC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,UAAU,EAAE,eAAe;oBAC3B,SAAS,EAAE,SAAS;oBACpB,aAAa,EAAE,YAAY;iBAC5B,CAAC;aACH,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACxC,MAAM,IAAI,qBAAU,CAClB,yBAAyB,QAAQ,CAAC,MAAM,MAAM,SAAS,EAAE,EACzD,gBAAgB,CACjB,CAAC;YACJ,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAyB,CAAC;YAE7D,qEAAqE;YACrE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,GAAG,gBAAgB,CAAC;YAEzE,OAAO;gBACL,WAAW,EAAE,IAAI,CAAC,YAAY;gBAC9B,YAAY,EAAE,IAAI,CAAC,aAAa;gBAChC,SAAS;aACV,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,qBAAU,EAAE,CAAC;gBAChC,MAAM,KAAK,CAAC;YACd,CAAC;YACD,MAAM,IAAI,qBAAU,CAClB,uCAAuC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAC/F,eAAe,EACf,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC3C,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,gBAAgB,CAAC,KAAiB;QAC9C,IAAI,CAAC;YACH,8CAA8C;YAC9C,MAAM,OAAO,GAAG,MAAM,IAAA,mBAAQ,EAAC,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;YAC9D,MAAM,IAAI,GAA0B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAExD,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;gBACxB,MAAM,IAAI,qBAAU,CAClB,0DAA0D,EAC1D,kBAAkB,CACnB,CAAC;YACJ,CAAC;YAED,+BAA+B;YAC/B,IAAI,CAAC,aAAa,GAAG;gBACnB,GAAG,IAAI,CAAC,aAAa;gBACrB,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,SAAS,EAAE,KAAK,CAAC,SAAS;aAC3B,CAAC;YAEF,MAAM,IAAA,oBAAS,EAAC,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAChF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,qBAAU,EAAE,CAAC;gBAChC,MAAM,KAAK,CAAC;YACd,CAAC;YACD,MAAM,IAAI,qBAAU,CAClB,qCAAqC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAC7F,kBAAkB,EAClB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC3C,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AApQD,oCAoQC"}
1
+ {"version":3,"file":"oauth-manager.js","sourceRoot":"","sources":["../../src/auth/oauth-manager.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/start.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAyBH,OAAO,EACL,UAAU,EAKX,MAAM,6BAA6B,CAAC;AA2crC;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwG5E;AAuQD;;GAEG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,EAC9C,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GACtC,OAAO,CAAC,IAAI,CAAC,CAi2Df"}
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/start.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAwBH,OAAO,EACL,UAAU,EAKX,MAAM,6BAA6B,CAAC;AAihBrC;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwG5E;AAuQD;;GAEG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,EAC9C,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GACtC,OAAO,CAAC,IAAI,CAAC,CAkjEf"}
@@ -47,7 +47,6 @@ const node_child_process_1 = require("node:child_process");
47
47
  const node_events_1 = require("node:events");
48
48
  const node_fs_1 = require("node:fs");
49
49
  const node_os_1 = require("node:os");
50
- const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
51
50
  const express_1 = __importDefault(require("express"));
52
51
  const node_path_1 = __importStar(require("node:path"));
53
52
  const ws_1 = require("ws");
@@ -67,6 +66,8 @@ const metrics_cleanup_js_1 = require("../../observability/metrics-cleanup.js");
67
66
  const health_score_js_1 = require("../../observability/health-score.js");
68
67
  const health_check_js_1 = require("../../observability/health-check.js");
69
68
  const upload_handler_js_1 = require("../../api/upload-handler.js");
69
+ const auth_middleware_js_1 = require("../../api/auth-middleware.js");
70
+ const security_monitor_js_1 = require("../../security/security-monitor.js");
70
71
  const setup_websocket_js_1 = require("../../setup/setup-websocket.js");
71
72
  // Onboarding state imports removed — onboarding is handled by Setup Wizard only
72
73
  const graph_api_js_1 = require("../../api/graph-api.js");
@@ -76,12 +77,40 @@ const { DebugLogger } = debugLogger;
76
77
  const startLogger = new DebugLogger('start');
77
78
  const skill_registry_js_1 = require("../../skills/skill-registry.js");
78
79
  const node_http_1 = __importDefault(require("node:http"));
80
+ const sqlite_js_1 = __importDefault(require("../../sqlite.js"));
79
81
  // Port configuration — single source of truth
80
82
  /** Public-facing API server port (REST API, Viewer UI, Setup Wizard) */
81
83
  const API_PORT = 3847;
82
84
  /** Internal embedding server port (model inference, mobile chat, graph) */
83
85
  const EMBEDDING_PORT = 3849;
86
+ function parseSecurityAlertTargets(config) {
87
+ const rawTargets = process.env.MAMA_SECURITY_ALERT_CHANNELS;
88
+ if (rawTargets && rawTargets.trim()) {
89
+ return rawTargets
90
+ .split(',')
91
+ .map((entry) => entry.trim())
92
+ .filter(Boolean)
93
+ .map((entry) => {
94
+ const [gateway, channelId] = entry.split(':', 2);
95
+ if ((gateway === 'discord' || gateway === 'slack') && channelId) {
96
+ return { gateway, channelId };
97
+ }
98
+ return null;
99
+ })
100
+ .filter((target) => target !== null);
101
+ }
102
+ if (config.discord?.default_channel_id) {
103
+ return [{ gateway: 'discord', channelId: config.discord.default_channel_id }];
104
+ }
105
+ const slackConfig = config.slack;
106
+ const slackDefaultChannel = slackConfig?.default_channel || slackConfig?.default_channel_id;
107
+ if (slackDefaultChannel) {
108
+ return [{ gateway: 'slack', channelId: slackDefaultChannel }];
109
+ }
110
+ return [];
111
+ }
84
112
  let embeddingServer = null;
113
+ let embeddingShutdownToken = null;
85
114
  function normalizeDiscordGuilds(raw) {
86
115
  // Reject arrays - they pass typeof 'object' check but get coerced to numeric keys
87
116
  if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
@@ -164,7 +193,7 @@ async function waitForPortAvailable(port, maxWaitMs = 5000) {
164
193
  * SECURITY P1: Validates health response before reuse
165
194
  * SECURITY P1: Uses port polling instead of fixed timeout
166
195
  */
167
- async function checkAndTakeoverExistingServer(port) {
196
+ async function checkAndTakeoverExistingServer(port, shutdownToken) {
168
197
  const targetModel = (0, config_loader_1.getModelName)();
169
198
  const targetDim = (0, config_loader_1.getEmbeddingDim)();
170
199
  return new Promise((resolve) => {
@@ -214,9 +243,14 @@ async function checkAndTakeoverExistingServer(port) {
214
243
  timeout: 2000,
215
244
  // SECURITY P1: Pass shutdown token
216
245
  headers: {
217
- 'X-Shutdown-Token': process.env.MAMA_SHUTDOWN_TOKEN || '',
246
+ 'X-Shutdown-Token': shutdownToken || process.env.MAMA_SHUTDOWN_TOKEN || '',
218
247
  },
219
- }, async () => {
248
+ }, async (shutdownRes) => {
249
+ if ((shutdownRes.statusCode ?? 0) < 200 || (shutdownRes.statusCode ?? 0) >= 300) {
250
+ console.warn(`[EmbeddingServer] Takeover shutdown rejected with HTTP ${shutdownRes.statusCode ?? 0}`);
251
+ resolve(false);
252
+ return;
253
+ }
220
254
  console.log('[EmbeddingServer] MCP server shutdown requested');
221
255
  // SECURITY P1: Use port polling instead of fixed timeout
222
256
  const portAvailable = await waitForPortAvailable(port, 10000);
@@ -260,14 +294,18 @@ sessionStore,
260
294
  graphHandler) {
261
295
  const port = EMBEDDING_PORT;
262
296
  try {
297
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
298
+ const embeddingServerModule = require('@jungjaehoon/mama-core/embedding-server');
299
+ embeddingShutdownToken =
300
+ typeof embeddingServerModule.SHUTDOWN_TOKEN === 'string'
301
+ ? embeddingServerModule.SHUTDOWN_TOKEN
302
+ : null;
263
303
  // Check if server already running
264
- const existingHasChat = await checkAndTakeoverExistingServer(port);
304
+ const existingHasChat = await checkAndTakeoverExistingServer(port, embeddingShutdownToken);
265
305
  if (existingHasChat) {
266
306
  // Another Standalone is running with chat, no need to start
267
307
  return;
268
308
  }
269
- // eslint-disable-next-line @typescript-eslint/no-require-imports
270
- const embeddingServerModule = require('@jungjaehoon/mama-core/embedding-server');
271
309
  embeddingServer = await embeddingServerModule.startEmbeddingServer(port, {
272
310
  messageRouter,
273
311
  sessionStore,
@@ -782,7 +820,7 @@ async function runAgentLoop(config, options = {}) {
782
820
  const oauthManager = new index_js_1.OAuthManager();
783
821
  // Initialize database for session storage
784
822
  const dbPath = (0, config_manager_js_1.expandPath)(config.database.path).replace('mama-memory.db', 'mama-sessions.db');
785
- const db = new better_sqlite3_1.default(dbPath);
823
+ const db = new sqlite_js_1.default(dbPath);
786
824
  // Initialize metrics store (respects config.metrics.enabled)
787
825
  const metricsEnabled = config.metrics?.enabled !== false;
788
826
  let metricsStore = null;
@@ -928,9 +966,8 @@ async function runAgentLoop(config, options = {}) {
928
966
  // - config.yaml roles.definitions.*.allowedTools / blockedTools / allowedPaths
929
967
  // - Multi-agent ToolPermissionManager (tier-based tool access)
930
968
  // - Source-based role mapping (viewer=os_agent, discord=chat_bot, etc.)
931
- // Claude CLI's interactive permission system is bypassed because it cannot work without a TTY.
932
- // This is controlled solely by config.yaml (multi_agent.dangerouslySkipPermissions, default: true).
933
- // DO NOT add env-var gates here — MAMA manages its own security via config.yaml roles.
969
+ // Headless daemon no TTY for interactive permission prompts.
970
+ // Security is enforced at the API/network layer (auth-middleware), not Claude CLI permissions.
934
971
  dangerouslySkipPermissions: config.multi_agent?.dangerouslySkipPermissions ?? true,
935
972
  sessionKey: 'default', // Will be updated per message
936
973
  systemPrompt: systemPrompt + (osCapabilities ? '\n\n---\n\n' + osCapabilities : ''),
@@ -1330,6 +1367,40 @@ async function runAgentLoop(config, options = {}) {
1330
1367
  if (slackGateway) {
1331
1368
  healthCheckService.addGateway('slack', slackGateway);
1332
1369
  }
1370
+ const securityAlertTargets = parseSecurityAlertTargets(config).filter((target) => {
1371
+ if (target.gateway === 'discord') {
1372
+ return !!discordGateway;
1373
+ }
1374
+ return !!slackGateway;
1375
+ });
1376
+ if (securityAlertTargets.length > 0) {
1377
+ (0, security_monitor_js_1.setSecurityAlertSender)(async (event) => {
1378
+ const message = (0, security_monitor_js_1.formatSecurityAlert)(event);
1379
+ const results = await Promise.allSettled(securityAlertTargets.map(async (target) => {
1380
+ if (target.gateway === 'discord' && discordGateway) {
1381
+ await discordGateway.sendMessage(target.channelId, message);
1382
+ return;
1383
+ }
1384
+ if (target.gateway === 'slack' && slackGateway) {
1385
+ await slackGateway.sendMessage(target.channelId, message);
1386
+ }
1387
+ }));
1388
+ results.forEach((result, index) => {
1389
+ if (result.status === 'rejected') {
1390
+ const target = securityAlertTargets[index];
1391
+ startLogger.warn('[SECURITY] Failed to deliver security alert to target', {
1392
+ gateway: target?.gateway || 'unknown',
1393
+ channelId: target?.channelId || 'unknown',
1394
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
1395
+ });
1396
+ }
1397
+ });
1398
+ });
1399
+ }
1400
+ else {
1401
+ (0, security_monitor_js_1.setSecurityAlertSender)(null);
1402
+ startLogger.warn('[SECURITY] No active security alert target configured. Set MAMA_SECURITY_ALERT_CHANNELS or configure an active Discord/Slack default channel.');
1403
+ }
1333
1404
  // Wire cron results directly to gateways (bypasses OS agent entirely)
1334
1405
  // Instantiated for side effects: subscribes to cronEmitter events
1335
1406
  new index_js_4.CronResultRouter({
@@ -1612,7 +1683,7 @@ async function runAgentLoop(config, options = {}) {
1612
1683
  }
1613
1684
  });
1614
1685
  // Add Discord message sending endpoint
1615
- apiServer.app.post('/api/discord/send', async (req, res) => {
1686
+ apiServer.app.post('/api/discord/send', auth_middleware_js_1.requireAuth, async (req, res) => {
1616
1687
  try {
1617
1688
  const { channelId, message } = req.body;
1618
1689
  if (!channelId || !message) {
@@ -1634,7 +1705,7 @@ async function runAgentLoop(config, options = {}) {
1634
1705
  }
1635
1706
  });
1636
1707
  // Add Slack message/file sending endpoint
1637
- apiServer.app.post('/api/slack/send', async (req, res) => {
1708
+ apiServer.app.post('/api/slack/send', auth_middleware_js_1.requireAuth, async (req, res) => {
1638
1709
  try {
1639
1710
  const { channelId, message, filePath, caption } = req.body;
1640
1711
  if (!channelId || (!message && !filePath)) {
@@ -1698,7 +1769,7 @@ async function runAgentLoop(config, options = {}) {
1698
1769
  }
1699
1770
  });
1700
1771
  // Add Discord cron job endpoint (run prompt and send result to Discord)
1701
- apiServer.app.post('/api/discord/cron', async (req, res) => {
1772
+ apiServer.app.post('/api/discord/cron', auth_middleware_js_1.requireAuth, async (req, res) => {
1702
1773
  try {
1703
1774
  const { channelId, prompt } = req.body;
1704
1775
  if (!channelId || !prompt) {
@@ -1720,7 +1791,7 @@ async function runAgentLoop(config, options = {}) {
1720
1791
  }
1721
1792
  });
1722
1793
  // Report endpoint - collect data and generate report (OpenClaw migration)
1723
- apiServer.app.post('/api/report', async (req, res) => {
1794
+ apiServer.app.post('/api/report', auth_middleware_js_1.requireAuth, async (req, res) => {
1724
1795
  const { exec } = await import('child_process');
1725
1796
  const { promisify } = await import('util');
1726
1797
  const execAsync = promisify(exec);
@@ -1793,7 +1864,7 @@ Keep the report under 2000 characters as it will be sent to Discord.`;
1793
1864
  }
1794
1865
  });
1795
1866
  // Screenshot endpoint - take HTML screenshot and send to Discord
1796
- apiServer.app.post('/api/screenshot', async (req, res) => {
1867
+ apiServer.app.post('/api/screenshot', auth_middleware_js_1.requireAuth, async (req, res) => {
1797
1868
  const { spawn } = await import('child_process');
1798
1869
  const path = await import('path');
1799
1870
  try {
@@ -1881,7 +1952,7 @@ Keep the report under 2000 characters as it will be sent to Discord.`;
1881
1952
  });
1882
1953
  // Send image endpoint
1883
1954
  // SECURITY P0: Path traversal prevention with 4-layer validation
1884
- apiServer.app.post('/api/discord/image', async (req, res) => {
1955
+ apiServer.app.post('/api/discord/image', auth_middleware_js_1.requireAuth, async (req, res) => {
1885
1956
  const path = await import('path');
1886
1957
  const fs = await import('fs/promises');
1887
1958
  try {
@@ -1956,6 +2027,20 @@ Keep the report under 2000 characters as it will be sent to Discord.`;
1956
2027
  });
1957
2028
  // Upload/download media endpoints
1958
2029
  apiServer.app.use('/api', (0, upload_handler_js_1.createUploadRouter)());
2030
+ // Auth gate for /graph/* write endpoints (not covered by /api middleware)
2031
+ apiServer.app.use('/graph', (req, res, next) => {
2032
+ const isRead = req.method === 'GET' || req.method === 'HEAD';
2033
+ if (!isRead && !(0, auth_middleware_js_1.isAuthenticated)(req)) {
2034
+ (0, auth_middleware_js_1.logUnauthorizedAttempt)(req);
2035
+ res.status(401).json({
2036
+ error: true,
2037
+ code: 'UNAUTHORIZED',
2038
+ message: 'Authentication required.',
2039
+ });
2040
+ return;
2041
+ }
2042
+ next();
2043
+ });
1959
2044
  apiServer.app.use(async (req, res, next) => {
1960
2045
  const handled = await graphHandler(req, res);
1961
2046
  if (!handled)
@@ -2145,8 +2230,8 @@ Keep the report under 2000 characters as it will be sent to Discord.`;
2145
2230
  res.json([]);
2146
2231
  }
2147
2232
  });
2148
- apiServer.app.delete('/api/playgrounds/:slug', (req, res) => {
2149
- const { slug } = req.params;
2233
+ apiServer.app.delete('/api/playgrounds/:slug', auth_middleware_js_1.requireAuth, (req, res) => {
2234
+ const slug = req.params.slug;
2150
2235
  if (!slug || /[^a-z0-9-]/.test(slug)) {
2151
2236
  res.status(400).json({ error: 'Invalid slug' });
2152
2237
  return;
@@ -2252,7 +2337,62 @@ Keep the report under 2000 characters as it will be sent to Discord.`;
2252
2337
  // Handle ALL WebSocket upgrades manually
2253
2338
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2254
2339
  apiServer.server.on('upgrade', (request, socket, head) => {
2255
- const url = new URL(request.url || '', `http://${request.headers.host}`);
2340
+ let url;
2341
+ try {
2342
+ url = new URL(request.url || '', `http://${request.headers.host || 'localhost'}`);
2343
+ }
2344
+ catch (error) {
2345
+ startLogger.warn('[SECURITY] Malformed WebSocket upgrade URL rejected', {
2346
+ rawUrl: request.url || null,
2347
+ error: error instanceof Error ? error.message : String(error),
2348
+ });
2349
+ socket.destroy();
2350
+ return;
2351
+ }
2352
+ // WebSocket auth: require token for non-localhost connections.
2353
+ // Browsers cannot set Authorization headers on WebSocket upgrades,
2354
+ // so we allow query-string token auth for this path only.
2355
+ const adminToken = process.env.MAMA_AUTH_TOKEN || process.env.MAMA_SERVER_TOKEN;
2356
+ const context = (0, auth_middleware_js_1.getSecurityLogContext)(request);
2357
+ const isTrustedLocalUpgrade = (0, auth_middleware_js_1.isLocalRequest)(request) && !context.viaTunnel;
2358
+ if (adminToken && !(0, auth_middleware_js_1.isAuthenticated)(request, { allowQueryToken: true })) {
2359
+ const details = { hasQueryToken: url.searchParams.has('token') };
2360
+ startLogger.warn('[SECURITY] Unauthorized WebSocket upgrade blocked', {
2361
+ ...context,
2362
+ ...details,
2363
+ path: url.pathname,
2364
+ });
2365
+ (0, security_monitor_js_1.recordSecurityEvent)({
2366
+ type: 'unauthorized_websocket_upgrade',
2367
+ severity: 'warn',
2368
+ message: 'Unauthorized WebSocket upgrade blocked',
2369
+ ...context,
2370
+ path: url.pathname,
2371
+ details,
2372
+ });
2373
+ socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
2374
+ socket.destroy();
2375
+ return;
2376
+ }
2377
+ if (!adminToken && !isTrustedLocalUpgrade) {
2378
+ const details = { hasQueryToken: url.searchParams.has('token') };
2379
+ startLogger.warn('[SECURITY] Accepting non-localhost WebSocket upgrade without auth token configured', {
2380
+ ...context,
2381
+ ...details,
2382
+ path: url.pathname,
2383
+ });
2384
+ (0, security_monitor_js_1.recordSecurityEvent)({
2385
+ type: 'unprotected_websocket_upgrade',
2386
+ severity: 'critical',
2387
+ message: 'Non-localhost WebSocket upgrade accepted without auth token configured',
2388
+ ...context,
2389
+ path: url.pathname,
2390
+ details,
2391
+ });
2392
+ socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
2393
+ socket.destroy();
2394
+ return;
2395
+ }
2256
2396
  if (url.pathname === '/setup-ws') {
2257
2397
  // Handle setup WebSocket locally
2258
2398
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -2299,6 +2439,7 @@ Keep the report under 2000 characters as it will be sent to Discord.`;
2299
2439
  gateways.push(apiServer);
2300
2440
  // Handle graceful shutdown with timeout
2301
2441
  let shuttingDown = false;
2442
+ let keepAliveInterval = null;
2302
2443
  const shutdown = async () => {
2303
2444
  if (shuttingDown)
2304
2445
  return; // Prevent double shutdown
@@ -2311,12 +2452,45 @@ Keep the report under 2000 characters as it will be sent to Discord.`;
2311
2452
  if (healthWarningInterval) {
2312
2453
  clearInterval(healthWarningInterval);
2313
2454
  }
2455
+ if (keepAliveInterval) {
2456
+ clearInterval(keepAliveInterval);
2457
+ keepAliveInterval = null;
2458
+ }
2459
+ const getBlockingHandleNames = () => {
2460
+ const processWithHandles = process;
2461
+ const getActiveHandles = processWithHandles._getActiveHandles;
2462
+ if (typeof getActiveHandles !== 'function') {
2463
+ return ['unknown'];
2464
+ }
2465
+ const handles = getActiveHandles.call(processWithHandles);
2466
+ const ignoredHandles = new Set(['WriteStream', 'ReadStream', 'TTY', 'TTYWrap']);
2467
+ return (handles
2468
+ ?.map((handle) => handle.constructor?.name)
2469
+ .filter((name) => typeof name === 'string' && !ignoredHandles.has(name)) ?? []);
2470
+ };
2471
+ const getActiveRequestNames = () => {
2472
+ const processWithRequests = process;
2473
+ const getActiveRequests = processWithRequests._getActiveRequests;
2474
+ if (typeof getActiveRequests !== 'function') {
2475
+ return ['unknown'];
2476
+ }
2477
+ const requests = getActiveRequests.call(processWithRequests);
2478
+ return (requests
2479
+ ?.map((request) => request.constructor?.name)
2480
+ .filter((name) => typeof name === 'string') ?? []);
2481
+ };
2314
2482
  // Force exit after 5 seconds if graceful shutdown hangs
2315
2483
  // exit(0) = intentional stop; systemd Restart=on-failure should NOT restart
2316
- setTimeout(() => {
2484
+ const forceExitTimer = setTimeout(() => {
2485
+ const blockingHandles = getBlockingHandleNames();
2486
+ const activeRequests = getActiveRequestNames();
2487
+ if (blockingHandles.length === 0 && activeRequests.length === 0) {
2488
+ return;
2489
+ }
2317
2490
  console.error('[MAMA] Graceful shutdown timed out, forcing exit');
2318
- process.exit(0);
2491
+ process.kill(process.pid, 'SIGKILL');
2319
2492
  }, 5000);
2493
+ forceExitTimer.unref();
2320
2494
  try {
2321
2495
  // Stop schedulers and cron worker first
2322
2496
  scheduler.shutdown();
@@ -2325,8 +2499,38 @@ Keep the report under 2000 characters as it will be sent to Discord.`;
2325
2499
  tokenKeepAlive.stop();
2326
2500
  // Close embedding server (port 3849) - drain connections first
2327
2501
  if (embeddingServer) {
2328
- embeddingServer.closeAllConnections();
2329
- embeddingServer.close();
2502
+ await new Promise((resolve) => {
2503
+ const shutdownReq = node_http_1.default.request({
2504
+ hostname: '127.0.0.1',
2505
+ port: EMBEDDING_PORT,
2506
+ path: '/shutdown',
2507
+ method: 'POST',
2508
+ timeout: 2000,
2509
+ headers: {
2510
+ 'X-Shutdown-Token': embeddingShutdownToken || process.env.MAMA_SHUTDOWN_TOKEN || '',
2511
+ },
2512
+ }, async (response) => {
2513
+ const statusCode = response.statusCode ?? 0;
2514
+ const released = await waitForPortAvailable(EMBEDDING_PORT, 5000);
2515
+ if (statusCode >= 200 && statusCode < 300 && released) {
2516
+ resolve();
2517
+ return;
2518
+ }
2519
+ startLogger.warn(`[EmbeddingServer] Shutdown endpoint did not fully stop server (status=${statusCode}, released=${released})`);
2520
+ if (embeddingServer) {
2521
+ embeddingServer.close(() => resolve());
2522
+ return;
2523
+ }
2524
+ resolve();
2525
+ });
2526
+ shutdownReq.on('error', () => resolve());
2527
+ shutdownReq.on('timeout', () => {
2528
+ shutdownReq.destroy();
2529
+ resolve();
2530
+ });
2531
+ shutdownReq.end();
2532
+ });
2533
+ embeddingServer = null;
2330
2534
  }
2331
2535
  // Stop all gateways with per-gateway 2s timeout
2332
2536
  const withTimeout = (p, ms) => Promise.race([p, new Promise((r) => setTimeout(r, ms))]);
@@ -2334,21 +2538,34 @@ Keep the report under 2000 characters as it will be sent to Discord.`;
2334
2538
  // Stop plugin gateways
2335
2539
  await withTimeout(pluginLoader.stopAll().catch(() => { }), 1000);
2336
2540
  // Stop agent loop
2337
- agentLoop.stop();
2541
+ await agentLoop.stop();
2338
2542
  // Release all CLI sessions
2339
2543
  (0, session_pool_js_1.getSessionPool)().dispose();
2340
2544
  // Close session database
2341
2545
  sessionStore.close();
2342
2546
  // Stop metrics cleanup
2343
2547
  metricsCleanup?.stop();
2548
+ metricsStore?.close();
2549
+ db.close();
2344
2550
  const { deletePid } = await import('../utils/pid-manager.js');
2345
2551
  await deletePid();
2552
+ const blockingHandles = getBlockingHandleNames();
2553
+ const activeRequests = getActiveRequestNames();
2554
+ if (blockingHandles.length === 0 && activeRequests.length === 0) {
2555
+ clearTimeout(forceExitTimer);
2556
+ }
2557
+ else if (blockingHandles.length === 0 &&
2558
+ activeRequests.length > 0 &&
2559
+ activeRequests.every((name) => name === 'FSReqPromise')) {
2560
+ process.kill(process.pid, 'SIGKILL');
2561
+ }
2346
2562
  }
2347
2563
  catch (error) {
2348
2564
  // Best effort cleanup
2349
2565
  console.warn('[MAMA] Cleanup error during shutdown:', error);
2350
2566
  }
2351
- process.exit(0);
2567
+ process.exitCode = 0;
2568
+ return;
2352
2569
  };
2353
2570
  process.on('SIGINT', shutdown);
2354
2571
  process.on('SIGTERM', shutdown);
@@ -2368,7 +2585,7 @@ Keep the report under 2000 characters as it will be sent to Discord.`;
2368
2585
  console.log('MAMA agent is waiting...\n');
2369
2586
  // Keep process alive using setInterval
2370
2587
  // This ensures the Node.js event loop stays active
2371
- setInterval(() => {
2588
+ keepAliveInterval = setInterval(() => {
2372
2589
  // Heartbeat - keeps the process running
2373
2590
  }, 30000); // Every 30 seconds
2374
2591
  }