securewithtrace 0.17.1 → 0.18.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 (3) hide show
  1. package/README.md +1 -0
  2. package/dist/index.js +97 -11
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -73,6 +73,7 @@ tracecli auth logout
73
73
  ```bash
74
74
  tracecli org list
75
75
  tracecli org current
76
+ tracecli org switch
76
77
  tracecli org switch <organization-id-or-slug>
77
78
  ```
78
79
 
package/dist/index.js CHANGED
@@ -3352,6 +3352,9 @@ var {
3352
3352
  Help
3353
3353
  } = import_index.default;
3354
3354
 
3355
+ // src/auth/commands.ts
3356
+ import { createInterface } from "readline/promises";
3357
+
3355
3358
  // ../../node_modules/.bun/@ts-rest+core@3.53.0-rc.1+f9199aabcde8d6a0/node_modules/@ts-rest/core/index.esm.mjs
3356
3359
  var util;
3357
3360
  (function(util2) {
@@ -25253,6 +25256,65 @@ Use an organization ID or exact slug.`
25253
25256
  error: `Organization '${input}' not found. Run \`tracecli org list\` to see available organizations.`
25254
25257
  };
25255
25258
  }
25259
+ function resolveOrganizationSelectionInput(organizations, input) {
25260
+ const trimmedInput = input.trim();
25261
+ if (trimmedInput.length === 0) {
25262
+ return { error: "Organization selection cannot be empty." };
25263
+ }
25264
+ if (/^\d+$/.test(trimmedInput)) {
25265
+ const byNumericId = organizations.find(
25266
+ (organization) => normalizeForComparison(organization.organizationId) === normalizeForComparison(trimmedInput)
25267
+ );
25268
+ if (byNumericId) {
25269
+ return { organization: byNumericId };
25270
+ }
25271
+ const selectedIndex = Number.parseInt(trimmedInput, 10);
25272
+ if (selectedIndex < 1 || selectedIndex > organizations.length) {
25273
+ return {
25274
+ error: `Selection '${trimmedInput}' is out of range. Enter a number between 1 and ${organizations.length}.`
25275
+ };
25276
+ }
25277
+ const selectedOrganization = organizations[selectedIndex - 1];
25278
+ if (!selectedOrganization) {
25279
+ return {
25280
+ error: "Failed to resolve organization selection. Please try again with an organization ID or slug."
25281
+ };
25282
+ }
25283
+ return { organization: selectedOrganization };
25284
+ }
25285
+ return findOrganizationByInput(organizations, trimmedInput);
25286
+ }
25287
+ async function promptForOrganizationSelection(organizations, activeOrganizationId) {
25288
+ console.log("Select an organization:");
25289
+ for (const [index, organization] of organizations.entries()) {
25290
+ const marker = organization.organizationId === activeOrganizationId ? "*" : " ";
25291
+ console.log(
25292
+ `${marker} ${index + 1}) ${sanitizeForOutput(organization.name)} (${sanitizeForOutput(organization.slug)}) - ${organization.organizationId}`
25293
+ );
25294
+ }
25295
+ console.log("");
25296
+ const readline = createInterface({
25297
+ input: process.stdin,
25298
+ output: process.stdout
25299
+ });
25300
+ try {
25301
+ while (true) {
25302
+ const input = await readline.question(
25303
+ "Enter number, organization ID, slug, or name (press Enter to cancel): "
25304
+ );
25305
+ if (input.trim().length === 0) {
25306
+ return null;
25307
+ }
25308
+ const selection = resolveOrganizationSelectionInput(organizations, input);
25309
+ if ("organization" in selection) {
25310
+ return selection.organization;
25311
+ }
25312
+ console.error(selection.error);
25313
+ }
25314
+ } finally {
25315
+ readline.close();
25316
+ }
25317
+ }
25256
25318
  async function refreshAfterOrganizationSwitch(workosOrganizationId, refreshForOrganization = (organizationId) => refreshStoredAccessTokenForOrganization(organizationId)) {
25257
25319
  if (!workosOrganizationId || workosOrganizationId.trim().length === 0) {
25258
25320
  return false;
@@ -25500,33 +25562,57 @@ Examples:
25500
25562
  });
