@meistrari/remy-cli 1.4.0 → 1.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.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/dist/remy.js +180 -125
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -58,7 +58,7 @@ Drag to select visible text in any Remy view; Remy copies it to the local clipbo
58
58
  | `/exit` | Leave Remy. |
59
59
  | `Ctrl+O` | Expand or collapse activity details. |
60
60
 
61
- After a session becomes terminal, press `Esc` to return to the dashboard or `n` to start new work. From the dashboard, press `q` to quit. `Shift+L` in the dashboard and `/logout` in a session ask for confirmation; press `y` to sign out or `Esc` to cancel. `Ctrl+C` exits Remy from the dashboard, wizard, or session view.
61
+ After a session becomes terminal, press `Esc` to return to the dashboard or `n` to start new work. From the dashboard, press `q` to quit; in an open session, `q` is ordinary composer text. `Shift+L` in the dashboard and `/logout` in a session ask for confirmation; press `y` to sign out or `Esc` to cancel. `Ctrl+C` exits Remy from the dashboard, wizard, or session view.
62
62
 
63
63
  ## Commands for direct or scripted use
64
64
 
@@ -116,7 +116,7 @@ remy login \
116
116
  --target-application-id <target-application-uuid>
117
117
  ```
118
118
 
119
- Remy stores endpoint configuration in `~/.config/remy/config.json` and tokens in `~/.local/state/remy/tokens.json`. Set `XDG_CONFIG_HOME` or `XDG_STATE_HOME` to use different base directories. It refreshes an expired access token before each Coding Agent API request, so long-running sessions and reconnects remain authenticated. `remy logout` removes tokens but retains endpoint configuration; pass `--api-url` to require that the supplied URL matches the active API host before logging out.
119
+ Remy stores endpoint configuration in `~/.config/remy/config.json` and tokens in `~/.local/state/remy/tokens.json`. Set `XDG_CONFIG_HOME` or `XDG_STATE_HOME` to use different base directories. Before each Coding Agent API request, it reloads the shared token file and refreshes an expired access token. When multiple Remy processes run at once, only one rotates and saves the refresh-token pair; the others wait and then use that newest saved pair. Long-running sessions and reconnects therefore stay authenticated after another process refreshes. `remy logout` removes tokens but retains endpoint configuration; pass `--api-url` to require that the supplied URL matches the active API host before logging out.
120
120
 
121
121
  ## Complete command reference
122
122
 
package/dist/remy.js CHANGED
@@ -33234,16 +33234,21 @@ function loginUsage() {
33234
33234
  }
33235
33235
 
33236
33236
  // src/credential-state.ts
33237
- import { randomUUID as randomUUID2 } from "crypto";
33238
- import { lstat, mkdir as mkdir3, open as open3, readFile as readFile3, rename as rename2, unlink as unlink2 } from "fs/promises";
33237
+ import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
33238
+ import { lstat, mkdir as mkdir3, open as open3, readFile as readFile3, rename as rename2, rmdir, unlink as unlink2 } from "fs/promises";
33239
33239
  import { dirname as dirname3, join as join2 } from "path";
33240
+ import { setTimeout as delay } from "timers/promises";
33240
33241
  var lockName = ".credential-state.lock";
33242
+ var lockOwnerName = "owner.json";
33241
33243
  var journalName = ".credential-promotion.journal.json";
33244
+ var lockRetryMilliseconds = 25;
33242
33245
  async function withCredentialState({
33243
33246
  stateDirectory,
33247
+ signal,
33248
+ lockFilesystem,
33244
33249
  action
33245
33250
  }) {
33246
- const owner = await acquireCredentialStateLock(stateDirectory);
33251
+ const owner = await acquireCredentialStateLock({ stateDirectory, signal, renamePath: lockFilesystem?.renamePath });
33247
33252
  try {
33248
33253
  return await action();
33249
33254
  } finally {
@@ -33318,51 +33323,146 @@ async function removeCredentialTokenWhileLocked({
33318
33323
  function lockPath(stateDirectory) {
33319
33324
  return join2(stateDirectory, lockName);
33320
33325
  }
33321
- async function acquireCredentialStateLock(stateDirectory) {
33326
+ async function acquireCredentialStateLock({
33327
+ stateDirectory,
33328
+ signal,
33329
+ renamePath = rename2
33330
+ }) {
33322
33331
  await mkdir3(stateDirectory, { recursive: true, mode: 448 });
33323
33332
  const owner = { ownerId: randomUUID2(), pid: process.pid };
33324
33333
  const path = lockPath(stateDirectory);
33334
+ const candidatePath = join2(stateDirectory, `.credential-state.candidate.${owner.ownerId}`);
33335
+ await mkdir3(candidatePath, { mode: 448 });
33325
33336
  try {
33326
- await writeExclusivePrivateFile({ path, contents: `${JSON.stringify(owner)}
33337
+ await writeExclusivePrivateFile({ path: join2(candidatePath, lockOwnerName), contents: `${JSON.stringify(owner)}
33327
33338
  ` });
