@threadbase-sh/streamer 1.69.3 → 1.69.5

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.cjs CHANGED
@@ -4142,7 +4142,7 @@ async function readGitBranch(dir) {
4142
4142
  // src/server.ts
4143
4143
  var import_node_ws = require("@hono/node-ws");
4144
4144
  var import_client = require("@temporalio/client");
4145
- var import_crypto13 = require("crypto");
4145
+ var import_crypto14 = require("crypto");
4146
4146
  var import_events = require("events");
4147
4147
  var import_fs28 = require("fs");
4148
4148
  var import_promises7 = require("fs/promises");
@@ -9251,6 +9251,7 @@ var ConversationHandlers = class {
9251
9251
  };
9252
9252
 
9253
9253
  // src/api/handlers/sessions.handlers.ts
9254
+ var import_crypto9 = require("crypto");
9254
9255
  var import_fs15 = require("fs");
9255
9256
  var import_path16 = require("path");
9256
9257
 
@@ -9622,19 +9623,39 @@ var UnknownOptionError = class extends Error {
9622
9623
  question;
9623
9624
  value;
9624
9625
  };
9626
+ var UnsupportedPromptShapeError = class extends Error {
9627
+ constructor(detail) {
9628
+ super(`Unsupported prompt shape: ${detail}`);
9629
+ this.detail = detail;
9630
+ this.name = "UnsupportedPromptShapeError";
9631
+ }
9632
+ detail;
9633
+ };
9634
+ var IncompleteAnswerError = class extends Error {
9635
+ constructor(question) {
9636
+ super(`Missing answer for question "${question}"`);
9637
+ this.question = question;
9638
+ this.name = "IncompleteAnswerError";
9639
+ }
9640
+ question;
9641
+ };
9625
9642
  function answersToKeystrokes(questions, answers) {
9626
- let out = "";
9627
- for (const q of questions) {
9628
- const raw = answers[q.question];
9629
- if (raw === void 0) {
9630
- throw new Error(`Missing answer for question "${q.question}"`);
9631
- }
9632
- const label = Array.isArray(raw) ? raw[0] : raw;
9633
- const target = q.options.findIndex((o) => o.label === label);
9634
- if (target < 0) throw new UnknownOptionError(q.question, label);
9635
- out += DOWN.repeat(target) + ENTER3;
9643
+ if (questions.length !== 1) {
9644
+ throw new UnsupportedPromptShapeError(`${questions.length} questions`);
9636
9645
  }
9637
- return out;
9646
+ const q = questions[0];
9647
+ if (q.multiSelect) throw new UnsupportedPromptShapeError("multiSelect");
9648
+ const raw = answers[q.question];
9649
+ if (raw === void 0 || Array.isArray(raw) && raw.length === 0) {
9650
+ throw new IncompleteAnswerError(q.question);
9651
+ }
9652
+ if (Array.isArray(raw) && raw.length > 1) {
9653
+ throw new UnsupportedPromptShapeError(`${raw.length} labels for a single-select question`);
9654
+ }
9655
+ const label = Array.isArray(raw) ? raw[0] : raw;
9656
+ const target = q.options.findIndex((o) => o.label === label);
9657
+ if (target < 0) throw new UnknownOptionError(q.question, label);
9658
+ return DOWN.repeat(target) + ENTER3;
9638
9659
  }
9639
9660
 
9640
9661
  // src/services/questions/resolveAnswer.ts
@@ -9648,6 +9669,10 @@ function resolveAnswer(pending, body) {
9648
9669
  return { ok: true, keys: answersToKeystrokes(pending.questions, answers) };
9649
9670
  } catch (e) {
9650
9671
  if (e instanceof UnknownOptionError) return { ok: false, reason: "unknown_option" };
9672
+ if (e instanceof UnsupportedPromptShapeError) {
9673
+ return { ok: false, reason: "unsupported_prompt_shape" };
9674
+ }
9675
+ if (e instanceof IncompleteAnswerError) return { ok: false, reason: "incomplete_answer" };
9651
9676
  throw e;
9652
9677
  }
9653
9678
  }
@@ -10546,7 +10571,9 @@ var SessionHandlers = class {
10546
10571
  }
10547
10572
  const key = permissionContentKey(gate);
10548
10573
  if (this.pendingPermissionKey.get(sessionId) === key) return;
10549
- this.pendingPermission.set(sessionId, gate);
10574
+ const prior = this.pendingPermission.get(sessionId);
10575
+ const gateId = prior && permissionGateKey(prior) === permissionGateKey(gate) ? prior.gateId : (0, import_crypto9.randomUUID)();
10576
+ this.pendingPermission.set(sessionId, { ...gate, gateId });
10550
10577
  this.pendingPermissionKey.set(sessionId, key);
10551
10578
  const subscriberCount = this.sessionSubscribers.get(sessionId)?.size ?? 0;
10552
10579
  this.log.info(
@@ -10560,7 +10587,8 @@ var SessionHandlers = class {
10560
10587
  ...gate.detail ? { detail: gate.detail } : {},
10561
10588
  options: gate.options,
10562
10589
  ...gate.cursor !== void 0 ? { cursor: gate.cursor } : {},
10563
- contentKey: permissionGateKey(gate)
10590
+ contentKey: permissionGateKey(gate),
10591
+ gateId
10564
10592
  });
10565
10593
  }
