switchboard-fyi 0.2.0 → 0.2.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/CHANGELOG.md CHANGED
@@ -3,6 +3,15 @@
3
3
  All notable Switchboard CLI changes are recorded here. Keep this file in sync
4
4
  with the hosted release notes at `https://www.switchboard.fyi/release/latest`.
5
5
 
6
+ ## 0.2.1 - 2026-05-26
7
+
8
+ ### Changed
9
+
10
+ - Require `switchboard login` before local routing, observe mode, dashboard,
11
+ install, uninstall, settings, and difficulty mapping commands do any work.
12
+ - Updated the signed-out home and status screens to point users toward login
13
+ instead of showing local routing stats or enabled actions.
14
+
6
15
  ## 0.2.0 - 2026-05-26
7
16
 
8
17
  ### Changed
@@ -516,6 +516,7 @@ async function runCodex(argv) {
516
516
  return runRealHarnessDirect("codex", codexBinary, argv, {
517
517
  env: directHarnessEnv(),
518
518
  });
519
+ requireSwitchboardAuth();
519
520
  const observeMode =
520
521
  requestedObserve ||
521
522
  activeState.mode === "observe";
@@ -726,6 +727,7 @@ async function runClaude(argv) {
726
727
  return runRealHarnessDirect("claude", claudeBinary, argv, {
727
728
  env: directClaudeEnv(),
728
729
  });
730
+ requireSwitchboardAuth();
729
731
  const observeMode =
730
732
  requestedObserve ||
731
733
  activeState.mode === "observe";
@@ -2142,6 +2144,7 @@ function renderHarnessEffortChoices(harness, model, value, active = false) {
2142
2144
  }
2143
2145
 
2144
2146
  async function showHarnessSettingsScreen(harness) {
2147
+ requireSwitchboardAuth();
2145
2148
  const config = ensureConfigFile();
2146
2149
  const levels = [...DIFFICULTY_LEVELS].sort((a, b) => b - a);
2147
2150
  const models = harnessModelOptions(harness);
@@ -2301,6 +2304,7 @@ function showDifficulty(argv = []) {
2301
2304
  return;
2302
2305
  }
2303
2306
  if (action !== "set") throw new Error(`Unknown difficulty command: ${action}`);
2307
+ requireSwitchboardAuth();
2304
2308
  const harness = assertHarness(argv.shift());
2305
2309
  const level = Number(argv.shift() || "");
2306
2310
  const model = String(argv.shift() || "").trim();
@@ -2762,6 +2766,13 @@ function authToken() {
2762
2766
  return process.env.SWITCHBOARD_API_TOKEN || readAuth()?.token || "";
2763
2767
  }
2764
2768
 
2769
+ const SIGN_IN_REQUIRED_MESSAGE = "Not logged in. Run `switchboard login` first.";
2770
+
2771
+ function requireSwitchboardAuth() {
2772
+ if (authToken()) return;
2773
+ throw new Error(SIGN_IN_REQUIRED_MESSAGE);
2774
+ }
2775
+
2765
2776
  const ACCOUNT_CACHE_TTL_MS = 30_000;
2766
2777
  const HOME_ACCOUNT_REFRESH_TIMEOUT_MS = 5000;
2767
2778
  let _balanceCache = null;
@@ -3990,13 +4001,15 @@ function switchboardAccountCard() {
3990
4001
  }
3991
4002
 
3992
4003
  function homeMenuOptions(installed, install = installSnapshot()) {
4004
+ const loggedIn = Boolean(authToken());
3993
4005
  const harnessCard = (key, displayName) => {
3994
4006
  const info = install.harnesses[key] || {};
3995
4007
  const present = Boolean(installed[key] || info.realAvailable);
3996
4008
  const installedShim = Boolean(info.installed);
3997
4009
  const active = activeHarnessState(key);
3998
- const startDisabled = active.active;
3999
- const observeDisabled = active.active;
4010
+ const signInDescription = `sign in first to use ${displayName}`;
4011
+ const startDisabled = active.active || !loggedIn;
4012
+ const observeDisabled = active.active || !loggedIn;
4000
4013
 
4001
4014
  let tag, tagTone;
4002
4015
  if (active.mode === "live") {
@@ -4021,12 +4034,20 @@ function homeMenuOptions(installed, install = installSnapshot()) {
4021
4034
  if (installedShim) {
4022
4035
  opts.push({
4023
4036
  type: "card-row",
4024
- content: ` ${dim(`switchboard is ready for ${displayName}`)}`,
4037
+ content: ` ${dim(
4038
+ loggedIn
4039
+ ? `switchboard is ready for ${displayName}`
4040
+ : `sign in before using switchboard for ${displayName}`,
4041
+ )}`,
4025
4042
  });
4026
4043
  } else if (present) {
4027
4044
  opts.push({
4028
4045
  type: "card-row",
4029
- content: ` ${dim(`${displayName} is on your computer but not connected to switchboard yet`)}`,
4046
+ content: ` ${dim(
4047
+ loggedIn
4048
+ ? `${displayName} is on your computer but not connected to switchboard yet`
4049
+ : `${displayName} is on your computer — sign in before connecting switchboard`,
4050
+ )}`,
4030
4051
  });
4031
4052
  } else {
4032
4053
  opts.push({
@@ -4040,38 +4061,50 @@ function homeMenuOptions(installed, install = installSnapshot()) {
4040
4061
  opts.push({
4041
4062
  label: "set up",
4042
4063
  value: `${key}-install`,
4043
- disabled: !present,
4044
- description: present
4045
- ? `start saving money with ${displayName}`
4046
- : `install ${displayName} on your computer first`,
4064
+ disabled: !present || !loggedIn,
4065
+ description: !present
4066
+ ? `install ${displayName} on your computer first`
4067
+ : loggedIn
4068
+ ? `connect switchboard for ${displayName}`
4069
+ : `sign in first to set up ${displayName}`,
4047
4070
  });
4048
4071
  } else {
4049
4072
  opts.push(
4050
4073
  {
4051
4074
  label: "start",
4052
4075
  value: `${key}-start`,
4053
- description: active.active
4054
- ? `close the other window first`
4055
- : `turn on switchboard for ${displayName}`,
4076
+ description: !loggedIn
4077
+ ? signInDescription
4078
+ : active.active
4079
+ ? `close the other window first`
4080
+ : `turn on switchboard for ${displayName}`,
4056
4081
  disabled: startDisabled,
4057
4082
  },
4058
4083
  {
4059
4084
  label: "observe",
4060
4085
  value: `${key}-observe`,
4061
- description: active.active
4062
- ? `close the other window first`
4063
- : `watch ${displayName} without changing anything`,
4086
+ description: !loggedIn
4087
+ ? signInDescription
4088
+ : active.active
4089
+ ? `close the other window first`
4090
+ : `watch ${displayName} without changing anything`,
4064
4091
  disabled: observeDisabled,
4065
4092
  },
4066
4093
  {
4067
4094
  label: "settings",
4068
4095
  value: `${key}-settings`,
4069
- description: `edit ${displayName} difficulty mappings`,
4096
+ description: loggedIn
4097
+ ? `edit ${displayName} difficulty mappings`
4098
+ : `sign in first to edit ${displayName} settings`,
4099
+ disabled: !loggedIn,
4070
4100
  },
4071
4101
  {
4072
4102
  label: "uninstall",
4073
4103
  value: `${key}-uninstall`,
4074
- description: `remove switchboard for ${displayName}`,
4104
+ description: loggedIn
4105
+ ? `remove switchboard for ${displayName}`
4106
+ : `sign in first to change ${displayName} setup`,
4107
+ disabled: !loggedIn,
4075
4108
  },
4076
4109
  );
4077
4110
  }
@@ -4130,6 +4163,7 @@ async function showActiveDashboard(
4130
4163
  activeMode = "live",
4131
4164
  options = {},
4132
4165
  ) {
4166
+ requireSwitchboardAuth();
4133
4167
  const targets = dashboardTargets(argv);
4134
4168
  const claim = claimActiveHarnesses(targets, activeMode);
4135
4169
  if (claim.conflicts.length) {
@@ -4447,6 +4481,7 @@ function showInstall(argv = []) {
4447
4481
  "Switchboard install currently supports macOS and Linux. Windows shims are not available in this release.",
4448
4482
  );
4449
4483
  }
4484
+ requireSwitchboardAuth();
4450
4485
  const noShellRc = argv.includes("--no-shell-rc");
4451
4486
  const forceShellRc = argv.includes("--shell-rc");
4452
4487
  const verbose =
@@ -4480,8 +4515,7 @@ function showInstall(argv = []) {
4480
4515
  );
4481
4516
  }
4482
4517
 
4483
- let shellRc = null;
4484
- if (!noShellRc || forceShellRc) shellRc = ensureShellPathBlock();
4518
+ if (!noShellRc || forceShellRc) ensureShellPathBlock();
4485
4519
 
4486
4520
  console.log(`✓ Switchboard installed`);
4487
4521
  for (const { harness } of installed) {
@@ -4490,14 +4524,6 @@ function showInstall(argv = []) {
4490
4524
  for (const item of skipped) {
4491
4525
  console.log(` ○ ${harnessLabel(item.harness)} not found`);
4492
4526
  }
4493
- console.log("");
4494
- if (noShellRc && !forceShellRc) {
4495
- console.log("Run this once in your shell:");
4496
- console.log(` export PATH=${shellQuote(shimDir())}:$PATH`);
4497
- } else if (shellRc) {
4498
- console.log("Open a new terminal, then use:");
4499
- console.log(` ${installed.map(({ entry }) => entry.command).join(" ")}`);
4500
- }
4501
4527
  ensureProcessPathHasShimDir();
4502
4528
  if (verbose) {
4503
4529
  console.log("");
@@ -4538,12 +4564,6 @@ function showUninstall(argv = []) {
4538
4564
  for (const harness of removed) {
4539
4565
  console.log(` ✓ ${harnessLabel(harness)}`);
4540
4566
  }
4541
- console.log("");
4542
- console.log(
4543
- anyInstalled
4544
- ? "Remaining installed tools still use Switchboard."
4545
- : "Your tools now run normally.",
4546
- );
4547
4567
  if (verbose) {
4548
4568
  console.log("");
4549
4569
  printInstallSummary({ verbose: true });
@@ -5234,18 +5254,7 @@ function renderStatusStrip(
5234
5254
  statLines = [` ${dim("checking account route stats…")}`];
5235
5255
  }
5236
5256
  } else {
5237
- const snapshot = statsSnapshot();
5238
- const totalReq = snapshot.rows.length;
5239
- const hasTraffic = totalReq > 0;
5240
- statLines = hasTraffic
5241
- ? [
5242
- statRow("local requests", compactNumber(totalReq)),
5243
- statRow(
5244
- "local tokens processed",
5245
- compactNumber(snapshot.processedTokens),
5246
- ),
5247
- ]
5248
- : [` ${dim("no routes yet — sign in and start routing")}`];
5257
+ statLines = [` ${dim("sign in to start routing with switchboard")}`];
5249
5258
  }
5250
5259
 
5251
5260
  let identityLine = "";
@@ -6706,11 +6715,13 @@ function routeTaskDisplay(row) {
6706
6715
  }
6707
6716
 
6708
6717
  async function showDashboard(argv, options = {}) {
6718
+ requireSwitchboardAuth();
6709
6719
  const harness = parseHarnessScope(argv);
6710
6720
  console.log(renderDashboard(harness));
6711
6721
  }
6712
6722
 
6713
6723
  async function showDashboardSession(argv, options = {}) {
6724
+ requireSwitchboardAuth();
6714
6725
  const harness = parseHarnessScope(argv);
6715
6726
  const exitOnClose = options.exitOnClose !== false;
6716
6727
  const sessionStart = new Date().toISOString();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "switchboard-fyi",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Local model-routing CLI for Codex and Claude Code.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.switchboard.fyi",