securewithtrace 0.16.1 → 0.17.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/dist/index.js +161 -60
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -24959,8 +24959,24 @@ async function authenticatedRequestSafe(request) {
|
|
|
24959
24959
|
return null;
|
|
24960
24960
|
}
|
|
24961
24961
|
}
|
|
24962
|
+
function extractOrganizationIdFromToken(token) {
|
|
24963
|
+
if (!token || token.trim().length === 0) {
|
|
24964
|
+
return null;
|
|
24965
|
+
}
|
|
24966
|
+
const parts = token.split(".");
|
|
24967
|
+
const payload = parts[1];
|
|
24968
|
+
if (!payload) {
|
|
24969
|
+
return null;
|
|
24970
|
+
}
|
|
24971
|
+
try {
|
|
24972
|
+
const decoded = JSON.parse(Buffer.from(payload, "base64url").toString("utf-8"));
|
|
24973
|
+
return typeof decoded.org_id === "string" && decoded.org_id.trim().length > 0 ? decoded.org_id : null;
|
|
24974
|
+
} catch {
|
|
24975
|
+
return null;
|
|
24976
|
+
}
|
|
24977
|
+
}
|
|
24962
24978
|
var SAFE_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
24963
|
-
function determineActiveOrganization(organizations, selectedOrganizationId) {
|
|
24979
|
+
function determineActiveOrganization(organizations, selectedOrganizationId, tokenOrganizationId) {
|
|
24964
24980
|
if (organizations.length === 0) {
|
|
24965
24981
|
return null;
|
|
24966
24982
|
}
|
|
@@ -24976,6 +24992,18 @@ function determineActiveOrganization(organizations, selectedOrganizationId) {
|
|
|
24976
24992
|
};
|
|
24977
24993
|
}
|
|
24978
24994
|
}
|
|
24995
|
+
if (tokenOrganizationId && SAFE_ID_PATTERN.test(tokenOrganizationId)) {
|
|
24996
|
+
const tokenOrganization = organizations.find(
|
|
24997
|
+
(organization) => organization.organizationId === tokenOrganizationId
|
|
24998
|
+
);
|
|
24999
|
+
if (tokenOrganization) {
|
|
25000
|
+
return {
|
|
25001
|
+
organization: tokenOrganization,
|
|
25002
|
+
source: "default",
|
|
25003
|
+
staleSelectionDetected: selectedOrganizationId != null
|
|
25004
|
+
};
|
|
25005
|
+
}
|
|
25006
|
+
}
|
|
24979
25007
|
const [fallbackOrganization] = organizations;
|
|
24980
25008
|
if (!fallbackOrganization) {
|
|
24981
25009
|
return null;
|
|
@@ -24996,7 +25024,12 @@ async function resolveOrganizationId(client) {
|
|
|
24996
25024
|
}
|
|
24997
25025
|
const organizations = result.body;
|
|
24998
25026
|
const selectedOrganizationId = readSelectedOrganizationId();
|
|
24999
|
-
const
|
|
25027
|
+
const tokenOrganizationId = extractOrganizationIdFromToken(readToken());
|
|
25028
|
+
const activeOrganization = determineActiveOrganization(
|
|
25029
|
+
organizations,
|
|
25030
|
+
selectedOrganizationId,
|
|
25031
|
+
tokenOrganizationId
|
|
25032
|
+
);
|
|
25000
25033
|
if (!activeOrganization) {
|
|
25001
25034
|
console.error("No organization found for your account.");
|
|
25002
25035
|
process.exit(1);
|
|
@@ -25032,7 +25065,12 @@ async function resolveOrganizationIdSafe(client) {
|
|
|
25032
25065
|
}
|
|
25033
25066
|
const organizations = result.body;
|
|
25034
25067
|
const selectedOrganizationId = readSelectedOrganizationId();
|
|
25035
|
-
const
|
|
25068
|
+
const tokenOrganizationId = extractOrganizationIdFromToken(readToken());
|
|
25069
|
+
const activeOrganization = determineActiveOrganization(
|
|
25070
|
+
organizations,
|
|
25071
|
+
selectedOrganizationId,
|
|
25072
|
+
tokenOrganizationId
|
|
25073
|
+
);
|
|
25036
25074
|
if (!activeOrganization) {
|
|
25037
25075
|
return null;
|
|
25038
25076
|
}
|
|
@@ -25222,7 +25260,13 @@ async function refreshAfterOrganizationSwitch(workosOrganizationId, refreshForOr
|
|
|
25222
25260
|
return refreshForOrganization(workosOrganizationId);
|
|
25223
25261
|
}
|
|
25224
25262
|
function createLoginCommand() {
|
|
25225
|
-
return new Command("login").description("Authenticate with Trace").
|
|
25263
|
+
return new Command("login").description("Authenticate with Trace").addHelpText(
|
|
25264
|
+
"after",
|
|
25265
|
+
`
|
|
25266
|
+
Examples:
|
|
25267
|
+
tracecli auth login
|
|
25268
|
+
tracecli auth logout && tracecli auth login # re-authenticate`
|
|
25269
|
+
).action(async () => {
|
|
25226
25270
|
const existingToken = readToken();
|
|
25227
25271
|
if (existingToken) {
|
|
25228
25272
|
console.log("You are already logged in. Run `trace auth logout` first to re-authenticate.");
|
|
@@ -25256,7 +25300,12 @@ Authenticated as ${displayName} (${tokenResponse.user.email})`);
|
|
|
25256
25300
|
});
|
|
25257
25301
|
}
|
|
25258
25302
|
function createLogoutCommand() {
|
|
25259
|
-
return new Command("logout").description("Log out of Trace").
|
|
25303
|
+
return new Command("logout").description("Log out of Trace").addHelpText(
|
|
25304
|
+
"after",
|
|
25305
|
+
`
|
|
25306
|
+
Examples:
|
|
25307
|
+
tracecli auth logout`
|
|
25308
|
+
).action(() => {
|
|
25260
25309
|
const removed = removeToken();
|
|
25261
25310
|
if (removed) {
|
|
25262
25311
|
console.log("Logged out successfully. Local session data removed from ~/.trace/");
|
|
@@ -25265,66 +25314,96 @@ function createLogoutCommand() {
|
|
|
25265
25314
|
}
|
|
25266
25315
|
});
|
|
25267
25316
|
}
|
|
25317
|
+
function buildAuthStatusInfo(token, refreshToken, selectedOrganizationId) {
|
|
25318
|
+
if (!token) {
|
|
25319
|
+
return { loggedIn: false, expired: false, hasRefreshToken: refreshToken !== null };
|
|
25320
|
+
}
|
|
25321
|
+
const base = {
|
|
25322
|
+
loggedIn: true,
|
|
25323
|
+
expired: false,
|
|
25324
|
+
hasRefreshToken: refreshToken !== null,
|
|
25325
|
+
...selectedOrganizationId ? { activeOrg: selectedOrganizationId } : {}
|
|
25326
|
+
};
|
|
25327
|
+
try {
|
|
25328
|
+
const parts = token.split(".");
|
|
25329
|
+
const payload = parts[1];
|
|
25330
|
+
if (!payload) {
|
|
25331
|
+
return base;
|
|
25332
|
+
}
|
|
25333
|
+
const decoded = JSON.parse(Buffer.from(payload, "base64url").toString("utf-8"));
|
|
25334
|
+
const expiry = decoded.exp ? new Date(decoded.exp * 1e3) : null;
|
|
25335
|
+
const isExpired = expiry ? expiry < /* @__PURE__ */ new Date() : false;
|
|
25336
|
+
return {
|
|
25337
|
+
...base,
|
|
25338
|
+
...decoded.sub ? { userId: decoded.sub } : {},
|
|
25339
|
+
...decoded.org_id ? { orgId: decoded.org_id } : {},
|
|
25340
|
+
...expiry ? { expires: expiry.toISOString() } : {},
|
|
25341
|
+
expired: isExpired
|
|
25342
|
+
};
|
|
25343
|
+
} catch {
|
|
25344
|
+
return base;
|
|
25345
|
+
}
|
|
25346
|
+
}
|
|
25268
25347
|
function createStatusCommand() {
|
|
25269
|
-
return new Command("status").description("Show current authentication status").
|
|
25348
|
+
return new Command("status").description("Show current authentication status").option("-o, --output <format>", "Output format: table (default), json, tsv").addHelpText(
|
|
25349
|
+
"after",
|
|
25350
|
+
`
|
|
25351
|
+
Examples:
|
|
25352
|
+
tracecli auth status
|
|
25353
|
+
tracecli auth status --output json
|
|
25354
|
+
tracecli auth status --output tsv`
|
|
25355
|
+
).action((options) => {
|
|
25270
25356
|
const token = readToken();
|
|
25271
25357
|
const refreshToken = readRefreshToken();
|
|
25272
25358
|
const selectedOrganizationId = readSelectedOrganizationId();
|
|
25273
|
-
|
|
25274
|
-
|
|
25275
|
-
|
|
25276
|
-
|
|
25277
|
-
|
|
25278
|
-
|
|
25279
|
-
|
|
25280
|
-
|
|
25281
|
-
|
|
25282
|
-
|
|
25283
|
-
|
|
25284
|
-
|
|
25285
|
-
|
|
25286
|
-
|
|
25287
|
-
|
|
25288
|
-
|
|
25289
|
-
|
|
25290
|
-
|
|
25291
|
-
|
|
25292
|
-
|
|
25293
|
-
|
|
25294
|
-
|
|
25295
|
-
|
|
25296
|
-
|
|
25297
|
-
|
|
25298
|
-
|
|
25299
|
-
|
|
25300
|
-
|
|
25301
|
-
|
|
25302
|
-
|
|
25303
|
-
|
|
25304
|
-
|
|
25305
|
-
|
|
25306
|
-
|
|
25307
|
-
|
|
25308
|
-
|
|
25309
|
-
|
|
25310
|
-
|
|
25311
|
-
|
|
25312
|
-
|
|
25313
|
-
|
|
25314
|
-
|
|
25315
|
-
|
|
25316
|
-
|
|
25317
|
-
} catch {
|
|
25318
|
-
console.log("Logged in to Trace (access token stored at ~/.trace/token)");
|
|
25319
|
-
if (selectedOrganizationId) {
|
|
25320
|
-
console.log(` Active Org (CLI): ${selectedOrganizationId}`);
|
|
25321
|
-
}
|
|
25359
|
+
const info = buildAuthStatusInfo(token, refreshToken, selectedOrganizationId);
|
|
25360
|
+
if (options.output === "json") {
|
|
25361
|
+
console.log(JSON.stringify(info, null, 2));
|
|
25362
|
+
return;
|
|
25363
|
+
}
|
|
25364
|
+
if (options.output === "tsv") {
|
|
25365
|
+
console.log(
|
|
25366
|
+
[
|
|
25367
|
+
String(info.loggedIn),
|
|
25368
|
+
String(info.expired),
|
|
25369
|
+
String(info.hasRefreshToken),
|
|
25370
|
+
info.userId ?? "",
|
|
25371
|
+
info.orgId ?? "",
|
|
25372
|
+
info.activeOrg ?? ""
|
|
25373
|
+
].join(" ")
|
|
25374
|
+
);
|
|
25375
|
+
return;
|
|
25376
|
+
}
|
|
25377
|
+
if (!info.loggedIn) {
|
|
25378
|
+
console.log("Not logged in. Run `tracecli auth login` to authenticate.");
|
|
25379
|
+
return;
|
|
25380
|
+
}
|
|
25381
|
+
console.log("Logged in to Trace");
|
|
25382
|
+
if (info.userId) {
|
|
25383
|
+
console.log(` User ID: ${info.userId}`);
|
|
25384
|
+
}
|
|
25385
|
+
if (info.orgId) {
|
|
25386
|
+
console.log(` Org ID: ${info.orgId}`);
|
|
25387
|
+
}
|
|
25388
|
+
if (info.activeOrg) {
|
|
25389
|
+
console.log(` Active Org (CLI): ${info.activeOrg}`);
|
|
25390
|
+
}
|
|
25391
|
+
if (info.expires) {
|
|
25392
|
+
console.log(` Expires: ${info.expires}${info.expired ? " (EXPIRED)" : ""}`);
|
|
25393
|
+
}
|
|
25394
|
+
console.log(
|
|
25395
|
+
` Refresh: ${info.hasRefreshToken ? "Available (auto-refresh enabled)" : "Not available"}`
|
|
25396
|
+
);
|
|
25397
|
+
if (info.expired) {
|
|
25398
|
+
if (info.hasRefreshToken) {
|
|
25399
|
+
console.log(
|
|
25400
|
+
"\nYour access token is expired, but a refresh token is available and will be used automatically."
|
|
25401
|
+
);
|
|
25402
|
+
} else {
|
|
25322
25403
|
console.log(
|
|
25323
|
-
`
|
|
25404
|
+
"\nYour token has expired. Run `tracecli auth login` after logging out to re-authenticate."
|
|
25324
25405
|
);
|
|
25325
25406
|
}
|
|
25326
|
-
} else {
|
|
25327
|
-
console.log("Not logged in. Run `trace auth login` to authenticate.");
|
|
25328
25407
|
}
|
|
25329
25408
|
});
|
|
25330
25409
|
}
|
|
@@ -25421,7 +25500,13 @@ Examples:
|
|
|
25421
25500
|
});
|
|
25422
25501
|
}
|
|
25423
25502
|
function createOrgSwitchCommand() {
|
|
25424
|
-
return new Command("switch").description("Switch the active organization for subsequent CLI commands").argument("<organization>", "Organization ID, slug, or name").
|
|
25503
|
+
return new Command("switch").description("Switch the active organization for subsequent CLI commands").argument("<organization>", "Organization ID, slug, or name").addHelpText(
|
|
25504
|
+
"after",
|
|
25505
|
+
`
|
|
25506
|
+
Examples:
|
|
25507
|
+
tracecli org switch engineering
|
|
25508
|
+
tracecli org switch org_01KJTJFFG497Z1C54Z07AP6NA3`
|
|
25509
|
+
).action(async (organization) => {
|
|
25425
25510
|
const organizationContext = await fetchOrganizationContext();
|
|
25426
25511
|
const match = findOrganizationByInput(organizationContext.organizations, organization);
|
|
25427
25512
|
if ("error" in match) {
|
|
@@ -26360,6 +26445,7 @@ function parseListLimit(value) {
|
|
|
26360
26445
|
return n;
|
|
26361
26446
|
}
|
|
26362
26447
|
function printRepositoriesTsv(repositories) {
|
|
26448
|
+
console.log("repositoryUrl platform status defaultBranch language");
|
|
26363
26449
|
for (const repo of repositories) {
|
|
26364
26450
|
console.log(
|
|
26365
26451
|
[
|
|
@@ -26425,6 +26511,10 @@ async function runRepositoryListCommand(options) {
|
|
|
26425
26511
|
params: { organizationId }
|
|
26426
26512
|
})
|
|
26427
26513
|
);
|
|
26514
|
+
if (result.status === 401) {
|
|
26515
|
+
console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
|
|
26516
|
+
process.exit(1);
|
|
26517
|
+
}
|
|
26428
26518
|
if (result.status !== 200) {
|
|
26429
26519
|
console.error(`Failed to list repositories (status ${result.status}).`);
|
|
26430
26520
|
process.exit(1);
|
|
@@ -26477,6 +26567,10 @@ async function runDependencyFilter(organizationId, options) {
|
|
|
26477
26567
|
}
|
|
26478
26568
|
})
|
|
26479
26569
|
);
|
|
26570
|
+
if (result.status === 401) {
|
|
26571
|
+
console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
|
|
26572
|
+
process.exit(1);
|
|
26573
|
+
}
|
|
26480
26574
|
if (result.status !== 200) {
|
|
26481
26575
|
console.error(`Failed to search dependencies (status ${result.status}).`);
|
|
26482
26576
|
process.exit(1);
|
|
@@ -26605,7 +26699,14 @@ async function fetchCloneToken(repoUrl) {
|
|
|
26605
26699
|
}
|
|
26606
26700
|
}
|
|
26607
26701
|
function createCloneCommand() {
|
|
26608
|
-
return new Command("clone").description("Clone a repository").argument("<repository>", "Repository URL or owner/repo shorthand").argument("[directory]", "Directory to clone into").option("--branch <branch>", "Branch to clone").option("--depth <depth>", "Shallow clone depth", parseCloneDepth).
|
|
26702
|
+
return new Command("clone").description("Clone a repository").argument("<repository>", "Repository URL or owner/repo shorthand").argument("[directory]", "Directory to clone into").option("--branch <branch>", "Branch to clone").option("--depth <depth>", "Shallow clone depth", parseCloneDepth).addHelpText(
|
|
26703
|
+
"after",
|
|
26704
|
+
`
|
|
26705
|
+
Examples:
|
|
26706
|
+
tracecli repo clone securewithtrace/trace
|
|
26707
|
+
tracecli repo clone securewithtrace/trace trace-src
|
|
26708
|
+
tracecli repo clone https://github.com/owner/repo --branch main --depth 1`
|
|
26709
|
+
).action(
|
|
26609
26710
|
async (repository, directory, options) => {
|
|
26610
26711
|
const cloneUrl = resolveRepoUrl(repository);
|
|
26611
26712
|
const targetDir = directory ?? extractRepoName(repository);
|