@ddtcorex/dsh-maestro-supervisor 0.5.0 → 0.5.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.
Files changed (2) hide show
  1. package/lib/debug-agent.js +62 -21
  2. package/package.json +1 -1
@@ -193,7 +193,8 @@ async function defaultDryBoot() {
193
193
  const { execSync } = await import('node:child_process');
194
194
  const tmp = execSync('mktemp -d', { encoding: 'utf-8' }).trim();
195
195
  const port = Math.floor(19000 + Math.random() * 1000);
196
- const out = execSync(`timeout 8 bash -c 'DSH_HOME=${tmp} pnpm --dir /home/kai/Work/htdocs/maestro-harness/deepseek-harness dsh web --port ${port} --no-open >${tmp}/dsh.log 2>&1 & pid=\\$!; for i in \\$(seq 1 5); do sleep 1; if curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:${port}/ 2>&1 | grep -q 200; then kill \\$pid 2>/dev/null || true; wait \\$pid 2>/dev/null || true; echo ok; exit 0; fi; done; kill \\$pid 2>/dev/null || true; wait \\$pid 2>/dev/null || true; echo fail; exit 1'`, { encoding: 'utf-8', timeout: 12000 });
196
+ // Use spawn-based dry-boot to avoid nested quoting hell (prev \\$! / \\$(seq) caused syntax error)
197
+ const out = execSync(`timeout 8 bash -c 'DSH_HOME=${tmp} pnpm --dir /home/kai/Work/htdocs/maestro-harness/deepseek-harness dsh web --port ${port} --no-open >${tmp}/dsh.log 2>&1 & pid=$!; for i in $(seq 1 5); do sleep 1; if curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:${port}/ 2>&1 | grep -q 200; then kill $pid 2>/dev/null || true; wait $pid 2>/dev/null || true; echo ok; exit 0; fi; done; kill $pid 2>/dev/null || true; wait $pid 2>/dev/null || true; echo fail; exit 1'`, { encoding: 'utf-8', timeout: 12000 });
197
198
  execSync(`rm -rf ${tmp}`);
198
199
  return out.includes('ok');
199
200
  }
@@ -202,17 +203,17 @@ async function defaultDryBoot() {
202
203
  }
203
204
  }
