securewithtrace 0.20.2 → 0.21.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.
Files changed (2) hide show
  1. package/dist/index.js +128 -37
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -26412,6 +26412,32 @@ async function resolveOrganizationId(client) {
26412
26412
  }
26413
26413
  return id;
26414
26414
  }
26415
+ async function resolveOrganizationSlug() {
26416
+ const result = await authenticatedRequest((client) => client.organization.listMyOrganizations());
26417
+ if (result.status === 401) {
26418
+ console.error(
26419
+ colorError("Authentication failed. Run `tracecli auth login` to re-authenticate.")
26420
+ );
26421
+ process.exit(1);
26422
+ }
26423
+ if (result.status !== 200) {
26424
+ console.error(colorError(`Failed to resolve organization (status ${result.status}).`));
26425
+ process.exit(1);
26426
+ }
26427
+ const organizations = result.body;
26428
+ const selectedOrganizationId = readSelectedOrganizationId();
26429
+ const tokenOrganizationId = extractOrganizationIdFromToken(readToken());
26430
+ const activeOrganization = determineActiveOrganization(
26431
+ organizations,
26432
+ selectedOrganizationId,
26433
+ tokenOrganizationId
26434
+ );
26435
+ if (!activeOrganization) {
26436
+ console.error(colorError("No organization found for your account."));
26437
+ process.exit(1);
26438
+ }
26439
+ return activeOrganization.organization.slug;
26440
+ }
26415
26441
  async function resolveOrganizationIdSafe(client) {
26416
26442
  try {
26417
26443
  const result = client ? await client.organization.listMyOrganizations() : await authenticatedRequestSafe(
@@ -26450,6 +26476,39 @@ async function resolveOrganizationIdSafe(client) {
26450
26476
  }
26451
26477
  }
26452
26478
 
26479
+ // src/browser.ts
26480
+ var DEFAULT_APP_BASE_URL = "https://app.securewithtrace.com";
26481
+ function getAppBaseUrl() {
26482
+ const override = process.env["TRACE_APP_URL"];
26483
+ if (typeof override === "string" && override.trim().length > 0) {
26484
+ try {
26485
+ const parsed = new URL(override);
26486
+ if (parsed.protocol === "https:") {
26487
+ return override.replace(/\/+$/, "");
26488
+ }
26489
+ } catch {
26490
+ }
26491
+ }
26492
+ return DEFAULT_APP_BASE_URL;
26493
+ }
26494
+ async function openBrowser(url2) {
26495
+ try {
26496
+ const parsed = new URL(url2);
26497
+ if (parsed.protocol !== "https:") {
26498
+ return false;
26499
+ }
26500
+ const proc = Bun.spawn(["open", url2], { stdout: "ignore", stderr: "ignore" });
26501
+ const exitCode = await proc.exited;
26502
+ if (exitCode !== 0) {
26503
+ const linuxProc = Bun.spawn(["xdg-open", url2], { stdout: "ignore", stderr: "ignore" });
26504
+ return await linuxProc.exited === 0;
26505
+ }
26506
+ return true;
26507
+ } catch {
26508
+ return false;
26509
+ }
26510
+ }
26511
+
26453
26512
  // src/format.ts
26454
26513
  var SEVERITY_ORDER = {
26455
26514
  CRITICAL: 0,
@@ -26475,29 +26534,6 @@ function getClientId() {
26475
26534
  "TRACE_WORKOS_CLIENT_ID is not set. Build the CLI with TRACE_WORKOS_CLIENT_ID set, or provide it as an environment variable."
26476
26535
  );
26477
26536
  }
26478
- async function openBrowser(url2) {
26479
- try {
26480
- const parsed = new URL(url2);
26481
- if (parsed.protocol !== "https:") {
26482
- return false;
26483
- }
26484
- const proc = Bun.spawn(["open", url2], {
26485
- stdout: "ignore",
26486
- stderr: "ignore"
26487
- });
26488
- const exitCode = await proc.exited;
26489
- if (exitCode !== 0) {
26490
- const linuxProc = Bun.spawn(["xdg-open", url2], {
26491
- stdout: "ignore",
26492
- stderr: "ignore"
26493
- });
26494
- return await linuxProc.exited === 0;
26495
- }
26496
- return true;
26497
- } catch {
26498
- return false;
26499
- }
26500
- }
26501
26537
  var SAFE_ORGANIZATION_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
26502
26538
  function sanitizeForOutput(value) {
26503
26539
  return value.replace(/[\r\n\t]/g, " ").trim();
@@ -26531,6 +26567,10 @@ function isSafeOrganizationId(organizationId) {
26531
26567
  }
26532
26568
  async function fetchOrganizationContext() {
26533
26569
  const result = await authenticatedRequest((client) => client.organization.listMyOrganizations());
26570
+ if (result.status === 401) {
26571
+ console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
26572
+ process.exit(1);
26573
+ }
26534
26574
  if (result.status !== 200) {
26535
26575
  console.error(`Failed to list organizations (status ${result.status}).`);
26536
26576
  process.exit(1);
@@ -26857,22 +26897,40 @@ Examples:
26857
26897
  });
26858
26898
  }
26859
26899
  function createOrgListCommand() {
26860
- return new Command("list").description("List organizations available to your account").option("-o, --output <format>", "Output format: table (default), json, tsv").addHelpText(
26900
+ return new Command("list").description("List organizations available to your account").option("-o, --output <format>", "Output format: table (default), json, tsv").option("-L, --limit <n>", "Max organizations to display (default: all)", (v) => {
26901
+ const n = Number.parseInt(v, 10);
26902
+ if (Number.isNaN(n) || n < 1) {
26903
+ throw new Error("--limit must be a positive integer");
26904
+ }
26905
+ return n;
26906
+ }).option("-w, --web", "Open the organization settings in a browser").addHelpText(
26861
26907
  "after",
26862
26908
  `
26863
26909
  Examples:
26864
26910
  tracecli org list
26865
26911
  tracecli org list --output json
26866
- tracecli org list --output tsv | cut -f1`
26912
+ tracecli org list --output tsv | cut -f1
26913
+ tracecli org list --limit 5
26914
+ tracecli org list --web`
26867
26915
  ).action(async (options) => {
26916
+ if (options.web) {
26917
+ const slug = await resolveOrganizationSlug();
26918
+ const url2 = `${getAppBaseUrl()}/dashboard/${slug}/settings`;
26919
+ const opened = await openBrowser(url2);
26920
+ if (!opened) {
26921
+ console.log(`Open in browser: ${url2}`);
26922
+ }
26923
+ return;
26924
+ }
26868
26925
  const organizationContext = await fetchOrganizationContext();
26869
- const items = organizationContext.organizations.map(
26926
+ const allItems = organizationContext.organizations.map(
26870
26927
  (organization) => ({
26871
26928
  ...organization,
26872
26929
  active: organization.organizationId === organizationContext.activeOrganization.organizationId,
26873
26930
  selected: organization.organizationId === organizationContext.selectedOrganizationId
26874
26931
  })
26875
26932
  );
26933
+ const items = options.limit !== void 0 ? allItems.slice(0, options.limit) : allItems;
26876
26934
  if (options.output === "json") {
26877
26935
  console.log(JSON.stringify(items, null, 2));
26878
26936
  return;
@@ -26891,15 +26949,27 @@ Examples:
26891
26949
  }
26892
26950
  return;
26893
26951
  }
26894
- for (const item of items) {
26895
- const marker = item.active ? "*" : " ";
26952
+ const headers = ["", "Name", "Slug", "Org ID"];
26953
+ const rows = items.map((item) => {
26896
26954
  const labels = [item.active ? "active" : null, item.selected ? "selected" : null].filter(
26897
26955
  Boolean
26898
26956
  );
26899
- const suffix = labels.length > 0 ? ` [${labels.join(", ")}]` : "";
26900
- console.log(
26901
- `${marker} ${sanitizeForOutput(item.name)} (${sanitizeForOutput(item.slug)}) - ${item.organizationId}${suffix}`
26902
- );
26957
+ const marker = item.active ? "*" : " ";
26958
+ const nameSuffix = labels.length > 0 ? ` [${labels.join(", ")}]` : "";
26959
+ return [
26960
+ marker,
26961
+ sanitizeForOutput(item.name) + nameSuffix,
26962
+ sanitizeForOutput(item.slug),
26963
+ item.organizationId
26964
+ ];
26965
+ });
26966
+ const colWidths = headers.map(
26967
+ (h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length))
26968
+ );
26969
+ const pad = (s, w3) => s.padEnd(w3);
26970
+ console.log(headers.map((h, i) => pad(h, colWidths[i] ?? h.length)).join(" "));
26971
+ for (const row of rows) {
26972
+ console.log(row.map((cell, i) => pad(cell, colWidths[i] ?? 0)).join(" "));
26903
26973
  }
26904
26974
  if (organizationContext.staleSelectionDetected) {
26905
26975
  console.log("\nYour previous organization selection is no longer available and was reset.");
@@ -26907,14 +26977,24 @@ Examples:
26907
26977
  });
26908
26978
  }
26909
26979
  function createOrgCurrentCommand() {
26910
- return new Command("current").description("Show the active organization used by CLI commands").option("-o, --output <format>", "Output format: table (default), json, tsv").addHelpText(
26980
+ return new Command("current").description("Show the active organization used by CLI commands").option("-o, --output <format>", "Output format: table (default), json, tsv").option("-w, --web", "Open the organization settings in a browser").addHelpText(
26911
26981
  "after",
26912
26982
  `
26913
26983
  Examples:
26914
26984
  tracecli org current
26915
26985
  tracecli org current --output json
26916
- tracecli org current --output tsv`
26986
+ tracecli org current --output tsv
26987
+ tracecli org current --web`
26917
26988
  ).action(async (options) => {
26989
+ if (options.web) {
26990
+ const slug = await resolveOrganizationSlug();
26991
+ const url2 = `${getAppBaseUrl()}/dashboard/${slug}/settings`;
26992
+ const opened = await openBrowser(url2);
26993
+ if (!opened) {
26994
+ console.log(`Open in browser: ${url2}`);
26995
+ }
26996
+ return;
26997
+ }
26918
26998
  const organizationContext = await fetchOrganizationContext();
26919
26999
  const active = organizationContext.activeOrganization;
26920
27000
  const source = organizationContext.selectedOrganizationId === active.organizationId ? "selected" : "default";
@@ -28153,9 +28233,18 @@ function addRepositoryFilterOptions(command) {
28153
28233
  ).option(
28154
28234
  "--dependency <pkg>",
28155
28235
  "Filter repositories that use a dependency (e.g. lodash, lodash@4.17.21, @types/node@20)"
28156
- );
28236
+ ).option("-w, --web", "Open the repositories dashboard in a browser");
28157
28237
  }
28158
28238
  async function runRepositoryListCommand(options) {
28239
+ if (options.web) {
28240
+ const slug = await resolveOrganizationSlug();
28241
+ const url2 = `${getAppBaseUrl()}/dashboard/${slug}/repositories`;
28242
+ const opened = await openBrowser(url2);
28243
+ if (!opened) {
28244
+ console.log(`Open in browser: ${url2}`);
28245
+ }
28246
+ return;
28247
+ }
28159
28248
  const organizationId = await resolveOrganizationId();
28160
28249
  if (options.dependency) {
28161
28250
  await runDependencyFilter(organizationId, {
@@ -28287,7 +28376,8 @@ Examples:
28287
28376
  tracecli repo list --output json | jq '.[].repositoryUrl'
28288
28377
  tracecli repo list --output tsv | cut -f1
28289
28378
  tracecli repo list --filter "language:typescript" --limit 10
28290
- tracecli repo list --dependency lodash@4.17.21`
28379
+ tracecli repo list --dependency lodash@4.17.21
28380
+ tracecli repo list --web`
28291
28381
  ).action(async (options) => {
28292
28382
  await runRepositoryListCommand(options);
28293
28383
  });
@@ -28300,7 +28390,8 @@ function createFilterCommand() {
28300
28390
  `
28301
28391
  Examples:
28302
28392
  tracecli repo filter --filter "platform=GITHUB" --filter "status=CONNECTED"
28303
- tracecli repo filter --language typescript --output json`
28393
+ tracecli repo filter --language typescript --output json
28394
+ tracecli repo filter --web`
28304
28395
  ).action(async (options) => {
28305
28396
  await runRepositoryListCommand(options);
28306
28397
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securewithtrace",
3
- "version": "0.20.2",
3
+ "version": "0.21.0",
4
4
  "description": "Official Trace CLI for vulnerability intelligence in your terminal",
5
5
  "type": "module",
6
6
  "bin": {