@meistrari/remy-cli 1.4.0 → 1.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.
- package/README.md +1 -1
- package/dist/remy.js +180 -120
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -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.
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
33331
|
-
|
|
33372
|
+
await removeLockDirectory({ path: candidatePath });
|
|
33373
|
+
throw error93;
|
|
33332
33374
|
}
|
|
33333
|
-
|
|
33334
|
-
|
|
33335
|
-
|
|
33336
|
-
|
|
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
|
|
33339
|
-
|
|
33340
|
-
|
|
33341
|
-
|
|
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
|
|
33345
|
-
|
|
33346
|
-
|
|
33347
|
-
|
|
33348
|
-
throw
|
|
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
|
-
|
|
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
|
|
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
|
|
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";
|
|
@@ -37197,7 +37297,7 @@ var compactMarkRows = 9;
|
|
|
37197
37297
|
var compactMinWidth = 48;
|
|
37198
37298
|
var compactMinHeight = 20;
|
|
37199
37299
|
var markBrightnessGain = 4.2;
|
|
37200
|
-
var remyCliVersion = "1.4.
|
|
37300
|
+
var remyCliVersion = "1.4.1";
|
|
37201
37301
|
async function showRemySplash({
|
|
37202
37302
|
createRenderer = createRemyRenderer,
|
|
37203
37303
|
durationMs = splashDurationMs,
|
|
@@ -37610,7 +37710,7 @@ function createCliShutdown({ abortSignal }) {
|
|
|
37610
37710
|
function exitCodeForSignal(signal) {
|
|
37611
37711
|
return signal === "SIGINT" ? 130 : 143;
|
|
37612
37712
|
}
|
|
37613
|
-
function
|
|
37713
|
+
function throwIfAborted2(signal) {
|
|
37614
37714
|
if (signal?.aborted)
|
|
37615
37715
|
throw signal.reason ?? new CliInterruptedError("SIGINT");
|
|
37616
37716
|
}
|
|
@@ -37678,19 +37778,20 @@ ${approvalUrl}
|
|
|
37678
37778
|
let journalPrepared = false;
|
|
37679
37779
|
try {
|
|
37680
37780
|
const tokens = await flow.authenticate();
|
|
37681
|
-
|
|
37781
|
+
throwIfAborted2(dependencies.abortSignal);
|
|
37682
37782
|
const candidateAuthConfiguration = toAuthConfiguration({ configuration, tokenPath: candidateTokenPath });
|
|
37683
37783
|
const candidateClient = createCodingAgentClient({
|
|
37684
37784
|
apiUrl: configuration.apiUrl,
|
|
37685
37785
|
getAccessToken: async (signal) => await requireAccessToken(candidateAuthConfiguration, signal)
|
|
37686
37786
|
});
|
|
37687
37787
|
const identity = await materializeRemoteCurrentUser({ client: candidateClient, signal: dependencies.abortSignal });
|
|
37688
|
-
|
|
37788
|
+
throwIfAborted2(dependencies.abortSignal);
|
|
37689
37789
|
await onCandidateIdentityValidated?.({ email: identity.email });
|
|
37690
|
-
|
|
37691
|
-
|
|
37790
|
+
throwIfAborted2(dependencies.abortSignal);
|
|
37791
|
+
throwIfAborted2(dependencies.abortSignal);
|
|
37692
37792
|
await withCredentialState({
|
|
37693
37793
|
stateDirectory: dirname6(paths.tokenPath),
|
|
37794
|
+
signal: dependencies.abortSignal,
|
|
37694
37795
|
action: async () => {
|
|
37695
37796
|
await recoverCredentialPromotionWhileLocked({
|
|
37696
37797
|
stateDirectory: dirname6(paths.tokenPath),
|
|
@@ -37762,7 +37863,7 @@ ${approvalUrl}
|
|
|
37762
37863
|
});
|
|
37763
37864
|
}
|
|
37764
37865
|
});
|
|
37765
|
-
|
|
37866
|
+
throwIfAborted2(dependencies.abortSignal);
|
|
37766
37867
|
dependencies.output.writeStderr(`Signed in as ${tokens.user.email ?? tokens.user.name ?? "authenticated user"}.
|
|
37767
37868
|
`);
|
|
37768
37869
|
} catch (error93) {
|
|
@@ -37782,6 +37883,7 @@ async function logout({ dependencies, flags }) {
|
|
|
37782
37883
|
const removeTokenFile = dependencies.removeTokenFile ?? unlink3;
|
|
37783
37884
|
await withCredentialState({
|
|
37784
37885
|
stateDirectory: dirname6(paths.tokenPath),
|
|
37886
|
+
signal: dependencies.abortSignal,
|
|
37785
37887
|
action: async () => {
|
|
37786
37888
|
await recoverCredentialPromotionWhileLocked({
|
|
37787
37889
|
stateDirectory: dirname6(paths.tokenPath),
|
|
@@ -37838,6 +37940,7 @@ async function createAuthenticatedClient(dependencies) {
|
|
|
37838
37940
|
const paths = resolveCliPaths(dependencies.environment);
|
|
37839
37941
|
const snapshot = await withCredentialState({
|
|
37840
37942
|
stateDirectory: dirname6(paths.tokenPath),
|
|
37943
|
+
signal: dependencies.abortSignal,
|
|
37841
37944
|
action: async () => {
|
|
37842
37945
|
await recoverCredentialPromotionWhileLocked({
|
|
37843
37946
|
stateDirectory: dirname6(paths.tokenPath),
|
|
@@ -37846,64 +37949,33 @@ async function createAuthenticatedClient(dependencies) {
|
|
|
37846
37949
|
...dependencies.credentialStateFilesystem
|
|
37847
37950
|
});
|
|
37848
37951
|
const configuration = await readCliConfiguration(paths.configPath);
|
|
37849
|
-
|
|
37850
|
-
return { configuration, tokenGeneration };
|
|
37952
|
+
return { configuration };
|
|
37851
37953
|
}
|
|
37852
37954
|
});
|
|
37853
37955
|
const authConfiguration = toAuthConfiguration({ configuration: snapshot.configuration, tokenPath: paths.tokenPath });
|
|
37854
37956
|
const identity = await loadStoredIdentity(authConfiguration);
|
|
37855
37957
|
const getAccessToken = createRefreshingAccessTokenProvider({ dependencies, paths, snapshot });
|
|
37856
|
-
|
|
37857
|
-
|
|
37858
|
-
|
|
37859
|
-
|
|
37860
|
-
|
|
37861
|
-
|
|
37862
|
-
|
|
37863
|
-
|
|
37864
|
-
|
|
37865
|
-
|
|
37866
|
-
|
|
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
|
-
}
|
|
37958
|
+
const client = createCodingAgentClient({
|
|
37959
|
+
apiUrl: snapshot.configuration.apiUrl,
|
|
37960
|
+
getAccessToken
|
|
37961
|
+
});
|
|
37962
|
+
const remoteIdentity = await materializeRemoteCurrentUser({ client, signal: dependencies.abortSignal });
|
|
37963
|
+
return {
|
|
37964
|
+
authConfiguration,
|
|
37965
|
+
client,
|
|
37966
|
+
identity,
|
|
37967
|
+
authenticatedEmail: remoteIdentity.email
|
|
37968
|
+
};
|
|
37891
37969
|
}
|
|
37892
37970
|
function createRefreshingAccessTokenProvider({
|
|
37893
37971
|
dependencies,
|
|
37894
37972
|
paths,
|
|
37895
37973
|
snapshot
|
|
37896
37974
|
}) {
|
|
37897
|
-
let tokenGeneration = snapshot.tokenGeneration;
|
|
37898
37975
|
let pendingAccessToken;
|
|
37899
37976
|
return async (signal) => {
|
|
37900
|
-
if (!pendingAccessToken)
|
|
37901
|
-
|
|
37902
|
-
pendingAccessToken = refreshAccessToken({ dependencies, paths, configuration: snapshot.configuration, expectedTokenGeneration, signal }).then(({ accessToken: accessToken2, tokenGeneration: refreshedTokenGeneration }) => {
|
|
37903
|
-
tokenGeneration = refreshedTokenGeneration;
|
|
37904
|
-
return accessToken2;
|
|
37905
|
-
});
|
|
37906
|
-
}
|
|
37977
|
+
if (!pendingAccessToken)
|
|
37978
|
+
pendingAccessToken = refreshAccessToken({ dependencies, paths, configuration: snapshot.configuration, signal });
|
|
37907
37979
|
const accessToken = pendingAccessToken;
|
|
37908
37980
|
try {
|
|
37909
37981
|
return await accessToken;
|
|
@@ -37917,33 +37989,13 @@ async function refreshAccessToken({
|
|
|
37917
37989
|
dependencies,
|
|
37918
37990
|
paths,
|
|
37919
37991
|
configuration,
|
|
37920
|
-
expectedTokenGeneration,
|
|
37921
37992
|
signal
|
|
37922
37993
|
}) {
|
|
37923
37994
|
const candidateTokenPath = await createCandidateTokenPath(paths.tokenPath);
|
|
37924
37995
|
try {
|
|
37925
|
-
await withCredentialState({
|
|
37926
|
-
stateDirectory: dirname6(paths.tokenPath),
|
|
37927
|
-
action: async () => {
|
|
37928
|
-
await recoverCredentialPromotionWhileLocked({
|
|
37929
|
-
stateDirectory: dirname6(paths.tokenPath),
|
|
37930
|
-
configPath: paths.configPath,
|
|
37931
|
-
tokenPath: paths.tokenPath,
|
|
37932
|
-
...dependencies.credentialStateFilesystem
|
|
37933
|
-
});
|
|
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({
|
|
37996
|
+
return await withCredentialState({
|
|
37946
37997
|
stateDirectory: dirname6(paths.tokenPath),
|
|
37998
|
+
signal,
|
|
37947
37999
|
action: async () => {
|
|
37948
38000
|
await recoverCredentialPromotionWhileLocked({
|
|
37949
38001
|
stateDirectory: dirname6(paths.tokenPath),
|
|
@@ -37951,11 +38003,28 @@ async function refreshAccessToken({
|
|
|
37951
38003
|
tokenPath: paths.tokenPath,
|
|
37952
38004
|
...dependencies.credentialStateFilesystem
|
|
37953
38005
|
});
|
|
37954
|
-
await
|
|
37955
|
-
|
|
37956
|
-
|
|
37957
|
-
|
|
37958
|
-
if (
|
|
38006
|
+
const currentConfiguration = await readCliConfiguration(paths.configPath);
|
|
38007
|
+
if (!sameCliConfiguration(currentConfiguration, configuration))
|
|
38008
|
+
throw new Error("Credential configuration changed while preparing authentication. Retry the command.");
|
|
38009
|
+
const currentTokenGeneration = await readOptionalTokenFile(paths.tokenPath);
|
|
38010
|
+
if (currentTokenGeneration !== undefined)
|
|
38011
|
+
await writeFile4(candidateTokenPath, currentTokenGeneration, { mode: 384 });
|
|
38012
|
+
const candidateAuthConfiguration = toAuthConfiguration({ configuration, tokenPath: candidateTokenPath });
|
|
38013
|
+
let accessToken;
|
|
38014
|
+
try {
|
|
38015
|
+
accessToken = await requireAccessToken(candidateAuthConfiguration, signal);
|
|
38016
|
+
} catch (error93) {
|
|
38017
|
+
if (error93 instanceof LoginRequiredError) {
|
|
38018
|
+
await removeCredentialTokenWhileLocked({
|
|
38019
|
+
tokenPath: paths.tokenPath,
|
|
38020
|
+
removeFile: dependencies.credentialStateFilesystem?.removeFile,
|
|
38021
|
+
syncDirectory: dependencies.credentialStateFilesystem?.syncDirectory
|
|
38022
|
+
});
|
|
38023
|
+
}
|
|
38024
|
+
throw error93;
|
|
38025
|
+
}
|
|
38026
|
+
const candidateTokenGeneration = await readFile5(candidateTokenPath, "utf8");
|
|
38027
|
+
if (candidateTokenGeneration !== currentTokenGeneration) {
|
|
37959
38028
|
await replaceCredentialTokenWhileLocked({
|
|
37960
38029
|
sourcePath: candidateTokenPath,
|
|
37961
38030
|
tokenPath: paths.tokenPath,
|
|
@@ -37963,22 +38032,13 @@ async function refreshAccessToken({
|
|
|
37963
38032
|
syncDirectory: dependencies.credentialStateFilesystem?.syncDirectory
|
|
37964
38033
|
});
|
|
37965
38034
|
}
|
|
38035
|
+
return accessToken;
|
|
37966
38036
|
}
|
|
37967
38037
|
});
|
|
37968
|
-
return { accessToken, tokenGeneration: candidateTokenGeneration };
|
|
37969
38038
|
} finally {
|
|
37970
38039
|
await removeCandidateToken({ path: candidateTokenPath, removeTokenFile: unlink3 });
|
|
37971
38040
|
}
|
|
37972
38041
|
}
|
|
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
38042
|
async function readOptionalTokenFile(path) {
|
|
37983
38043
|
try {
|
|
37984
38044
|
return await readFile5(path, "utf8");
|
|
@@ -38007,7 +38067,7 @@ async function dashboard({
|
|
|
38007
38067
|
bootstrap
|
|
38008
38068
|
}) {
|
|
38009
38069
|
assertInteractiveDashboard(dependencies);
|
|
38010
|
-
|
|
38070
|
+
throwIfAborted2(dependencies.abortSignal);
|
|
38011
38071
|
const { operations, repositories, initialSessions } = bootstrap;
|
|
38012
38072
|
const openDashboardTui = dependencies.openDashboardTui ?? createDashboardTui;
|
|
38013
38073
|
const pages = [initialSessions];
|
|
@@ -38188,7 +38248,7 @@ async function authenticateBareDashboard({
|
|
|
38188
38248
|
splash?.completeAuthentication({ email: email5 });
|
|
38189
38249
|
}
|
|
38190
38250
|
});
|
|
38191
|
-
|
|
38251
|
+
throwIfAborted2(dependencies.abortSignal);
|
|
38192
38252
|
return await awaitWithAbort({
|
|
38193
38253
|
value: prepareDashboardBootstrap({
|
|
38194
38254
|
dependencies,
|