routstrd 0.4.0 → 0.4.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.
@@ -170,6 +170,22 @@ async function startDaemonUnlocked(
170
170
 
171
171
  proc.unref();
172
172
 
173
+ // The daemon must outlive a successful start, but a start we give up on must not
174
+ // leave a detached process holding the wallet locks. `detached` makes it a group leader.
175
+ const killSpawnedDaemon = (): void => {
176
+ try {
177
+ process.kill(-proc.pid, "SIGTERM");
178
+ } catch {
179
+ // Already exited.
180
+ }
181
+ };
182
+ const abandonOnSignal = (): void => {
183
+ killSpawnedDaemon();
184
+ process.exit(1);
185
+ };
186
+ process.once("SIGINT", abandonOnSignal);
187
+ process.once("SIGTERM", abandonOnSignal);
188
+
173
189
  let exitCode: number | null = null;
174
190
  proc.exited.then((code) => {
175
191
  exitCode = code;
@@ -212,6 +228,8 @@ async function startDaemonUnlocked(
212
228
  }
213
229
 
214
230
  if (await healthyDaemonHost(port, host)) {
231
+ process.off("SIGINT", abandonOnSignal);
232
+ process.off("SIGTERM", abandonOnSignal);
215
233
  printStartupProgress(progressLogOffset);
216
234
  console.log(
217
235
  `Routstr daemon started (PID: ${proc.pid}, ${formatElapsed(Date.now() - startedAt)}).`,
@@ -220,8 +238,10 @@ async function startDaemonUnlocked(
220
238
  }
221
239
  }
222
240
 
241
+ killSpawnedDaemon();
223
242
  throw new Error(
224
- `Daemon failed to start within ${Math.round(startupTimeoutMs / 1000)} seconds. Check logs in ${LOGS_DIR}`,
243
+ `Daemon did not become healthy within ${Math.round(startupTimeoutMs / 1000)} seconds; ` +
244
+ `the incomplete daemon (PID ${proc.pid}) was stopped. Check logs in ${LOGS_DIR}`,
225
245
  );
226
246
  }
227
247
 
@@ -66,7 +66,7 @@ other_setting: true
66
66
  });
67
67
  });
68
68
 
69
- it("keeps an existing Routstr provider unchanged without duplicating it", () => {
69
+ it("updates an existing Routstr provider in place without duplicating it", () => {
70
70
  const existing = `custom_providers:
71
71
  - name: Routstr (old-host:8008)
72
72
  base_url: http://old-host:8008/v1
@@ -83,14 +83,51 @@ other_setting: true
83
83
 
84
84
  expect(routstrProviders).toHaveLength(1);
85
85
  expect(routstrProviders[0]).toEqual({
86
- name: "Routstr (old-host:8008)",
87
- base_url: "http://old-host:8008/v1",
88
- api_key: "old-key",
89
- model: "old-model",
86
+ name: "Routstr (localhost:8008)",
87
+ base_url: ROUTSTR.baseUrl,
88
+ api_key: ROUTSTR.apiKey,
89
+ model: ROUTSTR.defaultModel,
90
90
  });
91
91
  expect(twice).toBe(once);
92
92
  });
93
93
 
94
+ it("repoints model.provider when the Routstr provider is renamed", () => {
95
+ const existing = `model:
96
+ default: old-model
97
+ provider: custom:routstr-(old-host:8008)
98
+
99
+ custom_providers:
100
+ - name: Routstr (old-host:8008)
101
+ base_url: http://old-host:8008/v1
102
+ api_key: old-key
103
+ model: old-model
104
+ `;
105
+
106
+ const merged = parse(mergeHermesConfig(existing, ROUTSTR));
107
+
108
+ expect(merged.model).toEqual({
109
+ default: "old-model",
110
+ provider: "custom:routstr-(localhost:8008)",
111
+ });
112
+ });
113
+
114
+ it("leaves model.provider alone when it does not reference the Routstr provider", () => {
115
+ const existing = `model:
116
+ default: user-model
117
+ provider: custom:user-provider
118
+
119
+ custom_providers:
120
+ - name: Routstr (old-host:8008)
121
+ base_url: http://old-host:8008/v1
122
+ api_key: old-key
123
+ model: old-model
124
+ `;
125
+
126
+ const merged = parse(mergeHermesConfig(existing, ROUTSTR));
127
+
128
+ expect(merged.model.provider).toBe("custom:user-provider");
129
+ });
130
+
94
131
  it("rejects malformed YAML instead of replacing it", () => {
95
132
  expect(() => mergeHermesConfig("model: [unterminated", ROUTSTR)).toThrow();
96
133
  });