10566
10594
  /**
@@ -10588,10 +10616,15 @@ var SessionHandlers = class {
10588
10616
  const body = await readBody2(req);
10589
10617
  const contentKey = body?.contentKey;
10590
10618
  const optionIndex = body?.optionIndex;
10619
+ const gateId = body?.gateId;
10591
10620
  if (typeof contentKey !== "string" || !Number.isInteger(optionIndex) || optionIndex < 0) {
10592
10621
  json(res, 400, { ok: false, reason: "Expected { contentKey: string, optionIndex: number }" });
10593
10622
  return;
10594
10623
  }
10624
+ if (gateId !== void 0 && typeof gateId !== "string") {
10625
+ json(res, 400, { ok: false, reason: "Expected gateId to be a string" });
10626
+ return;
10627
+ }
10595
10628
  const gateClosed = () => {
10596
10629
  this.pendingPermission.delete(sessionId);
10597
10630
  this.pendingPermissionKey.delete(sessionId);
@@ -10603,6 +10636,16 @@ var SessionHandlers = class {
10603
10636
  gateClosed();
10604
10637
  return;
10605
10638
  }
10639
+ if (gateId !== void 0 && gateId !== gate.gateId) {
10640
+ json(res, 409, { ok: false, reason: "gate_mismatch" });
10641
+ return;
10642
+ }
10643
+ if (gateId === void 0) {
10644
+ this.log.info(`[permission.answer_legacy_identity] ${sessionId.slice(0, 8)}`, {
10645
+ event: "permission.answer_legacy_identity",
10646
+ sessionId
10647
+ });
10648
+ }
10606
10649
  if (permissionGateKey(gate) !== contentKey) {
10607
10650
  json(res, 409, { ok: false, reason: "gate_mismatch" });
10608
10651
  return;
@@ -10612,7 +10655,8 @@ var SessionHandlers = class {
10612
10655
  json(res, 409, { ok: false, reason: "unknown_option" });
10613
10656
  return;
10614
10657
  }
10615
- if (!await this.permissionGateStillOpen(sessionId, contentKey)) {
10658
+ const provider = this.sessionStore.getManaged(sessionId)?.provider;
10659
+ if (provider !== CODEX_CLI_PROVIDER && !await this.permissionGateStillOpen(sessionId, contentKey)) {
10616
10660
  gateClosed();
10617
10661
  return;
10618
10662
  }
@@ -10656,7 +10700,14 @@ var SessionHandlers = class {
10656
10700
  const pending = this.pendingQuestions.get(sessionId);
10657
10701
  const resolution = resolveAnswer(pending, body);
10658
10702
  if (!resolution.ok) {
10659
- json(res, 400, { ok: false, reason: resolution.reason });
10703
+ const unanswerable = resolution.reason === "unsupported_prompt_shape" || resolution.reason === "incomplete_answer";
10704
+ json(res, 400, {
10705
+ ok: false,
10706
+ reason: resolution.reason,
10707
+ ...unanswerable ? {
10708
+ error: "This prompt needs an answer the app cannot give yet; answer it in the terminal"
10709
+ } : {}
10710
+ });
10660
10711
  return;
10661
10712
  }
10662
10713
  const toolUseId = pending?.toolUseId ?? "";
@@ -11320,7 +11371,7 @@ var ManagedSessionsRepository = class {
11320
11371
  };
11321
11372
 
11322
11373
  // src/db/repositories/projects.repository.ts
11323
- var import_crypto9 = require("crypto");
11374
+ var import_crypto10 = require("crypto");
11324
11375
  function rowToProject(row) {
11325
11376
  return {
11326
11377
  id: row.id,
@@ -11405,7 +11456,7 @@ var ProjectsRepository = class {
11405
11456
  });
11406
11457
  return rowToProject(this.getById.get(existing.id));
11407
11458
  }
11408
- const id = (0, import_crypto9.randomUUID)();
11459
+ const id = (0, import_crypto10.randomUUID)();
11409
11460
  this.insert.run({
11410
11461
  id,
11411
11462
  path,
@@ -11535,7 +11586,7 @@ var RuntimeStore = class _RuntimeStore {
11535
11586
  };
11536
11587
 
11537
11588
  // src/e2ee/noise.ts
11538
- var import_crypto10 = require("crypto");
11589
+ var import_crypto11 = require("crypto");
11539
11590
  var NOISE_PROTOCOL_NAME = "Noise_IKpsk1_25519_ChaChaPoly_SHA256";
11540
11591
  var PAIR_PROLOGUE = Buffer.from("threadbase-e2ee/1 pair", "utf-8");
11541
11592
  var DHLEN = 32;
@@ -11551,15 +11602,15 @@ var NoiseError = class extends Error {
11551
11602
  }
11552
11603
  };
11553
11604
  function generateKeyPair() {
11554
- const { publicKey, privateKey } = (0, import_crypto10.generateKeyPairSync)("x25519");
11605
+ const { publicKey, privateKey } = (0, import_crypto11.generateKeyPairSync)("x25519");
11555
11606
  return { publicKey, privateKey, publicKeyRaw: rawPublicKey(publicKey) };
11556
11607
  }
11557
11608
  function keyPairFrom(privateKey) {
11558
- const publicKey = (0, import_crypto10.createPublicKey)(privateKey);
11609
+ const publicKey = (0, import_crypto11.createPublicKey)(privateKey);
11559
11610
  return { publicKey, privateKey, publicKeyRaw: rawPublicKey(publicKey) };
11560
11611
  }
11561
11612
  function rawPublicKey(key) {
11562
- const pub = key.type === "public" ? key : (0, import_crypto10.createPublicKey)(key);
11613
+ const pub = key.type === "public" ? key : (0, import_crypto11.createPublicKey)(key);
11563
11614
  const jwk = pub.export({ format: "jwk" });
11564
11615
  if (jwk.crv !== "X25519" || typeof jwk.x !== "string") {
11565
11616
  throw new NoiseError("Not an X25519 public key");
@@ -11571,7 +11622,7 @@ function publicKeyFromRaw(raw) {
11571
11622
  throw new NoiseError(`X25519 public key must be ${DHLEN} bytes, got ${raw.length}`);
11572
11623
  }
11573
11624
  try {
11574
- return (0, import_crypto10.createPublicKey)({
11625
+ return (0, import_crypto11.createPublicKey)({
11575
11626
  key: { kty: "OKP", crv: "X25519", x: raw.toString("base64url") },
11576
11627
  format: "jwk"
11577
11628
  });
@@ -11580,7 +11631,7 @@ function publicKeyFromRaw(raw) {
11580
11631
  }
11581
11632
  }
11582
11633
  function pskFromPairToken(token) {
11583
- return (0, import_crypto10.createHash)("sha256").update("threadbase-e2ee/1 psk", "utf-8").update(token, "utf-8").digest();
11634
+ return (0, import_crypto11.createHash)("sha256").update("threadbase-e2ee/1 psk", "utf-8").update(token, "utf-8").digest();
11584
11635
  }
11585
11636
  function chachaNonce(n) {
11586
11637
  const nonce = Buffer.alloc(12);
@@ -11599,7 +11650,7 @@ var CipherState = class {
11599
11650
  }
11600
11651
  encryptWithAd(ad, plaintext) {
11601
11652
  if (!this.k) return plaintext;
11602
- const cipher = (0, import_crypto10.createCipheriv)("chacha20-poly1305", this.k, chachaNonce(this.n), {
11653
+ const cipher = (0, import_crypto11.createCipheriv)("chacha20-poly1305", this.k, chachaNonce(this.n), {
11603
11654
  authTagLength: TAGLEN
11604
11655
  });
11605
11656
  cipher.setAAD(ad, { plaintextLength: plaintext.length });
@@ -11612,7 +11663,7 @@ var CipherState = class {
11612
11663
  if (ciphertext.length < TAGLEN) throw new NoiseError("Ciphertext shorter than its tag");
11613
11664
  const body = ciphertext.subarray(0, ciphertext.length - TAGLEN);
11614
11665
  const tag = ciphertext.subarray(ciphertext.length - TAGLEN);
11615
- const decipher = (0, import_crypto10.createDecipheriv)("chacha20-poly1305", this.k, chachaNonce(this.n), {
11666
+ const decipher = (0, import_crypto11.createDecipheriv)("chacha20-poly1305", this.k, chachaNonce(this.n), {
11616
11667
  authTagLength: TAGLEN
11617
11668
  });
11618
11669
  decipher.setAAD(ad, { plaintextLength: body.length });
@@ -11633,7 +11684,7 @@ var SymmetricState = class {
11633
11684
  cipher = new CipherState();
11634
11685
  constructor(protocolName) {
11635
11686
  const name = Buffer.from(protocolName, "utf-8");
11636
- this.h = name.length <= HASHLEN ? Buffer.concat([name, Buffer.alloc(HASHLEN - name.length)]) : (0, import_crypto10.createHash)("sha256").update(name).digest();
11687
+ this.h = name.length <= HASHLEN ? Buffer.concat([name, Buffer.alloc(HASHLEN - name.length)]) : (0, import_crypto11.createHash)("sha256").update(name).digest();
11637
11688
  this.ck = Buffer.from(this.h);
11638
11689
  this.cipher.initializeKey(null);
11639
11690
  }
@@ -11645,7 +11696,7 @@ var SymmetricState = class {
11645
11696
  * subtly wrong.
11646
11697
  */
