@maci0/dsh-omniroute 0.0.0-stage → 0.8.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/LICENSE +21 -0
- package/README.md +226 -2
- package/cordis.patch.yml +10 -0
- package/icon.svg +8 -0
- package/lib/client.js +383 -0
- package/lib/host.js +12 -0
- package/lib/index.js +403 -0
- package/lib/omniroute.js +206 -0
- package/lib/types/host.d.ts +112 -0
- package/lib/types/index.d.ts +111 -0
- package/lib/types/omniroute.d.ts +159 -0
- package/lib/types/util.d.ts +15 -0
- package/lib/util.js +24 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +93 -4
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The slice of the DeepSeek Harness host surface this plugin uses, declared
|
|
3
|
+
* structurally.
|
|
4
|
+
*
|
|
5
|
+
* The plugin installs from outside the harness checkout and has no runtime
|
|
6
|
+
* dependency on harness packages: the services it reaches are typed here, and
|
|
7
|
+
* a composition that mounts none of them simply omits that capability. The
|
|
8
|
+
* same shape also lets the unit tests drive `apply` with a plain fake context.
|
|
9
|
+
*
|
|
10
|
+
* @module dsh-omniroute/host
|
|
11
|
+
*/
|
|
12
|
+
/** Disposer returned by every host registration. */
|
|
13
|
+
export type Disposable = () => void;
|
|
14
|
+
/** The slice of the credentials service this plugin reads. */
|
|
15
|
+
export interface CredentialsLike {
|
|
16
|
+
/**
|
|
17
|
+
* Resolve one credential reference to its value.
|
|
18
|
+
* @param ref - environment-variable-style reference name.
|
|
19
|
+
* @returns the resolved value, or `undefined` when nothing is stored.
|
|
20
|
+
*/
|
|
21
|
+
resolve(ref: string): Promise<{
|
|
22
|
+
readonly value: string;
|
|
23
|
+
} | undefined>;
|
|
24
|
+
}
|
|
25
|
+
/** The slice of the settings service this plugin writes through. */
|
|
26
|
+
export interface SettingsLike {
|
|
27
|
+
/**
|
|
28
|
+
* Merge a patch into one profile entry.
|
|
29
|
+
* @param ns - profile entry id, not a settings namespace.
|
|
30
|
+
* @param patch - plain-object patch over that entry.
|
|
31
|
+
* @returns when the write is persisted.
|
|
32
|
+
*/
|
|
33
|
+
update(ns: string, patch: object): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
/** Request subset the trust fence reads. */
|
|
36
|
+
export interface RequestLike {
|
|
37
|
+
/** Request method. */
|
|
38
|
+
readonly method?: string | undefined;
|
|
39
|
+
/** Request target, including the query string. */
|
|
40
|
+
readonly url?: string | undefined;
|
|
41
|
+
/** Request headers, read by the composition's trust fence. */
|
|
42
|
+
readonly headers: object | undefined;
|
|
43
|
+
}
|
|
44
|
+
/** Response subset this plugin writes. */
|
|
45
|
+
export interface ResponseLike {
|
|
46
|
+
/** HTTP status code. */
|
|
47
|
+
statusCode: number;
|
|
48
|
+
/** Set one response header. */
|
|
49
|
+
setHeader(name: string, value: string): void;
|
|
50
|
+
/** End the response, optionally with a body. */
|
|
51
|
+
end(body?: string): void;
|
|
52
|
+
}
|
|
53
|
+
/** One exact-path route registration. */
|
|
54
|
+
export interface WebRouteLike {
|
|
55
|
+
/** Match kind; this plugin registers only exact paths. */
|
|
56
|
+
readonly kind: 'exact';
|
|
57
|
+
/** Absolute pathname, no trailing slash. */
|
|
58
|
+
readonly path: string;
|
|
59
|
+
/** Owns the full response lifecycle. */
|
|
60
|
+
handler: (req: RequestLike, res: ResponseLike) => void | Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
/** The slice of the HTTP carrier this plugin registers on. */
|
|
63
|
+
export interface WebServerLike {
|
|
64
|
+
/**
|
|
65
|
+
* Register one route.
|
|
66
|
+
* @param route - path, match kind, and handler.
|
|
67
|
+
* @returns the disposer that withdraws it.
|
|
68
|
+
*/
|
|
69
|
+
register(route: WebRouteLike): Disposable;
|
|
70
|
+
}
|
|
71
|
+
/** The composition's trust fence. */
|
|
72
|
+
export interface ConnectionLike {
|
|
73
|
+
/**
|
|
74
|
+
* Reject an untrusted or unauthenticated request.
|
|
75
|
+
* @param request - headers of the incoming request.
|
|
76
|
+
* @returns the rejection status, or `undefined` when the request may proceed.
|
|
77
|
+
*/
|
|
78
|
+
requestRejection(request: {
|
|
79
|
+
readonly headers: object | undefined;
|
|
80
|
+
}): 401 | 403 | undefined;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Structural view of the Cordis context this plugin uses.
|
|
84
|
+
*
|
|
85
|
+
* `inject` guarantees `webServer` and `connection`; the credentials and
|
|
86
|
+
* settings services are read through `get`, so a composition without them
|
|
87
|
+
* degrades to "no credential to read with" and refuses the model sync instead
|
|
88
|
+
* of failing to mount.
|
|
89
|
+
*/
|
|
90
|
+
export interface HostContext {
|
|
91
|
+
/** Bind a registration's lifetime to this plugin's fiber. */
|
|
92
|
+
effect(callback: () => Disposable | void, label?: string): unknown;
|
|
93
|
+
/**
|
|
94
|
+
* Subscribe to a host event. This plugin watches `loader/volatile-update`,
|
|
95
|
+
* which is what a settings write emits once the live row references moved.
|
|
96
|
+
* @param event - the event name.
|
|
97
|
+
* @param listener - the callback.
|
|
98
|
+
* @returns the disposer that removes this listener.
|
|
99
|
+
*/
|
|
100
|
+
on(event: 'loader/volatile-update', listener: () => void): Disposable;
|
|
101
|
+
/** Read one optional service. */
|
|
102
|
+
get(name: string): unknown;
|
|
103
|
+
/** Structured log surface. */
|
|
104
|
+
readonly logger: {
|
|
105
|
+
warn(message: string): void;
|
|
106
|
+
error(message: string): void;
|
|
107
|
+
};
|
|
108
|
+
/** HTTP route carrier (guaranteed by `inject`). */
|
|
109
|
+
readonly webServer: WebServerLike;
|
|
110
|
+
/** Trust fence every route checks first (guaranteed by `inject`). */
|
|
111
|
+
readonly connection: ConnectionLike;
|
|
112
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-omniroute: OmniRoute's own API surface, inside DeepSeek Harness.
|
|
3
|
+
*
|
|
4
|
+
* Three read-only routes, all behind the composition's trust fence and all
|
|
5
|
+
* serving JSON:
|
|
6
|
+
*
|
|
7
|
+
* - `GET /omniroute/models`: the live model catalog from OmniRoute's
|
|
8
|
+
* `GET /api/models`: which models exist, which ones the router can serve
|
|
9
|
+
* right now, and which take images. `?available=1`, `?q=<text>` and
|
|
10
|
+
* `?provider=<key>` narrow it. `?sync=1` also writes the available models
|
|
11
|
+
* into a configured provider route, which is how the model picker learns
|
|
12
|
+
* them.
|
|
13
|
+
* - `GET /omniroute/connections`: the upstream connections from
|
|
14
|
+
* `GET /api/providers`.
|
|
15
|
+
* - `GET /omniroute/quota`: the plan and windows of every connection that
|
|
16
|
+
* publishes one, from `GET /api/usage/<connectionId>`.
|
|
17
|
+
*
|
|
18
|
+
* The API key stays in this process: the browser or the model only ever reads
|
|
19
|
+
* the JSON these routes return. Readings are cached for a few seconds per
|
|
20
|
+
* route, so a refresh loop cannot multiply requests to the router.
|
|
21
|
+
*
|
|
22
|
+
* @module dsh-omniroute
|
|
23
|
+
*/
|
|
24
|
+
import type { Volatile } from '@deepseek-ai/cordis';
|
|
25
|
+
import Schema from '@deepseek-ai/schemastery';
|
|
26
|
+
import type { HostContext } from './host.ts';
|
|
27
|
+
/** Plugin name as it appears in the loader. */
|
|
28
|
+
export declare const name = "omniroute";
|
|
29
|
+
/**
|
|
30
|
+
* The route carrier, and the trust fence every route checks first: without
|
|
31
|
+
* `connection` nothing would refuse a cross-origin or unauthenticated caller,
|
|
32
|
+
* so the plugin waits for it rather than serving unfenced.
|
|
33
|
+
*/
|
|
34
|
+
export declare const inject: string[];
|
|
35
|
+
/** The live model catalog. */
|
|
36
|
+
export declare const ROUTE_MODELS = "/omniroute/models";
|
|
37
|
+
/** The upstream connections. */
|
|
38
|
+
export declare const ROUTE_CONNECTIONS = "/omniroute/connections";
|
|
39
|
+
/** The quota every connection publishes. */
|
|
40
|
+
export declare const ROUTE_QUOTA = "/omniroute/quota";
|
|
41
|
+
/** Where an OmniRoute deployment listens unless configuration says otherwise. */
|
|
42
|
+
export declare const DEFAULT_BASE_URL = "http://localhost:20128";
|
|
43
|
+
/** Credential reference tried when the plugin row names none. */
|
|
44
|
+
export declare const DEFAULT_API_KEY_ENV = "OMNIROUTE_API_KEY";
|
|
45
|
+
/** Per-request deadline for one OmniRoute call, in milliseconds. */
|
|
46
|
+
export declare const DEFAULT_TIMEOUT_MS = 10000;
|
|
47
|
+
/** Seconds one reading is served without asking OmniRoute again. */
|
|
48
|
+
export declare const DEFAULT_CACHE_SECONDS = 30;
|
|
49
|
+
/** Profile entry id of the provider route a model sync writes into. */
|
|
50
|
+
export declare const DEFAULT_SYNC_NAMESPACE = "llm-pi-ai";
|
|
51
|
+
/** Provider route a model sync writes into. */
|
|
52
|
+
export declare const DEFAULT_SYNC_PROVIDER = "omniroute";
|
|
53
|
+
/**
|
|
54
|
+
* Configuration this plugin's row resolves to, as `apply` receives it.
|
|
55
|
+
*
|
|
56
|
+
* Every field is `volatile()` (the settings document accepts writes only under
|
|
57
|
+
* a volatile node, and the Plugins page's OmniRoute card edits exactly these),
|
|
58
|
+
* so the loader hands live references and each is read per request. `baseURL`
|
|
59
|
+
* and `apiKeyEnv` decide where the plugin talks and as whom; the two sync
|
|
60
|
+
* fields name the row `?sync=1` writes a model catalog into.
|
|
61
|
+
*/
|
|
62
|
+
export interface Config {
|
|
63
|
+
/** OmniRoute origin, with or without its `/v1` path. @default http://localhost:20128 */
|
|
64
|
+
readonly baseURL: Volatile<string>;
|
|
65
|
+
/** Credential reference holding the OmniRoute API key. @default OMNIROUTE_API_KEY */
|
|
66
|
+
readonly apiKeyEnv: Volatile<string>;
|
|
67
|
+
/** Per-request deadline in milliseconds. @default 10000 */
|
|
68
|
+
readonly timeoutMs: Volatile<number>;
|
|
69
|
+
/** Seconds a reading stays cached. `0` re-asks on every request. @default 30 */
|
|
70
|
+
readonly cacheSeconds: Volatile<number>;
|
|
71
|
+
/** Profile entry id `?sync=1` writes the model catalog into. @default llm-pi-ai */
|
|
72
|
+
readonly syncNamespace: Volatile<string>;
|
|
73
|
+
/** Provider route `?sync=1` writes the model catalog into. @default omniroute */
|
|
74
|
+
readonly syncProvider: Volatile<string>;
|
|
75
|
+
}
|
|
76
|
+
/** Raw row values, as a profile patch states them and as direct callers pass them. */
|
|
77
|
+
export type Options = {
|
|
78
|
+
[K in keyof Config]?: Config[K] extends Volatile<infer T> ? T : Config[K];
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Row schema as Cordis resolves it: what this plugin's `config` is validated
|
|
82
|
+
* against, and where each default lives. Every field is editable from the
|
|
83
|
+
* Plugins page, so every one is volatile.
|
|
84
|
+
*/
|
|
85
|
+
export declare const Config: Schema<Schemastery.ObjectS<NoInfer<{
|
|
86
|
+
baseURL: Schema<string, string, "volatile-defined">;
|
|
87
|
+
apiKeyEnv: Schema<string, string, "volatile-defined">;
|
|
88
|
+
timeoutMs: Schema<number, number, "volatile-defined">;
|
|
89
|
+
cacheSeconds: Schema<number, number, "volatile-defined">;
|
|
90
|
+
syncNamespace: Schema<string, string, "volatile-defined">;
|
|
91
|
+
syncProvider: Schema<string, string, "volatile-defined">;
|
|
92
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
93
|
+
baseURL: Schema<string, string, "volatile-defined">;
|
|
94
|
+
apiKeyEnv: Schema<string, string, "volatile-defined">;
|
|
95
|
+
timeoutMs: Schema<number, number, "volatile-defined">;
|
|
96
|
+
cacheSeconds: Schema<number, number, "volatile-defined">;
|
|
97
|
+
syncNamespace: Schema<string, string, "volatile-defined">;
|
|
98
|
+
syncProvider: Schema<string, string, "volatile-defined">;
|
|
99
|
+
}>>, "plain">;
|
|
100
|
+
/**
|
|
101
|
+
* Turn a row (live references or plain values) into validated plain options.
|
|
102
|
+
* @param row - the configured row.
|
|
103
|
+
* @returns the resolved options, defaults filled by the schema.
|
|
104
|
+
*/
|
|
105
|
+
export declare function resolveRow(row?: Config | Options): Required<Options>;
|
|
106
|
+
/**
|
|
107
|
+
* Mount the host half.
|
|
108
|
+
* @param ctx - host context carrying the route carrier.
|
|
109
|
+
* @param config - this plugin's row configuration.
|
|
110
|
+
*/
|
|
111
|
+
export declare function apply(ctx: HostContext, row?: Config | Options): void;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OmniRoute management-API client: the live model catalog, the provider
|
|
3
|
+
* connections, and the quota each connection reports.
|
|
4
|
+
*
|
|
5
|
+
* The endpoints are OmniRoute's own, read from a running `v3.8.50` server's
|
|
6
|
+
* `/api/openapi/spec` and `/api/agent-skills` directory:
|
|
7
|
+
*
|
|
8
|
+
* - `GET /api/models`: every model across the configured providers, each with
|
|
9
|
+
* an `available` flag and a `supportsVision` flag. This is the management
|
|
10
|
+
* listing, not the OpenAI-shaped `GET /v1/models`: it is the one that says
|
|
11
|
+
* which models the router can actually serve *now*.
|
|
12
|
+
* - `GET /api/providers`: the upstream connections. Each carries the `id` that
|
|
13
|
+
* the per-connection usage route addresses, plus `quotaVisible` and
|
|
14
|
+
* `isActive`.
|
|
15
|
+
* - `GET /api/usage/<connectionId>`: that connection's plan, its windows
|
|
16
|
+
* (`session`, `weekly`, `credits_usd`, …), and whether the limit is reached.
|
|
17
|
+
*
|
|
18
|
+
* OmniRoute routes to accounts it holds, so it publishes no figure for the
|
|
19
|
+
* router itself; quota is read per connection and flattened into one list.
|
|
20
|
+
* `/api/quota/plans` resolves plans but carries no consumption, and
|
|
21
|
+
* `/api/quota/pools` is empty unless the deployment defines pools, so neither
|
|
22
|
+
* is used here.
|
|
23
|
+
*
|
|
24
|
+
* Everything below the `createOmniRouteApi` line is pure parsing; the tests
|
|
25
|
+
* drive it with payloads copied from a live server.
|
|
26
|
+
*
|
|
27
|
+
* @module dsh-omniroute/omniroute
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* A failure this plugin phrased itself. Only its message reaches a route
|
|
31
|
+
* reply; any other error is logged on the host and answered generically.
|
|
32
|
+
*/
|
|
33
|
+
export declare class OmniRouteError extends Error {
|
|
34
|
+
readonly name = "OmniRouteError";
|
|
35
|
+
}
|
|
36
|
+
/** One model OmniRoute advertises. */
|
|
37
|
+
export interface OmniRouteModel {
|
|
38
|
+
/** Id a request names: the `fullModel` when the listing has one. */
|
|
39
|
+
readonly id: string;
|
|
40
|
+
/** Upstream provider key, e.g. `glm` or `cc`. */
|
|
41
|
+
readonly provider: string;
|
|
42
|
+
/** Display name, when the listing carries one. */
|
|
43
|
+
readonly name?: string;
|
|
44
|
+
/** Short alias, when the listing carries one. */
|
|
45
|
+
readonly alias?: string;
|
|
46
|
+
/** Whether the router can serve it right now. */
|
|
47
|
+
readonly available: boolean;
|
|
48
|
+
/** Whether it accepts images. */
|
|
49
|
+
readonly vision: boolean;
|
|
50
|
+
}
|
|
51
|
+
/** One upstream connection OmniRoute holds. */
|
|
52
|
+
export interface OmniRouteConnection {
|
|
53
|
+
/** Connection id, which the usage route addresses. */
|
|
54
|
+
readonly id: string;
|
|
55
|
+
/** Upstream provider key, e.g. `deepseek`. */
|
|
56
|
+
readonly provider: string;
|
|
57
|
+
/** Human label for the connection, e.g. an account address. */
|
|
58
|
+
readonly name?: string;
|
|
59
|
+
/** Whether the connection is switched on. */
|
|
60
|
+
readonly active: boolean;
|
|
61
|
+
/** Whether the connection publishes its quota. */
|
|
62
|
+
readonly quotaVisible: boolean;
|
|
63
|
+
}
|
|
64
|
+
/** One quota window of one connection. */
|
|
65
|
+
export interface OmniRouteQuota {
|
|
66
|
+
/** Connection the window belongs to. */
|
|
67
|
+
readonly connection: string;
|
|
68
|
+
/** Upstream provider key of that connection. */
|
|
69
|
+
readonly provider: string;
|
|
70
|
+
/** Resolved plan name the connection reported, e.g. `plus` or `DeepSeek`. */
|
|
71
|
+
readonly plan?: string;
|
|
72
|
+
/** Window name as OmniRoute spells it, e.g. `session (5h)` or `credits_usd`. */
|
|
73
|
+
readonly window: string;
|
|
74
|
+
/** Consumed amount, in the window's own unit. */
|
|
75
|
+
readonly used?: number;
|
|
76
|
+
/** Window ceiling, when it has one. */
|
|
77
|
+
readonly total?: number;
|
|
78
|
+
/** Amount left, when the connection reports one. */
|
|
79
|
+
readonly remaining?: number;
|
|
80
|
+
/** Percentage left as the connection reports it. */
|
|
81
|
+
readonly remainingPercent?: number;
|
|
82
|
+
/** Whether the connection reports no ceiling at all. */
|
|
83
|
+
readonly unlimited: boolean;
|
|
84
|
+
/** ISO instant the window resets, when it reports one. */
|
|
85
|
+
readonly resetAt?: string;
|
|
86
|
+
/** Currency of a money window, when it reports one. */
|
|
87
|
+
readonly currency?: string;
|
|
88
|
+
/** Whether the connection says the limit is reached. */
|
|
89
|
+
readonly limitReached: boolean;
|
|
90
|
+
}
|
|
91
|
+
/** Everything the client needs to talk to one OmniRoute deployment. */
|
|
92
|
+
export interface OmniRouteApiOptions {
|
|
93
|
+
/** Deployment origin, without the `/v1` path, e.g. `http://box:20128`. */
|
|
94
|
+
readonly origin: string;
|
|
95
|
+
/** OmniRoute API key, sent as a bearer token. */
|
|
96
|
+
readonly key: string;
|
|
97
|
+
/** Per-request deadline in milliseconds. */
|
|
98
|
+
readonly timeoutMs: number;
|
|
99
|
+
/** Transport override, for tests. @default globalThis.fetch */
|
|
100
|
+
readonly fetchImpl?: typeof fetch;
|
|
101
|
+
}
|
|
102
|
+
/** The three reads this plugin offers. */
|
|
103
|
+
export interface OmniRouteApi {
|
|
104
|
+
/**
|
|
105
|
+
* List every advertised model.
|
|
106
|
+
* @returns the catalog in listing order.
|
|
107
|
+
*/
|
|
108
|
+
models(): Promise<readonly OmniRouteModel[]>;
|
|
109
|
+
/**
|
|
110
|
+
* List every upstream connection.
|
|
111
|
+
* @returns the connections in listing order.
|
|
112
|
+
*/
|
|
113
|
+
connections(): Promise<readonly OmniRouteConnection[]>;
|
|
114
|
+
/**
|
|
115
|
+
* Read the quota of every connection that publishes one.
|
|
116
|
+
* @param listed - connections already read, so the caller does not make this
|
|
117
|
+
* client ask `/api/providers` a second time for the same reading.
|
|
118
|
+
* @returns one entry per window, connections in listing order.
|
|
119
|
+
*/
|
|
120
|
+
quota(listed?: readonly OmniRouteConnection[]): Promise<readonly OmniRouteQuota[]>;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Parse a `GET /api/models` body.
|
|
124
|
+
* @param payload - the decoded body.
|
|
125
|
+
* @returns one entry per model the body carried.
|
|
126
|
+
*/
|
|
127
|
+
export declare function parseModels(payload: unknown): readonly OmniRouteModel[];
|
|
128
|
+
/**
|
|
129
|
+
* Parse a `GET /api/providers` body.
|
|
130
|
+
* @param payload - the decoded body.
|
|
131
|
+
* @returns one entry per connection the body carried.
|
|
132
|
+
*/
|
|
133
|
+
export declare function parseConnections(payload: unknown): readonly OmniRouteConnection[];
|
|
134
|
+
/**
|
|
135
|
+
* Parse one `GET /api/usage/<connectionId>` body.
|
|
136
|
+
* @param payload - the decoded body.
|
|
137
|
+
* @param connection - the connection it was asked about.
|
|
138
|
+
* @returns one entry per window the body carried.
|
|
139
|
+
*/
|
|
140
|
+
export declare function parseUsage(payload: unknown, connection: OmniRouteConnection): readonly OmniRouteQuota[];
|
|
141
|
+
/**
|
|
142
|
+
* What a configured base URL must start with: an http(s) scheme and a host.
|
|
143
|
+
* A scheme-less `box:20128` parses as a URL whose origin is the string "null".
|
|
144
|
+
*/
|
|
145
|
+
export declare const BASE_URL_PATTERN: RegExp;
|
|
146
|
+
/**
|
|
147
|
+
* Origin of a configured base URL, which is what carries OmniRoute's own API.
|
|
148
|
+
* @param baseURL - the configured value, with or without its `/v1` path.
|
|
149
|
+
* @returns the origin.
|
|
150
|
+
* @throws OmniRouteError when the value is not an absolute http(s) URL: no
|
|
151
|
+
* other origin is guessed, so the key never goes to a host nobody configured.
|
|
152
|
+
*/
|
|
153
|
+
export declare function originOf(baseURL: string): string;
|
|
154
|
+
/**
|
|
155
|
+
* Build the client for one deployment.
|
|
156
|
+
* @param options - origin, credential, deadline, and transport.
|
|
157
|
+
* @returns the three reads, each failing loud on a refused request.
|
|
158
|
+
*/
|
|
159
|
+
export declare function createOmniRouteApi(options: OmniRouteApiOptions): OmniRouteApi;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Narrowing helpers for the OmniRoute client: every payload field is an
|
|
3
|
+
* `unknown` until one of these reads it.
|
|
4
|
+
*
|
|
5
|
+
* This package's own copy. The two plugins ship independently and share no
|
|
6
|
+
* package, so each keeps the handful of lines it narrows with.
|
|
7
|
+
*
|
|
8
|
+
* @module dsh-omniroute/util
|
|
9
|
+
*/
|
|
10
|
+
/** Narrow an unknown to an indexable object. */
|
|
11
|
+
export declare function record(value: unknown): Record<string, unknown> | undefined;
|
|
12
|
+
/** Read one field as a non-empty string, or `undefined`. */
|
|
13
|
+
export declare function stringOf(value: unknown): string | undefined;
|
|
14
|
+
/** Read one field as a finite number, or `undefined`. */
|
|
15
|
+
export declare function numberOf(value: unknown): number | undefined;
|
package/lib/util.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Narrowing helpers for the OmniRoute client: every payload field is an
|
|
3
|
+
* `unknown` until one of these reads it.
|
|
4
|
+
*
|
|
5
|
+
* This package's own copy. The two plugins ship independently and share no
|
|
6
|
+
* package, so each keeps the handful of lines it narrows with.
|
|
7
|
+
*
|
|
8
|
+
* @module dsh-omniroute/util
|
|
9
|
+
*/
|
|
10
|
+
/** Narrow an unknown to an indexable object. */
|
|
11
|
+
export function record(value) {
|
|
12
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
13
|
+
? value
|
|
14
|
+
: undefined;
|
|
15
|
+
}
|
|
16
|
+
/** Read one field as a non-empty string, or `undefined`. */
|
|
17
|
+
export function stringOf(value) {
|
|
18
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
19
|
+
}
|
|
20
|
+
/** Read one field as a finite number, or `undefined`. */
|
|
21
|
+
export function numberOf(value) {
|
|
22
|
+
const parsed = typeof value === 'string' && value.trim() !== '' ? Number(value) : value;
|
|
23
|
+
return typeof parsed === 'number' && Number.isFinite(parsed) ? parsed : undefined;
|
|
24
|
+
}
|
package/locale/en.json
ADDED
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,95 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maci0/dsh-omniroute",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
"version": "0.8.2",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public",
|
|
6
|
+
"registry": "https://registry.npmjs.org/"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "lib/index.js",
|
|
10
|
+
"types": "lib/types/index.d.ts",
|
|
11
|
+
"description": "OmniRoute provider surface for DeepSeek Harness: query its live model catalog, its connections, and the quota each upstream connection reports.",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/maci0/dsh-omniroute.git"
|
|
16
|
+
},
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./lib/types/index.d.ts",
|
|
20
|
+
"default": "./lib/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./client": {
|
|
23
|
+
"default": "./lib/client.js"
|
|
24
|
+
},
|
|
25
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
26
|
+
"./locale/*.json": "./locale/*.json",
|
|
27
|
+
"./package.json": "./package.json"
|
|
28
|
+
},
|
|
29
|
+
"dsh": {
|
|
30
|
+
"bundle": {
|
|
31
|
+
"patch": "./cordis.patch.yml"
|
|
32
|
+
},
|
|
33
|
+
"compatibility": {
|
|
34
|
+
"dsh": ">=0.2.0-rc.2 <0.3.0"
|
|
35
|
+
},
|
|
36
|
+
"client": {
|
|
37
|
+
"platform": "web",
|
|
38
|
+
"inject": [
|
|
39
|
+
"@deepseek-ai/dsh-client-locale",
|
|
40
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
41
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
42
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"icon.svg",
|
|
48
|
+
"locale/*.json",
|
|
49
|
+
"lib/**/*.js",
|
|
50
|
+
"lib/client.js",
|
|
51
|
+
"lib/types/**/*.d.ts",
|
|
52
|
+
"cordis.patch.yml",
|
|
53
|
+
"README.md",
|
|
54
|
+
"LICENSE"
|
|
55
|
+
],
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "bunx --bun tsc -p tsconfig.build.json",
|
|
58
|
+
"test": "bun test",
|
|
59
|
+
"typecheck": "bunx --bun tsc -p tsconfig.json",
|
|
60
|
+
"test:node": "node --test tests/*.test.*"
|
|
61
|
+
},
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": "^22.19.0 || >=24.0.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@deepseek-ai/cordis": "4.0.5-alpha.1",
|
|
67
|
+
"@deepseek-ai/dsh-host-webserver": "0.2.1-alpha.1",
|
|
68
|
+
"@types/node": "^22.20.2",
|
|
69
|
+
"typescript": "^7.0.2"
|
|
70
|
+
},
|
|
71
|
+
"icon": "./icon.svg",
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"@deepseek-ai/schemastery": "^3.18.4"
|
|
74
|
+
},
|
|
75
|
+
"peerDependencies": {
|
|
76
|
+
"@deepseek-ai/dsh-client-locale": "*",
|
|
77
|
+
"@deepseek-ai/dsh-client-ui-renderer": "*",
|
|
78
|
+
"@deepseek-ai/dsh-client-ui-settings": "*",
|
|
79
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager": "*"
|
|
80
|
+
},
|
|
81
|
+
"peerDependenciesMeta": {
|
|
82
|
+
"@deepseek-ai/dsh-client-locale": {
|
|
83
|
+
"optional": true
|
|
84
|
+
},
|
|
85
|
+
"@deepseek-ai/dsh-client-ui-renderer": {
|
|
86
|
+
"optional": true
|
|
87
|
+
},
|
|
88
|
+
"@deepseek-ai/dsh-client-ui-settings": {
|
|
89
|
+
"optional": true
|
|
90
|
+
},
|
|
91
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager": {
|
|
92
|
+
"optional": true
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|