33328
- return owner;
33339
+ while (true) {
33340
+ throwIfAborted(signal);
33341
+ const recordedState = await readLockOwnerState(path);
33342
+ if (recordedState.status === "missing") {
33343
+ try {
33344
+ await renamePath(candidatePath, path);
33345
+ return owner;
33346
+ } catch (error93) {
33347
+ if (!isLockContentionError(error93))
33348
+ throw error93;
33349
+ continue;
33350
+ }
33351
+ }
33352
+ if (recordedState.status === "malformed")
33353
+ throw new Error("another Remy credential operation is active");
33354
+ const recordedOwner = recordedState.owner;
33355
+ if (!isPidAbsent(recordedOwner.pid)) {
33356
+ await waitForCredentialStateLock(signal);
33357
+ continue;
33358
+ }
33359
+ const confirmedState = await readLockOwnerState(path);
33360
+ if (confirmedState.status === "missing")
33361
+ continue;
33362
+ if (confirmedState.status === "malformed")
33363
+ throw new Error("another Remy credential operation is active");
33364
+ const confirmedOwner = confirmedState.owner;
33365
+ if (!sameLockOwner(confirmedOwner, recordedOwner) || !isPidAbsent(confirmedOwner.pid)) {
33366
+ await waitForCredentialStateLock(signal);
33367
+ continue;
33368
+ }
33369
+ await moveStaleLockAside({ stateDirectory, path, owner: recordedOwner, signal, renamePath });
33370
+ }
33329
33371
  } catch (error93) {
33330
- if (!isExistsError(error93))
33331
- throw error93;
33372
+ await removeLockDirectory({ path: candidatePath });
33373
+ throw error93;
33332
33374
  }
