dazzer-connect 0.4.0 → 0.4.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.
Files changed (2) hide show
  1. package/dist/index.js +35 -2
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ function writeErr(message) {
23
23
  }
24
24
  var CANNOT_REACH_DAZZER = "I can't reach Dazzer right now \u2014 nothing was sent. Try again shortly.";
25
25
  var SIGN_IN_NO_LONGER_VALID = "Your Dazzer sign-in is no longer valid. Reconnect your client (or run `dazzer auth login`).";
26
+ var RENEWED_SIGN_IN_NOT_SAVED = "Your sign-in was renewed, but this machine's credential store would not save it. Run: dazzer auth login";
26
27
  var UPDATE_THE_TOOL = "npm install -g dazzer-connect@latest";
27
28
  var newerToolIsOut = (version2) => `Version ${version2} of dazzer is out. Update it with: ${UPDATE_THE_TOOL}`;
28
29
  function printDeviceInstructions(deviceAuth) {
@@ -95,6 +96,9 @@ function printAuthStatus(params) {
95
96
  writeOut(" Common on servers and containers, which often have none.");
96
97
  writeOut(" Run: dazzer auth login \u2014 it prints a pass you can paste in.");
97
98
  return;
99
+ case "save-refused":
100
+ writeOut(RENEWED_SIGN_IN_NOT_SAVED);
101
+ return;
98
102
  case "expired":
99
103
  writeOut("Your sign-in has expired or been revoked. Run: dazzer auth login");
100
104
  return;
@@ -199,6 +203,12 @@ var RefreshRejectedError = class extends Error {
199
203
  }
200
204
  oauthError;
201
205
  };
206
+ var RenewedSignInNotSavedError = class extends Error {
207
+ constructor() {
208
+ super("The credential store would not save the renewed sign-in.");
209
+ this.name = "RenewedSignInNotSavedError";
210
+ }
211
+ };
202
212
  async function refreshAccessToken(options) {
203
213
  const fetchFn = options.fetchFn ?? fetch;
204
214
  const params = new URLSearchParams({
@@ -226,7 +236,11 @@ async function refreshAccessToken(options) {
226
236
  }
227
237
  const newRefresh = typeof refreshToken === "string" ? refreshToken : options.refreshToken;
228
238
  const expiresIn = typeof body.expires_in === "number" ? body.expires_in : null;
229
- await options.tokenStore.save({ accessToken, refreshToken: newRefresh });
239
+ try {
240
+ await options.tokenStore.save({ accessToken, refreshToken: newRefresh });
241
+ } catch {
242
+ throw new RenewedSignInNotSavedError();
243
+ }
230
244
  return { accessToken, refreshToken: newRefresh, expiresIn };
231
245
  }
232
246
  async function getValidAccessToken(options) {
@@ -241,6 +255,9 @@ async function getValidAccessToken(options) {
241
255
  if (err instanceof RefreshRejectedError) {
242
256
  return { status: "rejected" };
243
257
  }
258
+ if (err instanceof RenewedSignInNotSavedError) {
259
+ return { status: "save-refused" };
260
+ }
244
261
  return { status: "unreachable" };
245
262
  }
246
263
  }
@@ -365,6 +382,8 @@ async function resolveCredential(auth2) {
365
382
  return { status: "signed-out" };
366
383
  case "rejected":
367
384
  return { status: "sign-in-invalid" };
385
+ case "save-refused":
386
+ return { status: "save-refused" };
368
387
  case "unreachable":
369
388
  return { held: held.accessToken };
370
389
  case "refreshed": {
@@ -384,6 +403,9 @@ async function callDazzer(request) {
384
403
  if (credential.status === "sign-in-invalid") {
385
404
  return { status: "sign-in-invalid", message: SIGN_IN_NO_LONGER_VALID };
386
405
  }
406
+ if (credential.status === "save-refused") {
407
+ return { status: "sign-in-invalid", message: RENEWED_SIGN_IN_NOT_SAVED };
408
+ }
387
409
  return { status: credential.status };
388
410
  }
389
411
  const fetchFn = request.fetchFn ?? fetch;
@@ -720,9 +742,17 @@ var KeychainTokenStore = class {
720
742
  this.openEntry = openEntry;
721
743
  }
722
744
  openEntry;
745
+ /**
746
+ * THE REFRESH TOKEN IS WRITTEN FIRST, AND IF IT WILL NOT BE, NOTHING ELSE IS TOUCHED. After a renewal
747
+ * the saved refresh token is already spent at the sign-in server. Writing the access token first and
748
+ * then failing on the refresh token left a new access token beside that spent one, which read as a
749
+ * fresh sign-in until it failed to renew. In this order a refused first write changes nothing, and
750
+ * a refused second write leaves the new refresh token, which can still renew. Either way the
751
+ * refusal reaches the caller, which says so rather than calling it anything else.
752
+ */
723
753
  async save(tokens) {
724
- this.openEntry(ACCESS_ACCOUNT).setPassword(tokens.accessToken);
725
754
  this.openEntry(REFRESH_ACCOUNT).setPassword(tokens.refreshToken);
755
+ this.openEntry(ACCESS_ACCOUNT).setPassword(tokens.accessToken);
726
756
  }
727
757
  async load() {
728
758
  const accessToken = this.openEntry(ACCESS_ACCOUNT).getPassword();
@@ -2559,6 +2589,9 @@ async function runStatus(options) {
2559
2589
  case "rejected":
2560
2590
  printAuthStatus({ state: "expired" });
2561
2591
  return;
2592
+ case "save-refused":
2593
+ printAuthStatus({ state: "save-refused" });
2594
+ return;
2562
2595
  case "unreachable":
2563
2596
  printAuthStatus({
2564
2597
  state: "unverified",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dazzer-connect",
3
3
  "private": false,
4
- "version": "0.4.0",
4
+ "version": "0.4.1",
5
5
  "description": "Sign in to Dazzer \u2014 the memory your AI tools share \u2014 from a terminal, for clients that cannot sign in themselves",
6
6
  "license": "MIT",
7
7
  "type": "module",
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "devDependencies": {
43
43
  "@dazzer/auth-ui": "workspace:*",
44
- "@types/node": "^26.6.2",
44
+ "@types/node": "^26.6.3",
45
45
  "esbuild": "^0.28.2",
46
46
  "tsx": "^4.23.15",
47
47
  "typescript": "^5.7.2",