@glassly/cloud-client 0.1.0-dev.90 → 0.1.0-dev.96

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassly/cloud-client",
3
- "version": "0.1.0-dev.90",
3
+ "version": "0.1.0-dev.96",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "exports": {
@@ -12,7 +12,7 @@
12
12
  "test": "bun test"
13
13
  },
14
14
  "dependencies": {
15
- "@glassly/cloud-protocol": "0.1.0-dev.90",
15
+ "@glassly/cloud-protocol": "0.1.0-dev.96",
16
16
  "tweetnacl": "^1.0.3"
17
17
  },
18
18
  "devDependencies": {
package/src/client.ts CHANGED
@@ -22,9 +22,8 @@ import { CloudClientError } from "./errors";
22
22
  import type { ConnectionInit } from "@glassly/cloud-protocol";
23
23
  import { systemTimers } from "./timers";
24
24
 
25
- // The module implementations. Each is owned by another agent under ./modules/**;
26
- // this file only constructs them, matching the constructor signatures fixed in
27
- // design.md exactly.
25
+ // The module implementations, under ./modules/**. This file only constructs
26
+ // them, matching the constructor signatures fixed in design.md exactly.
28
27
  import { Auth } from "./modules/auth/auth";
29
28
  import { TokenStore } from "./modules/auth/token-store";
30
29
  import { Runtime } from "./modules/runtime/runtime";
@@ -51,12 +50,12 @@ import { Core } from "./modules/core/core";
51
50
  const DEFAULT_RECONNECT = { baseMs: 500, maxMs: 5_000, jitter: true };
52
51
 
53
52
  /**
54
- * The default audio codec the client announces in the handshake.
53
+ * The default audio codec the client announces in the handshake when a host
54
+ * does not set `config.audio.codec`.
55
55
  *
56
- * LC3 at 16 kHz matches the glasses' on-device codec, so the cloud transcribes
57
- * the same bytes the device captures. A future config knob can override this; for
58
- * now the handshake announces the device default so audio that starts immediately
59
- * after connect is decoded correctly.
56
+ * PCM at 16 kHz needs no frame-size negotiation, so it is the safe default; a
57
+ * host whose device captures LC3 sets `config.audio.codec = "lc3"` (and the
58
+ * required `frameSizeBytes`) explicitly.
60
59
  */
61
60
  const DEFAULT_AUDIO_CODEC = "pcm" as const;
62
61
  const DEFAULT_AUDIO_SAMPLE_RATE = 16_000;
@@ -411,9 +411,9 @@ export class Auth implements AuthModule {
411
411
  * dead refresh token will not heal on its own, and retrying would loop.
412
412
  *
413
413
  * A transient failure — network error, 5xx during a deploy, 429 — keeps
414
- * the stored token and propagates as-is. Clearing on those is how a flaky
415
- * cold start (the first launch after an app update, typically) used to log
416
- * users out of a perfectly healthy session.
414
+ * the stored token and propagates as-is: clearing on a transient failure
415
+ * would log out a perfectly healthy session on a flaky cold start (e.g.
416
+ * first launch after an app update).
417
417
  */
418
418
  private async refresh(refreshToken: string, opts?: { deferExpired?: boolean }): Promise<string> {
419
419
  const body = new URLSearchParams({
@@ -12,30 +12,14 @@
12
12
  * It never imports a real socket: the platform supplies a `WebSocketLike`
13
13
  * factory, so the same code runs on the phone and in a Node/Bun test harness.
14
14
  *
15
- * Reconnect robustness (why the loop is self-sustaining + watchdogged):
16
- * The reconnect loop used to be driven ONLY by the socket's `onClose` event
17
- * (`handleClose` -> `scheduleReconnect`), and a scheduled attempt's
18
- * `connectOnce().catch()` just swallowed the rejection, trusting that a close
19
- * event would always fire and schedule the next try. That assumption is the
20
- * bug: a connect attempt can fail WITHOUT ever firing a clean `onClose` -- a
21
- * transient network/DNS blip mid-handshake, or a `WebSocketLike` transport that
22
- * emits only `onError` and no `onClose`. When that happens the chain breaks:
23
- * nothing schedules the next retry, so the client sits SILENTLY disconnected
24
- * forever until a full app relaunch. We hit this in dev (ADB/Metro flapping
25
- * wedged the v2 socket; only a cold relaunch recovered it).
26
- *
27
- * Three changes close that gap, belt-and-suspenders:
28
- * 0. A failed initial `open()` now also enters the reconnect loop. Before
29
- * this, reconnect was robust only AFTER the first successful session had
30
- * dropped; if the app booted while cloud was down, `open()` rejected and no
31
- * retry was queued.
32
- * 1. The scheduled attempt's `.catch()` now reschedules itself, so a failure
33
- * that did NOT fire `onClose` still queues the next try. `scheduleReconnect`
34
- * is idempotent (guarded by `reconnectTimer`), so the double call from
35
- * `onClose` + this catch never stacks two timers.
36
- * 2. A lightweight watchdog interval revives the loop if some unforeseen path
37
- * ever leaves us `closed`, not host-closed, with no reconnect pending. Even
38
- * if reasoning (1) misses a case, the watchdog guarantees we never sit dead.
15
+ * Reconnect must not depend on `onClose`: a connect attempt can fail without
16
+ * ever firing a clean close (network/DNS blip mid-handshake, or a
17
+ * `WebSocketLike` transport that emits only `onError`), which would otherwise
18
+ * leave the client silently disconnected until relaunch. So the loop is driven
19
+ * from three places: a failed initial `open()`, the scheduled attempt's own
20
+ * `.catch()`, and a watchdog interval that revives it if we are ever `closed`,
21
+ * not host-closed, with no reconnect pending. `scheduleReconnect` is idempotent
22
+ * (guarded by `reconnectTimer`) so the overlapping callers never stack timers.
39
23
  *
40
24
  * See docs/issues/004-cloud-client/design.md ("src/modules/runtime/connection.ts")
41
25
  * and docs/issues/002-cloud-runtime/protocol.md (envelope, handshake, control).