33333
- const recordedOwner = await readLockOwner(path);
33334
- if (!recordedOwner || !isPidAbsent(recordedOwner.pid))
33335
- throw new Error("another Remy credential operation is active");
33336
- const stalePath = join2(stateDirectory, `.credential-state.stale.${recordedOwner.ownerId}.${randomUUID2()}`);
33375
+ }
33376
+ async function moveStaleLockAside({
33377
+ stateDirectory,
33378
+ path,
33379
+ owner,
33380
+ signal,
33381
+ renamePath
33382
+ }) {
33383
+ const stalePath = join2(stateDirectory, `.credential-state.stale.${lockOwnerFingerprint(owner)}`);
33337
33384
  try {
33338
- await rename2(path, stalePath);
33339
- await removeIfPresent({ path: stalePath });
33340
- } catch {
33341
- throw new Error("another Remy credential operation is active");
33385
+ await renamePath(path, stalePath);
33386
+ } catch (error93) {
33387
+ if (isLockContentionError(error93)) {
33388
+ await waitForCredentialStateLock(signal);
33389
+ return;
33390
+ }
33391
+ if (isMissingFileError(error93))
33392
+ return;
33393
+ throw error93;
33342
33394
  }
33395
+ }
33396
+ function lockOwnerFingerprint(owner) {
33397
+ return createHash2("sha256").update(`${owner.ownerId}\x00${owner.pid}`).digest("hex");
33398
+ }
33399
+ function sameLockOwner(left, right) {
33400
+ return left?.ownerId === right.ownerId && left.pid === right.pid;
33401
+ }
33402
+ async function waitForCredentialStateLock(signal) {
33403
+ throwIfAborted(signal);
33343
33404
  try {
33344
- await writeExclusivePrivateFile({ path, contents: `${JSON.stringify(owner)}
33345
- ` });
33346
- return owner;
33347
- } catch {
33348
- throw new Error("another Remy credential operation is active");
33405
+ await delay(lockRetryMilliseconds, undefined, signal ? { signal } : undefined);
33406
+ } catch (error93) {
33407
+ if (signal?.aborted)
33408
+ throw signal.reason ?? error93;
33409
+ throw error93;
33349
33410
  }
33350
33411
  }
33412
+ function throwIfAborted(signal) {
33413
+ if (signal?.aborted)
33414
+ throw signal.reason ?? new Error("Credential operation aborted.");
33415
+ }
33351
33416
  async function releaseCredentialStateLock({ stateDirectory, owner }) {
33352
33417
  const path = lockPath(stateDirectory);
33353
33418
  const currentOwner = await readLockOwner(path);
33354
33419
  if (!currentOwner || currentOwner.ownerId !== owner.ownerId)
33355
33420
  return;
33356
- await removeIfPresent({ path });
33421
+ const releasedPath = join2(stateDirectory, `.credential-state.released.${owner.ownerId}.${randomUUID2()}`);
33422
+ try {
33423
+ await rename2(path, releasedPath);
33424
+ } catch (error93) {
33425
+ if (isMissingFileError(error93))
33426
+ return;
33427
+ throw error93;
33428
+ }
33429
+ await removeLockDirectory({ path: releasedPath });
33357
33430
  }
33358
33431
  async function readLockOwner(path) {
33432
+ const state = await readLockOwnerState(path);
33433
+ return state.status === "valid" ? state.owner : undefined;
33434
+ }
33435
+ async function readLockOwnerState(path) {
33436
+ const firstObservation = await observeLockOwner(path);
33437
+ if (firstObservation.status !== "owner-missing")
33438
+ return firstObservation;
33439
+ const secondObservation = await observeLockOwner(path);
33440
+ return secondObservation.status === "owner-missing" ? { status: "malformed" } : secondObservation;
33441
+ }
33442
+ async function observeLockOwner(path) {
33443
+ let ownerPath;
33359
33444
  try {
33360
- const parsed = JSON.parse(await readFile3(path, "utf8"));
33445
+ const metadata = await lstat(path);
33446
+ ownerPath = metadata.isDirectory() ? join2(path, lockOwnerName) : path;
33447
+ } catch (error93) {
33448
+ return isMissingFileError(error93) ? { status: "missing" } : { status: "malformed" };
33449
+ }
33450
+ try {
33451
+ const parsed = JSON.parse(await readFile3(ownerPath, "utf8"));
33361
33452
  if (!isLockOwner(parsed))
33362
- return;
33363
- return parsed;
33364
- } catch {
33365
- return;
33453
+ return { status: "malformed" };
33454
+ return { status: "valid", owner: parsed };
33455
+ } catch (error93) {
33456
+ return isMissingFileError(error93) ? { status: "owner-missing" } : { status: "malformed" };
33457
+ }
33458
+ }
33459
+ async function removeLockDirectory({ path }) {
33460
+ await removeIfPresent({ path: join2(path, lockOwnerName) });
33461
+ try {
33462
+ await rmdir(path);
33463
+ } catch (error93) {
33464
+ if (!isMissingFileError(error93))
33465
+ throw error93;
33366
33466
  }
33367
33467
  }
