@profullstack/qryptchat 0.4.1 → 0.4.2

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.
@@ -964,19 +964,28 @@ class QcClient {
964
964
  await this.refresh();
965
965
  return this.session.access_token;
966
966
  }
967
- async request(path, { method = "GET", body, signal, retry = true } = {}) {
967
+ async reauth(usedToken) {
968
+ if (this.session.access_token === usedToken)
969
+ await this.refresh();
970
+ }
971
+ async request(path, { method = "GET", body, signal, retry = true, throttled = 0 } = {}) {
972
+ const used = await this.token();
968
973
  const res = await this.fetch(`${this.base}${path}`, {
969
974
  method,
970
975
  signal,
971
976
  headers: {
972
- Authorization: `Bearer ${await this.token()}`,
977
+ Authorization: `Bearer ${used}`,
973
978
  ...body !== undefined ? { "Content-Type": "application/json" } : {}
974
979
  },
975
980
  body: body !== undefined ? JSON.stringify(body) : undefined
976
981
  });
977
982
  if (res.status === 401 && retry) {
978
- await this.refresh();
979
- return this.request(path, { method, body, signal, retry: false });
983
+ await this.reauth(used);
984
+ return this.request(path, { method, body, signal, retry: false, throttled });
985
+ }
986
+ if (res.status === 429 && throttled < 3) {
987
+ await sleep(retryAfterMs(res, throttled), signal);
988
+ return this.request(path, { method, body, signal, retry, throttled: throttled + 1 });
980
989
  }
981
990
  const data = await res.json().catch(() => ({}));
982
991
  if (!res.ok)
@@ -1047,21 +1056,31 @@ class QcClient {
1047
1056
  }
1048
1057
  async events(onEvent, { signal, onStatus = () => {} } = {}) {
1049
1058
  let delay = 1000;
1059
+ let reauthed = false;
1050
1060
  while (!signal?.aborted) {
1051
1061
  onStatus("connecting");
1052
1062
  try {
1063
+ const used = await this.token();
1053
1064
  const res = await this.fetch(`${this.base}/api/events`, {
1054
- headers: { Authorization: `Bearer ${await this.token()}`, Accept: "text/event-stream" },
1065
+ headers: { Authorization: `Bearer ${used}`, Accept: "text/event-stream" },
1055
1066
  signal
1056
1067
  });
1057
1068
  if (res.status === 401) {
1058
- await this.refresh();
1069
+ if (reauthed)
1070
+ throw new QcError("The server refused this session. Run qc login.", 401);
1071
+ reauthed = true;
1072
+ await this.reauth(used);
1073
+ continue;
1074
+ }
1075
+ if (res.status === 429) {
1076
+ await sleep(retryAfterMs(res, 2), signal);
1059
1077
  continue;
1060
1078
  }
1061
1079
  if (!res.ok || !res.body)
1062
1080
  throw new Error(`HTTP ${res.status}`);
1063
1081
  onStatus("live");
1064
1082
  delay = 1000;
1083
+ reauthed = false;
1065
1084
  await readSse(res.body, onEvent);
1066
1085
  } catch (err) {
1067
1086
  if (signal?.aborted)
@@ -1111,6 +1130,10 @@ async function readSse(body, onEvent) {
1111
1130
  }
1112
1131
  }
1113
1132
  }
1133
+ function retryAfterMs(res, attempt = 0) {
1134
+ const s = Number(res.headers?.get?.("retry-after"));
1135
+ return Number.isFinite(s) && s > 0 ? Math.min(s, 60) * 1000 : 2000 * 2 ** attempt;
1136
+ }
1114
1137
  function sleep(ms, signal) {
1115
1138
  return new Promise((resolve) => {
1116
1139
  const t = setTimeout(resolve, ms);
package/dist/commands.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  parseArgs2,
4
4
  findChat2,
5
5
  main2
6
- } from "./chunk-7ane8p3w.js";
6
+ } from "./chunk-7mn96363.js";
7
7
  import"./chunk-vfahhydf.js";
8
8
  export {
9
9
  HELP2 as HELP,
package/dist/mcp.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import"./chunk-vfahhydf.js";
2
2
  import {
3
3
  findChat2
4
- } from "./chunk-7ane8p3w.js";
4
+ } from "./chunk-7mn96363.js";
5
5
 
6
6
  // src/cli/mcp.js
7
7
  import { createInterface } from "node:readline";
package/dist/tui.js CHANGED
@@ -243,10 +243,6 @@ async function runTui(client, { initialChat } = {}) {
243
243
  state.selected = Math.max(0, state.conversations.findIndex((c) => c.id === before));
244
244
  }
245
245
  redraw();
246
- for (const c of state.conversations.slice(0, 40)) {
247
- if (c.id !== state.activeId && !state.messages[c.id])
248
- await loadMessages(c.id);
249
- }
250
246
  } catch (err) {
251
247
  state.loading = false;
252
248
  note(err.status === 401 ? "Session ended: run qc login" : `Could not load chats: ${err.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profullstack/qryptchat",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "qc: qrypt.chat in your terminal. A full-screen end-to-end encrypted chat client (ML-KEM-1024), plus a scriptable CLI and an MCP server.",
5
5
  "type": "module",
6
6
  "bin": {