dsh-multi-router 0.6.12 → 1.0.0

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 (56) hide show
  1. package/README.md +2 -430
  2. package/package.json +5 -81
  3. package/CHANGELOG.md +0 -243
  4. package/LICENSE +0 -21
  5. package/THIRD_PARTY_NOTICES.md +0 -171
  6. package/cordis.patch.yml +0 -3
  7. package/lib/accounts.js +0 -19
  8. package/lib/accounts.js.map +0 -7
  9. package/lib/api-service.js +0 -11
  10. package/lib/api-service.js.map +0 -7
  11. package/lib/chunk-2UM45OXY.js +0 -181
  12. package/lib/chunk-2UM45OXY.js.map +0 -7
  13. package/lib/chunk-4L2CBGXN.js +0 -674
  14. package/lib/chunk-4L2CBGXN.js.map +0 -7
  15. package/lib/chunk-4V52V5GO.js +0 -105
  16. package/lib/chunk-4V52V5GO.js.map +0 -7
  17. package/lib/chunk-DNQPZMPF.js +0 -33
  18. package/lib/chunk-DNQPZMPF.js.map +0 -7
  19. package/lib/chunk-FLL3DUVP.js +0 -2017
  20. package/lib/chunk-FLL3DUVP.js.map +0 -7
  21. package/lib/chunk-GEBZMIMA.js +0 -287
  22. package/lib/chunk-GEBZMIMA.js.map +0 -7
  23. package/lib/chunk-KWXLQJS5.js +0 -1102
  24. package/lib/chunk-KWXLQJS5.js.map +0 -7
  25. package/lib/chunk-NZF36O5I.js +0 -5421
  26. package/lib/chunk-NZF36O5I.js.map +0 -7
  27. package/lib/chunk-S5KMFAQM.js +0 -50
  28. package/lib/chunk-S5KMFAQM.js.map +0 -7
  29. package/lib/chunk-TM5XDMGY.js +0 -332
  30. package/lib/chunk-TM5XDMGY.js.map +0 -7
  31. package/lib/chunk-V37VG7MN.js +0 -2101
  32. package/lib/chunk-V37VG7MN.js.map +0 -7
  33. package/lib/chunk-WDIT4E2W.js +0 -120
  34. package/lib/chunk-WDIT4E2W.js.map +0 -7
  35. package/lib/cli.js +0 -103
  36. package/lib/cli.js.map +0 -7
  37. package/lib/client.js +0 -2805
  38. package/lib/client.js.map +0 -7
  39. package/lib/clients.js +0 -30
  40. package/lib/clients.js.map +0 -7
  41. package/lib/host-v0.6.12.js +0 -414
  42. package/lib/host.js +0 -414
  43. package/lib/index.js +0 -414
  44. package/lib/index.js.map +0 -7
  45. package/lib/manager.js +0 -27
  46. package/lib/manager.js.map +0 -7
  47. package/lib/region-DO3KAV7V.js +0 -15
  48. package/lib/region-DO3KAV7V.js.map +0 -7
  49. package/lib/router.js +0 -14
  50. package/lib/router.js.map +0 -7
  51. package/lib/shim.js +0 -10
  52. package/lib/shim.js.map +0 -7
  53. package/lib/upstream-VP6J7LU2.js +0 -25
  54. package/lib/upstream-VP6J7LU2.js.map +0 -7
  55. package/router.example.json +0 -289
  56. package/upstream-lock.json +0 -17
