@gotcos/glasses-server 6.3.1 → 6.6.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.
Files changed (37) hide show
  1. package/.env.example +23 -7
  2. package/CHANGELOG.md +108 -0
  3. package/README.md +28 -8
  4. package/bin/cli.cjs +22 -10
  5. package/package.json +18 -6
  6. package/server/bin/cos-output-image-publisher.mjs +324 -0
  7. package/server/bootstrap.ts +16 -0
  8. package/server/index.ts +61 -21
  9. package/server/lib/activity-preview.ts +168 -0
  10. package/server/lib/archive.ts +20 -6
  11. package/server/lib/claude-bridge.ts +215 -60
  12. package/server/lib/claude-run-ledger.ts +7 -2
  13. package/server/lib/codex-bridge.ts +186 -71
  14. package/server/lib/codex-engine-sessions.ts +24 -2
  15. package/server/lib/codex-model-catalog.ts +450 -0
  16. package/server/lib/codex-run-ledger.ts +20 -4
  17. package/server/lib/conversation.ts +64 -2
  18. package/server/lib/display-bus.ts +61 -3
  19. package/server/lib/image-safety.ts +458 -0
  20. package/server/lib/listener-startup.ts +29 -0
  21. package/server/lib/media-store.ts +833 -0
  22. package/server/lib/model-image-input.ts +27 -0
  23. package/server/lib/model-router.ts +67 -8
  24. package/server/lib/query-attachments.ts +132 -0
  25. package/server/lib/run-output-images.ts +442 -0
  26. package/server/lib/server-instance-id.ts +55 -0
  27. package/server/lib/server-instance-lock.ts +122 -0
  28. package/server/lib/server-metrics.ts +7 -0
  29. package/server/routes/display.ts +43 -22
  30. package/server/routes/health.ts +19 -2
  31. package/server/routes/media.ts +285 -0
  32. package/server/routes/message-ref.ts +18 -6
  33. package/server/routes/openai-compat.ts +44 -11
  34. package/server/routes/query.ts +51 -16
  35. package/server/routes/sessions.ts +33 -4
  36. package/shared/media-attachment.ts +126 -0
  37. package/shared/model-preference.ts +140 -17
package/.env.example CHANGED
@@ -19,18 +19,34 @@ BIND_HOST=0.0.0.0
19
19
  # here for a stable token across restarts.
20
20
  # COS_API_TOKEN=pick-any-long-random-string
21
21
 
22
+ # Optional durable image-store location. Defaults to
23
+ # ~/.cos-glasses/data/media alongside the standalone conversation/archive data.
24
+ # COS_MEDIA_ROOT=/path/on-a-local-volume/media
25
+
26
+ # Optional logical server identity location. Most installs should keep the
27
+ # default (~/.cos-glasses/server-instance-id) so reconnects can verify the same
28
+ # server after Wi-Fi/Tailscale changes and process restarts.
29
+ # COS_SERVER_INSTANCE_ID_PATH=/path/to/server-instance-id
30
+
22
31
  # ── THE LLM (chat) ──────────────────────────────────────────────────────
23
32
  # Chat runs through your LOCAL agent CLI — NOT an API key:
24
- # Opus / Sonnet / Haiku -> Claude Code CLI (https://claude.ai/download, then `claude login`)
25
- # Codex High -> Codex CLI (https://developers.openai.com/codex/, then `codex login`)
33
+ # Opus / Fable / Sonnet -> Claude Code CLI (https://claude.ai/download, then `claude login`)
34
+ # GPT Frontier/Balanced -> Codex CLI (https://developers.openai.com/codex/, then `codex login`)
26
35
  # Install at least one. Pick the default model the glasses use:
27
- # COS_G2_DEFAULT_MODEL=sonnet # opus | sonnet | haiku | codex-high
36
+ # COS_G2_DEFAULT_MODEL=sonnet # opus | fable | sonnet | codex-frontier | codex-balanced
28
37
  #
29
- # Codex options (only used for the codex-high model):
30
- # COS_CODEX_MODEL pins a specific codex model id; blank = use your codex CLI default.
31
- # COS_CODEX_REASONING_EFFORT is low | medium | high (default high).
38
+ # GPT slots refresh from the Codex model catalog every 15 minutes and retain
39
+ # their last-known-good resolution if discovery is temporarily unavailable.
40
+ # COS_CODEX_MODEL_REFRESH_TTL_MS=900000
41
+ # COS_CODEX_MODEL_REFRESH_TIMEOUT_MS=7000
42
+ #
43
+ # Legacy explicit overrides remain supported for migrated codex-high/frontier
44
+ # installs. Leave blank for auto-latest; Balanced always stays auto-catalog.
32
45
  # COS_CODEX_MODEL=
