securewithtrace 0.35.0 → 0.36.0
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 +287 -70
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -27177,10 +27177,11 @@ var CreateOrganizationResponseSchema = external_exports.object({
|
|
|
27177
27177
|
name: external_exports.string()
|
|
27178
27178
|
});
|
|
27179
27179
|
var OnboardingItemValues = [
|
|
27180
|
+
"INVITE_MEMBERS",
|
|
27180
27181
|
"CONNECT_CODE",
|
|
27181
27182
|
"CONNECT_CLOUD",
|
|
27182
|
-
"
|
|
27183
|
-
"
|
|
27183
|
+
"FILL_QUESTIONNAIRE",
|
|
27184
|
+
"CREATE_APPLICATION"
|
|
27184
27185
|
];
|
|
27185
27186
|
var OnboardingItemStatusValues = ["NOT_DONE", "DONE", "DISMISSED"];
|
|
27186
27187
|
var OnboardingChecklistItemSchema = external_exports.object({
|
|
@@ -27189,6 +27190,7 @@ var OnboardingChecklistItemSchema = external_exports.object({
|
|
|
27189
27190
|
});
|
|
27190
27191
|
var OnboardingChecklistResponseSchema = external_exports.object({
|
|
27191
27192
|
hasProvisionedPentest: external_exports.boolean(),
|
|
27193
|
+
provisionedPentestDisplayId: external_exports.number().int().nullable().optional(),
|
|
27192
27194
|
items: external_exports.array(OnboardingChecklistItemSchema)
|
|
27193
27195
|
});
|
|
27194
27196
|
var OnboardingItemParamsSchema = external_exports.object({
|
|
@@ -35605,6 +35607,22 @@ import { join } from "path";
|
|
|
35605
35607
|
var ACCESS_TOKEN_FILENAME = "token";
|
|
35606
35608
|
var REFRESH_TOKEN_FILENAME = "refresh-token";
|
|
35607
35609
|
var SELECTED_ORGANIZATION_FILENAME = "organization";
|
|
35610
|
+
var TRACE_API_KEY_PREFIX = "trace_sk_";
|
|
35611
|
+
var TRACE_API_KEY_ENV = "TRACE_API_KEY";
|
|
35612
|
+
var API_KEY_ID_LENGTH = 16;
|
|
35613
|
+
var API_KEY_SECRET_LENGTH = 22;
|
|
35614
|
+
var API_KEY_CHECKSUM_LENGTH = 6;
|
|
35615
|
+
var TRACE_API_KEY_PATTERN = new RegExp(
|
|
35616
|
+
`^${TRACE_API_KEY_PREFIX}[0-9A-Za-z]{${API_KEY_ID_LENGTH}}_[0-9A-Za-z]{${API_KEY_SECRET_LENGTH}}[0-9A-Za-z]{${API_KEY_CHECKSUM_LENGTH}}$`
|
|
35617
|
+
);
|
|
35618
|
+
var InvalidTraceApiKeyError = class extends Error {
|
|
35619
|
+
constructor() {
|
|
35620
|
+
super(
|
|
35621
|
+
`${TRACE_API_KEY_ENV} does not look like a Trace API key (expected ${TRACE_API_KEY_PREFIX}...)`
|
|
35622
|
+
);
|
|
35623
|
+
this.name = "InvalidTraceApiKeyError";
|
|
35624
|
+
}
|
|
35625
|
+
};
|
|
35608
35626
|
function resolveTraceDir() {
|
|
35609
35627
|
const override = process.env["TRACE_CLI_AUTH_DIR"];
|
|
35610
35628
|
if (override && override.trim().length > 0) {
|
|
@@ -35717,6 +35735,48 @@ function removeToken() {
|
|
|
35717
35735
|
}
|
|
35718
35736
|
return removed;
|
|
35719
35737
|
}
|
|
35738
|
+
function getTraceApiKeyEnv() {
|
|
35739
|
+
const value = process.env[TRACE_API_KEY_ENV];
|
|
35740
|
+
if (value === void 0) {
|
|
35741
|
+
return null;
|
|
35742
|
+
}
|
|
35743
|
+
const trimmed = value.trim();
|
|
35744
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
35745
|
+
}
|
|
35746
|
+
function isTraceApiKeyEnvSet() {
|
|
35747
|
+
return getTraceApiKeyEnv() !== null;
|
|
35748
|
+
}
|
|
35749
|
+
function isTraceApiKeyToken(token) {
|
|
35750
|
+
return token.startsWith(TRACE_API_KEY_PREFIX);
|
|
35751
|
+
}
|
|
35752
|
+
function isValidTraceApiKeyShape(token) {
|
|
35753
|
+
return TRACE_API_KEY_PATTERN.test(token);
|
|
35754
|
+
}
|
|
35755
|
+
function publicApiKeyId(token) {
|
|
35756
|
+
const keyId = token.slice(
|
|
35757
|
+
TRACE_API_KEY_PREFIX.length,
|
|
35758
|
+
TRACE_API_KEY_PREFIX.length + API_KEY_ID_LENGTH
|
|
35759
|
+
);
|
|
35760
|
+
return `${TRACE_API_KEY_PREFIX}${keyId}`;
|
|
35761
|
+
}
|
|
35762
|
+
function resolveCredential(readStoredToken = readToken) {
|
|
35763
|
+
const envKey = getTraceApiKeyEnv();
|
|
35764
|
+
if (envKey !== null) {
|
|
35765
|
+
if (!isValidTraceApiKeyShape(envKey)) {
|
|
35766
|
+
throw new InvalidTraceApiKeyError();
|
|
35767
|
+
}
|
|
35768
|
+
return { token: envKey, kind: "api_key", source: "env" };
|
|
35769
|
+
}
|
|
35770
|
+
const token = readStoredToken();
|
|
35771
|
+
if (!token) {
|
|
35772
|
+
return null;
|
|
35773
|
+
}
|
|
35774
|
+
return {
|
|
35775
|
+
token,
|
|
35776
|
+
kind: isTraceApiKeyToken(token) ? "api_key" : "user",
|
|
35777
|
+
source: "file"
|
|
35778
|
+
};
|
|
35779
|
+
}
|
|
35720
35780
|
|
|
35721
35781
|
// ../../node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
35722
35782
|
var ANSI_BACKGROUND_OFFSET = 10;
|
|
@@ -36256,7 +36316,11 @@ function colorAccentUnderline(text, colorizer = source_default) {
|
|
|
36256
36316
|
}
|
|
36257
36317
|
|
|
36258
36318
|
// src/api-client.ts
|
|
36259
|
-
var
|
|
36319
|
+
var HUMAN_PRINCIPAL_REQUIRED_MESSAGE = "Human principal required for this operation";
|
|
36320
|
+
var FORBIDDEN_STATUS = 403;
|
|
36321
|
+
function readTokenSafe() {
|
|
36322
|
+
return resolveCredential(readToken)?.token ?? null;
|
|
36323
|
+
}
|
|
36260
36324
|
var PRODUCTION_API_URL = "https://api.securewithtrace.com";
|
|
36261
36325
|
var UNAUTHORIZED_STATUS = 401;
|
|
36262
36326
|
var REFRESH_AUTH_FAILURE_STATUS_CODES = /* @__PURE__ */ new Set([400, 401]);
|
|
@@ -36290,6 +36354,84 @@ function getApiBaseUrl() {
|
|
|
36290
36354
|
}
|
|
36291
36355
|
return raw.replace(/\/+$/, "");
|
|
36292
36356
|
}
|
|
36357
|
+
function loadCredentialOrExit() {
|
|
36358
|
+
const credential = resolveCredentialOrExit();
|
|
36359
|
+
if (!credential) {
|
|
36360
|
+
console.error(colorError("Not logged in. Run `tracecli auth login` first."));
|
|
36361
|
+
process.exit(1);
|
|
36362
|
+
}
|
|
36363
|
+
return credential;
|
|
36364
|
+
}
|
|
36365
|
+
function resolveCredentialOrExit() {
|
|
36366
|
+
try {
|
|
36367
|
+
return resolveCredential(readToken);
|
|
36368
|
+
} catch (error48) {
|
|
36369
|
+
if (error48 instanceof InvalidTraceApiKeyError) {
|
|
36370
|
+
console.error(colorError(error48.message));
|
|
36371
|
+
process.exit(1);
|
|
36372
|
+
}
|
|
36373
|
+
throw error48;
|
|
36374
|
+
}
|
|
36375
|
+
}
|
|
36376
|
+
function jwtOrganizationId(credential) {
|
|
36377
|
+
if (!credential || credential.kind !== "user") {
|
|
36378
|
+
return null;
|
|
36379
|
+
}
|
|
36380
|
+
return extractOrganizationIdFromToken(credential.token);
|
|
36381
|
+
}
|
|
36382
|
+
function storedSelectionFor(credential) {
|
|
36383
|
+
if (!credential || credential.kind === "api_key") {
|
|
36384
|
+
return null;
|
|
36385
|
+
}
|
|
36386
|
+
return readSelectedOrganizationId();
|
|
36387
|
+
}
|
|
36388
|
+
function persistActiveOrganizationSelection(credential, activeOrganization, selectedOrganizationId, warnOnStaleSelection) {
|
|
36389
|
+
if (credential?.kind === "api_key") {
|
|
36390
|
+
return;
|
|
36391
|
+
}
|
|
36392
|
+
if (activeOrganization.staleSelectionDetected) {
|
|
36393
|
+
removeSelectedOrganizationId();
|
|
36394
|
+
writeSelectedOrganizationId(activeOrganization.organization.organizationId);
|
|
36395
|
+
if (warnOnStaleSelection && selectedOrganizationId) {
|
|
36396
|
+
console.warn(
|
|
36397
|
+
colorWarning(
|
|
36398
|
+
`Stored organization '${selectedOrganizationId}' is unavailable. Using '${activeOrganization.organization.name}' (${activeOrganization.organization.organizationId}) instead.`
|
|
36399
|
+
)
|
|
36400
|
+
);
|
|
36401
|
+
}
|
|
36402
|
+
return;
|
|
36403
|
+
}
|
|
36404
|
+
if (activeOrganization.source === "default") {
|
|
36405
|
+
writeSelectedOrganizationId(activeOrganization.organization.organizationId);
|
|
36406
|
+
}
|
|
36407
|
+
}
|
|
36408
|
+
function responseBody(result) {
|
|
36409
|
+
if (!Object.prototype.hasOwnProperty.call(result, "body")) {
|
|
36410
|
+
return void 0;
|
|
36411
|
+
}
|
|
36412
|
+
return Reflect.get(result, "body");
|
|
36413
|
+
}
|
|
36414
|
+
function responseMessage(result) {
|
|
36415
|
+
const body = responseBody(result);
|
|
36416
|
+
if (typeof body !== "object" || body === null || !("message" in body)) {
|
|
36417
|
+
return void 0;
|
|
36418
|
+
}
|
|
36419
|
+
const message = body.message;
|
|
36420
|
+
return typeof message === "string" ? message : void 0;
|
|
36421
|
+
}
|
|
36422
|
+
function isHumanPrincipalRequiredResult(result) {
|
|
36423
|
+
return result.status === FORBIDDEN_STATUS && responseMessage(result) === HUMAN_PRINCIPAL_REQUIRED_MESSAGE;
|
|
36424
|
+
}
|
|
36425
|
+
function exitApiKeyUnauthorized() {
|
|
36426
|
+
console.error(
|
|
36427
|
+
colorError("The API key is invalid, expired, or revoked. Check the dashboard's API Keys page.")
|
|
36428
|
+
);
|
|
36429
|
+
process.exit(1);
|
|
36430
|
+
}
|
|
36431
|
+
function exitHumanPrincipalRequired() {
|
|
36432
|
+
console.error(colorError("This command requires a human login; run `tracecli auth login`."));
|
|
36433
|
+
process.exit(1);
|
|
36434
|
+
}
|
|
36293
36435
|
function createClient(token) {
|
|
36294
36436
|
return initClient(v1Contract, {
|
|
36295
36437
|
baseUrl: getApiBaseUrl(),
|
|
@@ -36371,7 +36513,7 @@ async function refreshStoredAccessToken() {
|
|
|
36371
36513
|
if (!clientId || !refreshToken) {
|
|
36372
36514
|
return null;
|
|
36373
36515
|
}
|
|
36374
|
-
const tokenOrganizationId =
|
|
36516
|
+
const tokenOrganizationId = jwtOrganizationId(resolveCredentialOrExit()) ?? void 0;
|
|
36375
36517
|
try {
|
|
36376
36518
|
return persistRefreshResult(await callRefresh(clientId, refreshToken), refreshToken);
|
|
36377
36519
|
} catch (error48) {
|
|
@@ -36416,12 +36558,17 @@ async function refreshStoredAccessTokenForOrganization(workosOrganizationId) {
|
|
|
36416
36558
|
}
|
|
36417
36559
|
}
|
|
36418
36560
|
async function authenticatedRequest(request) {
|
|
36419
|
-
const
|
|
36420
|
-
|
|
36421
|
-
|
|
36422
|
-
|
|
36561
|
+
const credential = loadCredentialOrExit();
|
|
36562
|
+
const firstResult = await request(createClient(credential.token));
|
|
36563
|
+
if (credential.kind === "api_key") {
|
|
36564
|
+
if (firstResult.status === UNAUTHORIZED_STATUS) {
|
|
36565
|
+
exitApiKeyUnauthorized();
|
|
36566
|
+
}
|
|
36567
|
+
if (isHumanPrincipalRequiredResult(firstResult)) {
|
|
36568
|
+
exitHumanPrincipalRequired();
|
|
36569
|
+
}
|
|
36570
|
+
return firstResult;
|
|
36423
36571
|
}
|
|
36424
|
-
const firstResult = await request(createClient(token));
|
|
36425
36572
|
if (firstResult.status !== UNAUTHORIZED_STATUS) {
|
|
36426
36573
|
return firstResult;
|
|
36427
36574
|
}
|
|
@@ -36432,13 +36579,22 @@ async function authenticatedRequest(request) {
|
|
|
36432
36579
|
return request(createClient(refreshedToken));
|
|
36433
36580
|
}
|
|
36434
36581
|
async function authenticatedRequestSafe(request) {
|
|
36435
|
-
|
|
36436
|
-
|
|
36582
|
+
let credential;
|
|
36583
|
+
try {
|
|
36584
|
+
credential = resolveCredential(readToken);
|
|
36585
|
+
} catch (error48) {
|
|
36586
|
+
if (error48 instanceof InvalidTraceApiKeyError) {
|
|
36587
|
+
console.error(colorError(error48.message));
|
|
36588
|
+
process.exit(1);
|
|
36589
|
+
}
|
|
36590
|
+
throw error48;
|
|
36591
|
+
}
|
|
36592
|
+
if (!credential) {
|
|
36437
36593
|
return null;
|
|
36438
36594
|
}
|
|
36439
36595
|
try {
|
|
36440
|
-
const firstResult = await request(createClient(token));
|
|
36441
|
-
if (firstResult.status !== UNAUTHORIZED_STATUS) {
|
|
36596
|
+
const firstResult = await request(createClient(credential.token));
|
|
36597
|
+
if (credential.kind === "api_key" || firstResult.status !== UNAUTHORIZED_STATUS) {
|
|
36442
36598
|
return firstResult;
|
|
36443
36599
|
}
|
|
36444
36600
|
const refreshedToken = await refreshStoredAccessToken();
|
|
@@ -36514,8 +36670,9 @@ async function resolveOrganizationId(client) {
|
|
|
36514
36670
|
process.exit(1);
|
|
36515
36671
|
}
|
|
36516
36672
|
const organizations = result.body;
|
|
36517
|
-
const
|
|
36518
|
-
const
|
|
36673
|
+
const credential = resolveCredentialOrExit();
|
|
36674
|
+
const selectedOrganizationId = storedSelectionFor(credential);
|
|
36675
|
+
const tokenOrganizationId = jwtOrganizationId(credential);
|
|
36519
36676
|
const activeOrganization = determineActiveOrganization(
|
|
36520
36677
|
organizations,
|
|
36521
36678
|
selectedOrganizationId,
|
|
@@ -36530,19 +36687,7 @@ async function resolveOrganizationId(client) {
|
|
|
36530
36687
|
console.error(colorError("Invalid organization ID received from API."));
|
|
36531
36688
|
process.exit(1);
|
|
36532
36689
|
}
|
|
36533
|
-
|
|
36534
|
-
removeSelectedOrganizationId();
|
|
36535
|
-
writeSelectedOrganizationId(id);
|
|
36536
|
-
if (selectedOrganizationId) {
|
|
36537
|
-
console.warn(
|
|
36538
|
-
colorWarning(
|
|
36539
|
-
`Stored organization '${selectedOrganizationId}' is unavailable. Using '${activeOrganization.organization.name}' (${id}) instead.`
|
|
36540
|
-
)
|
|
36541
|
-
);
|
|
36542
|
-
}
|
|
36543
|
-
} else if (activeOrganization.source === "default") {
|
|
36544
|
-
writeSelectedOrganizationId(id);
|
|
36545
|
-
}
|
|
36690
|
+
persistActiveOrganizationSelection(credential, activeOrganization, selectedOrganizationId, true);
|
|
36546
36691
|
return id;
|
|
36547
36692
|
}
|
|
36548
36693
|
async function resolveOrganizationSlug() {
|
|
@@ -36558,8 +36703,9 @@ async function resolveOrganizationSlug() {
|
|
|
36558
36703
|
process.exit(1);
|
|
36559
36704
|
}
|
|
36560
36705
|
const organizations = result.body;
|
|
36561
|
-
const
|
|
36562
|
-
const
|
|
36706
|
+
const credential = resolveCredentialOrExit();
|
|
36707
|
+
const selectedOrganizationId = storedSelectionFor(credential);
|
|
36708
|
+
const tokenOrganizationId = jwtOrganizationId(credential);
|
|
36563
36709
|
const activeOrganization = determineActiveOrganization(
|
|
36564
36710
|
organizations,
|
|
36565
36711
|
selectedOrganizationId,
|
|
@@ -36583,8 +36729,9 @@ async function resolveOrganizationIdSafe(client) {
|
|
|
36583
36729
|
return null;
|
|
36584
36730
|
}
|
|
36585
36731
|
const organizations = result.body;
|
|
36586
|
-
const
|
|
36587
|
-
const
|
|
36732
|
+
const credential = resolveCredentialOrExit();
|
|
36733
|
+
const selectedOrganizationId = storedSelectionFor(credential);
|
|
36734
|
+
const tokenOrganizationId = jwtOrganizationId(credential);
|
|
36588
36735
|
const activeOrganization = determineActiveOrganization(
|
|
36589
36736
|
organizations,
|
|
36590
36737
|
selectedOrganizationId,
|
|
@@ -36597,12 +36744,12 @@ async function resolveOrganizationIdSafe(client) {
|
|
|
36597
36744
|
if (!SAFE_ID_PATTERN.test(id)) {
|
|
36598
36745
|
return null;
|
|
36599
36746
|
}
|
|
36600
|
-
|
|
36601
|
-
|
|
36602
|
-
|
|
36603
|
-
|
|
36604
|
-
|
|
36605
|
-
|
|
36747
|
+
persistActiveOrganizationSelection(
|
|
36748
|
+
credential,
|
|
36749
|
+
activeOrganization,
|
|
36750
|
+
selectedOrganizationId,
|
|
36751
|
+
false
|
|
36752
|
+
);
|
|
36606
36753
|
return id;
|
|
36607
36754
|
} catch {
|
|
36608
36755
|
return null;
|
|
@@ -37115,6 +37262,9 @@ function decodeJwtExpiration(token) {
|
|
|
37115
37262
|
return null;
|
|
37116
37263
|
}
|
|
37117
37264
|
}
|
|
37265
|
+
function shouldRetryCloneTokenAfterAuthFailure(kind) {
|
|
37266
|
+
return kind === "user";
|
|
37267
|
+
}
|
|
37118
37268
|
function resolveLocalCloneAuthFailure() {
|
|
37119
37269
|
const token = readTokenSafe();
|
|
37120
37270
|
if (!token) {
|
|
@@ -37173,7 +37323,10 @@ function resolveWorkosOrganizationId(organizations, organizationId) {
|
|
|
37173
37323
|
}
|
|
37174
37324
|
return workosOrganizationId;
|
|
37175
37325
|
}
|
|
37176
|
-
async function maybeRefreshCloneTokenSessionForOrganization(organizationId, listOrganizations = listOrganizationsSafe, refreshForOrganization = (workosOrganizationId) => refreshStoredAccessTokenForOrganization(workosOrganizationId)) {
|
|
37326
|
+
async function maybeRefreshCloneTokenSessionForOrganization(organizationId, listOrganizations = listOrganizationsSafe, refreshForOrganization = (workosOrganizationId) => refreshStoredAccessTokenForOrganization(workosOrganizationId), credentialKind = () => resolveCredential(readToken)?.kind ?? null) {
|
|
37327
|
+
if (credentialKind() === "api_key") {
|
|
37328
|
+
return false;
|
|
37329
|
+
}
|
|
37177
37330
|
const organizations = await listOrganizations();
|
|
37178
37331
|
if (!organizations) {
|
|
37179
37332
|
return false;
|
|
@@ -37479,7 +37632,7 @@ async function fetchCloneToken(repoUrl) {
|
|
|
37479
37632
|
body: { repositoryUrl: repoUrl }
|
|
37480
37633
|
})
|
|
37481
37634
|
);
|
|
37482
|
-
if (result && (result.status === 401 || result.status === 403)) {
|
|
37635
|
+
if (shouldRetryCloneTokenAfterAuthFailure(resolveCredential(readToken)?.kind) && result && (result.status === 401 || result.status === 403)) {
|
|
37483
37636
|
const refreshed = await maybeRefreshCloneTokenSessionForOrganization(organizationId);
|
|
37484
37637
|
if (refreshed) {
|
|
37485
37638
|
result = await authenticatedRequestSafe(
|
|
@@ -39775,6 +39928,14 @@ function matchesOrgFilter(item, filter) {
|
|
|
39775
39928
|
function sanitizeForOutput(value) {
|
|
39776
39929
|
return value.replace(/[\r\n\t]/g, " ").trim();
|
|
39777
39930
|
}
|
|
39931
|
+
var API_KEY_ENV_REFUSAL = "TRACE_API_KEY is set and takes precedence over ~/.trace. Unset it to manage a device-flow session.";
|
|
39932
|
+
function refuseIfApiKeyEnv(action) {
|
|
39933
|
+
if (!isTraceApiKeyEnvSet()) {
|
|
39934
|
+
return;
|
|
39935
|
+
}
|
|
39936
|
+
console.error(colorError(`Cannot ${action} while TRACE_API_KEY is set. ${API_KEY_ENV_REFUSAL}`));
|
|
39937
|
+
process.exit(1);
|
|
39938
|
+
}
|
|
39778
39939
|
function normalizeForComparison(value) {
|
|
39779
39940
|
return value.trim().toLowerCase();
|
|
39780
39941
|
}
|
|
@@ -39813,7 +39974,8 @@ async function fetchOrganizationContext() {
|
|
|
39813
39974
|
process.exit(1);
|
|
39814
39975
|
}
|
|
39815
39976
|
const organizations = result.body;
|
|
39816
|
-
const
|
|
39977
|
+
const credential = resolveCredential(readToken);
|
|
39978
|
+
const selectedOrganizationId = credential?.kind === "api_key" ? null : readSelectedOrganizationId();
|
|
39817
39979
|
const active = determineActiveOrganization(organizations, selectedOrganizationId);
|
|
39818
39980
|
if (!active) {
|
|
39819
39981
|
console.error("No organizations found for your account.");
|
|
@@ -39824,15 +39986,17 @@ async function fetchOrganizationContext() {
|
|
|
39824
39986
|
activeSource: active.source,
|
|
39825
39987
|
staleSelectionDetected: active.staleSelectionDetected
|
|
39826
39988
|
});
|
|
39827
|
-
if (
|
|
39828
|
-
|
|
39829
|
-
|
|
39830
|
-
|
|
39831
|
-
|
|
39832
|
-
|
|
39833
|
-
|
|
39834
|
-
|
|
39835
|
-
|
|
39989
|
+
if (credential?.kind !== "api_key") {
|
|
39990
|
+
if (persistencePlan.shouldRemoveStaleSelection) {
|
|
39991
|
+
removeSelectedOrganizationId();
|
|
39992
|
+
}
|
|
39993
|
+
if (persistencePlan.shouldWriteSelection) {
|
|
39994
|
+
writeSelectedOrganizationId(persistencePlan.effectiveSelectedOrganizationId);
|
|
39995
|
+
if (selectedOrganizationId) {
|
|
39996
|
+
console.warn(
|
|
39997
|
+
`Stored organization '${selectedOrganizationId}' is unavailable. Using '${active.organization.name}' (${active.organization.organizationId}) instead.`
|
|
39998
|
+
);
|
|
39999
|
+
}
|
|
39836
40000
|
}
|
|
39837
40001
|
}
|
|
39838
40002
|
return {
|
|
@@ -39984,6 +40148,7 @@ Examples:
|
|
|
39984
40148
|
tracecli auth login
|
|
39985
40149
|
tracecli auth logout && tracecli auth login # re-authenticate`
|
|
39986
40150
|
).action(async () => {
|
|
40151
|
+
refuseIfApiKeyEnv("run `tracecli auth login`");
|
|
39987
40152
|
const existingToken = readToken();
|
|
39988
40153
|
if (existingToken) {
|
|
39989
40154
|
console.log(
|
|
@@ -40028,6 +40193,7 @@ Examples:
|
|
|
40028
40193
|
tracecli auth logout
|
|
40029
40194
|
tracecli auth logout --no-confirm`
|
|
40030
40195
|
).action(async (options) => {
|
|
40196
|
+
refuseIfApiKeyEnv("run `tracecli auth logout`");
|
|
40031
40197
|
if (options.confirm && process.stdin.isTTY && process.stdout.isTTY) {
|
|
40032
40198
|
const readline = createInterface3({ input: process.stdin, output: process.stdout });
|
|
40033
40199
|
try {
|
|
@@ -40053,12 +40219,45 @@ Examples:
|
|
|
40053
40219
|
function findOrganizationById(organizations, activeOrgId) {
|
|
40054
40220
|
return organizations.find((org) => org.organizationId === activeOrgId) ?? null;
|
|
40055
40221
|
}
|
|
40056
|
-
function
|
|
40057
|
-
if (
|
|
40222
|
+
function applyOrganizationsToAuthStatus(info, organizations) {
|
|
40223
|
+
if (info.authMethod === "api_key") {
|
|
40224
|
+
const active = determineActiveOrganization(organizations, null);
|
|
40225
|
+
if (!active) {
|
|
40226
|
+
return;
|
|
40227
|
+
}
|
|
40228
|
+
info.activeOrg = active.organization.organizationId;
|
|
40229
|
+
info.activeOrgName = active.organization.name;
|
|
40230
|
+
info.activeOrgSlug = active.organization.slug;
|
|
40231
|
+
return;
|
|
40232
|
+
}
|
|
40233
|
+
if (!info.activeOrg) {
|
|
40234
|
+
return;
|
|
40235
|
+
}
|
|
40236
|
+
const match = findOrganizationById(organizations, info.activeOrg);
|
|
40237
|
+
if (!match) {
|
|
40238
|
+
return;
|
|
40239
|
+
}
|
|
40240
|
+
info.activeOrgName = match.name;
|
|
40241
|
+
info.activeOrgSlug = match.slug;
|
|
40242
|
+
}
|
|
40243
|
+
function buildAuthStatusInfo(credential, refreshToken, selectedOrganizationId) {
|
|
40244
|
+
if (!credential) {
|
|
40058
40245
|
return { loggedIn: false, expired: false, hasRefreshToken: refreshToken !== null };
|
|
40059
40246
|
}
|
|
40247
|
+
if (credential.kind === "api_key") {
|
|
40248
|
+
return {
|
|
40249
|
+
loggedIn: true,
|
|
40250
|
+
authMethod: "api_key",
|
|
40251
|
+
keyId: publicApiKeyId(credential.token),
|
|
40252
|
+
source: credential.source === "env" ? "TRACE_API_KEY" : "file",
|
|
40253
|
+
expired: false,
|
|
40254
|
+
hasRefreshToken: false
|
|
40255
|
+
};
|
|
40256
|
+
}
|
|
40257
|
+
const token = credential.token;
|
|
40060
40258
|
const base = {
|
|
40061
40259
|
loggedIn: true,
|
|
40260
|
+
authMethod: "user",
|
|
40062
40261
|
expired: false,
|
|
40063
40262
|
hasRefreshToken: refreshToken !== null,
|
|
40064
40263
|
...selectedOrganizationId ? { activeOrg: selectedOrganizationId } : {}
|
|
@@ -40092,20 +40291,26 @@ Examples:
|
|
|
40092
40291
|
tracecli auth status --output json
|
|
40093
40292
|
tracecli auth status --output tsv`
|
|
40094
40293
|
).action(async (options) => {
|
|
40095
|
-
|
|
40096
|
-
|
|
40097
|
-
|
|
40098
|
-
|
|
40099
|
-
|
|
40294
|
+
let credential;
|
|
40295
|
+
try {
|
|
40296
|
+
credential = resolveCredential(readToken);
|
|
40297
|
+
} catch (error48) {
|
|
40298
|
+
if (error48 instanceof InvalidTraceApiKeyError) {
|
|
40299
|
+
console.error(colorError(error48.message));
|
|
40300
|
+
process.exitCode = 1;
|
|
40301
|
+
return;
|
|
40302
|
+
}
|
|
40303
|
+
throw error48;
|
|
40304
|
+
}
|
|
40305
|
+
const refreshToken = credential?.kind === "user" ? readRefreshToken() : null;
|
|
40306
|
+
const selectedOrganizationId = credential?.kind === "api_key" ? null : readSelectedOrganizationId();
|
|
40307
|
+
const info = buildAuthStatusInfo(credential, refreshToken, selectedOrganizationId);
|
|
40308
|
+
if (info.loggedIn) {
|
|
40100
40309
|
const result = await authenticatedRequestSafe(
|
|
40101
40310
|
(client) => client.organization.listMyOrganizations()
|
|
40102
40311
|
);
|
|
40103
40312
|
if (result && result.status === 200) {
|
|
40104
|
-
|
|
40105
|
-
if (match) {
|
|
40106
|
-
info.activeOrgName = match.name;
|
|
40107
|
-
info.activeOrgSlug = match.slug;
|
|
40108
|
-
}
|
|
40313
|
+
applyOrganizationsToAuthStatus(info, result.body);
|
|
40109
40314
|
}
|
|
40110
40315
|
}
|
|
40111
40316
|
if (options.output === "json") {
|
|
@@ -40117,11 +40322,12 @@ Examples:
|
|
|
40117
40322
|
}
|
|
40118
40323
|
if (options.output === "tsv") {
|
|
40119
40324
|
console.log(
|
|
40120
|
-
"loggedIn expired hasRefreshToken userId orgId activeOrg activeOrgName activeOrgSlug"
|
|
40325
|
+
"loggedIn authMethod expired hasRefreshToken userId orgId activeOrg activeOrgName activeOrgSlug"
|
|
40121
40326
|
);
|
|
40122
40327
|
console.log(
|
|
40123
40328
|
[
|
|
40124
40329
|
String(info.loggedIn),
|
|
40330
|
+
info.authMethod ?? "",
|
|
40125
40331
|
String(info.expired),
|
|
40126
40332
|
String(info.hasRefreshToken),
|
|
40127
40333
|
info.userId ?? "",
|
|
@@ -40142,11 +40348,20 @@ Examples:
|
|
|
40142
40348
|
return;
|
|
40143
40349
|
}
|
|
40144
40350
|
console.log("Logged in to Trace");
|
|
40145
|
-
if (info.
|
|
40146
|
-
|
|
40147
|
-
|
|
40148
|
-
|
|
40149
|
-
|
|
40351
|
+
if (info.authMethod === "api_key") {
|
|
40352
|
+
const sourceLabel = info.source === "TRACE_API_KEY" ? "API key (TRACE_API_KEY)" : "API key (~/.trace/token)";
|
|
40353
|
+
console.log(` Auth method: ${sourceLabel}`);
|
|
40354
|
+
if (info.keyId) {
|
|
40355
|
+
console.log(` Key ID: ${info.keyId}`);
|
|
40356
|
+
}
|
|
40357
|
+
} else {
|
|
40358
|
+
console.log(" Auth method: user session");
|
|
40359
|
+
if (info.userId) {
|
|
40360
|
+
console.log(` User ID: ${info.userId}`);
|
|
40361
|
+
}
|
|
40362
|
+
if (info.orgId) {
|
|
40363
|
+
console.log(` Org ID: ${info.orgId}`);
|
|
40364
|
+
}
|
|
40150
40365
|
}
|
|
40151
40366
|
if (info.activeOrg) {
|
|
40152
40367
|
const orgLabel = info.activeOrgName ? `${sanitizeForOutput(info.activeOrgName)}${info.activeOrgSlug ? ` (${sanitizeForOutput(info.activeOrgSlug)})` : ""} \u2014 ${info.activeOrg}` : info.activeOrg;
|
|
@@ -40355,6 +40570,7 @@ Examples:
|
|
|
40355
40570
|
tracecli org switch org_01KJTJFFG497Z1C54Z07AP6NA3
|
|
40356
40571
|
tracecli org switch engineering --web`
|
|
40357
40572
|
).action(async (organization, options) => {
|
|
40573
|
+
refuseIfApiKeyEnv("switch organization");
|
|
40358
40574
|
const normalizedOrganizationInput = organization?.trim() ?? "";
|
|
40359
40575
|
if (options.web) {
|
|
40360
40576
|
if (normalizedOrganizationInput.length > 0) {
|
|
@@ -40603,10 +40819,11 @@ Examples:
|
|
|
40603
40819
|
console.error(`Failed to resolve organization (status ${listed.status}).`);
|
|
40604
40820
|
process.exit(1);
|
|
40605
40821
|
}
|
|
40822
|
+
const credential = resolveCredential(readToken);
|
|
40606
40823
|
const resolution = determineActiveOrganization(
|
|
40607
40824
|
listed.body,
|
|
40608
|
-
readSelectedOrganizationId(),
|
|
40609
|
-
extractOrganizationIdFromToken(
|
|
40825
|
+
credential?.kind === "api_key" ? null : readSelectedOrganizationId(),
|
|
40826
|
+
credential?.kind === "user" ? extractOrganizationIdFromToken(credential.token) : null
|
|
40610
40827
|
);
|
|
40611
40828
|
if (!resolution) {
|
|
40612
40829
|
console.error("No organization found for your account.");
|