pi-llama-cpp 0.1.2 → 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/README.md CHANGED
@@ -5,12 +5,22 @@ A [Pi Coding Agent](https://pi.dev/) extension that integrates with a running [l
5
5
  ## Features
6
6
 
7
7
  - **Auto-detect models** — discovers all models available on your running llama.cpp server
8
- - **Live status indicators** — see which models are loaded, loading, failed, or unloaded with color-coded icons
8
+ - **Live status indicators** — see which models are loaded, loading, failed, sleeping, or unloaded with color-coded icons
9
9
  - **Load / unload / switch** — manage models directly from the Pi command palette
10
10
  - **Multi-model router support** — works with both single-model and multi-model llama.cpp server configurations
11
- - **Image model support** — detects multimodal models automatically
11
+ - **Image capabilities detection** — detects multimodal models automatically
12
12
  - **Flexible URL resolution** — configures the server URL via project config, environment variable, or global settings
13
13
 
14
+ ### Status Indicators
15
+
16
+ | Icon | Status | Description |
17
+ |------|--------|-------------|
18
+ | 🟢 | Loaded | Model is active and ready to use |
19
+ | 🟡 | Loading | Model is currently being loaded |
20
+ | 🔴 | Failed | Model failed to load |
21
+ | 🔵 | Sleeping | Model is loaded but inactive (router mode) |
22
+ | ⚪ | Unloaded | Model is not loaded on the server |
23
+
14
24
  ## Installation
15
25
 
16
26
  This package is a Pi extension. Install it with
@@ -51,15 +61,15 @@ The extension resolves the llama.cpp server URL using the following priority ord
51
61
 
52
62
  ### API Key
53
63
 
54
- If your llama.cpp server requires authentication, use `/login` in Pi, select the "API key" option, and choose the `llama-server` provider.
64
+ If your llama.cpp server requires authentication, use `/login` in Pi, select the "API key" option, and choose the `Llama.cpp` provider from the list.
55
65
 
56
- Alternatively, configure the API key in `~/.pi/agent/auth.json`:
66
+ Alternatively, configure the API key in `~/.pi/agent/auth.json` using the provider ID `llama-server`:
57
67
 
58
68
  ```json
59
69
  {
60
70
  "llama-server": {
61
- "type": "bearer",
62
- "key": "your-api-key-here"
71
+ "type": "api_key",
72
+ "key": "<your-api-key-here>"
63
73
  }
64
74
  }
65
75
  ```
@@ -68,15 +78,17 @@ Alternatively, configure the API key in `~/.pi/agent/auth.json`:
68
78
 
69
79
  ### Prerequisites
70
80
 
71
- Make sure your llama.cpp server is running with the appropriate flags. For multi-model support (model router), start the server with:
81
+ Make sure your llama.cpp server is running with the appropriate flags.
82
+
83
+ - For multi-model support (model router), start the server with:
72
84
 
73
85
  ```bash
74
- llama-server --models-preset path/to/presets.ini
86
+ llama-server --models-preset path/to/presets.ini ...
75
87
  ```
76
88
 
77
- (You can use both `--fit-ctx` and `--ctx-size` in the preset the extension checks both.)
89
+ The extension reads the context size from the preset file using the `ctx-size` and/or `fit-ctx` keys.
78
90
 
79
- For single-model mode, a standard invocation works:
91
+ - For single-model mode, start the server with:
80
92
 
81
93
  ```bash
82
94
  llama-server --model path/to/model.gguf --ctx-size 128000 ...
@@ -86,7 +98,9 @@ llama-server --model path/to/model.gguf --ctx-size 128000 ...
86
98
 
87
99
  | Command | Description |
88
100
  | --------- | ------------------------------------------------------------------------------------------ |
89
- | `/models` | Browse llama-server models with live status. Select a model to load, switch, or unload it. |
101
+ | `/models` | Browse your models with live status. Select a model to load, switch, or unload it. |
102
+
103
+ > **Note:** When the llama.cpp server is unreachable, `/models` is still available but displays an error notification with the configured server URL.
90
104
 
91
105
  ### Model Actions
92
106
 
@@ -95,10 +109,15 @@ When browsing models via the `/models` command, you can:
95
109
  - **Load & switch** — Load an unloaded model and switch to it
96
110
  - **Switch model** — Switch to a model that is already loaded
97
111
  - **Unload** — Unload a loaded model to free memory
112
+ - **Retry** — Retry loading a failed model
113
+ - **Info** — View model details (ID, capabilities, context size)
114
+ - **Cancel** — Cancel the current operation
115
+
116
+ > **Note:** In single-model mode, only **Info** and **Cancel** are available, since there is only one model loaded on the server.
98
117
 
99
118
  ### Model Selection Event
100
119
 
101
- When Pi switches models (e.g., via `model_select`), the extension automatically loads the selected model on the llama.cpp server. This keeps the server in sync with the active model in Pi.
120
+ When Pi switches models (via `model_select`), the extension automatically loads the selected model on the llama.cpp server. This keeps the server in sync with the active model in Pi.
102
121
 
103
122
  ### Model Configuration
104
123
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-llama-cpp",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "Pi extension for llama.cpp integration. Supports both router and single modes",
5
5
  "keywords": [
6
6
  "pi",
@@ -20,7 +20,19 @@
20
20
  },
21
21
  "pi": {
22
22
  "extensions": [
23
- "./index.ts"
23
+ "./src/index.ts"
24
24
  ]
25
+ },
26
+ "prettier": {
27
+ "plugins": [
28
+ "prettier-plugin-organize-imports"
29
+ ]
30
+ },
31
+ "peerDependencies": {
32
+ "@mariozechner/pi-coding-agent": "*"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^25.6.0",
36
+ "prettier-plugin-organize-imports": "^4.3.0"
25
37
  }
26
38
  }
package/src/constants.ts CHANGED
@@ -1,7 +1,12 @@
1
+ /**
2
+ * This provider's id
3
+ */
4
+ export const PROVIDER_ID = "llama-server";
5
+
1
6
  /**
2
7
  * This provider's name
3
8
  */
4
- export const PROVIDER_NAME = "llama-server";
9
+ export const PROVIDER_NAME = "Llama.cpp";
5
10
 
6
11
  /**
7
12
  * The default URL if the resolver couldn't find it
@@ -1,7 +1,9 @@
1
1
  /** The possible actions for the /models command */
2
- export enum Actions {
2
+ export enum Action {
3
3
  SWITCH = "Switch model",
4
+ RETRY = "Retry",
4
5
  LOAD = "Load & switch",
5
6
  UNLOAD = "Unload",
7
+ INFO = "Info",
6
8
  CANCEL = "Cancel",
7
9
  }
@@ -0,0 +1,5 @@
1
+ /** The possible modes of llama-server models */
2
+ export enum Mode {
3
+ ROUTER = "router",
4
+ SINGLE = "single",
5
+ }
@@ -3,5 +3,6 @@ export enum Status {
3
3
  LOADED = "loaded",
4
4
  LOADING = "loading",
5
5
  FAILED = "failed",
6
+ SLEEPING = "sleeping",
6
7
  UNLOADED = "unloaded",
7
8
  }
package/src/events.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ExtensionContext } from "@mariozechner/pi-coding-agent";
2
2
  import { PROVIDER_NAME } from "./constants";
3
- import { listModels } from "./tools/retriever";
4
3
  import { ModelSelectEvent } from "./interfaces/IModelSelectEvent";
4
+ import { listModels } from "./tools/retriever";
5
5
 
6
6
  /**
7
7
  * Reacts to a new model event triggered by Pi
package/src/handlers.ts CHANGED
@@ -2,50 +2,116 @@ import type {
2
2
  ExtensionAPI,
3
3
  ExtensionCommandContext,
4
4
  } from "@mariozechner/pi-coding-agent";
5
+ import { PROVIDER_ID, PROVIDER_NAME } from "./constants";
6
+ import { Action } from "./enums/action";
7
+ import { Mode } from "./enums/mode";
5
8
  import { Status } from "./enums/status";
6
9
  import { BaseModel } from "./models/baseModel";
7
- import { Actions } from "./enums/actions";
8
- import { PROVIDER_NAME } from "./constants";
9
10
 
10
11
  /**
11
- * Defines a handler when llama-server is running
12
+ * Select a model from the list. Returns null if user cancels.
13
+ *
12
14
  * @param ctx Pi context
13
- * @returns The action and model, if detected
15
+ * @param models A list of models
16
+ * @returns The selected model
14
17
  */
15
- const modelSelectionHandler = async (
18
+ const selectModel = async (
16
19
  ctx: ExtensionCommandContext,
17
20
  models: BaseModel[],
18
- ): Promise<{ action: Actions; model: BaseModel } | null> => {
19
- // Setup the labels
21
+ ): Promise<BaseModel | null> => {
20
22
  const labels = await Promise.all(models.map((m) => m.getLabel()));
21
-
22
- // Detect the selected model
23
23
  const choice = await ctx.ui.select(`${PROVIDER_NAME} models:`, labels);
24
24
  if (!choice) return null;
25
-
26
25
  const idx = labels.indexOf(choice);
27
- const model = models[idx];
28
-
29
- // Define the actions that the user can do
30
- const allActions = {
31
- [Status.LOADED]: [Actions.UNLOAD, Actions.CANCEL],
32
- [Status.LOADING]: [Actions.CANCEL],
33
- [Status.FAILED]: [Actions.SWITCH, Actions.CANCEL],
34
- [Status.UNLOADED]: [Actions.SWITCH, Actions.CANCEL],
26
+ return models[idx];
27
+ };
28
+
29
+ /**
30
+ * Get available actions for a model based on its mode and status.
31
+ *
32
+ * @param model The selected model
33
+ * @returns
34
+ */
35
+ const getActionsForModel = async (model: BaseModel): Promise<Array<Action>> => {
36
+ const routerModeActions: Record<Status, Array<Action>> = {
37
+ [Status.LOADED]: [Action.SWITCH, Action.UNLOAD, Action.INFO, Action.CANCEL],
38
+ [Status.LOADING]: [Action.CANCEL],
39
+ [Status.FAILED]: [Action.RETRY, Action.CANCEL],
40
+ [Status.SLEEPING]: [Action.UNLOAD, Action.INFO, Action.CANCEL],
41
+ [Status.UNLOADED]: [Action.LOAD, Action.CANCEL],
42
+ };
43
+
44
+ const singleModeActions: Record<Status, Array<Action>> = {
45
+ [Status.LOADED]: [Action.INFO, Action.CANCEL],
46
+ [Status.LOADING]: [Action.CANCEL],
47
+ [Status.FAILED]: [Action.CANCEL],
48
+ [Status.SLEEPING]: [Action.CANCEL],
49
+ [Status.UNLOADED]: [Action.CANCEL],
35
50
  };
36
51
 
52
+ const allActions =
53
+ model.mode === Mode.ROUTER ? routerModeActions : singleModeActions;
54
+
37
55
  const status = await model.getStatus();
38
- const actions = allActions[status];
56
+ return allActions[status];
57
+ };
58
+
59
+ /**
60
+ * Selects an action for a model.
61
+ *
62
+ * @param ctx Pi context
63
+ * @param model The selected model
64
+ * @param actions Possible actions to execute
65
+ * @returns The action, or null if user cancels
66
+ */
67
+ const selectAction = async (
68
+ ctx: ExtensionCommandContext,
69
+ model: BaseModel,
70
+ actions: Array<Action>,
71
+ ): Promise<Action | null> => {
72
+ const labels = actions.map((a) => String(a));
73
+ const choice = await ctx.ui.select(`${model.name}`, labels);
74
+ if (!choice) return null;
75
+
76
+ const idx = labels.indexOf(choice);
77
+ return actions[idx];
78
+ };
79
+
80
+ /**
81
+ * Handles the menu for model selection
82
+ * Loops: select model → select action → handle action.
83
+ *
84
+ * Escape on actions menu goes back to model selection.
85
+ * Escape on model selection exits.
86
+ *
87
+ * @param ctx Pi context
88
+ * @returns The action and model, if detected
89
+ */
90
+ const modelSelectionHandler = async (
91
+ ctx: ExtensionCommandContext,
92
+ models: BaseModel[],
93
+ ): Promise<{ action: Action; model: BaseModel } | null> => {
94
+ while (true) {
95
+ // Select the model
96
+ const model = await selectModel(ctx, models);
97
+ if (!model) return null;
39
98
 
40
- const action = (await ctx.ui.select(`${model.id}`, actions)) as Actions;
41
- if (!action || action === Actions.CANCEL) return null;
99
+ // Select the action
100
+ const actions = await getActionsForModel(model);
101
+ const action = await selectAction(ctx, model, actions);
102
+ if (action === null) {
103
+ // Escape key pressed => back to model selection
104
+ continue;
105
+ }
42
106
 
43
- // Send the selected action with the corresponding model
44
- return { action, model };
107
+ // Return the selected action and model
108
+ return { action, model };
109
+ }
45
110
  };
46
111
 
47
112
  /**
48
113
  * Handles the /models command
114
+ *
49
115
  * @param ctx The context used by Pi
50
116
  * @param pi The Pi extension
51
117
  */
@@ -60,25 +126,40 @@ export const modelsCommandHandler = async (
60
126
  // Detect the model
61
127
  const { action, model } = event;
62
128
 
63
- // Execute the selected action
64
- if (action === Actions.UNLOAD) {
129
+ // Action: Cancel
130
+ if (!action || action === Action.CANCEL) return;
131
+
132
+ // Action: Info
133
+ if (action === Action.INFO) {
134
+ const info = await model.getInfo();
135
+ ctx.ui.notify(`${info}`, "info");
136
+ return;
137
+ }
138
+
139
+ // Action: Unload
140
+ if (action === Action.UNLOAD) {
65
141
  await model.unload();
66
- ctx.ui.notify(`Unloaded ${model.id}`, "info");
67
- } else {
68
- const status = await model.getStatus();
69
- if (status === Status.LOADED) return;
142
+ ctx.ui.notify(`Unloaded ${model.name}`, "info");
143
+ return;
144
+ }
70
145
 
71
- ctx.ui.notify(`Loading ${model.id}...`, "info");
146
+ // Actions: Load/Switch/Retry
147
+ const loadActions = [Action.LOAD, Action.SWITCH, Action.RETRY];
148
+ if (loadActions.includes(action)) {
149
+ ctx.ui.notify(`Loading ${model.name}...`, "info");
72
150
 
73
- // Load the model without blocking the UI
74
151
  const onSuccess = async () => {
75
- const piModel = ctx.modelRegistry.find(PROVIDER_NAME, model.id);
152
+ const piModel = ctx.modelRegistry.find(PROVIDER_ID, model.id);
76
153
  if (!piModel) {
77
- throw new Error(`Cannot find model ${model.id} in pi registry`);
154
+ throw new Error(`Cannot find model ${model.name} in pi registry`);
155
+ }
156
+
157
+ if ((await model.getStatus()) === Status.FAILED) {
158
+ throw new Error("Failed to load model");
78
159
  }
79
160
 
80
161
  await pi.setModel(piModel);
81
- ctx.ui.notify(`Model ${model.id} ready`, "info");
162
+ ctx.ui.notify(`Model ${model.name} ready`, "info");
82
163
  };
83
164
 
84
165
  const onFailure = (err: any) => {
@@ -86,6 +167,7 @@ export const modelsCommandHandler = async (
86
167
  ctx.ui.notify(message, "error");
87
168
  };
88
169
 
170
+ // Load the model without blocking the UI
89
171
  model.load().then(onSuccess).catch(onFailure);
90
172
  }
91
173
  };
@@ -2,11 +2,11 @@ import type {
2
2
  ExtensionAPI,
3
3
  ExtensionCommandContext,
4
4
  } from "@mariozechner/pi-coding-agent";
5
- import { modelsCommandHandler } from "./src/handlers";
6
- import { isServerReady, listModels } from "./src/tools/retriever";
7
- import { resolveApiKey, resolveUrl } from "./src/tools/resolver";
8
- import { PROVIDER_NAME } from "./src/constants";
9
- import { onModelSelect } from "./src/events";
5
+ import { PROVIDER_ID, PROVIDER_NAME } from "./constants";
6
+ import { onModelSelect } from "./events";
7
+ import { modelsCommandHandler } from "./handlers";
8
+ import { resolveApiKey, resolveUrl } from "./tools/resolver";
9
+ import { isServerReady, listModels } from "./tools/retriever";
10
10
 
11
11
  export default async function (pi: ExtensionAPI) {
12
12
  // Command registration
@@ -36,7 +36,8 @@ export default async function (pi: ExtensionAPI) {
36
36
  });
37
37
 
38
38
  // Provider registration
39
- pi.registerProvider(PROVIDER_NAME, {
39
+ pi.registerProvider(PROVIDER_ID, {
40
+ name: PROVIDER_NAME,
40
41
  baseUrl: `${url}/v1`,
41
42
  api: "openai-completions",
42
43
  apiKey: await resolveApiKey(),
@@ -1,8 +1,10 @@
1
1
  import { PROVIDER_NAME } from "../constants";
2
2
 
3
+ export interface IAuth {
4
+ type: string;
5
+ key: string;
6
+ }
7
+
3
8
  export interface IAuthFile {
4
- [PROVIDER_NAME]: {
5
- type: string;
6
- key: string;
7
- };
9
+ [PROVIDER_NAME]: IAuth;
8
10
  }
@@ -1,3 +1,11 @@
1
+ interface IRouterModelStatus {
2
+ value: string;
3
+ args: string[];
4
+ preset: string;
5
+ exit_code?: number;
6
+ failed?: boolean;
7
+ }
8
+
1
9
  export interface IRouterModel {
2
10
  id: string;
3
11
  aliases?: string[];
@@ -5,5 +13,5 @@ export interface IRouterModel {
5
13
  object: string;
6
14
  owned_by: string;
7
15
  created: number;
8
- status: { value: string; args: string[] };
16
+ status: IRouterModelStatus;
9
17
  }
@@ -1,5 +1,6 @@
1
1
  import type { ProviderModelConfig } from "@mariozechner/pi-coding-agent";
2
2
  import { MAX_TOKENS, POLLING_INTERVAL, POLLING_TIMEOUT } from "../constants";
3
+ import { Mode } from "../enums/mode";
3
4
  import { Status } from "../enums/status";
4
5
  import { rpc } from "../tools/retriever";
5
6
 
@@ -8,6 +9,7 @@ export abstract class BaseModel {
8
9
  loaded: Status.LOADED,
9
10
  loading: Status.LOADING,
10
11
  failed: Status.FAILED,
12
+ sleeping: Status.SLEEPING,
11
13
  unloaded: Status.UNLOADED,
12
14
  };
13
15
 
@@ -15,9 +17,12 @@ export abstract class BaseModel {
15
17
  [Status.LOADED]: "🟢",
16
18
  [Status.LOADING]: "🟡",
17
19
  [Status.FAILED]: "🔴",
20
+ [Status.SLEEPING]: "🔵",
18
21
  [Status.UNLOADED]: "⚪",
19
22
  };
20
23
 
24
+ abstract get mode(): Mode;
25
+
21
26
  abstract get id(): string;
22
27
 
23
28
  abstract get name(): string;
@@ -43,13 +48,31 @@ export abstract class BaseModel {
43
48
  abstract getContextSize(): Promise<number>;
44
49
 
45
50
  /**
46
- * Returns the corresponding label of our load status
51
+ * Sets up a label for the model selection screen
52
+ * @returns A label structured as "<icon> <name>"
47
53
  */
48
54
  async getLabel(): Promise<string> {
49
55
  const status = await this.getStatus();
50
56
  return `${this.labelIcons[status]} ${this.name}`;
51
57
  }
52
58
 
59
+ /**
60
+ * Returns a human-readable information about the model
61
+ * @returns A string with the model information
62
+ */
63
+ async getInfo(): Promise<string> {
64
+ const messages = [
65
+ `ID : ${this.id}`,
66
+ `Model : ${this.name}`,
67
+ `Reasoning : ${this.reasoning}`,
68
+ `Capabilities : ${this.capabilities.join(", ")}`,
69
+ `Context size : ${await this.getContextSize()}`,
70
+ ];
71
+
72
+ const response = `${messages.join("\n")}\n`;
73
+ return response;
74
+ }
75
+
53
76
  /**
54
77
  * Converts the llama-server model into a configuration object used by Pi
55
78
  * @returns A Pi configuration object
@@ -1,7 +1,8 @@
1
- import { IRouterModel } from "../interfaces/IRouterModel";
2
1
  import { DEFAULT_CTX } from "../constants";
3
- import { rpc } from "../tools/retriever";
2
+ import { Mode } from "../enums/mode";
4
3
  import { Status } from "../enums/status";
4
+ import { IRouterModel } from "../interfaces/IRouterModel";
5
+ import { rpc } from "../tools/retriever";
5
6
  import { BaseModel } from "./baseModel";
6
7
 
7
8
  export class RouterModel extends BaseModel {
@@ -9,6 +10,10 @@ export class RouterModel extends BaseModel {
9
10
  super();
10
11
  }
11
12
 
13
+ get mode(): Mode {
14
+ return Mode.ROUTER;
15
+ }
16
+
12
17
  get id(): string {
13
18
  return this.model.id;
14
19
  }
@@ -25,12 +30,16 @@ export class RouterModel extends BaseModel {
25
30
  async getStatus(): Promise<Status> {
26
31
  const { data } = await rpc<{ data: IRouterModel[] }>("/models");
27
32
  const model = data.find((m) => m.id === this.id);
28
- if (!model) return Status.UNLOADED;
33
+ if (!model) return Status.FAILED;
34
+
35
+ const status = this.statusMapper[model.status.value];
36
+ if (status === Status.UNLOADED) {
37
+ if (this.model.status.failed) return Status.FAILED;
29
38
 
30
- const response = this.statusMapper[model.status.value];
31
- if (!response) return Status.UNLOADED;
39
+ return Status.UNLOADED;
40
+ }
32
41
 
33
- return response;
42
+ return status;
34
43
  }
35
44
 
36
45
  async getContextSize(): Promise<number> {
@@ -1,7 +1,8 @@
1
- import { ISingleModel } from "../interfaces/ISingleModel";
2
1
  import { DEFAULT_CTX } from "../constants";
3
- import { rpc } from "../tools/retriever";
2
+ import { Mode } from "../enums/mode";
4
3
  import { Status } from "../enums/status";
4
+ import { ISingleModel } from "../interfaces/ISingleModel";
5
+ import { rpc } from "../tools/retriever";
5
6
  import { BaseModel } from "./baseModel";
6
7
 
7
8
  export class SingleModel extends BaseModel {
@@ -9,6 +10,10 @@ export class SingleModel extends BaseModel {
9
10
  super();
10
11
  }
11
12
 
13
+ get mode(): Mode {
14
+ return Mode.SINGLE;
15
+ }
16
+
12
17
  get id(): string {
13
18
  return this.model.name;
14
19
  }
@@ -1,7 +1,7 @@
1
- import { DEFAULT_LLAMA_SERVER_URL, PROVIDER_NAME } from "../constants";
2
- import { access, readFile, constants } from "node:fs/promises";
1
+ import { access, constants, readFile } from "node:fs/promises";
3
2
  import { join } from "node:path";
4
- import { IAuthFile } from "../interfaces/IAuthFile";
3
+ import { DEFAULT_LLAMA_SERVER_URL, PROVIDER_ID } from "../constants";
4
+ import { IAuth, IAuthFile } from "../interfaces/IAuthFile";
5
5
 
6
6
  // The URL is detected once, to reuse forever
7
7
  let resolvedUrl: string | undefined;
@@ -42,10 +42,10 @@ const readContents = async <T>(filePath: string): Promise<T | null> => {
42
42
  * @param key Key to extract from the parsed JSON
43
43
  * @returns The string value, or null if file/key missing or invalid
44
44
  */
45
- const readConfigValue = async <T>(
45
+ const readConfigValue = async <T, U>(
46
46
  filePath: string,
47
47
  key: string,
48
- ): Promise<string | null> => {
48
+ ): Promise<U> => {
49
49
  const cfg = await readContents<T>(filePath);
50
50
  return (cfg as Record<string, any>)?.[key] || null;
51
51
  };
@@ -60,8 +60,11 @@ export const resolveApiKey = async (): Promise<string> => {
60
60
  const authPath = join(process.env.HOME || ".", ".pi", "agent", "auth.json");
61
61
  if (!(await fileExists(authPath))) return placeholder;
62
62
 
63
- const response = await readConfigValue<IAuthFile>(authPath, PROVIDER_NAME);
64
- return response ?? placeholder;
63
+ const cfg = await readConfigValue<IAuthFile, IAuth | null>(
64
+ authPath,
65
+ PROVIDER_ID,
66
+ );
67
+ return cfg?.key ?? placeholder;
65
68
  };
66
69
 
67
70
  /**
@@ -77,7 +80,11 @@ const resolveGlobalUrl = async (): Promise<string | null> => {
77
80
  );
78
81
 
79
82
  if (!(await fileExists(globalPath))) return null;
80
- return readConfigValue<Record<string, string>>(globalPath, "llamaServerUrl");
83
+
84
+ return readConfigValue<Record<string, string>, string>(
85
+ globalPath,
86
+ "llamaServerUrl",
87
+ );
81
88
  };
82
89
 
83
90
  /**
@@ -89,7 +96,7 @@ const resolveProjectUrl = async (cwd: string): Promise<string | null> => {
89
96
  const projectPath = join(cwd, ".pi", "llama-server.json");
90
97
 
91
98
  if (!(await fileExists(projectPath))) return null;
92
- return readConfigValue<Record<string, string>>(projectPath, "url");
99
+ return readConfigValue<Record<string, string>, string>(projectPath, "url");
93
100
  };
94
101
 
95
102
  /**
@@ -1,8 +1,8 @@
1
- import { ISingleModel } from "../interfaces/ISingleModel";
2
1
  import { IRouterModel } from "../interfaces/IRouterModel";
3
- import { SingleModel } from "../models/singleModel";
4
- import { RouterModel } from "../models/routerModel";
2
+ import { ISingleModel } from "../interfaces/ISingleModel";
5
3
  import { BaseModel } from "../models/baseModel";
4
+ import { RouterModel } from "../models/routerModel";
5
+ import { SingleModel } from "../models/singleModel";
6
6
  import { resolveApiKey, resolveUrl } from "./resolver";
7
7
 
8
8
  /**
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "types": ["node"]
11
+ },
12
+ "include": ["src/**/*.ts"]
13
+ }