33
- # COS_CODEX_REASONING_EFFORT=high
46
+ # COS_CODEX_REASONING_EFFORT=high # low | medium | high | xhigh | max | ultra
47
+ #
48
+ # Codex remains read-only by default. This is the only broader trust opt-in:
49
+ # COS_CODEX_SANDBOX=workspace-write
34
50
 
35
51
  # ── VOICE (optional) ────────────────────────────────────────────────────
36
52
  # Local transcription is FREE via whisper.cpp (brew install whisper-cpp; the
package/CHANGELOG.md CHANGED
@@ -1,5 +1,113 @@
1
1
  # Changelog
2
2
 
3
+ ## 6.6.0
4
+
5
+ Reconnect compatibility for COS Glasses build 188, without importing private
6
+ COS day-context or Mac service-control behavior into the public package.
7
+
8
+ - **Stable logical server identity.** The server creates one atomic UUID under
9
+ `~/.cos-glasses/server-instance-id`, preserves it across process and network
10
+ restarts, and returns it from authenticated `/api/models` probes. Files are
11
+ mode `0600`; identity is minted only after every required listener binds.
12
+ - **Boot-scoped display cursors.** Display events receive one publish-owned ID
13
+ before fan-out, so multiple subscribers see the same cursor and cannot
14
+ duplicate replay records. Each process boot has a distinct UUID.
15
+ - **Deterministic reconnect handshake.** `/api/display-stream` emits `ready`
16
+ before application events, accepts boot/event cursors, replays the last 200
17
+ publish-owned events, and reports typed `boot_changed`, `cursor_ahead`, or
18
+ `buffer_overflow` gaps so clients reconcile durable history instead of
19
+ guessing or silently dropping replies.
20
+ - **Privacy boundary preserved.** Authenticated query activity remains off the
21
+ unauthenticated global display bus. The npm server does not include private
22
+ daily evidence exports, personal COS paths, launchd ownership, or remote
23
+ machine-restart controls.
24
+ - **Backward compatible.** Older clients can continue opening the same SSE
25
+ endpoint and ignoring the additive `ready`, cursor metadata, and replay-gap
26
+ events.
27
+
28
+ ## 6.5.0
29
+
30
+ Durable phone photos and assistant-selected output images for COS Glasses
31
+ build 179, while preserving the public server's sandbox and privacy boundary.
32
+
33
+ - **One media contract.** Authenticated phone uploads become opaque attachment
34
+ refs, survive queues/restarts/archives/numbered-message recall, and resolve to
35
+ normalized server-owned files for Claude/Codex. Bytes and storage paths never
36
+ enter SSE, run ledgers, or archives.
37
+ - **Answer images.** Claude or Codex can publish an already-local generated,
38
+ researched, or explicitly used email image through a private run-scoped
39
+ capability. The server accepts JPEG/PNG/WebP/HEIC/HEIF/AVIF up to 16 MiB and
40
+ 16 megapixels, strips metadata, re-encodes through the existing media store,
41
+ and appends refs to the completed answer.
42
+ - **No mailbox or URL crawler.** The publisher rejects URLs, data URIs, base64,
43
+ unrelated discovery, symlinks, directory replacement, content-id tampering,
44
+ over-capacity output, and manifest fields that could carry private paths.
45
+ - **Codex remains read-only.** Output publishing adds only the random private
46
+ run directory via `codex exec --add-dir`; global sandbox flags precede
47
+ `resume`. Older CLIs without `--add-dir` keep chat working and simply disable
48
+ Codex output-image publishing. There is no full-access fallback.
49
+ - **Durable finalization.** Assistant text is persisted before image
50
+ normalization, request media associates even if SSE disconnects, partial
51
+ image failures do not discard successful refs, and completion emits one
52
+ canonical `attachments` list with safe aggregate stats.
53
+ - **Lens contract.** `/api/health` advertises `mediaProcessingReady` and
54
+ `g2LensVariant=png-288x144-v1`; the media endpoint serves the validated phone,
55
+ thumbnail, and exact 288×144 G2 variants expected by build 179.
56
+ - **Fresh-install diagnostics.** The npm launcher now reports whether ffmpeg is
57
+ ready for phone/output/lens images, gives a non-blocking install command when
58
+ absent, and sends setup questions directly to `gotcos.com/wizard/`.
59
+ - **One server owner.** The public runner now claims the same atomic
60
+ machine-wide lock as the installed LaunchAgent before mutable modules load.
61
+ A duplicate exits with code 75, and HTTP/HTTPS listeners bind as one required
62
+ set: if either port is occupied, any earlier listener closes and the process
63
+ exits instead of surviving half-bound with separate SSE and media state.
64
+
65
+ Release evidence: TypeScript, 130/130 tests across 23 files, package dry-run
66
+ including the executable publisher and startup hardening, and a live duplicate
67
+ start against the installed LaunchAgent rejected before server initialization.
68
+
69
+ ## 6.4.0
70
+
71
+ Fresh-install parity for COS Glasses builds 170–173, without weakening the
72
+ public server's sandbox defaults.
73
+
74
+ - **Auto-updating GPT Frontier + Balanced.** Stable client slots resolve to the
75
+ top two capable models in the newest visible GPT generation through Codex's
76
+ official `model/list` catalog. The server refreshes at boot and every 15
77
+ minutes, and each Codex run awaits the same TTL-cached/coalesced refresh
78
+ before resolving its slot. It preserves the last-known-good catalog on
79
+ failures and falls back to the CLI default only before any discovery succeeds.
80
+ - **Fable + effort controls.** Fable joins Opus and Sonnet as a first-class
81
+ Claude tier alias, and High / Extra High / Max / Ultracode now propagate from
82
+ `/api/query` to both Claude and Codex. Claude aliases remain versionless and
83
+ 1M-context capable; Codex effort is clamped to each live model's advertised
84
+ support. Per-run ledgers record the concrete resolved model and effort.
85
+ - **Safe live job activity.** `activityToolMode` supports off, status-only, or
86
+ bounded observable tool input/output previews. ANSI/control data, credential
87
+ assignments, auth headers, provider tokens, JWTs, URL credentials, and opaque
88
+ blobs are redacted, including 40–72-character PEM/private-key body chunks.
89
+ Hidden reasoning is never surfaced.
90
+ - **Same-session run safety.** Turns for one conversation now serialize until
91
+ the active bridge sends a terminal callback, while different sessions remain
92
+ concurrent. Failed or cancelled Claude/Codex runs remove the exact pending
93
+ user exchange by object identity, preventing phantom prompts, duplicate-text
94
+ deletion, and resume-history contamination.
95
+ - **Authenticated transport boundary.** Activity lines are returned only on the
96
+ authenticated `/api/query` SSE stream. They are deliberately excluded from
97
+ the unauthenticated global display bus and its replay buffer.
98
+ - **Public trust model retained.** Codex remains read-only by default with only
99
+ the existing `workspace-write` opt-in. Existing archive traversal, local-day,
100
+ malformed-file, starter-kit launch-directory, and conversation behavior are
101
+ unchanged. Legacy `codex-high` state migrates to the frontier slot without
102
+ changing saved thread trust mode.
103
+ - **Diagnostics and compatibility.** `/api/models`, health data, and `/v1/models`
104
+ expose stable slots plus concrete live models. `cos-codex-high` remains an
105
+ accepted alias for older clients. Existing `COS_CODEX_MODEL` and
106
+ `COS_CODEX_REASONING_EFFORT` overrides continue to apply to the migrated
107
+ legacy/frontier slot; leave them unset for auto-latest. A new regression suite covers catalog
108
+ selection/fallback/refresh, sandbox arguments, migrations, effort mappings,
109
+ activity redaction, and the display-bus security boundary.
110
+
3
111
  ## 6.3.1
