chorus-codes 0.7.0 → 0.7.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/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/cache/.previewinfo +1 -1
- package/.next/cache/.rscinfo +1 -1
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/new.html +1 -1
- package/.next/server/app/new.rsc +1 -1
- package/.next/server/app/new.segments/_full.segment.rsc +1 -1
- package/.next/server/app/new.segments/_head.segment.rsc +1 -1
- package/.next/server/app/new.segments/_index.segment.rsc +1 -1
- package/.next/server/app/new.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/new.segments/new/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/new.segments/new.segment.rsc +1 -1
- package/.next/server/app/onboarding.html +1 -1
- package/.next/server/app/onboarding.rsc +1 -1
- package/.next/server/app/onboarding.segments/_full.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/_head.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/_index.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/onboarding/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/onboarding.segments/onboarding.segment.rsc +1 -1
- package/.next/server/app/personas.html +1 -1
- package/.next/server/app/personas.rsc +1 -1
- package/.next/server/app/personas.segments/_full.segment.rsc +1 -1
- package/.next/server/app/personas.segments/_head.segment.rsc +1 -1
- package/.next/server/app/personas.segments/_index.segment.rsc +1 -1
- package/.next/server/app/personas.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/personas.segments/personas/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/personas.segments/personas.segment.rsc +1 -1
- package/.next/server/app/settings.html +1 -1
- package/.next/server/app/settings.rsc +1 -1
- package/.next/server/app/settings.segments/_full.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/server/app/templates.html +1 -1
- package/.next/server/app/templates.rsc +1 -1
- package/.next/server/app/templates.segments/_full.segment.rsc +1 -1
- package/.next/server/app/templates.segments/_head.segment.rsc +1 -1
- package/.next/server/app/templates.segments/_index.segment.rsc +1 -1
- package/.next/server/app/templates.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/templates.segments/templates/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/templates.segments/templates.segment.rsc +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/trace +1 -1
- package/.next/trace-build +1 -1
- package/README.md +38 -15
- package/dist/cli/commands/doctor.js +116 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/init.js +211 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/start.js +298 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.js +54 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.js +97 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/connect.js +108 -0
- package/dist/cli/connect.js.map +1 -0
- package/dist/cli/index.js +99 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/port-utils.js +260 -0
- package/dist/cli/port-utils.js.map +1 -0
- package/dist/cli/runtime-env.js +60 -0
- package/dist/cli/runtime-env.js.map +1 -0
- package/dist/cli/shared.js +54 -0
- package/dist/cli/shared.js.map +1 -0
- package/dist/cli/ui.js +60 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/daemon/agents/claude.js +98 -0
- package/dist/daemon/agents/claude.js.map +1 -0
- package/dist/daemon/agents/codex.js +160 -0
- package/dist/daemon/agents/codex.js.map +1 -0
- package/dist/daemon/agents/gemini.js +111 -0
- package/dist/daemon/agents/gemini.js.map +1 -0
- package/dist/daemon/agents/index.js +59 -0
- package/dist/daemon/agents/index.js.map +1 -0
- package/dist/daemon/agents/kimi.js +206 -0
- package/dist/daemon/agents/kimi.js.map +1 -0
- package/dist/daemon/agents/opencode.js +228 -0
- package/dist/daemon/agents/opencode.js.map +1 -0
- package/dist/daemon/agents/openrouter.js +274 -0
- package/dist/daemon/agents/openrouter.js.map +1 -0
- package/dist/daemon/agents/parsers/claude.js +63 -0
- package/dist/daemon/agents/parsers/claude.js.map +1 -0
- package/dist/daemon/agents/parsers/codex.js +51 -0
- package/dist/daemon/agents/parsers/codex.js.map +1 -0
- package/dist/daemon/agents/parsers/gemini.js +144 -0
- package/dist/daemon/agents/parsers/gemini.js.map +1 -0
- package/dist/daemon/agents/parsers/index.js +31 -0
- package/dist/daemon/agents/parsers/index.js.map +1 -0
- package/dist/daemon/agents/parsers/kimi.js +8 -0
- package/dist/daemon/agents/parsers/kimi.js.map +1 -0
- package/dist/daemon/agents/parsers/opencode.js +105 -0
- package/dist/daemon/agents/parsers/opencode.js.map +1 -0
- package/dist/daemon/agents/parsers/openrouter.js +69 -0
- package/dist/daemon/agents/parsers/openrouter.js.map +1 -0
- package/dist/daemon/agents/parsers/shared.js +17 -0
- package/dist/daemon/agents/parsers/shared.js.map +1 -0
- package/dist/daemon/agents/preflight.js +83 -0
- package/dist/daemon/agents/preflight.js.map +1 -0
- package/dist/daemon/agents/quote.js +45 -0
- package/dist/daemon/agents/quote.js.map +1 -0
- package/dist/daemon/agents/sandbox-guard.js +69 -0
- package/dist/daemon/agents/sandbox-guard.js.map +1 -0
- package/dist/daemon/agents/types.js +6 -0
- package/dist/daemon/agents/types.js.map +1 -0
- package/dist/daemon/api-response.js +65 -0
- package/dist/daemon/api-response.js.map +1 -0
- package/dist/daemon/error-detector.js +329 -0
- package/dist/daemon/error-detector.js.map +1 -0
- package/dist/daemon/headless.js +533 -0
- package/dist/daemon/headless.js.map +1 -0
- package/dist/daemon/index.js +333 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/openrouter.js +192 -0
- package/dist/daemon/openrouter.js.map +1 -0
- package/dist/daemon/orchestrators/claude.js +163 -0
- package/dist/daemon/orchestrators/claude.js.map +1 -0
- package/dist/daemon/orchestrators/codex.js +101 -0
- package/dist/daemon/orchestrators/codex.js.map +1 -0
- package/dist/daemon/orchestrators/cursor-windsurf.js +118 -0
- package/dist/daemon/orchestrators/cursor-windsurf.js.map +1 -0
- package/dist/daemon/orchestrators/gemini.js +108 -0
- package/dist/daemon/orchestrators/gemini.js.map +1 -0
- package/dist/daemon/orchestrators/index.js +90 -0
- package/dist/daemon/orchestrators/index.js.map +1 -0
- package/dist/daemon/orchestrators/kimi.js +108 -0
- package/dist/daemon/orchestrators/kimi.js.map +1 -0
- package/dist/daemon/orchestrators/opencode.js +152 -0
- package/dist/daemon/orchestrators/opencode.js.map +1 -0
- package/dist/daemon/orchestrators/shared.js +60 -0
- package/dist/daemon/orchestrators/shared.js.map +1 -0
- package/dist/daemon/output-watcher.js +131 -0
- package/dist/daemon/output-watcher.js.map +1 -0
- package/dist/daemon/participant-aborts.js +123 -0
- package/dist/daemon/participant-aborts.js.map +1 -0
- package/dist/daemon/reaper.js +46 -0
- package/dist/daemon/reaper.js.map +1 -0
- package/dist/daemon/routes/chats-events.js +62 -0
- package/dist/daemon/routes/chats-events.js.map +1 -0
- package/dist/daemon/routes/chats-stream.js +241 -0
- package/dist/daemon/routes/chats-stream.js.map +1 -0
- package/dist/daemon/routes/chats-validation.js +13 -0
- package/dist/daemon/routes/chats-validation.js.map +1 -0
- package/dist/daemon/routes/chats.js +545 -0
- package/dist/daemon/routes/chats.js.map +1 -0
- package/dist/daemon/routes/openrouter.js +103 -0
- package/dist/daemon/routes/openrouter.js.map +1 -0
- package/dist/daemon/routes/settings.js +199 -0
- package/dist/daemon/routes/settings.js.map +1 -0
- package/dist/daemon/routes/stats.js +155 -0
- package/dist/daemon/routes/stats.js.map +1 -0
- package/dist/daemon/routes/system.js +208 -0
- package/dist/daemon/routes/system.js.map +1 -0
- package/dist/daemon/routes/templates-personas.js +254 -0
- package/dist/daemon/routes/templates-personas.js.map +1 -0
- package/dist/daemon/routes/voices.js +139 -0
- package/dist/daemon/routes/voices.js.map +1 -0
- package/dist/daemon/runner/doer-driver.js +346 -0
- package/dist/daemon/runner/doer-driver.js.map +1 -0
- package/dist/daemon/runner/doer.js +336 -0
- package/dist/daemon/runner/doer.js.map +1 -0
- package/dist/daemon/runner/prior-round.js +140 -0
- package/dist/daemon/runner/prior-round.js.map +1 -0
- package/dist/daemon/runner/prompt-builder.js +292 -0
- package/dist/daemon/runner/prompt-builder.js.map +1 -0
- package/dist/daemon/runner/review-only-phase.js +103 -0
- package/dist/daemon/runner/review-only-phase.js.map +1 -0
- package/dist/daemon/runner/reviewer-driver.js +410 -0
- package/dist/daemon/runner/reviewer-driver.js.map +1 -0
- package/dist/daemon/runner/reviewer.js +384 -0
- package/dist/daemon/runner/reviewer.js.map +1 -0
- package/dist/daemon/runner/run-with-fallback.js +56 -0
- package/dist/daemon/runner/run-with-fallback.js.map +1 -0
- package/dist/daemon/runner/sanitize-name.js +8 -0
- package/dist/daemon/runner/sanitize-name.js.map +1 -0
- package/dist/daemon/runner/stream-file-writer.js +116 -0
- package/dist/daemon/runner/stream-file-writer.js.map +1 -0
- package/dist/daemon/runner/swap-sidecar.js +102 -0
- package/dist/daemon/runner/swap-sidecar.js.map +1 -0
- package/dist/daemon/runner/template-fallback.js +119 -0
- package/dist/daemon/runner/template-fallback.js.map +1 -0
- package/dist/daemon/runner/types.js +3 -0
- package/dist/daemon/runner/types.js.map +1 -0
- package/dist/daemon/runner/verdict.js +51 -0
- package/dist/daemon/runner/verdict.js.map +1 -0
- package/dist/daemon/runner-multiplex.js +364 -0
- package/dist/daemon/runner-multiplex.js.map +1 -0
- package/dist/daemon/runner.js +427 -0
- package/dist/daemon/runner.js.map +1 -0
- package/dist/daemon/ship.js +340 -0
- package/dist/daemon/ship.js.map +1 -0
- package/dist/daemon/template-cache.js +37 -0
- package/dist/daemon/template-cache.js.map +1 -0
- package/dist/daemon/tmux-types.js +9 -0
- package/dist/daemon/tmux-types.js.map +1 -0
- package/dist/daemon/tmux.js +341 -0
- package/dist/daemon/tmux.js.map +1 -0
- package/dist/lib/atomic-write.js +55 -0
- package/dist/lib/atomic-write.js.map +1 -0
- package/dist/lib/chat-events-bus.js +27 -0
- package/dist/lib/chat-events-bus.js.map +1 -0
- package/dist/lib/chat-slug.js +105 -0
- package/dist/lib/chat-slug.js.map +1 -0
- package/dist/lib/cli-detect.js +388 -0
- package/dist/lib/cli-detect.js.map +1 -0
- package/dist/lib/cli-health.js +156 -0
- package/dist/lib/cli-health.js.map +1 -0
- package/dist/lib/cli-paths.js +113 -0
- package/dist/lib/cli-paths.js.map +1 -0
- package/dist/lib/cli-precheck.js +141 -0
- package/dist/lib/cli-precheck.js.map +1 -0
- package/dist/lib/db/chats.js +244 -0
- package/dist/lib/db/chats.js.map +1 -0
- package/dist/lib/db/connection.js +254 -0
- package/dist/lib/db/connection.js.map +1 -0
- package/dist/lib/db/index.js +34 -0
- package/dist/lib/db/index.js.map +1 -0
- package/dist/lib/db/personas.js +65 -0
- package/dist/lib/db/personas.js.map +1 -0
- package/dist/lib/db/phase-events.js +172 -0
- package/dist/lib/db/phase-events.js.map +1 -0
- package/dist/lib/db/secrets.js +53 -0
- package/dist/lib/db/secrets.js.map +1 -0
- package/dist/lib/db/settings.js +47 -0
- package/dist/lib/db/settings.js.map +1 -0
- package/dist/lib/db/templates.js +75 -0
- package/dist/lib/db/templates.js.map +1 -0
- package/dist/lib/db/voices.js +184 -0
- package/dist/lib/db/voices.js.map +1 -0
- package/dist/lib/lineage-maps.js +200 -0
- package/dist/lib/lineage-maps.js.map +1 -0
- package/dist/lib/logger.js +186 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/personas.js +117 -0
- package/dist/lib/personas.js.map +1 -0
- package/dist/lib/runtime-path.js +222 -0
- package/dist/lib/runtime-path.js.map +1 -0
- package/dist/lib/settings/billing.js +58 -0
- package/dist/lib/settings/billing.js.map +1 -0
- package/dist/lib/settings/permissions.js +81 -0
- package/dist/lib/settings/permissions.js.map +1 -0
- package/dist/lib/settings/transport.js +113 -0
- package/dist/lib/settings/transport.js.map +1 -0
- package/dist/lib/telemetry.js +290 -0
- package/dist/lib/telemetry.js.map +1 -0
- package/dist/lib/template-schema.js +319 -0
- package/dist/lib/template-schema.js.map +1 -0
- package/dist/lib/template-validation.js +82 -0
- package/dist/lib/template-validation.js.map +1 -0
- package/dist/lib/voices.js +533 -0
- package/dist/lib/voices.js.map +1 -0
- package/dist/mcp/client.js +138 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/index.js +178 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/tools.js +355 -0
- package/dist/mcp/tools.js.map +1 -0
- package/package.json +2 -1
- package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0beh7rg._.js +0 -6077
- package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0beh7rg._.js.map +0 -69
- package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0pjsj.j._.js +0 -6318
- package/.next/dev/static/chunks/05w9_next_dist_shared_lib_0pjsj.j._.js.map +0 -71
- package/.next/dev/types/cache-life.d.ts +0 -145
- package/.next/dev/types/routes.d.ts +0 -84
- package/.next/dev/types/validator.ts +0 -178
- /package/.next/static/{dJlbRLlhISA0JGtHKVqgQ → 9cD3yIOGe_Aqr17uJHTQS}/_buildManifest.js +0 -0
- /package/.next/static/{dJlbRLlhISA0JGtHKVqgQ → 9cD3yIOGe_Aqr17uJHTQS}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{dJlbRLlhISA0JGtHKVqgQ → 9cD3yIOGe_Aqr17uJHTQS}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Chorus daemon — Fastify HTTP server.
|
|
4
|
+
*
|
|
5
|
+
* Boots the DB, seeds builtin personas/voices/templates, registers route
|
|
6
|
+
* groups, and starts the reaper. Routes live in `routes/*.ts`; the
|
|
7
|
+
* runChat multi-subscriber wrapper lives in `runner-multiplex.ts`.
|
|
8
|
+
*/
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.successResponse = exports.errorResponse = exports.isValidChatId = exports.getParsedTemplate = void 0;
|
|
14
|
+
exports.getTmuxManager = getTmuxManager;
|
|
15
|
+
const cors_1 = __importDefault(require("@fastify/cors"));
|
|
16
|
+
const fastify_1 = __importDefault(require("fastify"));
|
|
17
|
+
const fs_1 = __importDefault(require("fs"));
|
|
18
|
+
const path_1 = __importDefault(require("path"));
|
|
19
|
+
const index_js_1 = require("../lib/db/index.js");
|
|
20
|
+
const logger_js_1 = require("../lib/logger.js");
|
|
21
|
+
const error_detector_js_1 = require("./error-detector.js");
|
|
22
|
+
const reaper_js_1 = require("./reaper.js");
|
|
23
|
+
const runner_multiplex_js_1 = require("./runner-multiplex.js");
|
|
24
|
+
const chats_js_1 = require("./routes/chats.js");
|
|
25
|
+
const chats_events_js_1 = require("./routes/chats-events.js");
|
|
26
|
+
const openrouter_js_1 = require("./routes/openrouter.js");
|
|
27
|
+
const templates_personas_js_1 = require("./routes/templates-personas.js");
|
|
28
|
+
const settings_js_1 = require("./routes/settings.js");
|
|
29
|
+
const stats_js_1 = require("./routes/stats.js");
|
|
30
|
+
const system_js_1 = require("./routes/system.js");
|
|
31
|
+
const voices_js_1 = require("./routes/voices.js");
|
|
32
|
+
const tmux_js_1 = require("./tmux.js");
|
|
33
|
+
const api_response_js_1 = require("./api-response.js");
|
|
34
|
+
Object.defineProperty(exports, "errorResponse", { enumerable: true, get: function () { return api_response_js_1.errorResponse; } });
|
|
35
|
+
Object.defineProperty(exports, "successResponse", { enumerable: true, get: function () { return api_response_js_1.successResponse; } });
|
|
36
|
+
var template_cache_js_1 = require("./template-cache.js");
|
|
37
|
+
Object.defineProperty(exports, "getParsedTemplate", { enumerable: true, get: function () { return template_cache_js_1.getParsedTemplate; } });
|
|
38
|
+
var chats_js_2 = require("./routes/chats.js");
|
|
39
|
+
Object.defineProperty(exports, "isValidChatId", { enumerable: true, get: function () { return chats_js_2.isValidChatId; } });
|
|
40
|
+
/**
|
|
41
|
+
* Resolve daemon port from env, with hard validation. parseInt('chorus', 10)
|
|
42
|
+
* silently returns NaN, which Fastify accepts as "let the OS pick a port" —
|
|
43
|
+
* the daemon would start, bind to a random port, and the cockpit would
|
|
44
|
+
* never find it. Catch this at boot with a useful error message instead.
|
|
45
|
+
*/
|
|
46
|
+
function resolveDaemonPort() {
|
|
47
|
+
const raw = process.env.CHORUS_DAEMON_PORT;
|
|
48
|
+
if (!raw)
|
|
49
|
+
return 7707;
|
|
50
|
+
const parsed = parseInt(raw, 10);
|
|
51
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
|
|
52
|
+
throw new Error(`CHORUS_DAEMON_PORT must be an integer between 1 and 65535. Got: ${JSON.stringify(raw)}`);
|
|
53
|
+
}
|
|
54
|
+
return parsed;
|
|
55
|
+
}
|
|
56
|
+
const PORT = resolveDaemonPort();
|
|
57
|
+
const HOST = '127.0.0.1';
|
|
58
|
+
const VERSION = '0.7.2';
|
|
59
|
+
const startTime = Date.now();
|
|
60
|
+
// Absolute path to bin/chorus.mjs — used by /orchestrators/:name/connect
|
|
61
|
+
// when the cockpit triggers a one-click wire-up. Both src/daemon/index.ts
|
|
62
|
+
// (tsx) and dist/daemon/index.js (PM2/built) resolve to <pkg-root>/bin/
|
|
63
|
+
// chorus.mjs.
|
|
64
|
+
const CHORUS_BIN_PATH = path_1.default.resolve(__dirname, '..', '..', 'bin', 'chorus.mjs');
|
|
65
|
+
// Singletons shared across the daemon lifetime.
|
|
66
|
+
let tmuxMgr;
|
|
67
|
+
let stopReaper = null;
|
|
68
|
+
const errorDetector = new error_detector_js_1.ErrorDetector();
|
|
69
|
+
async function main() {
|
|
70
|
+
// Eager DB probe: trying to open the sqlite file at startup catches
|
|
71
|
+
// permission errors, schema-migration crashes, and missing-init
|
|
72
|
+
// issues *before* the first HTTP request fails opaquely.
|
|
73
|
+
try {
|
|
74
|
+
await index_js_1.chats.list({ limit: 1 });
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
78
|
+
console.error(`\n[chorus] Could not open database. Run \`chorus init\` first, ` +
|
|
79
|
+
`or check permissions on ~/.chorus/chorus.db.\n detail: ${msg}\n`);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
const fastify = (0, fastify_1.default)({ logger: false });
|
|
83
|
+
await fastify.register(cors_1.default, {
|
|
84
|
+
origin: ['http://127.0.0.1:5050'],
|
|
85
|
+
credentials: true,
|
|
86
|
+
});
|
|
87
|
+
// Seed built-in personas from prompts/personas/*.md. Idempotent:
|
|
88
|
+
// builtin rows refresh from the file source of truth on every
|
|
89
|
+
// startup; user-created rows (builtin=0) are not touched.
|
|
90
|
+
try {
|
|
91
|
+
const { seedBuiltinPersonas } = await import('../lib/personas.js');
|
|
92
|
+
const count = await seedBuiltinPersonas();
|
|
93
|
+
console.log(`[daemon] seeded ${count} built-in personas`);
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
// Non-fatal: daemon still works without personas.
|
|
97
|
+
console.warn('[daemon] persona seed failed:', err instanceof Error ? err.message : err);
|
|
98
|
+
}
|
|
99
|
+
// Prime the merged spawn PATH BEFORE voice seed. seedCliVoices runs
|
|
100
|
+
// detectAllClis(), which honours saved manual paths via the cli-paths
|
|
101
|
+
// cache; without priming, the cache is empty and a custom-location
|
|
102
|
+
// CLI shows up as undetected on the first boot after the user pasted
|
|
103
|
+
// its path into onboarding.
|
|
104
|
+
try {
|
|
105
|
+
const { buildRuntimePath } = await import('../lib/runtime-path.js');
|
|
106
|
+
const { cliPaths } = await import('../lib/cli-paths.js');
|
|
107
|
+
const { setSpawnPath } = await import('./headless.js');
|
|
108
|
+
await cliPaths.refreshCache();
|
|
109
|
+
const merged = await buildRuntimePath({
|
|
110
|
+
additionalDirs: cliPaths.cachedDirs(),
|
|
111
|
+
});
|
|
112
|
+
setSpawnPath(merged);
|
|
113
|
+
console.log(`[daemon] runtime PATH primed (${merged.split(':').length} dirs)`);
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
console.warn('[daemon] runtime PATH prime failed (falling back to process.env.PATH):', err instanceof Error ? err.message : err);
|
|
117
|
+
}
|
|
118
|
+
// Voices Phase 1 — synchronous, pre-listen seed of single-model CLIs
|
|
119
|
+
// + first-boot migration from <lineage>.enabled_models. Fast (no
|
|
120
|
+
// shell-outs); blocks listen on intent (we want voices ready before
|
|
121
|
+
// routes serve).
|
|
122
|
+
try {
|
|
123
|
+
const { seedCliVoices } = await import('../lib/voices.js');
|
|
124
|
+
const result = await seedCliVoices();
|
|
125
|
+
console.log(`[daemon] voices Phase 1: +${result.added} added, ${result.updated} updated, ${result.disabled} auto-disabled`);
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
console.warn('[daemon] voices Phase 1 seed failed:', err instanceof Error ? err.message : err);
|
|
129
|
+
}
|
|
130
|
+
// ─── Routes ─────────────────────────────────────────────────────────
|
|
131
|
+
//
|
|
132
|
+
// Every public REST + SSE route mounts under /api/v1. Pre-launch
|
|
133
|
+
// shape-freeze (v0.7) — adding /api/v2 later is non-breaking.
|
|
134
|
+
//
|
|
135
|
+
// Bare paths (`/health`, `/chats`, ...) are kept as transitional
|
|
136
|
+
// aliases for one minor (v0.7) so that globally-installed MCP servers
|
|
137
|
+
// shipping older chorus-codes versions don't break the moment a user
|
|
138
|
+
// upgrades the daemon. Callers should migrate to /api/v1; the bare
|
|
139
|
+
// paths are dropped in v0.8.
|
|
140
|
+
// Initialize tmux manager BEFORE registering chat routes — the chat
|
|
141
|
+
// route handlers capture it for the duration of the daemon.
|
|
142
|
+
tmuxMgr = new tmux_js_1.TmuxManagerImpl();
|
|
143
|
+
const registerAll = (api) => {
|
|
144
|
+
api.get('/health', async () => {
|
|
145
|
+
// The redundant inner `ok: true` from earlier shipped versions
|
|
146
|
+
// was dropped here — the envelope's outer `ok: true` is the
|
|
147
|
+
// canonical liveness signal. Consumers that want a flat
|
|
148
|
+
// monitor-friendly probe still read `data.version` /
|
|
149
|
+
// `data.uptime`.
|
|
150
|
+
return (0, api_response_js_1.successResponse)({
|
|
151
|
+
version: VERSION,
|
|
152
|
+
uptime: Date.now() - startTime,
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
(0, chats_js_1.registerChatRoutes)(api, { tmuxMgr: tmuxMgr, errorDetector });
|
|
156
|
+
(0, chats_events_js_1.registerChatEventsRoute)(api);
|
|
157
|
+
(0, templates_personas_js_1.registerTemplateRoutes)(api);
|
|
158
|
+
(0, templates_personas_js_1.registerPersonaRoutes)(api);
|
|
159
|
+
(0, settings_js_1.registerSettingsRoutes)(api);
|
|
160
|
+
(0, settings_js_1.registerSecretRoutes)(api);
|
|
161
|
+
(0, system_js_1.registerSystemRoutes)(api, { chorusBinPath: CHORUS_BIN_PATH });
|
|
162
|
+
(0, voices_js_1.registerVoiceRoutes)(api);
|
|
163
|
+
(0, openrouter_js_1.registerOpenRouterRoutes)(api);
|
|
164
|
+
(0, stats_js_1.registerStatsRoutes)(api);
|
|
165
|
+
};
|
|
166
|
+
await fastify.register(async (api) => registerAll(api), { prefix: '/api/v1' });
|
|
167
|
+
// v0.7 transitional aliases — drop in v0.8.
|
|
168
|
+
await fastify.register(async (api) => registerAll(api));
|
|
169
|
+
await seedBuiltinTemplates();
|
|
170
|
+
// Reap orphan headless subprocesses from any prior daemon crash.
|
|
171
|
+
// Without this, a hung CLI from a previous run keeps burning
|
|
172
|
+
// subscription quota until manually killed.
|
|
173
|
+
try {
|
|
174
|
+
const { reapOrphanProcesses } = await import('./headless.js');
|
|
175
|
+
const result = reapOrphanProcesses();
|
|
176
|
+
if (result.reaped > 0 || result.cleared > 0) {
|
|
177
|
+
console.log(`[chorus] reaper: killed ${result.reaped} orphan headless processes, cleared ${result.cleared} stale records`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
// Non-fatal — orphan cleanup is best-effort.
|
|
182
|
+
console.warn('[chorus] reaper: failed to scan PID dir', err);
|
|
183
|
+
}
|
|
184
|
+
stopReaper = (0, reaper_js_1.startReaper)(tmuxMgr, async () => {
|
|
185
|
+
// getActiveChats: chatId → status for active chats only (drafting
|
|
186
|
+
// and reviewing). Terminal states are reaped.
|
|
187
|
+
const allChats = await index_js_1.chats.list({ limit: 1000, offset: 0 });
|
|
188
|
+
const activeMap = new Map();
|
|
189
|
+
const activeStatuses = new Set(['drafting', 'reviewing']);
|
|
190
|
+
for (const chat of allChats) {
|
|
191
|
+
if (activeStatuses.has(chat.status)) {
|
|
192
|
+
activeMap.set(chat.id, chat.status);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return activeMap;
|
|
196
|
+
}, {
|
|
197
|
+
intervalMs: 5 * 60 * 1000,
|
|
198
|
+
idleDestroyMinutes: 30,
|
|
199
|
+
});
|
|
200
|
+
// ─── Graceful shutdown ──────────────────────────────────────────────
|
|
201
|
+
const shutdown = async (signal) => {
|
|
202
|
+
if ((0, runner_multiplex_js_1.activeRunsCount)() > 0) {
|
|
203
|
+
const runs = (0, runner_multiplex_js_1.activeRunsSnapshot)();
|
|
204
|
+
for (const entry of runs) {
|
|
205
|
+
entry.abortController.abort();
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
await Promise.race([
|
|
209
|
+
Promise.allSettled(runs.map((e) => e.promise)),
|
|
210
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout waiting for active runs')), 10000)),
|
|
211
|
+
]);
|
|
212
|
+
console.log(`[chorus] aborted ${runs.length} active runs (${signal})`);
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
console.warn('[chorus] timeout or error waiting for active runs to abort');
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (stopReaper)
|
|
219
|
+
stopReaper();
|
|
220
|
+
await fastify.close();
|
|
221
|
+
process.exit(0);
|
|
222
|
+
};
|
|
223
|
+
process.on('SIGTERM', () => void shutdown('SIGTERM'));
|
|
224
|
+
process.on('SIGINT', () => void shutdown('SIGINT'));
|
|
225
|
+
await fastify.listen({ port: PORT, host: HOST });
|
|
226
|
+
logger_js_1.logger.info({ port: PORT, host: HOST, version: VERSION }, 'daemon listening');
|
|
227
|
+
// Keep the human-readable startup line — the install script +
|
|
228
|
+
// onboarding grep for it. Structured line above is what `chorus logs`
|
|
229
|
+
// consumes.
|
|
230
|
+
console.log(`Chorus daemon listening on http://${HOST}:${PORT}`);
|
|
231
|
+
// Anonymous opt-out telemetry. First send is delayed 5s so the
|
|
232
|
+
// listener is definitely up; subsequent sends every 24h. All three
|
|
233
|
+
// opt-out paths (env, touch-file, settings) are honoured per send.
|
|
234
|
+
const { startTelemetryHeartbeat } = await import('../lib/telemetry.js');
|
|
235
|
+
const telemetryHandle = startTelemetryHeartbeat({
|
|
236
|
+
version: VERSION,
|
|
237
|
+
daemonStartedAt: startTime,
|
|
238
|
+
});
|
|
239
|
+
process.on('SIGTERM', () => telemetryHandle.stop());
|
|
240
|
+
process.on('SIGINT', () => telemetryHandle.stop());
|
|
241
|
+
// Voices Phase 2 — background warmup. `opencode models` shells out
|
|
242
|
+
// and can take up to 10s; running it post-listen avoids that boot-
|
|
243
|
+
// latency hit. Errors are logged but don't crash the daemon.
|
|
244
|
+
void (async () => {
|
|
245
|
+
try {
|
|
246
|
+
const { seedOpencodeVoicesAsync } = await import('../lib/voices.js');
|
|
247
|
+
const result = await seedOpencodeVoicesAsync();
|
|
248
|
+
if (result) {
|
|
249
|
+
console.log(`[daemon] voices Phase 2 (opencode): +${result.added} added, ${result.updated} updated, ${result.disabled} auto-disabled`);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
console.log('[daemon] voices Phase 2 (opencode): skipped (CLI not detected or shell-out failed)');
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
console.warn('[daemon] voices Phase 2 failed:', err instanceof Error ? err.message : err);
|
|
257
|
+
}
|
|
258
|
+
})();
|
|
259
|
+
}
|
|
260
|
+
async function seedBuiltinTemplates() {
|
|
261
|
+
const templatesDir = path_1.default.join(__dirname, '..', '..', 'templates');
|
|
262
|
+
if (!fs_1.default.existsSync(templatesDir)) {
|
|
263
|
+
console.log('No templates directory found, skipping seed');
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const files = fs_1.default.readdirSync(templatesDir).filter((f) => f.endsWith('.yaml'));
|
|
267
|
+
const onDiskIds = new Set();
|
|
268
|
+
for (const file of files) {
|
|
269
|
+
const id = file.replace('.yaml', '');
|
|
270
|
+
onDiskIds.add(id);
|
|
271
|
+
const yamlPath = path_1.default.join(templatesDir, file);
|
|
272
|
+
const yamlContent = fs_1.default.readFileSync(yamlPath, 'utf-8');
|
|
273
|
+
const existing = await index_js_1.templates.getById(id);
|
|
274
|
+
if (!existing) {
|
|
275
|
+
await index_js_1.templates.create(id, yamlContent, 'builtin');
|
|
276
|
+
console.log(`[daemon] seeded template: ${id}`);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
// Re-sync builtin rows from disk on every boot. User-cloned rows
|
|
280
|
+
// (source='user') are NEVER overwritten — those belong to the user
|
|
281
|
+
// and will be edited via /templates POST. Keeps YAML source-of-
|
|
282
|
+
// truth aligned with the DB after a chorus upgrade.
|
|
283
|
+
if (existing.source === 'builtin' && existing.yaml !== yamlContent) {
|
|
284
|
+
await index_js_1.templates.create(id, yamlContent, 'builtin');
|
|
285
|
+
console.log(`[daemon] refreshed builtin template from disk: ${id}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Flag any builtin templates that are no longer present on disk.
|
|
289
|
+
// templates.delete is exposed by the DB layer but the loop only logs
|
|
290
|
+
// intent today — refresh-on-disk-change keeps content fresh, and
|
|
291
|
+
// stale rows are inert (just don't appear in templatesDir). Tracked
|
|
292
|
+
// outside the libsql migration scope.
|
|
293
|
+
try {
|
|
294
|
+
const allTemplates = await index_js_1.templates.list();
|
|
295
|
+
let staleCount = 0;
|
|
296
|
+
for (const tmpl of allTemplates) {
|
|
297
|
+
if (tmpl.source === 'builtin' && !onDiskIds.has(tmpl.id)) {
|
|
298
|
+
console.log(`[daemon] would delete stale builtin template (no delete method): ${tmpl.id}`);
|
|
299
|
+
staleCount++;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (staleCount > 0) {
|
|
303
|
+
console.log(`[daemon] flagged ${staleCount} stale builtin templates for cleanup`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
// Non-fatal: if templates.list() fails, skip cleanup.
|
|
308
|
+
console.warn('[daemon] failed to scan stale builtin templates:', err);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
// Auto-run main() only when this file is the process entry point. When
|
|
312
|
+
// the daemon module is imported from a test (e.g.
|
|
313
|
+
// tests/template-cache.test.ts importing the exported getParsedTemplate),
|
|
314
|
+
// we don't want a side-effecty fastify boot or DB probe firing on module
|
|
315
|
+
// load.
|
|
316
|
+
const isEntryPoint = typeof require !== 'undefined' && require.main === module;
|
|
317
|
+
if (isEntryPoint) {
|
|
318
|
+
main().catch((error) => {
|
|
319
|
+
console.error('Failed to start daemon:', error);
|
|
320
|
+
process.exit(1);
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* @internal — for tests / cross-module daemon code. Throws if called
|
|
325
|
+
* before main() has wired the singleton.
|
|
326
|
+
*/
|
|
327
|
+
function getTmuxManager() {
|
|
328
|
+
if (!tmuxMgr) {
|
|
329
|
+
throw new Error('TmuxManager not initialized. Daemon may not have started yet.');
|
|
330
|
+
}
|
|
331
|
+
return tmuxMgr;
|
|
332
|
+
}
|
|
333
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OpenRouter inline integration — validate API key, list available models,
|
|
4
|
+
* add models as voices.
|
|
5
|
+
*
|
|
6
|
+
* Why an inline flow: the v0.7 voices table can already store
|
|
7
|
+
* `source='api'` voices, but until now there was no UX path to populate
|
|
8
|
+
* them — users could only enable CLI-detected voices. OpenRouter is the
|
|
9
|
+
* widest practical API gateway (Anthropic, OpenAI, Google, Meta, Kimi,
|
|
10
|
+
* DeepSeek, Mistral, Grok, …) so a single key unlocks dozens of models
|
|
11
|
+
* across every lineage we score for diversity.
|
|
12
|
+
*
|
|
13
|
+
* Architecture lifted from 9router's `/api/providers/validate` route
|
|
14
|
+
* (which we studied before writing this) — same Bearer-auth contract,
|
|
15
|
+
* same `GET /api/v1/models` validation. Differences:
|
|
16
|
+
* - We persist the key in chorus's `secrets` table, not a JSON blob.
|
|
17
|
+
* - We classify each model into a chorus lineage via the existing
|
|
18
|
+
* `classifyOpencodeModel` helper (the "anthropic/claude-..." prefix
|
|
19
|
+
* pattern matches OpenRouter's id format directly).
|
|
20
|
+
* - We do NOT proxy chat completions through this module yet — that
|
|
21
|
+
* comes in a follow-up HTTP-shim PR. This PR ships the validate +
|
|
22
|
+
* model-catalog + voices-insert flow only; selecting an OpenRouter
|
|
23
|
+
* voice in a template won't dispatch successfully until the shim
|
|
24
|
+
* lands. The voices appear in the picker as preview-only.
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.validateKey = validateKey;
|
|
28
|
+
exports.listModels = listModels;
|
|
29
|
+
exports.saveKey = saveKey;
|
|
30
|
+
exports.addModelsAsVoices = addModelsAsVoices;
|
|
31
|
+
const index_js_1 = require("../lib/db/index.js");
|
|
32
|
+
const voices_js_1 = require("../lib/voices.js");
|
|
33
|
+
const OPENROUTER_BASE = 'https://openrouter.ai/api/v1';
|
|
34
|
+
const VALIDATE_TIMEOUT_MS = 8_000;
|
|
35
|
+
const MODELS_TIMEOUT_MS = 15_000;
|
|
36
|
+
/**
|
|
37
|
+
* Live-probe an API key by hitting `/api/v1/models`. OpenRouter rejects
|
|
38
|
+
* bad keys with HTTP 401, so a 200 means the key is real. Anything else
|
|
39
|
+
* (network errors, 5xx, 403) we surface verbatim — the cockpit then
|
|
40
|
+
* shows the user what to fix.
|
|
41
|
+
*/
|
|
42
|
+
async function validateKey(apiKey) {
|
|
43
|
+
if (!apiKey || apiKey.trim().length === 0) {
|
|
44
|
+
return { valid: false, error: 'API key is empty' };
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
// Hit /auth/key — the only catalog-side endpoint that actually
|
|
48
|
+
// requires the bearer to be valid. /models is public and returns
|
|
49
|
+
// 200 for ANY auth header (including junk), so the previous
|
|
50
|
+
// implementation rubber-stamped invalid keys as valid. Verified
|
|
51
|
+
// behaviour: 401 on invalid bearer, 200 with key metadata on valid.
|
|
52
|
+
const res = await fetch(`${OPENROUTER_BASE}/auth/key`, {
|
|
53
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
54
|
+
signal: AbortSignal.timeout(VALIDATE_TIMEOUT_MS),
|
|
55
|
+
});
|
|
56
|
+
if (res.ok)
|
|
57
|
+
return { valid: true };
|
|
58
|
+
if (res.status === 401 || res.status === 403) {
|
|
59
|
+
return { valid: false, error: 'Invalid API key' };
|
|
60
|
+
}
|
|
61
|
+
return { valid: false, error: `OpenRouter returned ${res.status}` };
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
65
|
+
return { valid: false, error: `Network error: ${message}` };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Fetch the full model catalog. Uses the stored OpenRouter key from
|
|
70
|
+
* the secrets table — callers must save the key first via
|
|
71
|
+
* `saveKey` (or pass an explicit key for one-off lookups).
|
|
72
|
+
*
|
|
73
|
+
* We trim the response to the four fields the cockpit picker actually
|
|
74
|
+
* needs, dropping the long descriptions, capability flags, and per-modality
|
|
75
|
+
* pricing. Keeps the wire payload manageable (200+ models * ~2 KB each
|
|
76
|
+
* crosses 400 KB unfiltered).
|
|
77
|
+
*
|
|
78
|
+
* Defensive pagination: OpenRouter currently returns the full catalog
|
|
79
|
+
* in a single response with no cursor. If they ever add pagination
|
|
80
|
+
* (`next_cursor` or `cursor` field), this loop follows it up to
|
|
81
|
+
* MAX_PAGES guard. Today's behaviour is identical to a single fetch.
|
|
82
|
+
*/
|
|
83
|
+
const MAX_PAGES = 20;
|
|
84
|
+
function mapModel(m) {
|
|
85
|
+
// OpenRouter pricing is reported as USD-per-token (e.g. "0.000003"
|
|
86
|
+
// for $3/Mtok). Convert to per-Mtok USD to match chorus's voices
|
|
87
|
+
// table convention. Strings parse to NaN if missing — guard that.
|
|
88
|
+
const promptCost = m.pricing?.prompt ? parseFloat(m.pricing.prompt) : NaN;
|
|
89
|
+
const completionCost = m.pricing?.completion ? parseFloat(m.pricing.completion) : NaN;
|
|
90
|
+
const result = { id: m.id, name: m.name ?? m.id };
|
|
91
|
+
if (typeof m.context_length === 'number') {
|
|
92
|
+
result.contextLength = m.context_length;
|
|
93
|
+
}
|
|
94
|
+
if (Number.isFinite(promptCost)) {
|
|
95
|
+
result.inputCostPerMtok = promptCost * 1_000_000;
|
|
96
|
+
}
|
|
97
|
+
if (Number.isFinite(completionCost)) {
|
|
98
|
+
result.outputCostPerMtok = completionCost * 1_000_000;
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
async function listModels(apiKey) {
|
|
103
|
+
const key = apiKey ?? (await index_js_1.secrets.get('openrouter'))?.value;
|
|
104
|
+
if (!key) {
|
|
105
|
+
throw new Error('No OpenRouter API key saved. POST /openrouter/save-key with {apiKey} first.');
|
|
106
|
+
}
|
|
107
|
+
const all = [];
|
|
108
|
+
let cursor;
|
|
109
|
+
let pages = 0;
|
|
110
|
+
do {
|
|
111
|
+
const url = cursor
|
|
112
|
+
? `${OPENROUTER_BASE}/models?cursor=${encodeURIComponent(cursor)}`
|
|
113
|
+
: `${OPENROUTER_BASE}/models`;
|
|
114
|
+
const res = await fetch(url, {
|
|
115
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
116
|
+
signal: AbortSignal.timeout(MODELS_TIMEOUT_MS),
|
|
117
|
+
});
|
|
118
|
+
if (!res.ok) {
|
|
119
|
+
throw new Error(`OpenRouter /models returned ${res.status}`);
|
|
120
|
+
}
|
|
121
|
+
const body = (await res.json());
|
|
122
|
+
if (Array.isArray(body.data)) {
|
|
123
|
+
all.push(...body.data.map(mapModel));
|
|
124
|
+
}
|
|
125
|
+
// Future-compat: OpenRouter may add `next_cursor` (most common naming)
|
|
126
|
+
// or `cursor`. We stop when neither is a non-empty string. Safety cap
|
|
127
|
+
// prevents an infinite loop if a buggy server echoes the same cursor.
|
|
128
|
+
const next = body.next_cursor ?? body.cursor;
|
|
129
|
+
cursor = typeof next === 'string' && next.length > 0 ? next : undefined;
|
|
130
|
+
pages += 1;
|
|
131
|
+
} while (cursor && pages < MAX_PAGES);
|
|
132
|
+
return all;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Validate, then persist the key under provider='openrouter' in secrets.
|
|
136
|
+
* Returns the validation result; on failure the key is NOT saved.
|
|
137
|
+
*/
|
|
138
|
+
async function saveKey(apiKey) {
|
|
139
|
+
const result = await validateKey(apiKey);
|
|
140
|
+
if (!result.valid)
|
|
141
|
+
return result;
|
|
142
|
+
await index_js_1.secrets.set('openrouter', 'api_key', apiKey);
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Add the chosen models as voices. Idempotent — voices.upsert dedupes
|
|
147
|
+
* by id. Returns the list of voice ids actually inserted/updated.
|
|
148
|
+
*
|
|
149
|
+
* Voice id format mirrors the kimi/opencode-go convention:
|
|
150
|
+
* `openrouter:<openrouter-model-id>`
|
|
151
|
+
* so the picker can show "openrouter:anthropic/claude-3.5-sonnet" and
|
|
152
|
+
* a future template can reference it unambiguously.
|
|
153
|
+
*/
|
|
154
|
+
async function addModelsAsVoices(modelIds, apiKey) {
|
|
155
|
+
if (modelIds.length === 0)
|
|
156
|
+
return { added: [], skipped: [] };
|
|
157
|
+
// Pull the catalog ONCE so we can fill labels + pricing for each id
|
|
158
|
+
// without re-hitting OpenRouter per-model. Optional `apiKey` plumbs
|
|
159
|
+
// through to listModels so callers that already hold the key don't
|
|
160
|
+
// race a concurrent save-key write to the secrets table.
|
|
161
|
+
const catalog = await listModels(apiKey);
|
|
162
|
+
const byId = new Map(catalog.map((m) => [m.id, m]));
|
|
163
|
+
const added = [];
|
|
164
|
+
const skipped = [];
|
|
165
|
+
for (const modelId of modelIds) {
|
|
166
|
+
const meta = byId.get(modelId);
|
|
167
|
+
if (!meta) {
|
|
168
|
+
skipped.push(modelId);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const { lineage, vendor_family } = (0, voices_js_1.classifyOpencodeModel)(modelId);
|
|
172
|
+
const voiceId = `openrouter:${modelId}`;
|
|
173
|
+
// Enabled by default — the HTTP dispatch shim now exists at
|
|
174
|
+
// src/daemon/agents/openrouter.ts, so a template referencing this
|
|
175
|
+
// voice will dispatch successfully via /api/v1/chat/completions.
|
|
176
|
+
await index_js_1.voices.upsert({
|
|
177
|
+
id: voiceId,
|
|
178
|
+
label: meta.name,
|
|
179
|
+
source: 'api',
|
|
180
|
+
provider: 'openrouter',
|
|
181
|
+
model_id: modelId,
|
|
182
|
+
lineage,
|
|
183
|
+
vendor_family: vendor_family ?? null,
|
|
184
|
+
input_cost_per_mtok: meta.inputCostPerMtok ?? null,
|
|
185
|
+
output_cost_per_mtok: meta.outputCostPerMtok ?? null,
|
|
186
|
+
enabled: true,
|
|
187
|
+
});
|
|
188
|
+
added.push(voiceId);
|
|
189
|
+
}
|
|
190
|
+
return { added, skipped };
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=openrouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/daemon/openrouter.ts"],"names":[],"mappings":"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"}
|