@ddtcorex/dsh-maestro-supervisor 0.1.0 → 0.2.0

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.
@@ -46,13 +46,30 @@ export async function pollHealth(opts = {}) {
46
46
  break;
47
47
  }
48
48
  }
49
- // If either fetch failed or log has error, consider down
50
- if (fetchError || logError) {
49
+ // Distinguish FULL (http !=200) vs DEGRADED (http 200 but log has plugin error)
50
+ if (fetchError) {
51
51
  return {
52
52
  up: false,
53
53
  httpCode,
54
- error: logError ?? fetchError,
55
- degraded: !!logError && httpCode === 200,
54
+ error: logError ? `${fetchError} + ${logError}` : fetchError,
55
+ degraded: false,
56
+ };
57
+ }
58
+ if (logError) {
59
+ // http 200 but log error → DEGRADED (isolatable), not FULL
60
+ if (httpCode === 200) {
61
+ return {
62
+ up: true,
63
+ httpCode,
64
+ error: logError,
65
+ degraded: true,
66
+ };
67
+ }
68
+ return {
69
+ up: false,
70
+ httpCode,
71
+ error: logError,
72
+ degraded: false,
56
73
  };
57
74
  }
58
75
  // Also check psAlive as secondary signal — if fetch ok but ps dead, still down
@@ -95,9 +112,16 @@ async function defaultLogTail() {
95
112
  try {
96
113
  const { readFileSync } = await import('node:fs');
97
114
  const { homedir } = await import('node:os');
98
- const logPath = `${homedir()}/.dsh.log`;
99
- const content = readFileSync(logPath, 'utf-8');
100
- return content.slice(-5000);
115
+ const candidates = [`${homedir()}/.dsh/dsh-web.log`, `${homedir()}/.dsh.log`];
116
+ for (const logPath of candidates) {
117
+ try {
118
+ const content = readFileSync(logPath, 'utf-8');
119
+ if (content)
120
+ return content.slice(-5000);
121
+ }
122
+ catch { }
123
+ }
124
+ return '';
101
125
  }
102
126
  catch {
103
127
  return '';
@@ -25,6 +25,7 @@ export declare class Supervisor {
25
25
  private lastRollback;
26
26
  private rollingBack;
27
27
  private lastLKGWrite;
28
+ private lastDegradedNotify;
28
29
  private timer;
29
30
  constructor(deps: SupervisorDeps);
30
31
  tick(): Promise<void>;
package/lib/supervisor.js CHANGED
@@ -3,12 +3,27 @@ export class Supervisor {
3
3
  lastRollback = 0;
4
4
  rollingBack = false;
5
5
  lastLKGWrite = 0;
6
+ lastDegradedNotify = 0;
6
7
  timer = null;
7
8
  constructor(deps) {
8
9
  this.deps = deps;
9
10
  }
10
11
  async tick() {
11
12
  const health = await this.deps.pollHealth();
13
+ // DEGRADED: http 200 but log has plugin error → report, notify, no rollback
14
+ if (health.degraded) {
15
+ const now = this.deps.getTime ? this.deps.getTime() : Date.now();
16
+ if (now - this.lastDegradedNotify < 60000)
17
+ return;
18
+ this.lastDegradedNotify = now;
19
+ try {
20
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
21
+ const reportPath = await this.deps.writeReport({ ts, health, action: `degraded — ${health.error ?? 'plugin'}` }).catch(() => '');
22
+ await this.deps.notify(`DEGRADED: ${health.error ?? 'plugin'} (report: ${reportPath})`).catch(() => { });
23
+ }
24
+ catch { }
25
+ return;
26
+ }
12
27
  if (health.up) {
13
28
  // Throttle LKG writes to at most once per 5 minutes
14
29
  const now = this.deps.getTime ? this.deps.getTime() : Date.now();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ddtcorex/dsh-maestro-supervisor",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Supervisor daemon for DSH Web resilience — auto-detect crashes, rollback to LKG, report",
5
5
  "type": "module",
6
6
  "bin": {