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.
Files changed (2) hide show
  1. package/dist/index.js +161 -60
  2. 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 activeOrganization = determineActiveOrganization(organizations, selectedOrganizationId);
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 activeOrganization = determineActiveOrganization(organizations, selectedOrganizationId);
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").action(async () => {
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").action(() => {
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").action(() => {
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
- if (token) {
25274
- try {
25275
- const parts = token.split(".");
25276
- const payload = parts[1];
25277
- if (payload) {
25278
- const decoded = JSON.parse(Buffer.from(payload, "base64url").toString("utf-8"));
25279
- const expiry = decoded.exp ? new Date(decoded.exp * 1e3) : null;
25280
- const isExpired = expiry ? expiry < /* @__PURE__ */ new Date() : false;
25281
- console.log("Logged in to Trace");
25282
- if (decoded.sub) {
25283
- console.log(` User ID: ${decoded.sub}`);
25284
- }
25285
- if (decoded.org_id) {
25286
- console.log(` Org ID: ${decoded.org_id}`);
25287
- }
25288
- if (selectedOrganizationId) {
25289
- console.log(` Active Org (CLI): ${selectedOrganizationId}`);
25290
- }
25291
- if (expiry) {
25292
- console.log(` Expires: ${expiry.toISOString()}${isExpired ? " (EXPIRED)" : ""}`);
25293
- }
25294
- console.log(
25295
- ` Refresh: ${refreshToken ? "Available (auto-refresh enabled)" : "Not available"}`
25296
- );
25297
- if (isExpired) {
25298
- if (refreshToken) {
25299
- console.log(
25300
- "\nYour access token is expired, but a refresh token is available and will be used automatically."
25301
- );
25302
- } else {
25303
- console.log(
25304
- "\nYour token has expired. Run `trace auth login` after logging out to re-authenticate."
25305
- );
25306
- }
25307
- }
25308
- } else {
25309
- console.log("Logged in to Trace (access token stored at ~/.trace/token)");
25310
- if (selectedOrganizationId) {
25311
- console.log(` Active Org (CLI): ${selectedOrganizationId}`);
25312
- }
25313
- console.log(
25314
- ` Refresh: ${refreshToken ? "Available (auto-refresh enabled)" : "Not available"}`
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
- ` Refresh: ${refreshToken ? "Available (auto-refresh enabled)" : "Not available"}`
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").action(async (organization) => {
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).action(
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securewithtrace",
3
- "version": "0.16.1",
3
+ "version": "0.17.1",
4
4
  "description": "Official Trace CLI for vulnerability intelligence in your terminal",
5
5
  "type": "module",
6
6
  "bin": {