@@ -1,120 +0,0 @@
1
- import {
2
- hostIsLoopback,
3
- originIsLoopback
4
- } from "./chunk-DNQPZMPF.js";
5
- import {
6
- RouteError
7
- } from "./chunk-4L2CBGXN.js";
8
- import {
9
- REASONING_EFFORTS
10
- } from "./chunk-GEBZMIMA.js";
11
-
12
- // src/shim.mjs
13
- import { createServer } from "node:http";
14
- import { randomBytes, timingSafeEqual } from "node:crypto";
15
- import { Readable } from "node:stream";
16
- import { pipeline } from "node:stream/promises";
17
- function createShim(router, { modelPrefix = "" } = {}) {
18
- let active = 0;
19
- const secret = randomBytes(32).toString("base64url");
20
- const server = createServer((req, res) => {
21
- void handle(req, res);
22
- });
23
- const ready = new Promise((resolve, reject) => {
24
- server.once("listening", resolve);
25
- server.once("error", reject);
26
- });
27
- server.listen(0, "127.0.0.1");
28
- const json = (res, status, value) => {
29
- res.writeHead(status, { "Content-Type": "application/json" });
30
- res.end(JSON.stringify(value));
31
- };
32
- async function handle(req, res) {
33
- active++;
34
- const controller = new AbortController();
35
- req.on("aborted", () => controller.abort());
36
- res.on("close", () => {
37
- if (!res.writableEnded) controller.abort();
38
- });
39
- const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(router.config.requestTimeoutMs)]);
40
- try {
41
- if (!hostIsLoopback(req.headers.host) || !originIsLoopback(req.headers.origin)) throw new RouteError(403, "loopback_required");
42
- const bearer = Buffer.from((req.headers.authorization ?? "").replace(/^Bearer\s+/i, ""));
43
- const expected = Buffer.from(secret);
44
- if (bearer.length !== expected.length || !timingSafeEqual(bearer, expected)) throw new RouteError(401, "unauthorized");
45
- if (req.method === "GET" && req.url === "/router/status") {
46
- json(res, 200, router.status());
47
- return;
48
- }
49
- if (req.method === "GET" && req.url === "/v1/models") {
50
- json(res, 200, { object: "list", data: Object.keys(router.config.routes).filter((id) => modelPrefix ? id.startsWith(modelPrefix) : !id.includes("::")).map((id) => ({ id: modelPrefix ? id.slice(modelPrefix.length) : id, object: "model", created: 0, owned_by: "multi-router" })) });
51
- return;
52
- }
53
- if (req.method !== "POST" || req.url !== "/v1/chat/completions") throw new RouteError(404, "not_found");
54
- if (!/^application\/json(?:\s*;|$)/i.test(req.headers["content-type"] ?? "")) throw new RouteError(415, "json_required");
55
- let size = 0;
56
- const chunks = [];
57
- for await (const chunk of req) {
58
- size += chunk.length;
59
- if (size > 32 * 1024 * 1024) throw new RouteError(413, "request_too_large");
60
- chunks.push(chunk);
61
- signal.throwIfAborted();
62
- }
63
- let input;
64
- try {
65
- input = JSON.parse(Buffer.concat(chunks).toString("utf8"));
66
- } catch {
67
- throw new RouteError(400, "invalid_json");
68
- }
69
- if (!input || typeof input !== "object" || Array.isArray(input)) throw new RouteError(400, "invalid_request");
70
- const effort = req.headers["x-multi-router-reasoning"];
71
- if (effort !== void 0) {
72
- if (effort !== "default" && !REASONING_EFFORTS.includes(effort)) throw new RouteError(400, "invalid_reasoning_effort");
73
- input.reasoning_effort = effort === "default" ? null : effort;
74
- }
75
- const selected = await router.chat({ ...input, model: modelPrefix + input.model }, signal, { callId: typeof req.headers["x-multi-router-call"] === "string" ? req.headers["x-multi-router-call"] : void 0, session: typeof req.headers["x-multi-router-session"] === "string" ? req.headers["x-multi-router-session"] : void 0 });
76
- signal.throwIfAborted();
77
- res.writeHead(200, {
78
- "Content-Type": "text/event-stream; charset=utf-8",
79
- "Cache-Control": "no-cache",
80
- "X-Accel-Buffering": "no",
81
- "X-Router-Account": selected.account,
82
- "X-Router-Provider": selected.provider
83
- });
84
- await pipeline(Readable.fromWeb(selected.response.body), res, { signal });
85
- } catch (error) {
86
- if (res.destroyed) return;
87
- if (res.headersSent) {
88
- res.destroy();
89
- return;
90
- }
91
- const status = error instanceof RouteError ? error.status : signal.aborted ? 504 : 500;
92
- json(res, status, { error: {
93
- type: error instanceof RouteError ? error.code : signal.aborted ? "request_timeout" : "internal_error",
94
- message: error instanceof RouteError ? error.message : "Router request failed",
95
- hint: error instanceof RouteError ? error.hint : void 0,
96
- attempts: error instanceof RouteError ? error.attempts : []
97
- } });
98
- } finally {
99
- active--;
100
- }
101
- }
102
- return {
103
- ready,
104
- active: () => active,
105
- token: () => secret,
106
- baseUrl: () => `http://127.0.0.1:${server.address().port}`,
107
- // Stop accepting new work, let an already streaming response finish. A
108
- // reload can race a call that began immediately after the idle check.
109
- close: async () => {
110
- await ready.catch(() => {
111
- });
112
- await new Promise((resolve) => server.close(resolve));
113
- }
114
- };
115
- }
116
-
117
- export {
118
- createShim
119
- };
120
- //# sourceMappingURL=chunk-WDIT4E2W.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/shim.mjs"],
4
- "sourcesContent": ["import { createServer } from 'node:http';\nimport { randomBytes, timingSafeEqual } from 'node:crypto';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\nimport { hostIsLoopback, originIsLoopback } from '../upstream/workbuddy/src/loopback.ts';\nimport { RouteError } from './router.mjs';\nimport { REASONING_EFFORTS } from './reasoning.mjs';\n\nexport function createShim(router, { modelPrefix = '' } = {}) {\n let active = 0;\n const secret = randomBytes(32).toString('base64url');\n const server = createServer((req, res) => { void handle(req, res); });\n const ready = new Promise((resolve, reject) => {\n server.once('listening', resolve); server.once('error', reject);\n });\n server.listen(0, '127.0.0.1');\n const json = (res, status, value) => {\n res.writeHead(status, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(value));\n };\n async function handle(req, res) {\n active++;\n const controller = new AbortController();\n req.on('aborted', () => controller.abort());\n res.on('close', () => { if (!res.writableEnded) controller.abort(); });\n const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(router.config.requestTimeoutMs)]);\n try {\n if (!hostIsLoopback(req.headers.host) || !originIsLoopback(req.headers.origin)) throw new RouteError(403, 'loopback_required');\n const bearer = Buffer.from((req.headers.authorization ?? '').replace(/^Bearer\\s+/i, ''));\n const expected = Buffer.from(secret);\n if (bearer.length !== expected.length || !timingSafeEqual(bearer, expected)) throw new RouteError(401, 'unauthorized');\n if (req.method === 'GET' && req.url === '/router/status') { json(res, 200, router.status()); return; }\n if (req.method === 'GET' && req.url === '/v1/models') {\n json(res, 200, { object: 'list', data: Object.keys(router.config.routes).filter(id => modelPrefix ? id.startsWith(modelPrefix) : !id.includes('::'))\n .map(id => ({ id: modelPrefix ? id.slice(modelPrefix.length) : id, object: 'model', created: 0, owned_by: 'multi-router' })) }); return;\n }\n if (req.method !== 'POST' || req.url !== '/v1/chat/completions') throw new RouteError(404, 'not_found');\n if (!/^application\\/json(?:\\s*;|$)/i.test(req.headers['content-type'] ?? '')) throw new RouteError(415, 'json_required');\n let size = 0; const chunks = [];\n for await (const chunk of req) {\n size += chunk.length;\n if (size > 32 * 1024 * 1024) throw new RouteError(413, 'request_too_large');\n chunks.push(chunk); signal.throwIfAborted();\n }\n let input;\n try { input = JSON.parse(Buffer.concat(chunks).toString('utf8')); }\n catch { throw new RouteError(400, 'invalid_json'); }\n if (!input || typeof input !== 'object' || Array.isArray(input)) throw new RouteError(400, 'invalid_request');\n const effort = req.headers['x-multi-router-reasoning'];\n if (effort !== undefined) {\n if (effort !== 'default' && !REASONING_EFFORTS.includes(effort)) throw new RouteError(400, 'invalid_reasoning_effort');\n input.reasoning_effort = effort === 'default' ? null : effort;\n }\n const selected = await router.chat({ ...input, model: modelPrefix + input.model }, signal, { callId: typeof req.headers['x-multi-router-call'] === 'string' ? req.headers['x-multi-router-call'] : undefined, session: typeof req.headers['x-multi-router-session'] === 'string' ? req.headers['x-multi-router-session'] : undefined });\n signal.throwIfAborted();\n res.writeHead(200, { 'Content-Type': 'text/event-stream; charset=utf-8', 'Cache-Control': 'no-cache',\n 'X-Accel-Buffering': 'no', 'X-Router-Account': selected.account, 'X-Router-Provider': selected.provider });\n await pipeline(Readable.fromWeb(selected.response.body), res, { signal });\n } catch (error) {\n if (res.destroyed) return;\n if (res.headersSent) { res.destroy(); return; }\n const status = error instanceof RouteError ? error.status : signal.aborted ? 504 : 500;\n json(res, status, { error: { type: error instanceof RouteError ? error.code : signal.aborted ? 'request_timeout' : 'internal_error',\n message: error instanceof RouteError ? error.message : 'Router request failed',\n hint: error instanceof RouteError ? error.hint : undefined,\n attempts: error instanceof RouteError ? error.attempts : [] } });\n } finally { active--; }\n }\n return { ready, active: () => active, token: () => secret, baseUrl: () => `http://127.0.0.1:${server.address().port}`,\n // Stop accepting new work, let an already streaming response finish. A\n // reload can race a call that began immediately after the idle check.\n close: async () => { await ready.catch(() => {}); await new Promise(resolve => server.close(resolve)); } };\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
package/lib/cli.js DELETED
@@ -1,103 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- createAccountClient
4
- } from "./chunk-NZF36O5I.js";
5
- import {
6
- importAccount
7
- } from "./chunk-2UM45OXY.js";
8
- import "./chunk-FLL3DUVP.js";
9
- import "./chunk-KWXLQJS5.js";
10
- import "./chunk-S5KMFAQM.js";
11
- import "./chunk-4V52V5GO.js";
12
- import {
13
- Router
14
- } from "./chunk-4L2CBGXN.js";
15
- import {
16
- DEFAULT_ROUTES,
17
- PROVIDERS,
18
- validateConfig,
19
- writePrivateJson
20
- } from "./chunk-GEBZMIMA.js";
21
-
22
- // src/cli.mjs
23
- import { parseArgs } from "node:util";
24
- import { readFile } from "node:fs/promises";
25
- import { dirname, join, relative, resolve } from "node:path";
26
- import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
27
- import { withFileLock } from "@deepseek-ai/dsh-atomic-write";
28
- var { values, positionals } = parseArgs({ allowPositionals: true, options: Object.fromEntries(
29
- ["config", "id", "provider", "source", "edition", "identity"].map((key) => [key, { type: "string" }])
30
- ) });
31
- var file = resolve(values.config ?? process.env.DSH_MULTI_ROUTER_CONFIG ?? join(resolveDshHome(), "multi-router", "router.json"));
32
- var [command] = positionals;
33
- async function main() {
34
- if (command === "init") {
35
- await readFile(file).then(() => {
36
- throw new Error("Config already exists");
37
- }, (error) => {
38
- if (error.code !== "ENOENT") throw error;
39
- });
40
- await writePrivateJson(file, { accounts: [], routes: DEFAULT_ROUTES });
41
- console.log(`Created ${file}`);
42
- return;
43
- }
44
- if (!["add", "remove", "enable", "disable", "status", "models"].includes(command)) {
45
- console.log("dsh-multi-router init|add|remove|enable|disable|status|models [--config /path/router.json]\nadd --id wb1 --provider workbuddy --source /path/auth-file\nTrae add: --edition solo; own credential JSON: --identity /path/identity.json\nremove|enable|disable|models: --id account-id\nProviders: " + PROVIDERS.join(", "));
46
- return;
47
- }
48
- const raw = JSON.parse(await readFile(file, "utf8"));
49
- let config = validateConfig(raw, file);
50
- if (command === "status") {
51
- console.log(JSON.stringify(new Router(config, () => {
52
- }).status(), null, 2));
53
- return;
54
- }
55
- if (command === "add") {
56
- const { id, provider, source } = values;
57
- if (!/^[a-zA-Z0-9_-]{1,64}$/.test(id ?? "") || !PROVIDERS.includes(provider) || !source) throw new Error("add needs valid --id, --provider and --source");
58
- if (raw.accounts.some((a) => a.id === id)) throw new Error("Account already exists; remove it before replacing");
59
- const dir = join(dirname(file), "accounts", id);
60
- const credentialFile = join(dir, "credential.json");
61
- const identityFile = join(dir, "identity.json");
62
- await importAccount({
63
- provider,
64
- source: resolve(source),
65
- credentialFile,
66
- identityFile,
67
- edition: values.edition,
68
- identitySource: values.identity ? resolve(values.identity) : void 0
69
- });
70
- raw.accounts.push({
71
- id,
72
- provider,
73
- credentialFile: relative(dirname(file), credentialFile),
74
- ...provider.startsWith("trae") ? { identityFile: relative(dirname(file), identityFile) } : {}
75
- });
76
- validateConfig(raw, file);
77
- await writePrivateJson(file, raw);
78
- console.log(`Added ${id} (${provider}). The running plugin applies changes automatically.`);
79
- return;
80
- }
81
- const account = config.accounts.find((a) => a.id === values.id);
82
- if (!account) throw new Error("Unknown --id");
83
- if (command === "models") {
84
- const client = await createAccountClient(account, config);
85
- console.log(JSON.stringify(await client.models(AbortSignal.timeout(6e4)), null, 2));
86
- return;
87
- }
88
- if (command === "remove") {
89
- raw.accounts = raw.accounts.filter((a) => a.id !== account.id);
90
- validateConfig(raw, file);
91
- await writePrivateJson(file, raw);
92
- console.log(`Removed ${account.id} from routing; credential files kept. Changes apply automatically.`);
93
- return;
94
- }
95
- raw.accounts.find((a) => a.id === account.id).enabled = command === "enable";
96
- await writePrivateJson(file, raw);
97
- console.log(`${command} ${account.id}. Changes apply automatically.`);
98
- }
99
- (["add", "remove", "enable", "disable"].includes(command) ? withFileLock(file, main, { waitMs: 3e4 }) : main()).catch((error) => {
100
- console.error(error instanceof Error ? error.message.replace(/Bearer\s+\S+/gi, "Bearer [redacted]") : "Command failed");
101
- process.exitCode = 1;
102
- });
103
- //# sourceMappingURL=cli.js.map
package/lib/cli.js.map DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/cli.mjs"],
4
- "sourcesContent": ["#!/usr/bin/env node\nimport { parseArgs } from 'node:util';\nimport { readFile } from 'node:fs/promises';\nimport { dirname, join, relative, resolve } from 'node:path';\nimport { resolveDshHome } from '@deepseek-ai/dsh-home-paths';\nimport { withFileLock } from '@deepseek-ai/dsh-atomic-write';\nimport { PROVIDERS, DEFAULT_ROUTES, readConfig, validateConfig, writePrivateJson } from './config.mjs';\nimport { importAccount } from './accounts.mjs';\nimport { createAccountClient } from './clients.mjs';\nimport { Router } from './router.mjs';\n\nconst { values, positionals } = parseArgs({ allowPositionals: true, options: Object.fromEntries(\n ['config', 'id', 'provider', 'source', 'edition', 'identity'].map(key => [key, { type: 'string' }])) });\nconst file = resolve(values.config ?? process.env.DSH_MULTI_ROUTER_CONFIG ?? join(resolveDshHome(), 'multi-router', 'router.json'));\nconst [command] = positionals;\nasync function main() {\n if (command === 'init') {\n await readFile(file).then(() => { throw new Error('Config already exists'); }, error => { if (error.code !== 'ENOENT') throw error; });\n await writePrivateJson(file, { accounts: [], routes: DEFAULT_ROUTES });\n console.log(`Created ${file}`); return;\n }\n if (!['add', 'remove', 'enable', 'disable', 'status', 'models'].includes(command)) {\n console.log('dsh-multi-router init|add|remove|enable|disable|status|models [--config /path/router.json]\\n'\n + 'add --id wb1 --provider workbuddy --source /path/auth-file\\n'\n + 'Trae add: --edition solo; own credential JSON: --identity /path/identity.json\\n'\n + 'remove|enable|disable|models: --id account-id\\nProviders: ' + PROVIDERS.join(', ')); return;\n }\n const raw = JSON.parse(await readFile(file, 'utf8'));\n let config = validateConfig(raw, file);\n if (command === 'status') {\n console.log(JSON.stringify(new Router(config, () => {}).status(), null, 2)); return;\n }\n if (command === 'add') {\n const { id, provider, source } = values;\n if (!/^[a-zA-Z0-9_-]{1,64}$/.test(id ?? '') || !PROVIDERS.includes(provider) || !source) throw new Error('add needs valid --id, --provider and --source');\n if (raw.accounts.some(a => a.id === id)) throw new Error('Account already exists; remove it before replacing');\n const dir = join(dirname(file), 'accounts', id);\n const credentialFile = join(dir, 'credential.json');\n const identityFile = join(dir, 'identity.json');\n await importAccount({ provider, source: resolve(source), credentialFile, identityFile, edition: values.edition,\n identitySource: values.identity ? resolve(values.identity) : undefined });\n raw.accounts.push({ id, provider, credentialFile: relative(dirname(file), credentialFile),\n ...(provider.startsWith('trae') ? { identityFile: relative(dirname(file), identityFile) } : {}) });\n validateConfig(raw, file); await writePrivateJson(file, raw);\n console.log(`Added ${id} (${provider}). The running plugin applies changes automatically.`); return;\n }\n const account = config.accounts.find(a => a.id === values.id);\n if (!account) throw new Error('Unknown --id');\n if (command === 'models') {\n const client = await createAccountClient(account, config);\n console.log(JSON.stringify(await client.models(AbortSignal.timeout(60000)), null, 2)); return;\n }\n if (command === 'remove') {\n raw.accounts = raw.accounts.filter(a => a.id !== account.id);\n // Reject dangling route bindings before changing files. Credential files remain recoverable.\n validateConfig(raw, file); await writePrivateJson(file, raw);\n console.log(`Removed ${account.id} from routing; credential files kept. Changes apply automatically.`); return;\n }\n raw.accounts.find(a => a.id === account.id).enabled = command === 'enable';\n await writePrivateJson(file, raw);\n console.log(`${command} ${account.id}. Changes apply automatically.`);\n}\n(['add', 'remove', 'enable', 'disable'].includes(command) ? withFileLock(file, main, { waitMs: 30000 }) : main())\n .catch(error => { console.error(error instanceof Error ? error.message.replace(/Bearer\\s+\\S+/gi, 'Bearer [redacted]') : 'Command failed'); process.exitCode = 1; });\n"],
5
- "mappings": "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",
6
- "names": []
7
- }