@threadbase-sh/streamer 1.58.0 → 1.58.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/dist/index.js CHANGED
@@ -1252,11 +1252,19 @@ var CodexPtyRunner = class {
1252
1252
  onStatusChange;
1253
1253
  onPhaseChange;
1254
1254
  onReady;
1255
- // Broadcasts Codex's blocking startup gates (directory trust, hooks review)
1256
- // as question cards; null dismisses the card once the gate leaves the screen.
1255
+ // Every Codex prompt the client can answer — startup gates (directory trust,
1256
+ // hooks review), command approvals, and the rate-limit model picker is
1257
+ // broadcast through this one channel; null dismisses the card once the prompt
1258
+ // leaves the screen.
1259
+ //
1260
+ // Deliberately NOT onLiveQuestion/onLiveQuestionGone, which is Claude's
1261
+ // AskUserQuestion transport. Both channels land on the same mobile
1262
+ // QuestionCard, and the permission one is the correct fit for Codex: its menus
1263
+ // are answered by the option's real on-screen number (parseCodexNumberedOptions
1264
+ // emits `answerKeys: "2\r"`), which is exactly what `permissionIndices` carries
1265
+ // and what AskUserQuestion's down-arrow-count model cannot express. Wiring the
1266
+ // question channel as well would be a second path to the same card.
1257
1267
  onPermissionChange;
1258
- onLiveQuestion;
1259
- onLiveQuestionGone;
1260
1268
  onUserMessage;
1261
1269
  log;
1262
1270
  // Tracks sessions whose PTY has spawned but Codex hasn't yet reached its
@@ -1306,8 +1314,6 @@ var CodexPtyRunner = class {
1306
1314
  this.onPhaseChange = options.onPhaseChange;
1307
1315
  this.onReady = options.onReady;
1308
1316
  this.onPermissionChange = options.onPermissionChange;
1309
- this.onLiveQuestion = options.onLiveQuestion;
1310
- this.onLiveQuestionGone = options.onLiveQuestionGone;
1311
1317
  this.onUserMessage = options.onUserMessage;
1312
1318
  this.log = options.logger ?? getLogger("codex-pty");
1313
1319
  }
@@ -11346,7 +11352,37 @@ var PairTokenStore = class {
11346
11352
  expiresInSeconds: Math.floor(this.ttlMs / 1e3)
11347
11353
  };
11348
11354
  }
11355
+ /**
11356
+ * Whether `consume` would succeed right now, WITHOUT spending the token.
11357
+ *
11358
+ * Exists so a caller can reject a bad token before doing any work, and still
11359
+ * spend the token only once the work has succeeded. A pair token is
11360
+ * single-use and lives 180 seconds, so spending it on a request that then
11361
+ * fails costs the user a whole new QR — and, worse, makes their retry
11362
+ * indistinguishable from an attacker replaying a photographed code, which is
11363
+ * the one signal `design.md` §2.6 designates as replay detection.
11364
+ *
11365
+ * Advisory, not a reservation: it takes no lock and holds nothing. The
11366
+ * authoritative answer is still `consume`'s.
11367
+ */
11368
+ verify(token) {
11369
+ const result = this.check(token);
11370
+ return result.ok ? { ok: true } : result;
11371
+ }
11349
11372
  consume(token) {
11373
+ const result = this.check(token);
11374
+ if (!result.ok) return result;
11375
+ result.record.used = true;
11376
+ return { ok: true };
11377
+ }
11378
+ /**
11379
+ * The shared predicate behind `verify` and `consume`.
11380
+ *
11381
+ * One implementation on purpose: two copies of "is this token usable" is two
11382
+ * places for the expiry or single-use rule to drift, and a drift in this
11383
+ * direction fails open.
11384
+ */
11385
+ check(token) {
11350
11386
  const record2 = this.current;
11351
11387
  if (!record2 || record2.token !== token) return { ok: false, reason: "unknown" };
11352
11388
  if (Date.now() > record2.expiresAt) {
@@ -11354,8 +11390,7 @@ var PairTokenStore = class {
11354
11390
  return { ok: false, reason: "expired" };
11355
11391
  }
11356
11392
  if (record2.used) return { ok: false, reason: "used" };
11357
- record2.used = true;
11358
- return { ok: true };
11393
+ return { ok: true, record: record2 };
11359
11394
  }
11360
11395
  peek() {
11361
11396
  return this.current;
@@ -16147,9 +16182,9 @@ var StreamerServer = class {
16147
16182
  json(res, 400, { error: "Missing token or clientPublicKey" });
16148
16183
  return;
16149
16184
  }
16150
- const result = this.pairTokens.consume(token);
16151
- if (!result.ok) {
16152
- json(res, 401, { error: `Pair token ${result.reason}` });
16185
+ const precheck = this.pairTokens.verify(token);
16186
+ if (!precheck.ok) {
16187
+ json(res, 401, { error: `Pair token ${precheck.reason}` });
16153
16188
  return;
16154
16189
  }
16155
16190
  let sealed;
@@ -16160,6 +16195,11 @@ var StreamerServer = class {
16160
16195
  json(res, 400, { error: message });
16161
16196
  return;
16162
16197
  }
16198
+ const result = this.pairTokens.consume(token);
16199
+ if (!result.ok) {
16200
+ json(res, 401, { error: `Pair token ${result.reason}` });
16201
+ return;
16202
+ }
16163
16203
  const ts = (/* @__PURE__ */ new Date()).toISOString();
16164
16204
  this.log.info(`[pair] token exchanged from ${ip} at ${ts}`, {
16165
16205
  event: "pair.token_exchanged",