sensorium-mcp 2.17.28 → 3.0.1
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/Install-Sensorium.ps1 +351 -0
- package/README.md +14 -0
- package/dist/config.d.ts +16 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +39 -2
- package/dist/config.js.map +1 -1
- package/dist/daily-session.d.ts +2 -1
- package/dist/daily-session.d.ts.map +1 -1
- package/dist/daily-session.js +23 -26
- package/dist/daily-session.js.map +1 -1
- package/dist/dashboard/routes/settings.d.ts +4 -0
- package/dist/dashboard/routes/settings.d.ts.map +1 -1
- package/dist/dashboard/routes/settings.js +57 -1
- package/dist/dashboard/routes/settings.js.map +1 -1
- package/dist/dashboard/routes/threads.d.ts +1 -0
- package/dist/dashboard/routes/threads.d.ts.map +1 -1
- package/dist/dashboard/routes/threads.js +23 -25
- package/dist/dashboard/routes/threads.js.map +1 -1
- package/dist/dashboard/routes.d.ts.map +1 -1
- package/dist/dashboard/routes.js +7 -2
- package/dist/dashboard/routes.js.map +1 -1
- package/dist/dashboard/spa.html +11 -11
- package/dist/data/interfaces.d.ts +36 -0
- package/dist/data/interfaces.d.ts.map +1 -0
- package/dist/data/interfaces.js +2 -0
- package/dist/data/interfaces.js.map +1 -0
- package/dist/data/memory/bootstrap.d.ts +36 -16
- package/dist/data/memory/bootstrap.d.ts.map +1 -1
- package/dist/data/memory/bootstrap.js +71 -217
- package/dist/data/memory/bootstrap.js.map +1 -1
- package/dist/data/memory/consolidation.d.ts +35 -34
- package/dist/data/memory/consolidation.d.ts.map +1 -1
- package/dist/data/memory/consolidation.js +43 -554
- package/dist/data/memory/consolidation.js.map +1 -1
- package/dist/data/memory/migration-runner.d.ts +5 -0
- package/dist/data/memory/migration-runner.d.ts.map +1 -0
- package/dist/data/memory/migration-runner.js +403 -0
- package/dist/data/memory/migration-runner.js.map +1 -0
- package/dist/data/memory/reflection.js +1 -1
- package/dist/data/memory/schema-ddl.d.ts +4 -0
- package/dist/data/memory/schema-ddl.d.ts.map +1 -0
- package/dist/data/memory/schema-ddl.js +194 -0
- package/dist/data/memory/schema-ddl.js.map +1 -0
- package/dist/data/memory/schema-guard.d.ts +3 -0
- package/dist/data/memory/schema-guard.d.ts.map +1 -0
- package/dist/data/memory/schema-guard.js +184 -0
- package/dist/data/memory/schema-guard.js.map +1 -0
- package/dist/data/memory/schema.d.ts +2 -5
- package/dist/data/memory/schema.d.ts.map +1 -1
- package/dist/data/memory/schema.js +6 -834
- package/dist/data/memory/schema.js.map +1 -1
- package/dist/data/memory/synthesis.js +2 -2
- package/dist/data/memory/synthesis.js.map +1 -1
- package/dist/data/memory/thread-registry.d.ts +18 -4
- package/dist/data/memory/thread-registry.d.ts.map +1 -1
- package/dist/data/memory/thread-registry.js +25 -0
- package/dist/data/memory/thread-registry.js.map +1 -1
- package/dist/data/sent-message.repository.d.ts +12 -0
- package/dist/data/sent-message.repository.d.ts.map +1 -0
- package/dist/data/sent-message.repository.js +31 -0
- package/dist/data/sent-message.repository.js.map +1 -0
- package/dist/http-server.d.ts.map +1 -1
- package/dist/http-server.js +23 -2
- package/dist/http-server.js.map +1 -1
- package/dist/index.js +27 -48
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +7 -2
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +89 -12
- package/dist/logger.js.map +1 -1
- package/dist/scheduler.d.ts +8 -0
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +15 -0
- package/dist/scheduler.js.map +1 -1
- package/dist/server/factory.d.ts +2 -1
- package/dist/server/factory.d.ts.map +1 -1
- package/dist/server/factory.js +11 -4
- package/dist/server/factory.js.map +1 -1
- package/dist/services/agent-spawn.service.d.ts +39 -0
- package/dist/services/agent-spawn.service.d.ts.map +1 -0
- package/dist/services/agent-spawn.service.js +348 -0
- package/dist/services/agent-spawn.service.js.map +1 -0
- package/dist/services/background-runner.d.ts +26 -0
- package/dist/services/background-runner.d.ts.map +1 -0
- package/dist/services/background-runner.js +71 -0
- package/dist/services/background-runner.js.map +1 -0
- package/dist/services/consolidation.service.d.ts +16 -0
- package/dist/services/consolidation.service.d.ts.map +1 -0
- package/dist/services/consolidation.service.js +508 -0
- package/dist/services/consolidation.service.js.map +1 -0
- package/dist/services/dispatcher/broker.d.ts +2 -0
- package/dist/services/dispatcher/broker.d.ts.map +1 -1
- package/dist/services/dispatcher/broker.js +5 -10
- package/dist/services/dispatcher/broker.js.map +1 -1
- package/dist/services/dispatcher/index.d.ts +1 -1
- package/dist/services/dispatcher/index.d.ts.map +1 -1
- package/dist/services/dispatcher/index.js +1 -1
- package/dist/services/dispatcher/index.js.map +1 -1
- package/dist/services/dispatcher/lock.d.ts.map +1 -1
- package/dist/services/dispatcher/lock.js +7 -11
- package/dist/services/dispatcher/lock.js.map +1 -1
- package/dist/services/maintenance-signal.d.ts +18 -0
- package/dist/services/maintenance-signal.d.ts.map +1 -0
- package/dist/services/maintenance-signal.js +48 -0
- package/dist/services/maintenance-signal.js.map +1 -0
- package/dist/services/memory-briefing.service.d.ts +4 -0
- package/dist/services/memory-briefing.service.d.ts.map +1 -0
- package/dist/services/memory-briefing.service.js +143 -0
- package/dist/services/memory-briefing.service.js.map +1 -0
- package/dist/services/process.service.d.ts +31 -0
- package/dist/services/process.service.d.ts.map +1 -0
- package/dist/services/process.service.js +100 -0
- package/dist/services/process.service.js.map +1 -0
- package/dist/services/thread-health.service.d.ts +18 -0
- package/dist/services/thread-health.service.d.ts.map +1 -0
- package/dist/services/thread-health.service.js +118 -0
- package/dist/services/thread-health.service.js.map +1 -0
- package/dist/services/thread-lifecycle.service.d.ts +52 -0
- package/dist/services/thread-lifecycle.service.d.ts.map +1 -0
- package/dist/services/thread-lifecycle.service.js +174 -0
- package/dist/services/thread-lifecycle.service.js.map +1 -0
- package/dist/services/topic.service.d.ts +25 -0
- package/dist/services/topic.service.d.ts.map +1 -0
- package/dist/services/topic.service.js +65 -0
- package/dist/services/topic.service.js.map +1 -0
- package/dist/services/worker-cleanup.service.d.ts +8 -0
- package/dist/services/worker-cleanup.service.d.ts.map +1 -0
- package/dist/services/worker-cleanup.service.js +82 -0
- package/dist/services/worker-cleanup.service.js.map +1 -0
- package/dist/sessions.d.ts +14 -0
- package/dist/sessions.d.ts.map +1 -1
- package/dist/sessions.js +55 -0
- package/dist/sessions.js.map +1 -1
- package/dist/telegram.d.ts +13 -6
- package/dist/telegram.d.ts.map +1 -1
- package/dist/telegram.js +43 -14
- package/dist/telegram.js.map +1 -1
- package/dist/tools/delegate-tool.d.ts +4 -0
- package/dist/tools/delegate-tool.d.ts.map +1 -1
- package/dist/tools/delegate-tool.js +48 -109
- package/dist/tools/delegate-tool.js.map +1 -1
- package/dist/tools/memory-tools.d.ts.map +1 -1
- package/dist/tools/memory-tools.js +1 -1
- package/dist/tools/memory-tools.js.map +1 -1
- package/dist/tools/shared-agent-utils.d.ts +9 -1
- package/dist/tools/shared-agent-utils.d.ts.map +1 -1
- package/dist/tools/shared-agent-utils.js +21 -38
- package/dist/tools/shared-agent-utils.js.map +1 -1
- package/dist/tools/start-session-tool.d.ts +2 -0
- package/dist/tools/start-session-tool.d.ts.map +1 -1
- package/dist/tools/start-session-tool.js +68 -118
- package/dist/tools/start-session-tool.js.map +1 -1
- package/dist/tools/thread-lifecycle.d.ts +5 -127
- package/dist/tools/thread-lifecycle.d.ts.map +1 -1
- package/dist/tools/thread-lifecycle.js +5 -1167
- package/dist/tools/thread-lifecycle.js.map +1 -1
- package/dist/tools/utility-tools.js +5 -2
- package/dist/tools/utility-tools.js.map +1 -1
- package/dist/tools/wait/drive-handler.d.ts +0 -1
- package/dist/tools/wait/drive-handler.d.ts.map +1 -1
- package/dist/tools/wait/drive-handler.js +5 -22
- package/dist/tools/wait/drive-handler.js.map +1 -1
- package/dist/tools/wait/message-delivery.js +1 -1
- package/dist/tools/wait/message-delivery.js.map +1 -1
- package/dist/tools/wait/message-processing.d.ts.map +1 -1
- package/dist/tools/wait/message-processing.js +9 -8
- package/dist/tools/wait/message-processing.js.map +1 -1
- package/dist/tools/wait/poll-loop.d.ts +2 -0
- package/dist/tools/wait/poll-loop.d.ts.map +1 -1
- package/dist/tools/wait/poll-loop.js +27 -29
- package/dist/tools/wait/poll-loop.js.map +1 -1
- package/dist/tools/wait/task-handler.d.ts +0 -3
- package/dist/tools/wait/task-handler.d.ts.map +1 -1
- package/dist/tools/wait/task-handler.js +3 -2
- package/dist/tools/wait/task-handler.js.map +1 -1
- package/dist/types.d.ts +0 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -8
- package/supervisor/config.go +182 -69
- package/supervisor/config_test.go +78 -0
- package/supervisor/go.mod +12 -0
- package/supervisor/go.sum +20 -0
- package/supervisor/health.go +24 -6
- package/supervisor/keeper.go +15 -10
- package/supervisor/log.go +109 -28
- package/supervisor/log_test.go +86 -6
- package/supervisor/main.go +146 -19
- package/supervisor/main_test.go +130 -0
- package/supervisor/process.go +47 -4
- package/supervisor/process_test.go +14 -0
- package/supervisor/secrets.go +95 -0
- package/supervisor/secrets_securevault_test.go +98 -0
- package/supervisor/secrets_test.go +119 -0
- package/supervisor/self_update.go +282 -0
- package/supervisor/self_update_test.go +177 -0
- package/supervisor/service_restart_stub.go +9 -0
- package/supervisor/service_restart_windows.go +63 -0
- package/supervisor/service_stub.go +15 -0
- package/supervisor/service_windows.go +216 -0
- package/supervisor/update_state.go +264 -0
- package/supervisor/update_state_test.go +306 -0
- package/supervisor/updater.go +341 -10
- package/supervisor/updater_test.go +64 -0
- package/scripts/install-supervisor.ps1 +0 -67
- package/scripts/install-supervisor.sh +0 -43
- package/scripts/start-supervisor.ps1 +0 -46
- package/scripts/start-supervisor.sh +0 -20
package/supervisor/updater.go
CHANGED
|
@@ -4,21 +4,32 @@ import (
|
|
|
4
4
|
"context"
|
|
5
5
|
"encoding/json"
|
|
6
6
|
"fmt"
|
|
7
|
+
"io"
|
|
7
8
|
"net/http"
|
|
8
9
|
"os"
|
|
10
|
+
"os/exec"
|
|
9
11
|
"path/filepath"
|
|
10
12
|
"runtime"
|
|
11
13
|
"strings"
|
|
14
|
+
"syscall"
|
|
12
15
|
"time"
|
|
13
16
|
)
|
|
14
17
|
|
|
15
18
|
const registryURL = "https://registry.npmjs.org/sensorium-mcp/latest"
|
|
19
|
+
const supervisorReleaseURL = "https://api.github.com/repos/andriyshevchenko/remote-copilot-mcp/releases/tags/supervisor-latest"
|
|
20
|
+
|
|
21
|
+
var (
|
|
22
|
+
notifyUpdaterOperator = NotifyOperator
|
|
23
|
+
mcpUpdateReadyPollInterval = 3 * time.Second
|
|
24
|
+
mcpUpdateReadyTimeout = 60 * time.Second
|
|
25
|
+
)
|
|
16
26
|
|
|
17
27
|
// Updater checks the npm registry for new versions and performs updates.
|
|
18
28
|
type Updater struct {
|
|
19
29
|
cfg Config
|
|
20
30
|
mcp *MCPClient
|
|
21
31
|
log *Logger
|
|
32
|
+
state *UpdateStateStore
|
|
22
33
|
startAt time.Time
|
|
23
34
|
cancel context.CancelFunc
|
|
24
35
|
done chan struct{}
|
|
@@ -29,6 +40,7 @@ func NewUpdater(cfg Config, mcp *MCPClient, log *Logger) *Updater {
|
|
|
29
40
|
cfg: cfg,
|
|
30
41
|
mcp: mcp,
|
|
31
42
|
log: log,
|
|
43
|
+
state: NewUpdateStateStore(cfg.Paths.UpdateState, log),
|
|
32
44
|
startAt: time.Now(),
|
|
33
45
|
done: make(chan struct{}),
|
|
34
46
|
}
|
|
@@ -75,6 +87,10 @@ func (u *Updater) run(ctx context.Context) {
|
|
|
75
87
|
}
|
|
76
88
|
|
|
77
89
|
u.checkAndUpdate(ctx)
|
|
90
|
+
if ctx.Err() != nil {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
u.checkSupervisorUpdate(ctx)
|
|
78
94
|
}
|
|
79
95
|
}
|
|
80
96
|
|
|
@@ -159,30 +175,54 @@ func (u *Updater) checkAndUpdate(ctx context.Context) {
|
|
|
159
175
|
}
|
|
160
176
|
|
|
161
177
|
u.log.Info("Update available: %s → %s", local, remote)
|
|
162
|
-
|
|
178
|
+
coordLock, ok := AcquireUpdateCoordinatorLock(u.cfg.Paths.UpdateApplyLock, updateScopeMCP, u.log)
|
|
179
|
+
if !ok {
|
|
180
|
+
u.log.Info("Deferring MCP update %s → %s due to active update apply lock", local, remote)
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
defer coordLock.Release()
|
|
184
|
+
|
|
185
|
+
u.state.Transition(updateScopeMCP, updatePhaseApplying, remote, local, "")
|
|
186
|
+
markFailed := func(err error) {
|
|
187
|
+
u.state.Transition(updateScopeMCP, updatePhaseFailed, remote, local, err.Error())
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
notifyUpdaterOperator(u.cfg, u.log, fmt.Sprintf("⚙️ Supervisor: updating sensorium v%s → v%s. Grace period %v...", local, remote, u.cfg.GracePeriod), 0)
|
|
163
191
|
|
|
164
192
|
// Grace period
|
|
165
193
|
u.log.Info("Grace period %v...", u.cfg.GracePeriod)
|
|
166
194
|
select {
|
|
167
195
|
case <-ctx.Done():
|
|
196
|
+
markFailed(ctx.Err())
|
|
168
197
|
return
|
|
169
198
|
case <-time.After(u.cfg.GracePeriod):
|
|
170
199
|
}
|
|
171
200
|
|
|
172
|
-
// Set maintenance flag — always clean up on exit
|
|
173
|
-
|
|
201
|
+
// Set maintenance flag — always clean up on exit.
|
|
202
|
+
// Written as JSON so TypeScript's checkMaintenanceFlag() can parse the
|
|
203
|
+
// version and timestamp fields for accurate maintenance notifications.
|
|
204
|
+
maintenanceJSON, err := json.Marshal(map[string]string{
|
|
205
|
+
"version": remote,
|
|
206
|
+
"timestamp": time.Now().Format(time.RFC3339),
|
|
207
|
+
})
|
|
208
|
+
if err != nil {
|
|
209
|
+
u.log.Warn("Failed to marshal maintenance flag: %v", err)
|
|
210
|
+
} else if err := atomicWrite(u.cfg.Paths.MaintenanceFlag, maintenanceJSON); err != nil {
|
|
174
211
|
u.log.Warn("Failed to write maintenance flag: %v", err)
|
|
175
212
|
}
|
|
176
213
|
defer os.Remove(u.cfg.Paths.MaintenanceFlag)
|
|
177
214
|
|
|
178
215
|
// Kill the current MCP server
|
|
179
216
|
if ctx.Err() != nil {
|
|
217
|
+
markFailed(ctx.Err())
|
|
180
218
|
return
|
|
181
219
|
}
|
|
220
|
+
u.state.Transition(updateScopeMCP, updatePhaseRestarting, remote, local, "")
|
|
182
221
|
u.killServer()
|
|
183
222
|
|
|
184
223
|
// Clean npx cache
|
|
185
224
|
if ctx.Err() != nil {
|
|
225
|
+
markFailed(ctx.Err())
|
|
186
226
|
return
|
|
187
227
|
}
|
|
188
228
|
u.clearNpxCache()
|
|
@@ -191,6 +231,7 @@ func (u *Updater) checkAndUpdate(ctx context.Context) {
|
|
|
191
231
|
var pid int
|
|
192
232
|
for attempt := 1; attempt <= 3; attempt++ {
|
|
193
233
|
if ctx.Err() != nil {
|
|
234
|
+
markFailed(ctx.Err())
|
|
194
235
|
return
|
|
195
236
|
}
|
|
196
237
|
pid, err = SpawnMCPServer(u.cfg, u.log)
|
|
@@ -204,26 +245,316 @@ func (u *Updater) checkAndUpdate(ctx context.Context) {
|
|
|
204
245
|
}
|
|
205
246
|
if err != nil {
|
|
206
247
|
u.log.Error("All spawn attempts failed — server is down!")
|
|
207
|
-
|
|
248
|
+
markFailed(err)
|
|
249
|
+
notifyUpdaterOperator(u.cfg, u.log, "🔴 Supervisor: update FAILED — server is down! Manual intervention required.", 0)
|
|
208
250
|
return
|
|
209
251
|
}
|
|
210
252
|
|
|
211
|
-
|
|
212
|
-
|
|
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")
|
|
253
|
+
if !u.verifyUpdatedMCPServerReady(ctx, remote, local, pid) {
|
|
254
|
+
return
|
|
216
255
|
}
|
|
217
256
|
|
|
218
257
|
u.setLocalVersion(remote)
|
|
258
|
+
u.state.Transition(updateScopeMCP, updatePhaseIdle, remote, local, "")
|
|
219
259
|
|
|
220
|
-
|
|
260
|
+
notifyUpdaterOperator(u.cfg, u.log, fmt.Sprintf("✅ Supervisor: update to v%s complete. Server ready.", remote), 0)
|
|
221
261
|
u.log.Info("Update complete: v%s → v%s", local, remote)
|
|
222
262
|
|
|
223
263
|
// Reset start time for min uptime tracking
|
|
224
264
|
u.startAt = time.Now()
|
|
225
265
|
}
|
|
226
266
|
|
|
267
|
+
func (u *Updater) verifyUpdatedMCPServerReady(ctx context.Context, remote, local string, pid int) bool {
|
|
268
|
+
u.state.Transition(updateScopeMCP, updatePhaseVerifying, remote, local, "")
|
|
269
|
+
if u.mcp.WaitForReady(ctx, mcpUpdateReadyPollInterval, mcpUpdateReadyTimeout) {
|
|
270
|
+
u.log.Info("Updated MCP server ready (PID %d)", pid)
|
|
271
|
+
return true
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
errMsg := fmt.Sprintf("updated MCP server did not become ready within %v after restart (pid=%d)", mcpUpdateReadyTimeout, pid)
|
|
275
|
+
u.log.Error(errMsg)
|
|
276
|
+
u.state.Transition(updateScopeMCP, updatePhaseFailed, remote, local, errMsg)
|
|
277
|
+
notifyUpdaterOperator(u.cfg, u.log, fmt.Sprintf("🔴 Supervisor: update to v%s FAILED verification. Server did not become ready after restart.", remote), 0)
|
|
278
|
+
return false
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
type githubRelease struct {
|
|
282
|
+
TagName string `json:"tag_name"`
|
|
283
|
+
Name string `json:"name"`
|
|
284
|
+
Assets []struct {
|
|
285
|
+
Name string `json:"name"`
|
|
286
|
+
URL string `json:"browser_download_url"`
|
|
287
|
+
Size int64 `json:"size"`
|
|
288
|
+
} `json:"assets"`
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
func (u *Updater) getSupervisorRelease(ctx context.Context) (string, string, error) {
|
|
292
|
+
ctx2, cancel := context.WithTimeout(ctx, 20*time.Second)
|
|
293
|
+
defer cancel()
|
|
294
|
+
|
|
295
|
+
req, err := http.NewRequestWithContext(ctx2, http.MethodGet, supervisorReleaseURL, nil)
|
|
296
|
+
if err != nil {
|
|
297
|
+
return "", "", err
|
|
298
|
+
}
|
|
299
|
+
req.Header.Set("Accept", "application/vnd.github+json")
|
|
300
|
+
req.Header.Set("User-Agent", "sensorium-supervisor-updater")
|
|
301
|
+
|
|
302
|
+
resp, err := http.DefaultClient.Do(req)
|
|
303
|
+
if err != nil {
|
|
304
|
+
return "", "", err
|
|
305
|
+
}
|
|
306
|
+
defer resp.Body.Close()
|
|
307
|
+
|
|
308
|
+
if resp.StatusCode != http.StatusOK {
|
|
309
|
+
return "", "", fmt.Errorf("GitHub releases HTTP %d", resp.StatusCode)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
var release githubRelease
|
|
313
|
+
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
|
|
314
|
+
return "", "", err
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
assetName := supervisorAssetName()
|
|
318
|
+
for _, asset := range release.Assets {
|
|
319
|
+
if asset.Name != assetName {
|
|
320
|
+
continue
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
version := strings.TrimSpace(release.Name)
|
|
324
|
+
if version == "" {
|
|
325
|
+
version = strings.TrimSpace(release.TagName)
|
|
326
|
+
}
|
|
327
|
+
if version == "" {
|
|
328
|
+
return "", "", fmt.Errorf("release version missing for %s", assetName)
|
|
329
|
+
}
|
|
330
|
+
if strings.TrimSpace(asset.URL) == "" {
|
|
331
|
+
return "", "", fmt.Errorf("release asset URL missing for %s", assetName)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return version, asset.URL, nil
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return "", "", fmt.Errorf("release asset %q not found", assetName)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
func supervisorAssetName() string {
|
|
341
|
+
suffix := ""
|
|
342
|
+
if runtime.GOOS == "windows" {
|
|
343
|
+
suffix = ".exe"
|
|
344
|
+
}
|
|
345
|
+
return fmt.Sprintf("sensorium-supervisor-%s-%s%s", runtime.GOOS, runtime.GOARCH, suffix)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
func (u *Updater) getLocalSupervisorVersion() string {
|
|
349
|
+
data, err := os.ReadFile(u.cfg.Paths.SupervisorVersion)
|
|
350
|
+
if err != nil {
|
|
351
|
+
return ""
|
|
352
|
+
}
|
|
353
|
+
return strings.TrimSpace(string(data))
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
func (u *Updater) setLocalSupervisorVersion(v string) {
|
|
357
|
+
os.MkdirAll(u.cfg.DataDir, 0755)
|
|
358
|
+
if err := atomicWrite(u.cfg.Paths.SupervisorVersion, []byte(v)); err != nil {
|
|
359
|
+
u.log.Warn("Failed to write supervisor version file: %v", err)
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
func (u *Updater) stagePendingSupervisorVersion(v string) error {
|
|
364
|
+
if err := os.MkdirAll(filepath.Dir(u.cfg.Paths.PendingVersion), 0755); err != nil {
|
|
365
|
+
return fmt.Errorf("create pending supervisor version dir: %w", err)
|
|
366
|
+
}
|
|
367
|
+
if err := atomicWrite(u.cfg.Paths.PendingVersion, []byte(v)); err != nil {
|
|
368
|
+
return fmt.Errorf("write pending supervisor version: %w", err)
|
|
369
|
+
}
|
|
370
|
+
return nil
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
func (u *Updater) checkSupervisorUpdate(ctx context.Context) {
|
|
374
|
+
uptime := time.Since(u.startAt)
|
|
375
|
+
if uptime < u.cfg.MinUptime {
|
|
376
|
+
u.log.Info("Deferring supervisor update — too early (uptime %v < %v)", uptime.Round(time.Second), u.cfg.MinUptime)
|
|
377
|
+
return
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
remote, downloadURL, err := u.getSupervisorRelease(ctx)
|
|
381
|
+
if err != nil {
|
|
382
|
+
u.log.Warn("Failed to check supervisor release: %v", err)
|
|
383
|
+
return
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
local := u.getLocalSupervisorVersion()
|
|
387
|
+
if local == "" {
|
|
388
|
+
u.log.Info("No local supervisor version recorded — storing %s", remote)
|
|
389
|
+
u.setLocalSupervisorVersion(remote)
|
|
390
|
+
return
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if local == remote {
|
|
394
|
+
u.log.Debug("Supervisor updater: version %s is up to date", local)
|
|
395
|
+
return
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
u.log.Info("Supervisor update available: %s → %s", local, remote)
|
|
399
|
+
coordLock, ok := AcquireUpdateCoordinatorLock(u.cfg.Paths.UpdateApplyLock, updateScopeSupervisor, u.log)
|
|
400
|
+
if !ok {
|
|
401
|
+
u.log.Info("Deferring supervisor binary update %s → %s due to active update apply lock", local, remote)
|
|
402
|
+
return
|
|
403
|
+
}
|
|
404
|
+
defer coordLock.Release()
|
|
405
|
+
|
|
406
|
+
markFailed := func(err error) {
|
|
407
|
+
u.state.Transition(updateScopeSupervisor, updatePhaseFailed, remote, local, err.Error())
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
notifyUpdaterOperator(u.cfg, u.log, fmt.Sprintf("⚙️ Supervisor: updating binary %s → %s. Grace period %v...", local, remote, u.cfg.GracePeriod), 0)
|
|
411
|
+
|
|
412
|
+
select {
|
|
413
|
+
case <-ctx.Done():
|
|
414
|
+
markFailed(ctx.Err())
|
|
415
|
+
return
|
|
416
|
+
case <-time.After(u.cfg.GracePeriod):
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if err := u.downloadSupervisorBinary(ctx, downloadURL); err != nil {
|
|
420
|
+
markFailed(err)
|
|
421
|
+
u.log.Error("Supervisor binary download failed: %v", err)
|
|
422
|
+
notifyUpdaterOperator(u.cfg, u.log, fmt.Sprintf("🔴 Supervisor: binary update to %s failed during download.", remote), 0)
|
|
423
|
+
return
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if err := u.stagePendingSupervisorVersion(remote); err != nil {
|
|
427
|
+
_ = os.Remove(u.cfg.Paths.PendingBinary)
|
|
428
|
+
markFailed(err)
|
|
429
|
+
u.log.Error("Failed to stage supervisor version %s: %v", remote, err)
|
|
430
|
+
notifyUpdaterOperator(u.cfg, u.log, fmt.Sprintf("🔴 Supervisor: binary update to %s failed during staging.", remote), 0)
|
|
431
|
+
return
|
|
432
|
+
}
|
|
433
|
+
u.state.Transition(updateScopeSupervisor, updatePhaseStaged, remote, local, "")
|
|
434
|
+
notifyUpdaterOperator(u.cfg, u.log, fmt.Sprintf("⚙️ Supervisor: downloaded %s. Restarting supervisor to apply update...", remote), 0)
|
|
435
|
+
|
|
436
|
+
isService, err := isWindowsService()
|
|
437
|
+
if err != nil {
|
|
438
|
+
markFailed(err)
|
|
439
|
+
u.log.Error("Failed to detect service mode for restart: %v", err)
|
|
440
|
+
notifyUpdaterOperator(u.cfg, u.log, "🔴 Supervisor: update downloaded but service detection failed.", 0)
|
|
441
|
+
return
|
|
442
|
+
}
|
|
443
|
+
u.state.Transition(updateScopeSupervisor, updatePhaseRestarting, remote, local, "")
|
|
444
|
+
|
|
445
|
+
if isService {
|
|
446
|
+
if err := scheduleServiceRestartForUpdate(u.log); err != nil {
|
|
447
|
+
markFailed(err)
|
|
448
|
+
u.log.Error("Failed to schedule service restart: %v", err)
|
|
449
|
+
notifyUpdaterOperator(u.cfg, u.log, "🔴 Supervisor: update downloaded but service restart scheduling failed.", 0)
|
|
450
|
+
}
|
|
451
|
+
return
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if err := requestSupervisorRestart(u.log); err != nil {
|
|
455
|
+
markFailed(err)
|
|
456
|
+
u.log.Error("Failed to signal supervisor for restart: %v", err)
|
|
457
|
+
notifyUpdaterOperator(u.cfg, u.log, "🔴 Supervisor: update downloaded but restart signal failed.", 0)
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
func (u *Updater) downloadSupervisorBinary(ctx context.Context, downloadURL string) error {
|
|
462
|
+
if err := os.MkdirAll(u.cfg.Paths.BinaryDir, 0755); err != nil {
|
|
463
|
+
return fmt.Errorf("create binary dir: %w", err)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
tmpPath := u.cfg.Paths.PendingBinary + ".download"
|
|
467
|
+
defer os.Remove(tmpPath)
|
|
468
|
+
|
|
469
|
+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, downloadURL, nil)
|
|
470
|
+
if err != nil {
|
|
471
|
+
return err
|
|
472
|
+
}
|
|
473
|
+
req.Header.Set("User-Agent", "sensorium-supervisor-updater")
|
|
474
|
+
|
|
475
|
+
resp, err := http.DefaultClient.Do(req)
|
|
476
|
+
if err != nil {
|
|
477
|
+
return err
|
|
478
|
+
}
|
|
479
|
+
defer resp.Body.Close()
|
|
480
|
+
|
|
481
|
+
if resp.StatusCode != http.StatusOK {
|
|
482
|
+
return fmt.Errorf("download HTTP %d", resp.StatusCode)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
f, err := os.OpenFile(tmpPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0755)
|
|
486
|
+
if err != nil {
|
|
487
|
+
return err
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
written, copyErr := io.Copy(f, resp.Body)
|
|
491
|
+
closeErr := f.Close()
|
|
492
|
+
if copyErr != nil {
|
|
493
|
+
return copyErr
|
|
494
|
+
}
|
|
495
|
+
if closeErr != nil {
|
|
496
|
+
return closeErr
|
|
497
|
+
}
|
|
498
|
+
if written <= 0 {
|
|
499
|
+
return fmt.Errorf("downloaded empty binary")
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
info, err := os.Stat(tmpPath)
|
|
503
|
+
if err != nil {
|
|
504
|
+
return err
|
|
505
|
+
}
|
|
506
|
+
if info.Size() <= 0 {
|
|
507
|
+
return fmt.Errorf("downloaded binary has invalid size %d", info.Size())
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if err := os.Remove(u.cfg.Paths.PendingBinary); err != nil && !os.IsNotExist(err) {
|
|
511
|
+
return err
|
|
512
|
+
}
|
|
513
|
+
if err := os.Rename(tmpPath, u.cfg.Paths.PendingBinary); err != nil {
|
|
514
|
+
return err
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
u.log.Info("Supervisor binary downloaded to %s (%d bytes)", u.cfg.Paths.PendingBinary, info.Size())
|
|
518
|
+
return nil
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
func signalSelf(sig os.Signal) error {
|
|
522
|
+
proc, err := os.FindProcess(os.Getpid())
|
|
523
|
+
if err != nil {
|
|
524
|
+
return err
|
|
525
|
+
}
|
|
526
|
+
return proc.Signal(sig)
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
func requestSupervisorRestart(log *Logger) error {
|
|
530
|
+
if runtime.GOOS != "windows" {
|
|
531
|
+
return signalSelf(syscall.SIGTERM)
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
exePath, err := os.Executable()
|
|
535
|
+
if err != nil {
|
|
536
|
+
return fmt.Errorf("resolve executable path: %w", err)
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
cmd := exec.Command(exePath)
|
|
540
|
+
cmd.Env = os.Environ()
|
|
541
|
+
setSysProcAttr(cmd)
|
|
542
|
+
if err := cmd.Start(); err != nil {
|
|
543
|
+
return fmt.Errorf("start replacement supervisor: %w", err)
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if log != nil {
|
|
547
|
+
log.Info("Spawned replacement supervisor process PID %d", cmd.Process.Pid)
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
go func() {
|
|
551
|
+
time.Sleep(250 * time.Millisecond)
|
|
552
|
+
os.Exit(0)
|
|
553
|
+
}()
|
|
554
|
+
|
|
555
|
+
return nil
|
|
556
|
+
}
|
|
557
|
+
|
|
227
558
|
func (u *Updater) killServer() {
|
|
228
559
|
u.log.Info("Updater: stopping current MCP server for update")
|
|
229
560
|
pid, err := ReadPIDFile(u.cfg.Paths.ServerPID)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"strings"
|
|
7
|
+
"testing"
|
|
8
|
+
"time"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
func TestVerifyUpdatedMCPServerReady_FailureSetsFailedStateAndNoSuccessMessage(t *testing.T) {
|
|
12
|
+
dir := t.TempDir()
|
|
13
|
+
log := NewLogger(filepath.Join(dir, "test.log"))
|
|
14
|
+
defer log.Close()
|
|
15
|
+
|
|
16
|
+
cfg := Config{
|
|
17
|
+
DataDir: dir,
|
|
18
|
+
Paths: Paths{
|
|
19
|
+
UpdateState: filepath.Join(dir, "update-state.json"),
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
u := NewUpdater(cfg, NewMCPClient(1, ""), log)
|
|
24
|
+
u.state = NewUpdateStateStore(cfg.Paths.UpdateState, log)
|
|
25
|
+
|
|
26
|
+
origNotify := notifyUpdaterOperator
|
|
27
|
+
origPoll := mcpUpdateReadyPollInterval
|
|
28
|
+
origTimeout := mcpUpdateReadyTimeout
|
|
29
|
+
defer func() {
|
|
30
|
+
notifyUpdaterOperator = origNotify
|
|
31
|
+
mcpUpdateReadyPollInterval = origPoll
|
|
32
|
+
mcpUpdateReadyTimeout = origTimeout
|
|
33
|
+
}()
|
|
34
|
+
|
|
35
|
+
mcpUpdateReadyPollInterval = 1 * time.Millisecond
|
|
36
|
+
mcpUpdateReadyTimeout = 5 * time.Millisecond
|
|
37
|
+
|
|
38
|
+
var messages []string
|
|
39
|
+
notifyUpdaterOperator = func(_ Config, _ *Logger, text string, _ int) {
|
|
40
|
+
messages = append(messages, text)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
ok := u.verifyUpdatedMCPServerReady(context.Background(), "2.0.0", "1.0.0", 4242)
|
|
44
|
+
if ok {
|
|
45
|
+
t.Fatal("expected verification to fail")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
state, err := u.state.Load()
|
|
49
|
+
if err != nil {
|
|
50
|
+
t.Fatalf("load update state: %v", err)
|
|
51
|
+
}
|
|
52
|
+
if state.Phase != updatePhaseFailed {
|
|
53
|
+
t.Fatalf("state phase = %q, want %q", state.Phase, updatePhaseFailed)
|
|
54
|
+
}
|
|
55
|
+
if !strings.Contains(state.LastError, "did not become ready") {
|
|
56
|
+
t.Fatalf("last error = %q, want readiness failure detail", state.LastError)
|
|
57
|
+
}
|
|
58
|
+
if len(messages) == 0 {
|
|
59
|
+
t.Fatal("expected failure notification message")
|
|
60
|
+
}
|
|
61
|
+
if strings.Contains(messages[len(messages)-1], "complete") {
|
|
62
|
+
t.Fatalf("unexpected success message: %q", messages[len(messages)-1])
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env pwsh
|
|
2
|
-
<#
|
|
3
|
-
.SYNOPSIS
|
|
4
|
-
Build and install the sensorium-supervisor Go binary.
|
|
5
|
-
.DESCRIPTION
|
|
6
|
-
Compiles the Go supervisor and places it in ~/.remote-copilot-mcp/bin/.
|
|
7
|
-
Requires Go 1.22+ installed and on PATH.
|
|
8
|
-
.PARAMETER Force
|
|
9
|
-
Rebuild even if the binary already exists.
|
|
10
|
-
#>
|
|
11
|
-
param(
|
|
12
|
-
[switch]$Force
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
$ErrorActionPreference = "Stop"
|
|
16
|
-
|
|
17
|
-
$DataDir = Join-Path $env:USERPROFILE ".remote-copilot-mcp"
|
|
18
|
-
$BinDir = Join-Path $DataDir "bin"
|
|
19
|
-
$Binary = Join-Path $BinDir "sensorium-supervisor.exe"
|
|
20
|
-
|
|
21
|
-
# Find the supervisor source directory (relative to this script)
|
|
22
|
-
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
23
|
-
$SupervisorDir = Join-Path (Split-Path -Parent $ScriptDir) "supervisor"
|
|
24
|
-
|
|
25
|
-
if (-not (Test-Path (Join-Path $SupervisorDir "go.mod"))) {
|
|
26
|
-
Write-Error "Cannot find supervisor source at $SupervisorDir"
|
|
27
|
-
exit 1
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
# Check if Go is available
|
|
31
|
-
$goExe = Get-Command go -ErrorAction SilentlyContinue
|
|
32
|
-
if (-not $goExe) {
|
|
33
|
-
Write-Host "Go is not installed. Install from https://go.dev/dl/ (requires Go 1.22+)" -ForegroundColor Red
|
|
34
|
-
exit 1
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
# Check version
|
|
38
|
-
$goVersion = (go version) -replace 'go version go', '' -replace ' .*', ''
|
|
39
|
-
Write-Host "Found Go $goVersion"
|
|
40
|
-
|
|
41
|
-
# Skip build if binary exists and is newer than source (unless -Force)
|
|
42
|
-
if (-not $Force -and (Test-Path $Binary)) {
|
|
43
|
-
$binaryTime = (Get-Item $Binary).LastWriteTime
|
|
44
|
-
$sourceFiles = Get-ChildItem $SupervisorDir -Filter "*.go"
|
|
45
|
-
$newestSource = ($sourceFiles | Sort-Object LastWriteTime -Descending | Select-Object -First 1).LastWriteTime
|
|
46
|
-
if ($binaryTime -gt $newestSource) {
|
|
47
|
-
Write-Host "sensorium-supervisor is up to date ($Binary)"
|
|
48
|
-
exit 0
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
# Ensure bin directory exists
|
|
53
|
-
New-Item -ItemType Directory -Path $BinDir -Force | Out-Null
|
|
54
|
-
|
|
55
|
-
Write-Host "Building sensorium-supervisor..."
|
|
56
|
-
Push-Location $SupervisorDir
|
|
57
|
-
try {
|
|
58
|
-
go build -o $Binary .
|
|
59
|
-
if ($LASTEXITCODE -ne 0) {
|
|
60
|
-
Write-Error "Go build failed"
|
|
61
|
-
exit 1
|
|
62
|
-
}
|
|
63
|
-
} finally {
|
|
64
|
-
Pop-Location
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
Write-Host "Installed: $Binary" -ForegroundColor Green
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# Build and install the sensorium-supervisor Go binary.
|
|
3
|
-
# Requires Go 1.22+ installed and on PATH.
|
|
4
|
-
set -e
|
|
5
|
-
|
|
6
|
-
FORCE="${1:-}"
|
|
7
|
-
DATA_DIR="$HOME/.remote-copilot-mcp"
|
|
8
|
-
BIN_DIR="$DATA_DIR/bin"
|
|
9
|
-
BINARY="$BIN_DIR/sensorium-supervisor"
|
|
10
|
-
|
|
11
|
-
# Find supervisor source relative to this script
|
|
12
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
13
|
-
SUPERVISOR_DIR="$(dirname "$SCRIPT_DIR")/supervisor"
|
|
14
|
-
|
|
15
|
-
if [ ! -f "$SUPERVISOR_DIR/go.mod" ]; then
|
|
16
|
-
echo "ERROR: Cannot find supervisor source at $SUPERVISOR_DIR" >&2
|
|
17
|
-
exit 1
|
|
18
|
-
fi
|
|
19
|
-
|
|
20
|
-
# Check Go is available
|
|
21
|
-
if ! command -v go >/dev/null 2>&1; then
|
|
22
|
-
echo "ERROR: Go is not installed. Install from https://go.dev/dl/ (requires Go 1.22+)" >&2
|
|
23
|
-
exit 1
|
|
24
|
-
fi
|
|
25
|
-
|
|
26
|
-
echo "Found $(go version)"
|
|
27
|
-
|
|
28
|
-
# Skip if binary is newer than source (unless --force)
|
|
29
|
-
if [ "$FORCE" != "--force" ] && [ -f "$BINARY" ]; then
|
|
30
|
-
NEWEST_SRC=$(find "$SUPERVISOR_DIR" -name '*.go' -newer "$BINARY" 2>/dev/null | head -1)
|
|
31
|
-
if [ -z "$NEWEST_SRC" ]; then
|
|
32
|
-
echo "sensorium-supervisor is up to date ($BINARY)"
|
|
33
|
-
exit 0
|
|
34
|
-
fi
|
|
35
|
-
fi
|
|
36
|
-
|
|
37
|
-
mkdir -p "$BIN_DIR"
|
|
38
|
-
|
|
39
|
-
echo "Building sensorium-supervisor..."
|
|
40
|
-
cd "$SUPERVISOR_DIR"
|
|
41
|
-
go build -o "$BINARY" .
|
|
42
|
-
|
|
43
|
-
echo "Installed: $BINARY"
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env pwsh
|
|
2
|
-
<#
|
|
3
|
-
.SYNOPSIS
|
|
4
|
-
Launch sensorium-supervisor. Builds automatically if needed.
|
|
5
|
-
.DESCRIPTION
|
|
6
|
-
Replaces update-watcher.ps1. Builds the Go supervisor if it doesn't exist,
|
|
7
|
-
then runs it. All environment variables (MCP_HTTP_PORT, TELEGRAM_TOKEN, etc.)
|
|
8
|
-
are passed through to the supervisor process.
|
|
9
|
-
.PARAMETER Mode
|
|
10
|
-
Watcher mode: production or development. Maps to WATCHER_MODE env var.
|
|
11
|
-
.PARAMETER Build
|
|
12
|
-
Force rebuild of the supervisor binary before starting.
|
|
13
|
-
#>
|
|
14
|
-
param(
|
|
15
|
-
[ValidateSet("production", "development")]
|
|
16
|
-
[string]$Mode = "production",
|
|
17
|
-
[switch]$Build
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
$ErrorActionPreference = "Stop"
|
|
21
|
-
|
|
22
|
-
$DataDir = Join-Path $env:USERPROFILE ".remote-copilot-mcp"
|
|
23
|
-
$BinDir = Join-Path $DataDir "bin"
|
|
24
|
-
$Binary = Join-Path $BinDir "sensorium-supervisor.exe"
|
|
25
|
-
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
26
|
-
|
|
27
|
-
# Build if missing or requested
|
|
28
|
-
if ($Build -or -not (Test-Path $Binary)) {
|
|
29
|
-
$installScript = Join-Path $ScriptDir "install-supervisor.ps1"
|
|
30
|
-
if ($Build) {
|
|
31
|
-
& $installScript -Force
|
|
32
|
-
} else {
|
|
33
|
-
& $installScript
|
|
34
|
-
}
|
|
35
|
-
if ($LASTEXITCODE -ne 0) { exit 1 }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
# Set WATCHER_MODE if not already set
|
|
39
|
-
if (-not $env:WATCHER_MODE) {
|
|
40
|
-
$env:WATCHER_MODE = $Mode
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
# Launch supervisor
|
|
44
|
-
Write-Host "Starting sensorium-supervisor ($Mode mode)..."
|
|
45
|
-
& $Binary
|
|
46
|
-
exit $LASTEXITCODE
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# Launch sensorium-supervisor. Builds automatically if needed.
|
|
3
|
-
# Replaces update-watcher.ps1 on Unix systems.
|
|
4
|
-
set -e
|
|
5
|
-
|
|
6
|
-
MODE="${1:-production}"
|
|
7
|
-
DATA_DIR="$HOME/.remote-copilot-mcp"
|
|
8
|
-
BIN_DIR="$DATA_DIR/bin"
|
|
9
|
-
BINARY="$BIN_DIR/sensorium-supervisor"
|
|
10
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
11
|
-
|
|
12
|
-
# Build if missing
|
|
13
|
-
if [ ! -f "$BINARY" ]; then
|
|
14
|
-
"$SCRIPT_DIR/install-supervisor.sh"
|
|
15
|
-
fi
|
|
16
|
-
|
|
17
|
-
export WATCHER_MODE="${WATCHER_MODE:-$MODE}"
|
|
18
|
-
|
|
19
|
-
echo "Starting sensorium-supervisor ($WATCHER_MODE mode)..."
|
|
20
|
-
exec "$BINARY"
|