@topolo/cli 0.4.4 → 0.4.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.js +75 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3817,6 +3817,46 @@ async function refreshCredentialIfNeeded(environment = selectedEnvironment(), op
|
|
|
3817
3817
|
return cred;
|
|
3818
3818
|
}
|
|
3819
3819
|
}
|
|
3820
|
+
async function refreshContextCredentialIfNeeded(environment, key, options = {}) {
|
|
3821
|
+
const config = await readConfig();
|
|
3822
|
+
const profile = profileForEnvironment(config, environment);
|
|
3823
|
+
const context = profile.contexts?.[key];
|
|
3824
|
+
const credential = context?.credential;
|
|
3825
|
+
if (!credential || credential.kind !== "access_token") return credential ?? null;
|
|
3826
|
+
const oauthClientId = context.oauthClientId ?? profile.oauthClientId;
|
|
3827
|
+
if (!credential.refreshToken || !oauthClientId) return credential;
|
|
3828
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
3829
|
+
if (!options.force && credential.expiresAt && credential.expiresAt - REFRESH_SKEW_SECONDS > now) return credential;
|
|
3830
|
+
try {
|
|
3831
|
+
const oauth = new TopoloOAuth2({
|
|
3832
|
+
serviceUrls: resolveServiceUrls(profile.serviceUrls, environment)
|
|
3833
|
+
});
|
|
3834
|
+
const tokens = await oauth.refreshToken({
|
|
3835
|
+
clientId: oauthClientId,
|
|
3836
|
+
refreshToken: credential.refreshToken
|
|
3837
|
+
});
|
|
3838
|
+
const refreshed = {
|
|
3839
|
+
kind: "access_token",
|
|
3840
|
+
accessToken: tokens.access_token,
|
|
3841
|
+
...tokens.refresh_token ? { refreshToken: tokens.refresh_token } : { refreshToken: credential.refreshToken },
|
|
3842
|
+
expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in
|
|
3843
|
+
};
|
|
3844
|
+
await writeConfig(writeProfileConfig(config, environment, {
|
|
3845
|
+
...profile.activeContext === key ? { credential: refreshed } : {},
|
|
3846
|
+
contexts: {
|
|
3847
|
+
...profile.contexts ?? {},
|
|
3848
|
+
[key]: {
|
|
3849
|
+
...context,
|
|
3850
|
+
credential: refreshed,
|
|
3851
|
+
oauthClientId
|
|
3852
|
+
}
|
|
3853
|
+
}
|
|
3854
|
+
}));
|
|
3855
|
+
return refreshed;
|
|
3856
|
+
} catch {
|
|
3857
|
+
return credential;
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3820
3860
|
function resolveAgentName(configAgentName) {
|
|
3821
3861
|
return process.env.TOPOLO_AGENT_NAME ?? configAgentName;
|
|
3822
3862
|
}
|
|
@@ -11181,6 +11221,10 @@ function hasSameAccessToken(left, right) {
|
|
|
11181
11221
|
const rightCredential = right;
|
|
11182
11222
|
return leftCredential?.kind === "access_token" && rightCredential?.kind === "access_token" && leftCredential.accessToken === rightCredential.accessToken;
|
|
11183
11223
|
}
|
|
11224
|
+
function identityMatchesContext(me, context) {
|
|
11225
|
+
if (context.contextType === "personal") return me.activeType === "personal";
|
|
11226
|
+
return me.activeType === "organization" && me.activeOrgId === context.organizationId;
|
|
11227
|
+
}
|
|
11184
11228
|
function registerContextCommand(program2) {
|
|
11185
11229
|
const context = program2.command("context").description("Show and switch the active context (personal or an organization)");
|
|
11186
11230
|
context.command("ls", { isDefault: true }).alias("list").description("List the contexts you can act in and which is active").option("--json", "Output JSON", true).option("--no-json", "Output human-readable text").action(async (opts) => {
|
|
@@ -11276,24 +11320,38 @@ function registerContextCommand(program2) {
|
|
|
11276
11320
|
const key = contextKey(resolved.contextType, resolved.organizationId);
|
|
11277
11321
|
const existing = profile.contexts?.[key];
|
|
11278
11322
|
let newCredential;
|
|
11279
|
-
|
|
11280
|
-
|
|
11281
|
-
|
|
11282
|
-
|
|
11283
|
-
|
|
11284
|
-
|
|
11285
|
-
|
|
11286
|
-
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
|
|
11290
|
-
|
|
11323
|
+
if (existing?.credential && profile.activeContext !== key) {
|
|
11324
|
+
const storedCredential = await refreshContextCredentialIfNeeded(environment, key);
|
|
11325
|
+
if (storedCredential) {
|
|
11326
|
+
try {
|
|
11327
|
+
const storedIdentity = await fetchMe(storedCredential, serviceUrls);
|
|
11328
|
+
if (identityMatchesContext(storedIdentity, resolved)) {
|
|
11329
|
+
newCredential = storedCredential;
|
|
11330
|
+
}
|
|
11331
|
+
} catch {
|
|
11332
|
+
}
|
|
11333
|
+
}
|
|
11334
|
+
}
|
|
11335
|
+
if (!newCredential) {
|
|
11336
|
+
try {
|
|
11337
|
+
newCredential = await exchangeForContext(cred, serviceUrls, {
|
|
11338
|
+
type: resolved.contextType,
|
|
11339
|
+
id: resolved.organizationId
|
|
11340
|
+
});
|
|
11341
|
+
} catch (error) {
|
|
11342
|
+
if (!isAuthenticationFailure(error)) throw error;
|
|
11343
|
+
const refreshed = await refreshCredentialIfNeeded(environment, { force: true });
|
|
11344
|
+
if (!refreshed || hasSameAccessToken(cred, refreshed)) {
|
|
11345
|
+
throw new Error(
|
|
11346
|
+
`The active credential could not be refreshed. Run \`topolo --env ${environment} auth login\` to sign in again.`
|
|
11347
|
+
);
|
|
11348
|
+
}
|
|
11349
|
+
cred = refreshed;
|
|
11350
|
+
newCredential = await exchangeForContext(cred, serviceUrls, {
|
|
11351
|
+
type: resolved.contextType,
|
|
11352
|
+
id: resolved.organizationId
|
|
11353
|
+
});
|
|
11291
11354
|
}
|
|
11292
|
-
cred = refreshed;
|
|
11293
|
-
newCredential = await exchangeForContext(cred, serviceUrls, {
|
|
11294
|
-
type: resolved.contextType,
|
|
11295
|
-
id: resolved.organizationId
|
|
11296
|
-
});
|
|
11297
11355
|
}
|
|
11298
11356
|
resolved.credential = newCredential;
|
|
11299
11357
|
resolved.oauthClientId = existing?.oauthClientId ?? profile.oauthClientId;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topolo/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
4
4
|
"description": "Command-line interface for Topolo developers, operators, and agents, with internal first-party scaffolding available under the topolo platform namespace.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|