4
112
 
5
113
  Security + robustness hardening on the 6.3.0 archive routes, from a 3-agent QA pass. (6.3.0 was never published; 6.3.1 is the first release of the expanded route set.)
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # COS Glasses Server
2
2
 
3
3
  Self-hosted AI heads-up display for **Even G2 smart glasses**. Runs on your Mac,
4
- talks to your local **Claude Code** CLI, and pushes answers, voice
4
+ talks to your local **Claude Code or Codex** CLI, and pushes answers, voice
5
5
  transcription, and notes to the lens. Your data never leaves your machine, and no
6
6
  API key is pasted into the phone for chat.
7
7
 
@@ -11,22 +11,34 @@ API key is pasted into the phone for chat.
11
11
  npx @gotcos/glasses-server
12
12
  ```
13
13
 
14
- The launcher checks Node, finds your CLI, downloads the local voice model, writes
15
- `~/.cos-glasses/.env`, and starts the server on `0.0.0.0:3141`. On boot it prints
16
- an **API token** paste that into the COS Glasses app.
14
+ The launcher checks Node, finds your CLI, checks voice and image processing,
15
+ downloads the local voice model when needed, writes `~/.cos-glasses/.env`, and
16
+ starts the server on `0.0.0.0:3141`. On boot it prints
17
+ an **API token** — paste that into the COS Glasses app. Only one COS Glasses
18
+ server may run on a Mac at a time; a second `npx` or source runner exits before
19
+ opening ports or touching shared conversation/media state. Version 6.6.0 also
20
+ gives that server a durable identity and boot-scoped display replay, allowing
21
+ build 188+ to reconnect after a Tailscale, Wi-Fi, or process interruption
22
+ without silently losing completed replies.
17
23
 
18
24
  ## Requirements
19
25
 
20
26
  - **Node.js 20.11+** — https://nodejs.org
21
- - **Claude Code CLI** (Opus/Sonnet/Haiku) — https://claude.ai/download, then `claude login`
22
- _or_ **Codex CLI** (Codex High) — https://developers.openai.com/codex/, then `codex login`
27
+ - **Claude Code CLI** (Opus/Fable/Sonnet) — https://claude.ai/download, then `claude login`
28
+ _or_ **Codex CLI** (GPT Frontier/Balanced) — https://developers.openai.com/codex/, then `codex login`
23
29
  - **Even G2 glasses** + the **COS Glasses** app from the Even Hub
24
30
  - _Optional:_ `brew install whisper-cpp` for free local voice (otherwise OpenAI API)
31
+ - _Optional:_ `brew install ffmpeg` for phone/output image attachments (text chat remains available without it)
25
32
  - _Optional:_ **Tailscale** so your phone reaches your Mac from anywhere
26
33
 
27
34
  > No `ANTHROPIC_API_KEY` is needed — chat runs through your installed CLI, billed
28
35
  > to your existing Claude or Codex subscription. Pick either per query, or set a
29
- > default with `COS_G2_DEFAULT_MODEL` (`opus`|`sonnet`|`haiku`|`codex-high`).
36
+ > default with `COS_G2_DEFAULT_MODEL` (`opus`|`fable`|`sonnet`|`codex-frontier`|`codex-balanced`).
37
+ > Claude tier aliases and the two GPT slots resolve dynamically, so new model
38
+ > releases do not require a new glasses package. GPT discovery refreshes every
39
+ > 15 minutes and retains its last-known-good catalog through transient failures.
40
+ > Existing `COS_CODEX_MODEL` / `COS_CODEX_REASONING_EFFORT` settings remain
41
+ > supported on the migrated Frontier slot; leave them blank for auto-latest.
30
42
  > Codex runs **sandboxed read-only** by default (`COS_CODEX_SANDBOX` to adjust).
31
43
 
32
44
  ## Connect your phone (the one gotcha)
@@ -44,8 +56,13 @@ The built-in IP allowlist blocks public-internet traffic regardless.
44
56
  ## What it does
45
57
 
46
58
  - Ask anything, get a streamed answer on the lens (`/api/query`, `/v1/chat/completions`)
59
+ - Choose Opus, Fable, Sonnet, GPT Frontier, or GPT Balanced plus High, Extra
60
+ High, Max, or Ultracode effort; optional redacted tool activity streams only
61
+ to the authenticated query that requested it
47
62
  - Message History + cross-day "reference message N" — your chats are archived by day
48
63
  and every message keeps a permanent number you can recall (`/api/archive`, `/api/message/:num`)
64
+ - Send phone photos with queued prompts, and review assistant-selected generated,
65
+ research, or explicitly used email images in Messages and on the G2 lens
49
66
  - Live voice capture + transcription during meetings
50
67
  - Local whisper.cpp transcription (free) with OpenAI fallback (optional)
51
68
  - Tasks / calendar / people context **if** you run the
@@ -57,7 +74,8 @@ The built-in IP allowlist blocks public-internet traffic regardless.
57
74
  Config lives at `~/.cos-glasses/.env` (created on first run). Every key is
58
75
  optional except an installed CLI. Highlights: `BIND_HOST`, `PORT`,
59
76
  `COS_API_TOKEN` (auto if unset), `OPENAI_API_KEY` (cloud voice fallback),
60
- `COS_SCRIPTS_DIR` (full pipeline). Your name + transcription vocabulary live in
77
+ `COS_SCRIPTS_DIR` (full pipeline), and `COS_MEDIA_ROOT` (optional image-store
78
+ location; default `~/.cos-glasses/data/media`). Your name + transcription vocabulary live in
61
79
  `~/.cos-glasses/.cos-profile.json` (see `.cos-profile.example.json`).
62
80
 
63
81
  ## Run from source
@@ -72,8 +90,10 @@ BIND_HOST=0.0.0.0 npm run start:server
72
90
  ## Troubleshooting
73
91
 
74
92
  - *Phone can't connect* — check `BIND_HOST=0.0.0.0`, the same Tailscale account on both devices, and the correct `100.x` IP + token.
93
+ - *Safari connects but the app does not* — confirm `npx @gotcos/glasses-server@latest` is 6.6.0+, then use the app's server reconnect/edit control to verify the current URL and token. Do not run a second source or `npx` server alongside it.
75
94
  - *AI queries fail* — run `claude --version` / `codex --version`, then `claude login` / `codex login`.
76
95
  - *Voice getting billed?* — install `whisper-cpp` for free local transcription.
96
+ - *Photos unavailable?* — install `ffmpeg`, restart the server, and confirm `/api/health` reports `features.mediaProcessingReady: true`.
77
97
 
78
98
  ## License
79
99
 
package/bin/cli.cjs CHANGED
@@ -34,13 +34,13 @@ if (process.argv.includes('--help') || process.argv.includes('-h')) {
34
34
  console.log('')
35
35
  console.log(' Requirements:')
36
36
  console.log(' - Node.js 20.11+')
37
- console.log(' - Claude Code CLI (Opus / Sonnet / Haiku) or Codex CLI (Codex High)')
37
+ console.log(' - Claude Code CLI (Opus / Fable / Sonnet) or Codex CLI (GPT Frontier / Balanced)')
38
38
  console.log(' - Even G2 smart glasses + the COS Glasses app (Even Hub)')
39
39
  console.log('')
40
40
  console.log(' No API key is needed for chat — it runs through your installed CLI.')
41
41
  console.log(' Config persists at ~/.cos-glasses/.env')
42
42
  console.log('')
43
- console.log(' Setup guide: https://www.gotcos.com')
43
+ console.log(' Setup guide: https://www.gotcos.com/wizard/')
44
44
  console.log('')
45
45
  process.exit(0)
46
46
  }
@@ -60,9 +60,9 @@ if (nodeMajor < 20 || (nodeMajor === 20 && nodeMinor < 11)) {
60
60
  console.log(green(' ✓') + ` Node.js ${process.versions.node}`)
61
61
 
62
62
  // Step 2: agent CLI detection — at least one of Claude Code / Codex is required
63
- function getCliVersion(command) {
63
+ function getCliVersion(command, versionArg = '--version') {
64
64
  try {
65
- return execSync(`${command} --version 2>&1`, { shell: '/bin/sh', stdio: 'pipe', timeout: 5000 }).toString().trim()
65
+ return execSync(`${command} ${versionArg} 2>&1`, { shell: '/bin/sh', stdio: 'pipe', timeout: 5000 }).toString().trim()
66
66
  } catch {
67
67
  return null
68
68
  }
@@ -75,15 +75,15 @@ function normalizeCodexVersion(raw) {
75
75
  const claudeVersion = getCliVersion('claude')
76
76
  const codexVersion = getCliVersion('codex')
77
77
  if (claudeVersion) {
78
- console.log(green(' ✓') + ` Claude Code ${claudeVersion} ` + dim('(Opus / Sonnet / Haiku)'))
78
+ console.log(green(' ✓') + ` Claude Code ${claudeVersion} ` + dim('(Opus / Fable / Sonnet)'))
79
79
  } else {
80
- console.log(yellow(' ⚠') + ' Claude Code CLI not found ' + dim('— Opus/Sonnet/Haiku unavailable'))
80
+ console.log(yellow(' ⚠') + ' Claude Code CLI not found ' + dim('— Opus/Fable/Sonnet unavailable'))
81
81
  console.log(' Install: ' + bold('https://claude.ai/download'))
82
82
  }
83
83
  if (codexVersion) {
84
- console.log(green(' ✓') + ` Codex CLI ${normalizeCodexVersion(codexVersion)} ` + dim('(Codex High)'))
84
+ console.log(green(' ✓') + ` Codex CLI ${normalizeCodexVersion(codexVersion)} ` + dim('(GPT Frontier / Balanced)'))
85
85
  } else {
86
- console.log(yellow(' ⚠') + ' Codex CLI not found ' + dim('— Codex High unavailable'))
86
+ console.log(yellow(' ⚠') + ' Codex CLI not found ' + dim('— GPT Frontier/Balanced unavailable'))
87
87
  }
88
88
  if (!claudeVersion && !codexVersion) {
89
89
  console.log('')
@@ -184,13 +184,25 @@ if (whisperCliPath && hasValidModel) {
184
184
  console.log(' Free local voice: ' + bold('brew install whisper-cpp') + dim(' (no Homebrew? https://brew.sh)'))
185
185
  }
186
186
 
187
- // Step 6: phone reachabilitythe glasses' phone app must reach this server.
187
+ // Step 6: image capabilityffmpeg validates, strips metadata, normalizes,
188
+ // and builds the exact 288x144 G2 variant. It is optional so text/voice remain
189
+ // useful on a minimal install, but the launcher should make the gap visible.
190
+ const ffmpegVersion = getCliVersion('ffmpeg', '-version')
191
+ if (ffmpegVersion) {
192
+ const firstLine = ffmpegVersion.split('\n')[0].trim()
193
+ console.log(green(' ✓') + ` ${firstLine} ` + dim('— phone + lens images ready'))
194
+ } else {
195
+ console.log(yellow(' ⚠') + ' ffmpeg not installed ' + dim('— phone and answer images disabled'))
196
+ console.log(' Enable photos: ' + bold('brew install ffmpeg') + dim(' (text + voice still work)'))
197
+ }
198
+
199
+ // Step 7: phone reachability — the glasses' phone app must reach this server.
188
200
  if (!process.env.BIND_HOST) {
189
201
  process.env.BIND_HOST = '0.0.0.0'
190
202
  console.log(yellow(' ⚠') + ' BIND_HOST not set — defaulting to 0.0.0.0 so your phone can reach the server')
191
203
  }
192
204
 
193
- // Step 7: start the bundled server
205
+ // Step 8: start the bundled server
194
206
  try {
195
207
  const ld = process.env.COS_LAUNCH_DIR
196
208
  if (ld && (existsSync(join(ld, '.cos', 'manifest.json')) || existsSync(join(ld, 'AGENTS.md')) || existsSync(join(ld, 'CLAUDE.md')))) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotcos/glasses-server",
3
- "version": "6.3.1",
3
+ "version": "6.6.0",
4
4
  "description": "COS Glasses — self-hosted AI heads-up-display server for Even G2 smart glasses, powered by your local Claude Code or Codex CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,14 +9,25 @@
9
9
  "scripts": {
10
10
  "start": "node bin/cli.cjs",
11
11
  "start:server": "node --import tsx/esm server/index.ts",
12
+ "test": "vitest run",
12
13
  "typecheck": "tsc --noEmit"
13
14
  },
14
- "keywords": ["even-g2", "smart-glasses", "claude-code", "codex", "ai", "cos", "heads-up-display", "whisper"],
15
+ "keywords": [
16
+ "even-g2",
17
+ "smart-glasses",
18
+ "claude-code",
19
+ "codex",
20
+ "ai",
21
+ "cos",
22
+ "heads-up-display",
23
+ "whisper"
24
+ ],
15
25
  "files": [
16
26
  "bin/cli.cjs",
17
- "server/**/*.ts",
18
- "server/**/*.js",
19
- "shared/**/*.ts",
27
+ "server",
28
+ "shared",
29
+ "!server/**/*.test.ts",
30
+ "!shared/**/*.test.ts",
20
31
  ".env.example",
21
32
  ".cos-profile.example.json",
22
33
  "README.md",
@@ -48,6 +59,7 @@
48
59
  "@types/cors": "^2.8.19",
49
60
  "@types/express": "^5.0.6",
50
61
  "@types/node": "^22.10.0",
51
- "typescript": "^5.9.3"
62
+ "typescript": "^5.9.3",
63
+ "vitest": "^3.2.7"
52
64
  }
53
65
  }