@gotcos/glasses-server 6.18.5 → 6.18.6

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/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 6.18.6
2
+
3
+ - **Phone Restart works on COS Control managed installs.** Recovery status /
4
+ `/api/recovery/server/restart` previously required `COS_HARNESS=daemon`, but
5
+ Control's LaunchAgent sets `COS_MANAGED=1` with `COS_HARNESS=foreground`.
6
+ Health correctly advertised `managed: true` while the restart route returned
7
+ 409 "Server is not managed by the COS LaunchAgent". Gate now uses
8
+ `COS_MANAGED=1` (`isManagedRuntime()`), matching capabilities.
9
+
1
10
  ## 6.18.5
2
11
 
3
12
  - **Codex workspace-write now includes outbound network.** `workspace-write`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotcos/glasses-server",
3
- "version": "6.18.5",
3
+ "version": "6.18.6",
4
4
  "description": "COS Glasses \u2014 self-hosted AI heads-up-display server for Even G2 smart glasses, powered by Claude Code, Codex, or Cursor Agent CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,9 +21,9 @@ export function managedRuntimeCapability(): ManagedRuntimeCapability {
21
21
  // Whisper lifecycle is private to the local controller. It is never
22
22
  // exposed as a network-reachable mutation capability.
23
23
  restartWhisper: false,
24
- // Server restart is performed by the trusted local helper through launchd,
25
- // never by an HTTP endpoint. This flag tells clients that managed recovery
26
- // exists without widening the network attack surface.
24
+ // Phone/companion may POST /api/recovery/server/restart when managed.
25
+ // launchd KeepAlive brings the process back after SIGTERM. Unmanaged
26
+ // foreground installs reject that route (restart_unmanaged).
27
27
  restartServer: managed,
28
28
  maintenanceDrain: managed,
29
29
  lifecycleProof: managed,
@@ -2,6 +2,7 @@ import { Router } from 'express'
2
2
  import { readFileSync } from 'node:fs'
3
3
  import { resolve } from 'node:path'
4
4
  import { atomicWriteFileSync } from '../lib/atomic-fs.js'
5
+ import { isManagedRuntime } from '../lib/managed-runtime.js'
5
6
  import { acquireMaintenance, getRecoveryActivityStatus } from '../lib/recovery-activity.js'
6
7
  import { getWhisperHealth, restartWhisperServer } from '../lib/whisper-local.js'
7
8
  import { serverMetrics } from '../lib/server-metrics.js'
@@ -19,14 +20,16 @@ recoveryRouter.get('/live', (_req, res) => {
19
20
  res.json({
20
21
  status: 'ok', bootId: serverMetrics.bootId, pid: process.pid,
21
22
  uptimeSeconds: Math.round((Date.now() - serverMetrics.startedAt) / 1000),
22
- managed: process.env.COS_HARNESS === 'daemon',
23
+ // COS Control LaunchAgent sets COS_MANAGED=1 with COS_HARNESS=foreground.
24
+ // Do not require COS_HARNESS=daemon — that false-negative blocked phone Restart.
25
+ managed: isManagedRuntime(),
23
26
  })
24
27
  })
25
28
 
26
29
  recoveryRouter.get('/recovery/status', (_req, res) => {
27
30
  res.json({
28
31
  bootId: serverMetrics.bootId,
29
- managed: process.env.COS_HARNESS === 'daemon',
32
+ managed: isManagedRuntime(),
30
33
  whisper: getWhisperHealth(),
31
34
  asr: { hqActive: false, hqQueued: 0, fastRestarting: false }, // public build: no HQ/fast ASR scheduler in this server
32
35
  activity: getRecoveryActivityStatus(),
@@ -46,7 +49,7 @@ recoveryRouter.post('/recovery/whisper/restart', async (_req, res) => {
46
49
  })
47
50
 
48
51
  recoveryRouter.post('/recovery/server/restart', (_req, res) => {
49
- if (process.env.COS_HARNESS !== 'daemon') {
52
+ if (!isManagedRuntime()) {
50
53
  return res.status(409).json({ error: 'Server is not managed by the COS LaunchAgent', reason: 'restart_unmanaged' })
51
54
  }
52
55
  const elapsed = Date.now() - lastRestartAt()