sensorium-mcp 2.17.26 → 2.17.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dashboard/routes/threads.d.ts.map +1 -1
- package/dist/dashboard/routes/threads.js +18 -5
- package/dist/dashboard/routes/threads.js.map +1 -1
- package/dist/data/memory/bootstrap.js +2 -2
- package/dist/data/memory/bootstrap.js.map +1 -1
- package/dist/data/memory/consolidation.d.ts.map +1 -1
- package/dist/data/memory/consolidation.js +75 -4
- package/dist/data/memory/consolidation.js.map +1 -1
- package/dist/data/memory/index.d.ts +1 -0
- package/dist/data/memory/index.d.ts.map +1 -1
- package/dist/data/memory/index.js +1 -0
- package/dist/data/memory/index.js.map +1 -1
- package/dist/data/memory/quality-scoring.d.ts +32 -0
- package/dist/data/memory/quality-scoring.d.ts.map +1 -0
- package/dist/data/memory/quality-scoring.js +182 -0
- package/dist/data/memory/quality-scoring.js.map +1 -0
- package/dist/data/memory/semantic.d.ts +12 -0
- package/dist/data/memory/semantic.d.ts.map +1 -1
- package/dist/data/memory/semantic.js +45 -2
- package/dist/data/memory/semantic.js.map +1 -1
- package/dist/data/memory/thread-registry.d.ts +7 -0
- package/dist/data/memory/thread-registry.d.ts.map +1 -1
- package/dist/data/memory/thread-registry.js +11 -1
- package/dist/data/memory/thread-registry.js.map +1 -1
- package/dist/index.js +17 -5
- package/dist/index.js.map +1 -1
- package/dist/tools/defs/memory-defs.d.ts.map +1 -1
- package/dist/tools/defs/memory-defs.js +19 -0
- package/dist/tools/defs/memory-defs.js.map +1 -1
- package/dist/tools/memory-tools.d.ts.map +1 -1
- package/dist/tools/memory-tools.js +15 -0
- package/dist/tools/memory-tools.js.map +1 -1
- package/dist/tools/thread-lifecycle.d.ts.map +1 -1
- package/dist/tools/thread-lifecycle.js +31 -17
- package/dist/tools/thread-lifecycle.js.map +1 -1
- package/package.json +10 -2
- package/scripts/install-supervisor.ps1 +67 -0
- package/scripts/install-supervisor.sh +43 -0
- package/scripts/start-supervisor.ps1 +46 -0
- package/scripts/start-supervisor.sh +20 -0
- package/supervisor/config.go +140 -0
- package/supervisor/go.mod +3 -0
- package/supervisor/health.go +390 -0
- package/supervisor/health_test.go +93 -0
- package/supervisor/keeper.go +303 -0
- package/supervisor/keeper_test.go +27 -0
- package/supervisor/lock.go +56 -0
- package/supervisor/lock_test.go +54 -0
- package/supervisor/log.go +114 -0
- package/supervisor/log_test.go +45 -0
- package/supervisor/main.go +325 -0
- package/supervisor/notify.go +53 -0
- package/supervisor/process.go +222 -0
- package/supervisor/process_test.go +94 -0
- package/supervisor/process_unix.go +14 -0
- package/supervisor/process_windows.go +15 -0
- package/supervisor/updater.go +281 -0
- package/templates/coding-task.default.md +12 -0
- package/dist/claude-keeper.d.ts +0 -24
- package/dist/claude-keeper.d.ts.map +0 -1
- package/dist/claude-keeper.js +0 -374
- package/dist/claude-keeper.js.map +0 -1
- package/dist/watcher-service.d.ts +0 -2
- package/dist/watcher-service.d.ts.map +0 -1
- package/dist/watcher-service.js +0 -997
- package/dist/watcher-service.js.map +0 -1
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"fmt"
|
|
7
|
+
"net/http"
|
|
8
|
+
"os"
|
|
9
|
+
"path/filepath"
|
|
10
|
+
"runtime"
|
|
11
|
+
"strings"
|
|
12
|
+
"time"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
const registryURL = "https://registry.npmjs.org/sensorium-mcp/latest"
|
|
16
|
+
|
|
17
|
+
// Updater checks the npm registry for new versions and performs updates.
|
|
18
|
+
type Updater struct {
|
|
19
|
+
cfg Config
|
|
20
|
+
mcp *MCPClient
|
|
21
|
+
log *Logger
|
|
22
|
+
startAt time.Time
|
|
23
|
+
cancel context.CancelFunc
|
|
24
|
+
done chan struct{}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
func NewUpdater(cfg Config, mcp *MCPClient, log *Logger) *Updater {
|
|
28
|
+
return &Updater{
|
|
29
|
+
cfg: cfg,
|
|
30
|
+
mcp: mcp,
|
|
31
|
+
log: log,
|
|
32
|
+
startAt: time.Now(),
|
|
33
|
+
done: make(chan struct{}),
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Start begins the update check loop.
|
|
38
|
+
func (u *Updater) Start() {
|
|
39
|
+
ctx, cancel := context.WithCancel(context.Background())
|
|
40
|
+
u.cancel = cancel
|
|
41
|
+
go u.run(ctx)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Stop signals the updater to shut down and waits.
|
|
45
|
+
func (u *Updater) Stop() {
|
|
46
|
+
if u.cancel != nil {
|
|
47
|
+
u.cancel()
|
|
48
|
+
}
|
|
49
|
+
<-u.done
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
func (u *Updater) run(ctx context.Context) {
|
|
53
|
+
defer close(u.done)
|
|
54
|
+
u.log.Info("Updater started (mode=%s)", u.cfg.Mode)
|
|
55
|
+
|
|
56
|
+
// In development mode, check every PollInterval.
|
|
57
|
+
// In production, check once per day at PollAtHour.
|
|
58
|
+
for {
|
|
59
|
+
var sleepDuration time.Duration
|
|
60
|
+
if u.cfg.Mode == "development" {
|
|
61
|
+
sleepDuration = u.cfg.PollInterval
|
|
62
|
+
} else {
|
|
63
|
+
sleepDuration = u.timeUntilNextPoll()
|
|
64
|
+
}
|
|
65
|
+
u.log.Debug("Updater: next version check in %v", sleepDuration.Round(time.Second))
|
|
66
|
+
|
|
67
|
+
select {
|
|
68
|
+
case <-ctx.Done():
|
|
69
|
+
return
|
|
70
|
+
case <-time.After(sleepDuration):
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if ctx.Err() != nil {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
u.checkAndUpdate(ctx)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
func (u *Updater) timeUntilNextPoll() time.Duration {
|
|
82
|
+
now := time.Now()
|
|
83
|
+
next := time.Date(now.Year(), now.Month(), now.Day(), u.cfg.PollAtHour, 0, 0, 0, now.Location())
|
|
84
|
+
if next.Before(now) {
|
|
85
|
+
next = next.Add(24 * time.Hour)
|
|
86
|
+
}
|
|
87
|
+
return time.Until(next)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// getRemoteVersion fetches the latest version from npm registry.
|
|
91
|
+
func (u *Updater) getRemoteVersion(ctx context.Context) (string, error) {
|
|
92
|
+
ctx2, cancel := context.WithTimeout(ctx, 15*time.Second)
|
|
93
|
+
defer cancel()
|
|
94
|
+
|
|
95
|
+
req, err := http.NewRequestWithContext(ctx2, "GET", registryURL, nil)
|
|
96
|
+
if err != nil {
|
|
97
|
+
return "", err
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
resp, err := http.DefaultClient.Do(req)
|
|
101
|
+
if err != nil {
|
|
102
|
+
return "", err
|
|
103
|
+
}
|
|
104
|
+
defer resp.Body.Close()
|
|
105
|
+
|
|
106
|
+
if resp.StatusCode != 200 {
|
|
107
|
+
return "", fmt.Errorf("npm registry HTTP %d", resp.StatusCode)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
var pkg struct {
|
|
111
|
+
Version string `json:"version"`
|
|
112
|
+
}
|
|
113
|
+
if err := json.NewDecoder(resp.Body).Decode(&pkg); err != nil {
|
|
114
|
+
return "", err
|
|
115
|
+
}
|
|
116
|
+
return pkg.Version, nil
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// getLocalVersion reads the current version from the version file.
|
|
120
|
+
func (u *Updater) getLocalVersion() string {
|
|
121
|
+
data, err := os.ReadFile(u.cfg.Paths.VersionFile)
|
|
122
|
+
if err != nil {
|
|
123
|
+
return ""
|
|
124
|
+
}
|
|
125
|
+
return strings.TrimSpace(string(data))
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
func (u *Updater) setLocalVersion(v string) {
|
|
129
|
+
os.MkdirAll(u.cfg.DataDir, 0755)
|
|
130
|
+
if err := atomicWrite(u.cfg.Paths.VersionFile, []byte(v)); err != nil {
|
|
131
|
+
u.log.Warn("Failed to write version file: %v", err)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
func (u *Updater) checkAndUpdate(ctx context.Context) {
|
|
136
|
+
// Enforce minimum uptime before updating
|
|
137
|
+
uptime := time.Since(u.startAt)
|
|
138
|
+
if uptime < u.cfg.MinUptime {
|
|
139
|
+
u.log.Info("Deferring update — too early (uptime %v < %v)", uptime.Round(time.Second), u.cfg.MinUptime)
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
remote, err := u.getRemoteVersion(ctx)
|
|
144
|
+
if err != nil {
|
|
145
|
+
u.log.Warn("Failed to check npm registry: %v", err)
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
local := u.getLocalVersion()
|
|
150
|
+
if local == "" {
|
|
151
|
+
u.log.Info("No local version recorded — storing %s", remote)
|
|
152
|
+
u.setLocalVersion(remote)
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if local == remote {
|
|
157
|
+
u.log.Debug("Updater: version %s is up to date", local)
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
u.log.Info("Update available: %s → %s", local, remote)
|
|
162
|
+
NotifyOperator(u.cfg, u.log, fmt.Sprintf("⚙️ Supervisor: updating sensorium v%s → v%s. Grace period %v...", local, remote, u.cfg.GracePeriod), 0)
|
|
163
|
+
|
|
164
|
+
// Grace period
|
|
165
|
+
u.log.Info("Grace period %v...", u.cfg.GracePeriod)
|
|
166
|
+
select {
|
|
167
|
+
case <-ctx.Done():
|
|
168
|
+
return
|
|
169
|
+
case <-time.After(u.cfg.GracePeriod):
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Set maintenance flag — always clean up on exit
|
|
173
|
+
if err := atomicWrite(u.cfg.Paths.MaintenanceFlag, []byte(time.Now().Format(time.RFC3339))); err != nil {
|
|
174
|
+
u.log.Warn("Failed to write maintenance flag: %v", err)
|
|
175
|
+
}
|
|
176
|
+
defer os.Remove(u.cfg.Paths.MaintenanceFlag)
|
|
177
|
+
|
|
178
|
+
// Kill the current MCP server
|
|
179
|
+
if ctx.Err() != nil {
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
u.killServer()
|
|
183
|
+
|
|
184
|
+
// Clean npx cache
|
|
185
|
+
if ctx.Err() != nil {
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
u.clearNpxCache()
|
|
189
|
+
|
|
190
|
+
// Spawn new server — retry up to 3 times on failure
|
|
191
|
+
var pid int
|
|
192
|
+
for attempt := 1; attempt <= 3; attempt++ {
|
|
193
|
+
if ctx.Err() != nil {
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
pid, err = SpawnMCPServer(u.cfg, u.log)
|
|
197
|
+
if err == nil {
|
|
198
|
+
break
|
|
199
|
+
}
|
|
200
|
+
u.log.Error("Failed to spawn updated MCP server (attempt %d/3): %v", attempt, err)
|
|
201
|
+
if attempt < 3 {
|
|
202
|
+
time.Sleep(2 * time.Second)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if err != nil {
|
|
206
|
+
u.log.Error("All spawn attempts failed — server is down!")
|
|
207
|
+
NotifyOperator(u.cfg, u.log, "🔴 Supervisor: update FAILED — server is down! Manual intervention required.", 0)
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Wait for new server to be ready
|
|
212
|
+
if u.mcp.WaitForReady(ctx, 3*time.Second, 60*time.Second) {
|
|
213
|
+
u.log.Info("Updated MCP server ready (PID %d)", pid)
|
|
214
|
+
} else {
|
|
215
|
+
u.log.Warn("Updated server did not become ready in 60s")
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
u.setLocalVersion(remote)
|
|
219
|
+
|
|
220
|
+
NotifyOperator(u.cfg, u.log, fmt.Sprintf("✅ Supervisor: update to v%s complete. Server ready.", remote), 0)
|
|
221
|
+
u.log.Info("Update complete: v%s → v%s", local, remote)
|
|
222
|
+
|
|
223
|
+
// Reset start time for min uptime tracking
|
|
224
|
+
u.startAt = time.Now()
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
func (u *Updater) killServer() {
|
|
228
|
+
u.log.Info("Updater: stopping current MCP server for update")
|
|
229
|
+
pid, err := ReadPIDFile(u.cfg.Paths.ServerPID)
|
|
230
|
+
if err != nil {
|
|
231
|
+
u.log.Warn("Could not read server PID file: %v", err)
|
|
232
|
+
// Try killing by port as fallback
|
|
233
|
+
KillByPort(u.cfg.MCPHttpPort, u.log)
|
|
234
|
+
return
|
|
235
|
+
}
|
|
236
|
+
if err := KillProcess(pid, u.log); err != nil {
|
|
237
|
+
u.log.Error("Failed to kill server (PID %d): %v", pid, err)
|
|
238
|
+
KillByPort(u.cfg.MCPHttpPort, u.log)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// clearNpxCache removes the cached sensorium-mcp package from the npx cache
|
|
243
|
+
// so the next `npx -y sensorium-mcp@latest` fetches the new version.
|
|
244
|
+
func (u *Updater) clearNpxCache() {
|
|
245
|
+
u.log.Info("Updater: clearing npx cache")
|
|
246
|
+
var base string
|
|
247
|
+
if runtime.GOOS == "windows" {
|
|
248
|
+
localAppData := os.Getenv("LOCALAPPDATA")
|
|
249
|
+
if localAppData == "" {
|
|
250
|
+
home, _ := os.UserHomeDir()
|
|
251
|
+
localAppData = filepath.Join(home, "AppData", "Local")
|
|
252
|
+
}
|
|
253
|
+
base = filepath.Join(localAppData, "npm-cache", "_npx")
|
|
254
|
+
} else {
|
|
255
|
+
home, _ := os.UserHomeDir()
|
|
256
|
+
base = filepath.Join(home, ".npm", "_npx")
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
u.log.Info("Clearing sensorium-mcp from npx cache (%s)", base)
|
|
260
|
+
|
|
261
|
+
entries, err := os.ReadDir(base)
|
|
262
|
+
if err != nil {
|
|
263
|
+
return
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
for _, e := range entries {
|
|
267
|
+
if !e.IsDir() {
|
|
268
|
+
continue
|
|
269
|
+
}
|
|
270
|
+
pkgDir := filepath.Join(base, e.Name(), "node_modules", "sensorium-mcp")
|
|
271
|
+
// Validate path doesn't escape base directory
|
|
272
|
+
if !strings.HasPrefix(pkgDir, base) {
|
|
273
|
+
continue
|
|
274
|
+
}
|
|
275
|
+
if _, err := os.Stat(pkgDir); err == nil {
|
|
276
|
+
if err := os.RemoveAll(pkgDir); err != nil {
|
|
277
|
+
u.log.Warn("Failed to clear npx cache entry %s: %v", pkgDir, err)
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
package/dist/claude-keeper.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Thread keeper — monitors keep-alive threads and restarts them
|
|
3
|
-
* via the start_thread MCP tool when they stop running.
|
|
4
|
-
*
|
|
5
|
-
* No direct process spawning — delegates to start_thread which handles
|
|
6
|
-
* all lifecycle concerns (PID tracking, registry updates, MCP config).
|
|
7
|
-
*/
|
|
8
|
-
export interface KeeperConfig {
|
|
9
|
-
threadId: number;
|
|
10
|
-
sessionName: string;
|
|
11
|
-
client: string;
|
|
12
|
-
mcpHttpPort: number;
|
|
13
|
-
mcpHttpSecret: string | null;
|
|
14
|
-
workingDirectory?: string;
|
|
15
|
-
maxRetries?: number;
|
|
16
|
-
cooldownMs?: number;
|
|
17
|
-
/** Called when the keeper detects the thread process has died. */
|
|
18
|
-
onDeath?: (threadId: number, sessionName: string) => void;
|
|
19
|
-
}
|
|
20
|
-
export interface KeeperHandle {
|
|
21
|
-
stop(): Promise<void>;
|
|
22
|
-
}
|
|
23
|
-
export declare function startClaudeKeeper(config: KeeperConfig): Promise<KeeperHandle>;
|
|
24
|
-
//# sourceMappingURL=claude-keeper.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"claude-keeper.d.ts","sourceRoot":"","sources":["../src/claude-keeper.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAuBH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3D;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AA4OD,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAgHnF"}
|