pi-keyrouter 0.2.1 → 0.2.2

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/README.md CHANGED
@@ -110,7 +110,15 @@ Every key switch notifies the user with a Box widget:
110
110
 
111
111
  ## 🔧 Config
112
112
 
113
- `~/.pi/keyrouter.json` (or `<cwd>/.soly/keyrouter.json`, `<cwd>/.pi/keyrouter.json`).
113
+ **`~/.pi/keyrouter.json` only** (user-level, never project-scoped).
114
+
115
+ - Windows: `%USERPROFILE%\.pi\keyrouter.json`
116
+ - macOS/Linux: `~/.pi/keyrouter.json`
117
+
118
+ Config is global because API keys are personal credentials — they do not
119
+ belong inside a project directory. Project-local `keyrouter.json` files are
120
+ **deliberately ignored** (security: prevents a malicious repo from overriding
121
+ your real keys).
114
122
 
115
123
  ```json5
116
124
  {
package/config.ts CHANGED
@@ -2,10 +2,13 @@
2
2
  // config.ts — load key router config from disk
3
3
  // =============================================================================
4
4
  //
5
- // Looks in this order (first hit wins):
6
- // 1. <cwd>/.pi/keyrouter.json — project override
7
- // 2. <cwd>/.soly/keyrouter.json — soly convention
8
- // 3. ~/.pi/keyrouter.json — user-level default
5
+ // Config is GLOBAL (user-level), never project-scoped. API keys are personal
6
+ // credentials that do not belong inside a project directory (risk of leaking
7
+ // via git, shared repos, etc.).
8
+ //
9
+ // Single location: ~/.pi/keyrouter.json
10
+ // - Windows: %USERPROFILE%\.pi\keyrouter.json
11
+ // - macOS/Linux: ~/.pi/keyrouter.json
9
12
  //
10
13
  // Schema:
11
14
  // {
@@ -28,8 +31,6 @@ import * as os from "node:os";
28
31
  import * as path from "node:path";
29
32
  import type { KeyRouterConfig } from "./types.ts";
30
33
 
31
- const CONFIG_FILENAMES = ["keyrouter.json"];
32
-
33
34
  export function defaultConfig(): KeyRouterConfig {
34
35
  return {
35
36
  providers: [],
@@ -38,33 +39,38 @@ export function defaultConfig(): KeyRouterConfig {
38
39
  };
39
40
  }
40
41
 
41
- export function loadConfig(cwd: string, home?: string): KeyRouterConfig {
42
+ /**
43
+ * Resolve the config path. Always under the user profile (~/.pi/), never
44
+ * project-scoped. The `cwd` argument is accepted for API symmetry but
45
+ * ignored — keys are global.
46
+ *
47
+ * @param _cwd ignored — config is always user-level
48
+ * @param home override home dir (for testing)
49
+ */
50
+ export function configPath(_cwd?: string, home?: string): string {
42
51
  const homeDir = home ?? os.homedir();
43
- const candidates: string[] = [];
44
- for (const dir of [
45
- path.join(cwd, ".soly"),
46
- path.join(cwd, ".pi"),
47
- cwd,
48
- path.join(homeDir, ".soly"),
49
- path.join(homeDir, ".pi"),
50
- homeDir,
51
- ]) {
52
- for (const name of CONFIG_FILENAMES) {
53
- candidates.push(path.join(dir, name));
54
- }
55
- }
56
- for (const file of candidates) {
57
- if (fs.existsSync(file)) {
58
- try {
59
- const raw = fs.readFileSync(file, "utf-8");
60
- const parsed = JSON.parse(raw) as Partial<KeyRouterConfig>;
61
- return normalize(parsed);
62
- } catch {
63
- // bad config — fall through to default
64
- }
65
- }
52
+ return path.join(homeDir, ".pi", "keyrouter.json");
53
+ }
54
+
55
+ /**
56
+ * Path displayed in error messages / /keyrouter status so the user can see
57
+ * exactly where we're looking.
58
+ */
59
+ export function configSearchPaths(): string[] {
60
+ return [configPath()];
61
+ }
62
+
63
+ export function loadConfig(_cwd?: string, home?: string): KeyRouterConfig {
64
+ const file = configPath(undefined, home);
65
+ if (!fs.existsSync(file)) return defaultConfig();
66
+ try {
67
+ const raw = fs.readFileSync(file, "utf-8");
68
+ const parsed = JSON.parse(raw) as Partial<KeyRouterConfig>;
69
+ return normalize(parsed);
70
+ } catch {
71
+ // bad config — fall through to default
72
+ return defaultConfig();
66
73
  }
67
- return defaultConfig();
68
74
  }
69
75
 
70
76
  function normalize(input: Partial<KeyRouterConfig>): KeyRouterConfig {
package/index.ts CHANGED
@@ -28,7 +28,7 @@
28
28
  // /reload
29
29
 
30
30
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
31
- import { loadConfig } from "./config.ts";
31
+ import { loadConfig, configPath } from "./config.ts";
32
32
  import {
33
33
  initKeyStates,
34
34
  isAvailable,
@@ -237,8 +237,8 @@ export default function keyRouterExtension(pi: ExtensionAPI): void {
237
237
  }
238
238
  if (!config || runtimes.size === 0) {
239
239
  ctx.ui.notify(
240
- "🔑 keyrouter: not active — no providers in ~/.pi/keyrouter.json " +
241
- "(checked cwd/.soly, cwd/.pi, cwd, ~/.soly, ~/.pi, ~)",
240
+ `🔑 keyrouter: not active — no ~/.pi/keyrouter.json found ` +
241
+ `(expected at ${configPath()}). Config is user-level only, never project-scoped.`,
242
242
  "warning",
243
243
  );
244
244
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-keyrouter",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "API key rotation for pi-coding-agent. Multiple keys per provider, automatic 429/401 fallback, max-retries guard.",
5
5
  "type": "module",
6
6
  "main": "index.ts",