11647
11698
  hkdf(ikm, outputs) {
11648
- const raw = Buffer.from((0, import_crypto10.hkdfSync)("sha256", ikm, this.ck, Buffer.alloc(0), HASHLEN * outputs));
11699
+ const raw = Buffer.from((0, import_crypto11.hkdfSync)("sha256", ikm, this.ck, Buffer.alloc(0), HASHLEN * outputs));
11649
11700
  const out = [];
11650
11701
  for (let i = 0; i < outputs; i++) out.push(raw.subarray(i * HASHLEN, (i + 1) * HASHLEN));
11651
11702
  return out;
@@ -11656,7 +11707,7 @@ var SymmetricState = class {
11656
11707
  this.cipher.initializeKey(tempK);
11657
11708
  }
11658
11709
  mixHash(data) {
11659
- this.h = (0, import_crypto10.createHash)("sha256").update(this.h).update(data).digest();
11710
+ this.h = (0, import_crypto11.createHash)("sha256").update(this.h).update(data).digest();
11660
11711
  }
11661
11712
  /** Spec §5.2. Used only by the `psk` token. */
11662
11713
  mixKeyAndHash(ikm) {
@@ -11686,7 +11737,7 @@ var SymmetricState = class {
11686
11737
  }
11687
11738
  };
11688
11739
  function dh(privateKey, publicKey) {
11689
- return (0, import_crypto10.diffieHellman)({ privateKey, publicKey });
11740
+ return (0, import_crypto11.diffieHellman)({ privateKey, publicKey });
11690
11741
  }
11691
11742
  function readMessage1(args) {
11692
11743
  assertMessageSize(args.message1, NOISE_MESSAGE_1_OVERHEAD);
@@ -11998,7 +12049,7 @@ var ExternalTailManager = class {
11998
12049
  };
11999
12050
 
12000
12051
  // src/pair-store.ts
12001
- var import_crypto11 = require("crypto");
12052
+ var import_crypto12 = require("crypto");
12002
12053
  var DEFAULT_TTL_SECONDS = 180;
12003
12054
  var SWEEP_INTERVAL_MS = 6e4;
12004
12055
  var PairTokenStore = class {
@@ -12013,7 +12064,7 @@ var PairTokenStore = class {
12013
12064
  }
12014
12065
  }
12015
12066
  mint() {
12016
- const token = `pt_${(0, import_crypto11.randomBytes)(16).toString("hex")}`;
12067
+ const token = `pt_${(0, import_crypto12.randomBytes)(16).toString("hex")}`;
12017
12068
  const expiresAt = Date.now() + this.ttlMs;
12018
12069
  this.current = { token, expiresAt, used: false };
12019
12070
  return {
@@ -13171,7 +13222,8 @@ function createApiDeps(deps) {
13171
13222
  ...pendingGate.detail ? { detail: pendingGate.detail } : {},
13172
13223
  options: pendingGate.options,
13173
13224
  ...pendingGate.cursor !== void 0 ? { cursor: pendingGate.cursor } : {},
13174
- contentKey: permissionGateKey(pendingGate)
13225
+ contentKey: permissionGateKey(pendingGate),
13226
+ gateId: pendingGate.gateId
13175
13227
  })
13176
13228
  );
13177
13229
  }
@@ -13238,7 +13290,7 @@ function createApiDeps(deps) {
13238
13290
  }
13239
13291
 
13240
13292
  // src/services/cache-integrity/cacheIntegrityMonitor.ts
13241
- var import_crypto12 = require("crypto");
13293
+ var import_crypto13 = require("crypto");
13242
13294
  var import_fs23 = require("fs");
13243
13295
 
13244
13296
  // src/services/cache-integrity/alertStore.ts
@@ -13303,7 +13355,7 @@ function envInt(name, fallback) {
13303
13355
  }
13304
13356
  function fingerprintOf(ids) {
13305
13357
  const sorted = [...ids].sort();
13306
- return `sha256:${(0, import_crypto12.createHash)("sha256").update(sorted.join("\n")).digest("hex")}`;
13358
+ return `sha256:${(0, import_crypto13.createHash)("sha256").update(sorted.join("\n")).digest("hex")}`;
13307
13359
  }
13308
13360
  var CacheIntegrityMonitor = class {
13309
13361
  constructor(cache, wsHub, log10, cacheDir, rescan, runDuringReset) {
@@ -16029,7 +16081,7 @@ var StreamerServer = class {
16029
16081
  runtimeStore = null;
16030
16082
  // Identifies this streamer run. A registry row carrying a different id is a
16031
16083
  // session that outlived the process that started it.
16032
- streamerInstanceId = (0, import_crypto13.randomUUID)();
16084
+ streamerInstanceId = (0, import_crypto14.randomUUID)();
16033
16085
  cacheMetadataRepo = null;
16034
16086
  // Push registration + delivery state (C7). Null when the cache DB failed to
16035
16087
  // open — registration then degrades to a no-op rather than 500ing.