33368
33468
  function isLockOwner(value) {
@@ -33474,8 +33574,8 @@ async function syncParentDirectory(path) {
33474
33574
  await handle.close();
33475
33575
  }
33476
33576
  }
33477
- function isExistsError(error93) {
33478
- return error93 instanceof Error && "code" in error93 && error93.code === "EEXIST";
33577
+ function isLockContentionError(error93) {
33578
+ return error93 instanceof Error && "code" in error93 && (error93.code === "EEXIST" || error93.code === "ENOTEMPTY" || error93.code === "EISDIR" || error93.code === "ENOTDIR");
33479
33579
  }
33480
33580
  function isMissingFileError(error93) {
33481
33581
  return error93 instanceof Error && "code" in error93 && error93.code === "ENOENT";
@@ -36398,11 +36498,6 @@ async function createSessionTui({
36398
36498
  finish({ kind: "new", composer: { text: "", attachments: [] } });
36399
36499
  return;
36400
36500
  }
36401
- if (key.name === "q" && (latestState.aggregateStatus !== "open" || composer.plainText.length === 0)) {
36402
- key.preventDefault();
36403
- finish({ kind: "exit" });
36404
- return;
36405
- }
36406
36501
  if (key.name === "o" && key.ctrl) {
36407
36502
  key.preventDefault();
36408
36503
  activityExpanded = !activityExpanded;
@@ -37197,7 +37292,7 @@ var compactMarkRows = 9;
37197
37292
  var compactMinWidth = 48;
37198
37293
  var compactMinHeight = 20;
37199
37294
  var markBrightnessGain = 4.2;
37200
- var remyCliVersion = "1.4.0";
37295
+ var remyCliVersion = "1.4.2";
37201
37296
  async function showRemySplash({
37202
37297
  createRenderer = createRemyRenderer,
37203
37298
  durationMs = splashDurationMs,
@@ -37610,7 +37705,7 @@ function createCliShutdown({ abortSignal }) {
37610
37705
  function exitCodeForSignal(signal) {
37611
37706
  return signal === "SIGINT" ? 130 : 143;
37612
37707
  }
37613
- function throwIfAborted(signal) {
37708
+ function throwIfAborted2(signal) {
37614
37709
  if (signal?.aborted)
37615
37710
  throw signal.reason ?? new CliInterruptedError("SIGINT");
37616
37711
  }
@@ -37678,19 +37773,20 @@ ${approvalUrl}
37678
37773
  let journalPrepared = false;
37679
37774
  try {
37680
37775
  const tokens = await flow.authenticate();
37681
- throwIfAborted(dependencies.abortSignal);
37776
+ throwIfAborted2(dependencies.abortSignal);
37682
37777
  const candidateAuthConfiguration = toAuthConfiguration({ configuration, tokenPath: candidateTokenPath });
37683
37778
  const candidateClient = createCodingAgentClient({
37684
37779
  apiUrl: configuration.apiUrl,
37685
37780
  getAccessToken: async (signal) => await requireAccessToken(candidateAuthConfiguration, signal)
37686
37781
  });
37687
37782
  const identity = await materializeRemoteCurrentUser({ client: candidateClient, signal: dependencies.abortSignal });
37688
- throwIfAborted(dependencies.abortSignal);
37783
+ throwIfAborted2(dependencies.abortSignal);
37689
37784
  await onCandidateIdentityValidated?.({ email: identity.email });
37690
- throwIfAborted(dependencies.abortSignal);
37691
- throwIfAborted(dependencies.abortSignal);
37785
+ throwIfAborted2(dependencies.abortSignal);
37786
+ throwIfAborted2(dependencies.abortSignal);
37692
37787
  await withCredentialState({
37693
37788
  stateDirectory: dirname6(paths.tokenPath),
37789
+ signal: dependencies.abortSignal,
37694
37790
  action: async () => {
37695
37791
  await recoverCredentialPromotionWhileLocked({
37696
37792
  stateDirectory: dirname6(paths.tokenPath),
@@ -37762,7 +37858,7 @@ ${approvalUrl}
37762
37858
  });
37763
37859
  }
37764
37860
  });
37765
- throwIfAborted(dependencies.abortSignal);
37861
+ throwIfAborted2(dependencies.abortSignal);
37766
37862
  dependencies.output.writeStderr(`Signed in as ${tokens.user.email ?? tokens.user.name ?? "authenticated user"}.
37767
37863
  `);
37768
37864
  } catch (error93) {
@@ -37782,6 +37878,7 @@ async function logout({ dependencies, flags }) {
37782
37878
  const removeTokenFile = dependencies.removeTokenFile ?? unlink3;
37783
37879
  await withCredentialState({
37784
37880
  stateDirectory: dirname6(paths.tokenPath),
37881
+ signal: dependencies.abortSignal,
37785
37882
  action: async () => {
37786
37883
  await recoverCredentialPromotionWhileLocked({
37787
37884
  stateDirectory: dirname6(paths.tokenPath),
@@ -37838,6 +37935,7 @@ async function createAuthenticatedClient(dependencies) {
37838
37935
  const paths = resolveCliPaths(dependencies.environment);
37839
37936
  const snapshot = await withCredentialState({
37840
37937
  stateDirectory: dirname6(paths.tokenPath),
37938
+ signal: dependencies.abortSignal,
37841
37939
  action: async () => {
37842
37940
  await recoverCredentialPromotionWhileLocked({
37843
37941
  stateDirectory: dirname6(paths.tokenPath),
@@ -37846,64 +37944,33 @@ async function createAuthenticatedClient(dependencies) {
37846
37944
  ...dependencies.credentialStateFilesystem
37847
37945
  });
37848
37946
  const configuration = await readCliConfiguration(paths.configPath);
37849
- const tokenGeneration = await readOptionalTokenFile(paths.tokenPath);
37850
- return { configuration, tokenGeneration };
37947
+ return { configuration };
37851
37948
  }
37852
37949
  });
37853
37950
  const authConfiguration = toAuthConfiguration({ configuration: snapshot.configuration, tokenPath: paths.tokenPath });
37854
37951
  const identity = await loadStoredIdentity(authConfiguration);
37855
37952
  const getAccessToken = createRefreshingAccessTokenProvider({ dependencies, paths, snapshot });
37856
- try {
37857
- await getAccessToken(dependencies.abortSignal);
37858
- const client = createCodingAgentClient({
37859
- apiUrl: snapshot.configuration.apiUrl,
37860
- getAccessToken
37861
- });
37862
- const remoteIdentity = await materializeRemoteCurrentUser({ client, signal: dependencies.abortSignal });
37863
- return {
37864
- authConfiguration,
37865
- client,
37866
- identity,
37867
- authenticatedEmail: remoteIdentity.email
37868
- };
37869
- } catch (error93) {
37870
- if (error93 instanceof LoginRequiredError) {
37871
- await withCredentialState({
37872
- stateDirectory: dirname6(paths.tokenPath),
37873
- action: async () => {
37874
- await recoverCredentialPromotionWhileLocked({
37875
- stateDirectory: dirname6(paths.tokenPath),
37876
- configPath: paths.configPath,
37877
- tokenPath: paths.tokenPath,
37878
- ...dependencies.credentialStateFilesystem
37879
- });
37880
- await assertCredentialSnapshotCurrent({ paths, snapshot });
37881
- await removeCredentialTokenWhileLocked({
37882
- tokenPath: paths.tokenPath,
37883
- removeFile: dependencies.credentialStateFilesystem?.removeFile,
37884
- syncDirectory: dependencies.credentialStateFilesystem?.syncDirectory
37885
- });
37886
- }
37887
- });
37888
- }
37889
- throw error93;
37890
- }
37953
+ const client = createCodingAgentClient({
37954
+ apiUrl: snapshot.configuration.apiUrl,
37955
+ getAccessToken
37956
+ });
37957
+ const remoteIdentity = await materializeRemoteCurrentUser({ client, signal: dependencies.abortSignal });
37958
+ return {
37959
+ authConfiguration,
37960
+ client,
37961
+ identity,
37962
+ authenticatedEmail: remoteIdentity.email
37963
+ };
37891
37964
  }
37892
37965
  function createRefreshingAccessTokenProvider({
37893
37966
  dependencies,
37894
37967
  paths,
37895
37968
  snapshot
37896
37969
  }) {
37897
- let tokenGeneration = snapshot.tokenGeneration;
37898
37970
  let pendingAccessToken;
37899
37971
  return async (signal) => {
37900
- if (!pendingAccessToken) {
37901
- const expectedTokenGeneration = tokenGeneration;
37902
- pendingAccessToken = refreshAccessToken({ dependencies, paths, configuration: snapshot.configuration, expectedTokenGeneration, signal }).then(({ accessToken: accessToken2, tokenGeneration: refreshedTokenGeneration }) => {
37903
- tokenGeneration = refreshedTokenGeneration;
37904
- return accessToken2;
37905
- });
37906
- }
37972
+ if (!pendingAccessToken)
37973
+ pendingAccessToken = refreshAccessToken({ dependencies, paths, configuration: snapshot.configuration, signal });
37907
37974
  const accessToken = pendingAccessToken;
37908
37975
  try {
37909
37976
  return await accessToken;
@@ -37917,13 +37984,13 @@ async function refreshAccessToken({
37917
37984
  dependencies,
37918
37985
  paths,
37919
37986
  configuration,
37920
- expectedTokenGeneration,
37921
37987
  signal
37922
37988
  }) {
37923
37989
  const candidateTokenPath = await createCandidateTokenPath(paths.tokenPath);
37924
37990
  try {
37925
- await withCredentialState({
37991
+ return await withCredentialState({
37926
37992
  stateDirectory: dirname6(paths.tokenPath),
37993
+ signal,
37927
37994
  action: async () => {
37928
37995
  await recoverCredentialPromotionWhileLocked({
37929
37996
  stateDirectory: dirname6(paths.tokenPath),
@@ -37931,31 +37998,28 @@ async function refreshAccessToken({
37931
37998
  tokenPath: paths.tokenPath,
37932
37999
  ...dependencies.credentialStateFilesystem
37933
38000
  });
37934
- await assertCredentialSnapshotCurrent({
37935
- paths,
37936
- snapshot: { configuration, tokenGeneration: expectedTokenGeneration }
37937
- });
37938
- if (expectedTokenGeneration !== undefined)
37939
- await writeFile4(candidateTokenPath, expectedTokenGeneration, { mode: 384 });
37940
- }
37941
- });
37942
- const candidateAuthConfiguration = toAuthConfiguration({ configuration, tokenPath: candidateTokenPath });
37943
- const accessToken = await requireAccessToken(candidateAuthConfiguration, signal);
37944
- const candidateTokenGeneration = await readFile5(candidateTokenPath, "utf8");
37945
- await withCredentialState({
37946
- stateDirectory: dirname6(paths.tokenPath),
37947
- action: async () => {
37948
- await recoverCredentialPromotionWhileLocked({
37949
- stateDirectory: dirname6(paths.tokenPath),
37950
- configPath: paths.configPath,
37951
- tokenPath: paths.tokenPath,
37952
- ...dependencies.credentialStateFilesystem
37953
- });
37954
- await assertCredentialSnapshotCurrent({
37955
- paths,
37956
- snapshot: { configuration, tokenGeneration: expectedTokenGeneration }
37957
- });
37958
- if (candidateTokenGeneration !== expectedTokenGeneration) {
38001
+ const currentConfiguration = await readCliConfiguration(paths.configPath);
38002
+ if (!sameCliConfiguration(currentConfiguration, configuration))
38003
+ throw new Error("Credential configuration changed while preparing authentication. Retry the command.");
38004
+ const currentTokenGeneration = await readOptionalTokenFile(paths.tokenPath);
38005
+ if (currentTokenGeneration !== undefined)
38006
+ await writeFile4(candidateTokenPath, currentTokenGeneration, { mode: 384 });
38007
+ const candidateAuthConfiguration = toAuthConfiguration({ configuration, tokenPath: candidateTokenPath });
38008
+ let accessToken;
38009
+ try {
38010
+ accessToken = await requireAccessToken(candidateAuthConfiguration, signal);
38011
+ } catch (error93) {
38012
+ if (error93 instanceof LoginRequiredError) {
38013
+ await removeCredentialTokenWhileLocked({
38014
+ tokenPath: paths.tokenPath,
38015
+ removeFile: dependencies.credentialStateFilesystem?.removeFile,
38016
+ syncDirectory: dependencies.credentialStateFilesystem?.syncDirectory
38017
+ });
38018
+ }
38019
+ throw error93;
38020
+ }
38021
+ const candidateTokenGeneration = await readFile5(candidateTokenPath, "utf8");
38022
+ if (candidateTokenGeneration !== currentTokenGeneration) {
37959
38023
  await replaceCredentialTokenWhileLocked({
37960
38024
  sourcePath: candidateTokenPath,
37961
38025
  tokenPath: paths.tokenPath,
@@ -37963,22 +38027,13 @@ async function refreshAccessToken({
37963
38027
  syncDirectory: dependencies.credentialStateFilesystem?.syncDirectory
37964
38028
  });
37965
38029
  }
38030
+ return accessToken;
37966
38031
  }
37967
38032
  });
37968
- return { accessToken, tokenGeneration: candidateTokenGeneration };
37969
38033
  } finally {
37970
38034
  await removeCandidateToken({ path: candidateTokenPath, removeTokenFile: unlink3 });
37971
38035
  }
37972
38036
  }
37973
- async function assertCredentialSnapshotCurrent({
37974
- paths,
37975
- snapshot
37976
- }) {
37977
- const currentConfiguration = await readCliConfiguration(paths.configPath);
37978
- const currentTokenGeneration = await readOptionalTokenFile(paths.tokenPath);
37979
- if (!sameCliConfiguration(currentConfiguration, snapshot.configuration) || currentTokenGeneration !== snapshot.tokenGeneration)
37980
- throw new Error("Credential state changed while preparing authentication. Retry the command.");
37981
- }
37982
38037
  async function readOptionalTokenFile(path) {
37983
38038
  try {
37984
38039
  return await readFile5(path, "utf8");
@@ -38007,7 +38062,7 @@ async function dashboard({
38007
38062
  bootstrap
38008
38063
  }) {
38009
38064
  assertInteractiveDashboard(dependencies);
38010
- throwIfAborted(dependencies.abortSignal);
38065
+ throwIfAborted2(dependencies.abortSignal);
38011
38066
  const { operations, repositories, initialSessions } = bootstrap;
38012
38067
  const openDashboardTui = dependencies.openDashboardTui ?? createDashboardTui;
38013
38068
  const pages = [initialSessions];
@@ -38188,7 +38243,7 @@ async function authenticateBareDashboard({
38188
38243
  splash?.completeAuthentication({ email: email5 });
38189
38244
  }
38190
38245
  });
38191
- throwIfAborted(dependencies.abortSignal);
38246
+ throwIfAborted2(dependencies.abortSignal);
38192
38247
  return await awaitWithAbort({
38193
38248
  value: prepareDashboardBootstrap({
38194
38249
  dependencies,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/remy-cli",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "Remy, the Coding Agent terminal client.",
5
5
  "type": "module",
6
6
  "bin": {