@topolo/cli 0.4.3 → 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 +91 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { platform as platform2 } from "os";
|
|
|
9
9
|
import { createInterface } from "readline/promises";
|
|
10
10
|
import { stdin as input } from "process";
|
|
11
11
|
|
|
12
|
-
//
|
|
12
|
+
// ../topolo-sdk/dist/index.js
|
|
13
13
|
function applyAuthHeaders(headers, credential) {
|
|
14
14
|
if (credential.kind === "api_key") {
|
|
15
15
|
headers.set("X-Api-Key", credential.apiKey);
|
|
@@ -3312,7 +3312,6 @@ var CrmModule = class {
|
|
|
3312
3312
|
constructor(client) {
|
|
3313
3313
|
this.client = client;
|
|
3314
3314
|
}
|
|
3315
|
-
client;
|
|
3316
3315
|
async listContacts(options = {}) {
|
|
3317
3316
|
const res = await this.client.request({
|
|
3318
3317
|
service: "crm",
|
|
@@ -3358,7 +3357,6 @@ var IdentityModule = class {
|
|
|
3358
3357
|
constructor(client) {
|
|
3359
3358
|
this.client = client;
|
|
3360
3359
|
}
|
|
3361
|
-
client;
|
|
3362
3360
|
/** Returns the resolved user, organization, and permission set for the
|
|
3363
3361
|
* current credential. Equivalent to `TopoloClient.introspect()` — exposed
|
|
3364
3362
|
* on this module for symmetry with the other domain modules. */
|
|
@@ -3453,7 +3451,7 @@ function createTopolo(options) {
|
|
|
3453
3451
|
};
|
|
3454
3452
|
}
|
|
3455
3453
|
|
|
3456
|
-
//
|
|
3454
|
+
// ../topolo-sdk/dist/node/credential-store.js
|
|
3457
3455
|
import { mkdir, readFile, writeFile, chmod } from "fs/promises";
|
|
3458
3456
|
import { existsSync } from "fs";
|
|
3459
3457
|
import { homedir, platform } from "os";
|
|
@@ -3783,9 +3781,9 @@ async function refreshCredentialIfNeeded(environment = selectedEnvironment(), op
|
|
|
3783
3781
|
const profile = profileForEnvironment(config, environment);
|
|
3784
3782
|
const cred = profile.credential;
|
|
3785
3783
|
if (!cred || cred.kind !== "access_token") return cred ?? null;
|
|
3786
|
-
if (!cred.refreshToken || !
|
|
3784
|
+
if (!cred.refreshToken || !profile.oauthClientId) return cred;
|
|
3787
3785
|
const now = Math.floor(Date.now() / 1e3);
|
|
3788
|
-
if (!options.force && cred.expiresAt - REFRESH_SKEW_SECONDS > now) return cred;
|
|
3786
|
+
if (!options.force && cred.expiresAt && cred.expiresAt - REFRESH_SKEW_SECONDS > now) return cred;
|
|
3789
3787
|
try {
|
|
3790
3788
|
const oauth = new TopoloOAuth2({
|
|
3791
3789
|
serviceUrls: resolveServiceUrls(profile.serviceUrls, environment)
|
|
@@ -3819,6 +3817,46 @@ async function refreshCredentialIfNeeded(environment = selectedEnvironment(), op
|
|
|
3819
3817
|
return cred;
|
|
3820
3818
|
}
|
|
3821
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
|
+
}
|
|
3822
3860
|
function resolveAgentName(configAgentName) {
|
|
3823
3861
|
return process.env.TOPOLO_AGENT_NAME ?? configAgentName;
|
|
3824
3862
|
}
|
|
@@ -11175,6 +11213,18 @@ function labelFor(ctx) {
|
|
|
11175
11213
|
if (ctx.contextType === "personal") return "Personal";
|
|
11176
11214
|
return ctx.organizationName || ctx.organizationSlug || ctx.organizationId || "organization";
|
|
11177
11215
|
}
|
|
11216
|
+
function isAuthenticationFailure(error) {
|
|
11217
|
+
return error instanceof Error && error.code === "auth_error";
|
|
11218
|
+
}
|
|
11219
|
+
function hasSameAccessToken(left, right) {
|
|
11220
|
+
const leftCredential = left;
|
|
11221
|
+
const rightCredential = right;
|
|
11222
|
+
return leftCredential?.kind === "access_token" && rightCredential?.kind === "access_token" && leftCredential.accessToken === rightCredential.accessToken;
|
|
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
|
+
}
|
|
11178
11228
|
function registerContextCommand(program2) {
|
|
11179
11229
|
const context = program2.command("context").description("Show and switch the active context (personal or an organization)");
|
|
11180
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) => {
|
|
@@ -11228,16 +11278,10 @@ function registerContextCommand(program2) {
|
|
|
11228
11278
|
const config = await readConfig();
|
|
11229
11279
|
const profile = profileForEnvironment(config, environment);
|
|
11230
11280
|
const serviceUrls = resolvedProfileConfig(config, environment).serviceUrls;
|
|
11231
|
-
|
|
11232
|
-
const cred = await refreshCredentialIfNeeded(environment, { force: true });
|
|
11281
|
+
let cred = await refreshCredentialIfNeeded(environment);
|
|
11233
11282
|
if (!cred) {
|
|
11234
11283
|
throw new Error("Not signed in. Run `topolo auth login` first.");
|
|
11235
11284
|
}
|
|
11236
|
-
if (priorCredential?.kind === "access_token" && priorCredential.refreshToken && cred.kind === "access_token" && cred.accessToken === priorCredential.accessToken) {
|
|
11237
|
-
throw new Error(
|
|
11238
|
-
`The active credential could not be refreshed. Run \`topolo --env ${environment} auth login\` to sign in again.`
|
|
11239
|
-
);
|
|
11240
|
-
}
|
|
11241
11285
|
const wantPersonal = /^(personal|me)$/i.test(target.trim());
|
|
11242
11286
|
let resolved;
|
|
11243
11287
|
const needle = target.trim().toLowerCase();
|
|
@@ -11275,10 +11319,40 @@ function registerContextCommand(program2) {
|
|
|
11275
11319
|
}
|
|
11276
11320
|
const key = contextKey(resolved.contextType, resolved.organizationId);
|
|
11277
11321
|
const existing = profile.contexts?.[key];
|
|
11278
|
-
|
|
11279
|
-
|
|
11280
|
-
|
|
11281
|
-
|
|
11322
|
+
let newCredential;
|
|
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
|
+
});
|
|
11354
|
+
}
|
|
11355
|
+
}
|
|
11282
11356
|
resolved.credential = newCredential;
|
|
11283
11357
|
resolved.oauthClientId = existing?.oauthClientId ?? profile.oauthClientId;
|
|
11284
11358
|
const updated = writeContextCredential(await readConfig(), environment, resolved, true);
|
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": {
|