25501
25563
  }
25502
25564
  function createOrgSwitchCommand() {
25503
- return new Command("switch").description("Switch the active organization for subsequent CLI commands").argument("<organization>", "Organization ID, slug, or name").addHelpText(
25565
+ return new Command("switch").description("Switch the active organization for subsequent CLI commands").argument("[organization]", "Organization ID, slug, name, or menu selection index").addHelpText(
25504
25566
  "after",
25505
25567
  `
25506
25568
  Examples:
25569
+ tracecli org switch
25507
25570
  tracecli org switch engineering
25508
25571
  tracecli org switch org_01KJTJFFG497Z1C54Z07AP6NA3`
25509
25572
  ).action(async (organization) => {
25510
- const organizationContext = await fetchOrganizationContext();
25511
- const match = findOrganizationByInput(organizationContext.organizations, organization);
25512
- if ("error" in match) {
25513
- console.error(match.error);
25573
+ const normalizedOrganizationInput = organization?.trim() ?? "";
25574
+ if (normalizedOrganizationInput.length === 0 && (!process.stdin.isTTY || !process.stdout.isTTY)) {
25575
+ console.error(
25576
+ "Missing required argument 'organization'. Run `tracecli org switch <organization>` in non-interactive environments."
25577
+ );
25514
25578
  process.exit(1);
25515
25579
  }
25516
- if (!isSafeOrganizationId(match.organization.organizationId)) {
25580
+ const organizationContext = await fetchOrganizationContext();
25581
+ let selectedOrganization = null;
25582
+ if (normalizedOrganizationInput.length > 0) {
25583
+ const selection = resolveOrganizationSelectionInput(
25584
+ organizationContext.organizations,
25585
+ normalizedOrganizationInput
25586
+ );
25587
+ if ("error" in selection) {
25588
+ console.error(selection.error);
25589
+ process.exit(1);
25590
+ }
25591
+ selectedOrganization = selection.organization;
25592
+ } else {
25593
+ selectedOrganization = await promptForOrganizationSelection(
25594
+ organizationContext.organizations,
25595
+ organizationContext.activeOrganization.organizationId
25596
+ );
25597
+ if (!selectedOrganization) {
25598
+ console.error("Organization switch cancelled.");
25599
+ process.exit(1);
25600
+ }
25601
+ }
25602
+ if (!isSafeOrganizationId(selectedOrganization.organizationId)) {
25517
25603
  console.error(
25518
- `Cannot switch to organization '${match.organization.organizationId}': invalid organization ID format.`
25604
+ `Cannot switch to organization '${selectedOrganization.organizationId}': invalid organization ID format.`
25519
25605
  );
25520
25606
  process.exit(1);
25521
25607
  }
25522
- writeSelectedOrganizationId(match.organization.organizationId);
25608
+ writeSelectedOrganizationId(selectedOrganization.organizationId);
25523
25609
  const refreshed = await refreshAfterOrganizationSwitch(
25524
- match.organization.workosOrganizationId
25610
+ selectedOrganization.workosOrganizationId
25525
25611
  );
25526
25612
  console.log(
25527
- `Active organization set to ${sanitizeForOutput(match.organization.name)} (${sanitizeForOutput(match.organization.slug)})`
25613
+ `Active organization set to ${sanitizeForOutput(selectedOrganization.name)} (${sanitizeForOutput(selectedOrganization.slug)})`
25528
25614
  );
25529
- console.log(` Org ID: ${match.organization.organizationId}`);
25615
+ console.log(` Org ID: ${selectedOrganization.organizationId}`);
25530
25616
  if (!refreshed) {
25531
25617
  console.warn(
25532
25618
  "Token refresh for the selected organization was not completed. If repository commands fail, run `tracecli auth login` again."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securewithtrace",
3
- "version": "0.17.1",
3
+ "version": "0.18.0",
4
4
  "description": "Official Trace CLI for vulnerability intelligence in your terminal",
5
5
  "type": "module",
6
6
  "bin": {