204
205
  async function defaultFetchLLM(prompt) {
205
- const key = await resolveApiKey();
206
- if (!key)
207
- throw new Error('no api key — set DEEPSEEK_API_KEY or ~/.dsh/.credentials.yaml');
208
- const res = await fetch('https://api.deepseek.com/v1/chat/completions', {
206
+ const cfg = await resolveLLMConfig();
207
+ if (!cfg.key)
208
+ throw new Error('no api key — set DEEPSEEK_API_KEY / OMNI_ROUTE_API_KEY / OPENCODE_GO_API_KEY / OPENAI_API_KEY or ~/.dsh/.credentials.yaml or AI_API_KEY');
209
+ const res = await fetch(cfg.url, {
209
210
  method: 'POST',
210
211
  headers: {
211
212
  'Content-Type': 'application/json',
212
- 'Authorization': `Bearer ${key}`,
213
+ 'Authorization': `Bearer ${cfg.key}`,
213
214
  },
214
215
  body: JSON.stringify({
215
- model: 'deepseek-chat',
216
+ model: cfg.model,
216
217
  temperature: 0.2,
217
218
  messages: [
218
219
  { role: 'system', content: 'You are a systematic-debugging agent for DSH Web resilience. Follow the 4 phases: root cause, pattern analysis, hypothesis, implementation. Always propose minimal single-file fix.' },
@@ -225,25 +226,65 @@ async function defaultFetchLLM(prompt) {
225
226
  const j = await res.json();
226
227
  return j.choices?.[0]?.message?.content ?? JSON.stringify(j);
227
228
  }
228
- async function resolveApiKey() {
229
- if (process.env.DEEPSEEK_API_KEY)
230
- return process.env.DEEPSEEK_API_KEY;
231
- if (process.env.OPENCODE_GO_API_KEY)
232
- return process.env.OPENCODE_GO_API_KEY;
229
+ async function resolveLLMConfig() {
230
+ // Custom AI provider: env overrides, then credentials.yaml, then settings.json, then defaults
231
+ // Url: AI_API_URL / DEEPSEEK_API_URL / OPENAI_API_BASE / OMNI_ROUTE_API_URL -> default deepseek
232
+ const url = process.env.AI_API_URL ??
233
+ process.env.DEAPSEEK_API_URL ??
234
+ process.env.DEEPSEEK_API_URL ??
235
+ process.env.OPENAI_API_BASE ??
236
+ process.env.OMNI_ROUTE_API_URL ??
237
+ process.env.OPENCODE_API_URL ??
238
+ 'https://api.deepseek.com/v1/chat/completions';
239
+ // Model: AI_MODEL / DEEPSEEK_MODEL / settings.json domains.review.model
240
+ let model = process.env.AI_MODEL ?? process.env.DEEPSEEK_MODEL ?? process.env.OPENAI_MODEL ?? 'deepseek-chat';
233
241
  try {
234
242
  const { readFileSync } = await import('node:fs');
235
243
  const { homedir } = await import('node:os');
236
- const p = `${homedir()}/.dsh/.credentials.yaml`;
237
- const content = readFileSync(p, 'utf-8');
238
- const m = content.match(/DEEPSEEK_API_KEY:\s*["']?([^"'\n]+)["']?/);
239
- if (m)
240
- return m[1].trim();
241
- const m2 = content.match(/OPENCODE_GO_API_KEY:\s*["']?([^"'\n]+)["']?/);
242
- if (m2)
243
- return m2[1].trim();
244
+ const settingsPath = `${homedir()}/.dsh/maestro/settings.json`;
245
+ const raw = readFileSync(settingsPath, 'utf-8');
246
+ const j = JSON.parse(raw);
247
+ const m = j?.domains?.review?.model?.model ?? j?.domains?.review?.model;
248
+ if (typeof m === 'string' && !process.env.AI_MODEL && !process.env.DEEPSEEK_MODEL)
249
+ model = m;
250
+ else if (m?.model && typeof m.model === 'string' && !process.env.AI_MODEL)
251
+ model = m.model;
244
252
  }
245
253
  catch { }
246
- return null;
254
+ // Key: AI_API_KEY / DEEPSEEK_API_KEY / OMNI_ROUTE_API_KEY / OPENCODE_GO_API_KEY / OPENAI_API_KEY
255
+ let key = process.env.AI_API_KEY ??
256
+ process.env.DEEPSEEK_API_KEY ??
257
+ process.env.OMNI_ROUTE_API_KEY ??
258
+ process.env.OPENCODE_GO_API_KEY ??
259
+ process.env.OPENAI_API_KEY ??
260
+ null;
261
+ if (!key) {
262
+ try {
263
+ const { readFileSync } = await import('node:fs');
264
+ const { homedir } = await import('node:os');
265
+ const p = `${homedir()}/.dsh/.credentials.yaml`;
266
+ const content = readFileSync(p, 'utf-8');
267
+ const keys = ['AI_API_KEY', 'DEEPSEEK_API_KEY', 'OMNI_ROUTE_API_KEY', 'OPENCODE_GO_API_KEY', 'OPENAI_API_KEY'];
268
+ for (const k of keys) {
269
+ const m = content.match(new RegExp(`${k}:\\s*["']?([^"'\\n]+)["']?`));
270
+ if (m) {
271
+ key = m[1].trim();
272
+ break;
273
+ }
274
+ }
275
+ }
276
+ catch { }
277
+ }
278
+ // If url is base without /v1/chat/completions, append
279
+ let finalUrl = url;
280
+ if (!url.includes('/v1/') && !url.includes('/chat/completions')) {
281
+ finalUrl = url.replace(/\/$/, '') + '/v1/chat/completions';
282
+ }
283
+ return { key, url: finalUrl, model };
284
+ }
285
+ async function resolveApiKey() {
286
+ const cfg = await resolveLLMConfig();
287
+ return cfg.key;
247
288
  }
248
289
  export function _resetDebugAgentForTest() {
249
290
  lastRun = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ddtcorex/dsh-maestro-supervisor",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Supervisor daemon for DSH Web resilience — auto-detect crashes, rollback to LKG, report",
5
5
  "type": "module",
6
6
  "bin": {