@duckmind/dm-darwin-x64 0.32.9 → 0.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dm +0 -0
- package/extensions/.dm-extensions.json +14 -65
- package/extensions/dm-9router-ext/README.md +142 -0
- package/extensions/dm-9router-ext/package.json +45 -0
- package/extensions/dm-9router-ext/src/index.ts +541 -0
- package/extensions/dm-9router-ext/tsconfig.json +17 -0
- package/extensions/dm-subagents/package-lock.json +3 -3
- package/extensions/dm-tasks/node_modules/.package-lock.json +3 -3
- package/extensions/dm-tasks/node_modules/typebox/build/type/script/mapping.d.mts +2 -2
- package/extensions/dm-tasks/node_modules/typebox/build/type/script/parser.d.mts +2 -2
- package/extensions/dm-tasks/node_modules/typebox/build/type/script/parser.mjs +2 -2
- package/extensions/dm-tasks/node_modules/typebox/build/type/types/number.d.mts +1 -1
- package/extensions/dm-tasks/node_modules/typebox/build/type/types/number.mjs +1 -1
- package/extensions/dm-tasks/node_modules/typebox/build/type/types/record.d.mts +1 -1
- package/extensions/dm-tasks/node_modules/typebox/package.json +1 -1
- package/extensions/dm-tasks/package-lock.json +3 -3
- package/package.json +1 -1
- package/theme/theme-alps.json +93 -0
- package/extensions/dm-chime/README.md +0 -11
- package/extensions/dm-chime/docs/protocols.md +0 -107
- package/extensions/dm-chime/index.ts +0 -205
- package/extensions/dm-chime/package.json +0 -33
- package/extensions/dm-phone/README.md +0 -24
- package/extensions/dm-phone/index.ts +0 -12
- package/extensions/dm-phone/node_modules/.package-lock.json +0 -29
- package/extensions/dm-phone/node_modules/ws/LICENSE +0 -20
- package/extensions/dm-phone/node_modules/ws/README.md +0 -548
- package/extensions/dm-phone/node_modules/ws/browser.js +0 -8
- package/extensions/dm-phone/node_modules/ws/index.js +0 -22
- package/extensions/dm-phone/node_modules/ws/lib/buffer-util.js +0 -131
- package/extensions/dm-phone/node_modules/ws/lib/constants.js +0 -19
- package/extensions/dm-phone/node_modules/ws/lib/event-target.js +0 -292
- package/extensions/dm-phone/node_modules/ws/lib/extension.js +0 -203
- package/extensions/dm-phone/node_modules/ws/lib/limiter.js +0 -55
- package/extensions/dm-phone/node_modules/ws/lib/permessage-deflate.js +0 -528
- package/extensions/dm-phone/node_modules/ws/lib/receiver.js +0 -760
- package/extensions/dm-phone/node_modules/ws/lib/sender.js +0 -607
- package/extensions/dm-phone/node_modules/ws/lib/stream.js +0 -161
- package/extensions/dm-phone/node_modules/ws/lib/subprotocol.js +0 -62
- package/extensions/dm-phone/node_modules/ws/lib/validation.js +0 -152
- package/extensions/dm-phone/node_modules/ws/lib/websocket-server.js +0 -562
- package/extensions/dm-phone/node_modules/ws/lib/websocket.js +0 -1407
- package/extensions/dm-phone/node_modules/ws/package.json +0 -70
- package/extensions/dm-phone/node_modules/ws/wrapper.mjs +0 -21
- package/extensions/dm-phone/package-lock.json +0 -66
- package/extensions/dm-phone/package.json +0 -35
- package/extensions/dm-phone/phone-session-pool.ts +0 -8
- package/extensions/dm-phone/public/app/attachments.js +0 -233
- package/extensions/dm-phone/public/app/autocomplete-controller.js +0 -81
- package/extensions/dm-phone/public/app/autocomplete.js +0 -135
- package/extensions/dm-phone/public/app/bindings.js +0 -178
- package/extensions/dm-phone/public/app/command-catalog.js +0 -76
- package/extensions/dm-phone/public/app/commands.js +0 -376
- package/extensions/dm-phone/public/app/constants.js +0 -60
- package/extensions/dm-phone/public/app/formatters.js +0 -131
- package/extensions/dm-phone/public/app/handlers.js +0 -442
- package/extensions/dm-phone/public/app/main.js +0 -6
- package/extensions/dm-phone/public/app/markdown.js +0 -105
- package/extensions/dm-phone/public/app/messages.js +0 -418
- package/extensions/dm-phone/public/app/sheet-actions.js +0 -113
- package/extensions/dm-phone/public/app/sheet-navigation.js +0 -19
- package/extensions/dm-phone/public/app/sheets-view.js +0 -287
- package/extensions/dm-phone/public/app/state.js +0 -95
- package/extensions/dm-phone/public/app/tool-rendering.js +0 -562
- package/extensions/dm-phone/public/app/transport.js +0 -176
- package/extensions/dm-phone/public/app/ui.js +0 -417
- package/extensions/dm-phone/public/app.js +0 -1
- package/extensions/dm-phone/public/icon.svg +0 -15
- package/extensions/dm-phone/public/index.html +0 -146
- package/extensions/dm-phone/public/manifest.webmanifest +0 -17
- package/extensions/dm-phone/public/styles.css +0 -1139
- package/extensions/dm-phone/public/sw.js +0 -78
- package/extensions/dm-phone/src/extension/duckmind-models.js +0 -264
- package/extensions/dm-phone/src/extension/phone-args.ts +0 -121
- package/extensions/dm-phone/src/extension/phone-paths.ts +0 -250
- package/extensions/dm-phone/src/extension/phone-quota.ts +0 -188
- package/extensions/dm-phone/src/extension/phone-runtime.ts +0 -154
- package/extensions/dm-phone/src/extension/phone-server-runtime.ts +0 -1217
- package/extensions/dm-phone/src/extension/phone-sessions.ts +0 -139
- package/extensions/dm-phone/src/extension/phone-static.ts +0 -30
- package/extensions/dm-phone/src/extension/phone-tailscale.ts +0 -148
- package/extensions/dm-phone/src/extension/phone-theme.ts +0 -85
- package/extensions/dm-phone/src/extension/register-phone-child-extension.ts +0 -112
- package/extensions/dm-phone/src/extension/register-phone-extension.ts +0 -106
- package/extensions/dm-phone/src/extension/types.ts +0 -73
- package/extensions/dm-phone/src/session-pool/parent-session-worker.ts +0 -882
- package/extensions/dm-phone/src/session-pool/session-pool.ts +0 -470
- package/extensions/dm-phone/src/session-pool/session-worker.ts +0 -739
- package/extensions/dm-phone/src/session-pool/types.ts +0 -111
- package/extensions/dm-phone/src/session-pool/utils.ts +0 -23
- package/extensions/dm-phone/test/duckmind-models.test.js +0 -147
- package/extensions/dm-thinking-timer/LICENSE +0 -21
- package/extensions/dm-thinking-timer/README.md +0 -7
- package/extensions/dm-thinking-timer/package.json +0 -20
- package/extensions/dm-thinking-timer/thinking-timer.ts +0 -250
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dm-9router-ext
|
|
3
|
+
*
|
|
4
|
+
* DM extension for 9router — an open-source AI routing proxy.
|
|
5
|
+
* Connects DM to your 9router instance via its OpenAI-compatible API.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Auto-discovers models and combos from 9router on startup
|
|
9
|
+
* - Registers 9router as a DM provider with dynamic base URL and API key
|
|
10
|
+
* - Status commands to view connection info and available models
|
|
11
|
+
* - User-persisted configuration shared by all DM instances
|
|
12
|
+
*
|
|
13
|
+
* Environment variables:
|
|
14
|
+
* NINE_ROUTER_BASE_URL - 9router endpoint (default: http://localhost:20128)
|
|
15
|
+
* NINE_ROUTER_API_KEY - API key if 9router requires authentication
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
19
|
+
import { homedir } from "node:os";
|
|
20
|
+
import { dirname, join } from "node:path";
|
|
21
|
+
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
22
|
+
import { Type } from "typebox";
|
|
23
|
+
|
|
24
|
+
// =============================================================================
|
|
25
|
+
// Types
|
|
26
|
+
// =============================================================================
|
|
27
|
+
|
|
28
|
+
interface NineRouterConfig {
|
|
29
|
+
baseUrl: string;
|
|
30
|
+
apiKey: string | undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface NineRouterModel {
|
|
34
|
+
id: string;
|
|
35
|
+
object: string;
|
|
36
|
+
owned_by?: string;
|
|
37
|
+
kind?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface NineRouterModelsResponse {
|
|
41
|
+
object: string;
|
|
42
|
+
data: NineRouterModel[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// =============================================================================
|
|
46
|
+
// Constants
|
|
47
|
+
// =============================================================================
|
|
48
|
+
|
|
49
|
+
const DEFAULT_BASE_URL = "http://localhost:20128";
|
|
50
|
+
const ENV_BASE_URL = process.env.NINE_ROUTER_BASE_URL;
|
|
51
|
+
const ENV_API_KEY = process.env.NINE_ROUTER_API_KEY;
|
|
52
|
+
const CONFIG_PATH = join(homedir(), ".dm", "agent", "9router-config.json");
|
|
53
|
+
|
|
54
|
+
const CUSTOM_TYPE_CONFIG = "9router-config";
|
|
55
|
+
const CUSTOM_TYPE_LAST_ROUTE = "9router-last-route";
|
|
56
|
+
|
|
57
|
+
// Headers that may indicate the actual upstream model used
|
|
58
|
+
const ROUTING_HEADERS = [
|
|
59
|
+
"x-9router-model",
|
|
60
|
+
"x-routed-model",
|
|
61
|
+
"x-actual-model",
|
|
62
|
+
"x-upstream-model",
|
|
63
|
+
"x-provider-model",
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
// =============================================================================
|
|
67
|
+
// Config Helpers
|
|
68
|
+
// =============================================================================
|
|
69
|
+
|
|
70
|
+
function normalizeBaseUrl(url: string): string {
|
|
71
|
+
return url.replace(/\/$/, "");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function maskApiKey(key: string): string {
|
|
75
|
+
if (key.length <= 8) return "●".repeat(key.length);
|
|
76
|
+
return key.slice(0, 4) + "●".repeat(Math.max(0, key.length - 8)) + key.slice(-4);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function applyEnvOverrides(config: NineRouterConfig): NineRouterConfig {
|
|
80
|
+
return {
|
|
81
|
+
baseUrl: normalizeBaseUrl(ENV_BASE_URL || config.baseUrl),
|
|
82
|
+
apiKey: ENV_API_KEY || config.apiKey,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function loadConfigFromDisk(): NineRouterConfig | null {
|
|
87
|
+
try {
|
|
88
|
+
if (!existsSync(CONFIG_PATH)) return null;
|
|
89
|
+
const data = JSON.parse(readFileSync(CONFIG_PATH, "utf8")) as Partial<NineRouterConfig>;
|
|
90
|
+
if (!data.baseUrl || typeof data.baseUrl !== "string") return null;
|
|
91
|
+
return {
|
|
92
|
+
baseUrl: normalizeBaseUrl(data.baseUrl),
|
|
93
|
+
apiKey: typeof data.apiKey === "string" && data.apiKey.trim()
|
|
94
|
+
? data.apiKey.trim()
|
|
95
|
+
: undefined,
|
|
96
|
+
};
|
|
97
|
+
} catch (err) {
|
|
98
|
+
console.error("[dm-9router-ext] Failed to load persisted config:", err);
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function saveConfigToDisk(config: NineRouterConfig) {
|
|
104
|
+
try {
|
|
105
|
+
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
106
|
+
writeFileSync(
|
|
107
|
+
CONFIG_PATH,
|
|
108
|
+
`${JSON.stringify({ baseUrl: config.baseUrl, apiKey: config.apiKey }, null, 2)}\n`,
|
|
109
|
+
{ mode: 0o600 },
|
|
110
|
+
);
|
|
111
|
+
} catch (err) {
|
|
112
|
+
console.error("[dm-9router-ext] Failed to persist config:", err);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getInitialConfig(): NineRouterConfig {
|
|
117
|
+
return applyEnvOverrides(loadConfigFromDisk() || {
|
|
118
|
+
baseUrl: DEFAULT_BASE_URL,
|
|
119
|
+
apiKey: undefined,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function loadConfigFromSession(ctx: ExtensionContext): NineRouterConfig | null {
|
|
124
|
+
for (const entry of ctx.sessionManager.getEntries()) {
|
|
125
|
+
if (entry.type === "custom" && entry.customType === CUSTOM_TYPE_CONFIG) {
|
|
126
|
+
const data = entry.data as Partial<NineRouterConfig> | undefined;
|
|
127
|
+
if (data?.baseUrl) {
|
|
128
|
+
return applyEnvOverrides({
|
|
129
|
+
baseUrl: normalizeBaseUrl(data.baseUrl),
|
|
130
|
+
apiKey: data.apiKey,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function persistConfig(pi: ExtensionAPI, config: NineRouterConfig) {
|
|
139
|
+
saveConfigToDisk(config);
|
|
140
|
+
pi.appendEntry(CUSTOM_TYPE_CONFIG, {
|
|
141
|
+
baseUrl: config.baseUrl,
|
|
142
|
+
apiKey: config.apiKey,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// =============================================================================
|
|
147
|
+
// 9router API Client
|
|
148
|
+
// =============================================================================
|
|
149
|
+
|
|
150
|
+
async function fetchModels(
|
|
151
|
+
config: NineRouterConfig,
|
|
152
|
+
signal?: AbortSignal,
|
|
153
|
+
): Promise<NineRouterModel[]> {
|
|
154
|
+
const headers: Record<string, string> = {
|
|
155
|
+
Accept: "application/json",
|
|
156
|
+
};
|
|
157
|
+
if (config.apiKey) {
|
|
158
|
+
headers.Authorization = `Bearer ${config.apiKey}`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const response = await fetch(`${config.baseUrl}/v1/models`, {
|
|
162
|
+
method: "GET",
|
|
163
|
+
headers,
|
|
164
|
+
signal,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
if (!response.ok) {
|
|
168
|
+
const text = await response.text().catch(() => "");
|
|
169
|
+
throw new Error(
|
|
170
|
+
`9router returned ${response.status}: ${text || response.statusText}`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const payload = (await response.json()) as NineRouterModelsResponse;
|
|
175
|
+
return payload.data || [];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function testConnection(
|
|
179
|
+
config: NineRouterConfig,
|
|
180
|
+
signal?: AbortSignal,
|
|
181
|
+
): Promise<{ ok: boolean; error?: string }> {
|
|
182
|
+
try {
|
|
183
|
+
const headers: Record<string, string> = {};
|
|
184
|
+
if (config.apiKey) {
|
|
185
|
+
headers.Authorization = `Bearer ${config.apiKey}`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const response = await fetch(`${config.baseUrl}/v1/models`, {
|
|
189
|
+
method: "GET",
|
|
190
|
+
headers,
|
|
191
|
+
signal,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
if (response.ok) {
|
|
195
|
+
return { ok: true };
|
|
196
|
+
}
|
|
197
|
+
return { ok: false, error: `HTTP ${response.status}: ${response.statusText}` };
|
|
198
|
+
} catch (err) {
|
|
199
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// =============================================================================
|
|
204
|
+
// Model Mapping
|
|
205
|
+
// =============================================================================
|
|
206
|
+
|
|
207
|
+
function mapNineRouterModel(model: NineRouterModel) {
|
|
208
|
+
const isCombo = model.owned_by === "combo";
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
id: model.id,
|
|
212
|
+
name: isCombo ? `🔀 ${model.id}` : model.id,
|
|
213
|
+
reasoning: false,
|
|
214
|
+
input: ["text"] as ("text" | "image")[],
|
|
215
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
216
|
+
contextWindow: 128000,
|
|
217
|
+
maxTokens: 4096,
|
|
218
|
+
compat: {
|
|
219
|
+
// 9router is an OpenAI-compatible proxy; keep requests conservative so
|
|
220
|
+
// using this extension does not force built-in-provider-specific features.
|
|
221
|
+
supportsDeveloperRole: false,
|
|
222
|
+
supportsReasoningEffort: false,
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// =============================================================================
|
|
228
|
+
// Provider Registration
|
|
229
|
+
// =============================================================================
|
|
230
|
+
|
|
231
|
+
function registerNineRouterProvider(
|
|
232
|
+
pi: ExtensionAPI,
|
|
233
|
+
config: NineRouterConfig,
|
|
234
|
+
models: NineRouterModel[],
|
|
235
|
+
) {
|
|
236
|
+
// Always use a dedicated provider id ("9router") and never override built-in
|
|
237
|
+
// providers like ollama-cloud/openrouter. DM requires apiKey for custom
|
|
238
|
+
// providers with models; 9router receives the real key only when configured,
|
|
239
|
+
// otherwise a harmless placeholder is scoped to the 9router provider.
|
|
240
|
+
pi.registerProvider("9router", {
|
|
241
|
+
name: "9router",
|
|
242
|
+
baseUrl: `${config.baseUrl}/v1`,
|
|
243
|
+
apiKey: config.apiKey || "9router-no-api-key",
|
|
244
|
+
api: "openai-completions",
|
|
245
|
+
models: models.map(mapNineRouterModel),
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function unregisterNineRouterProvider(pi: ExtensionAPI) {
|
|
250
|
+
pi.unregisterProvider("9router");
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// =============================================================================
|
|
254
|
+
// Extension Factory
|
|
255
|
+
// =============================================================================
|
|
256
|
+
|
|
257
|
+
export default async function (pi: ExtensionAPI) {
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
// Load configuration (env vars are defaults; session config applied later)
|
|
260
|
+
// ---------------------------------------------------------------------------
|
|
261
|
+
let config: NineRouterConfig = getInitialConfig();
|
|
262
|
+
|
|
263
|
+
// State that survives across the extension lifetime
|
|
264
|
+
let discoveredModels: NineRouterModel[] = [];
|
|
265
|
+
let lastRoutedModel: string | undefined;
|
|
266
|
+
let activeProvider: string | undefined;
|
|
267
|
+
let isConnected = false;
|
|
268
|
+
|
|
269
|
+
// ---------------------------------------------------------------------------
|
|
270
|
+
// Provider registration (async factory = models available immediately)
|
|
271
|
+
// ---------------------------------------------------------------------------
|
|
272
|
+
try {
|
|
273
|
+
const models = await fetchModels(config);
|
|
274
|
+
discoveredModels = models;
|
|
275
|
+
isConnected = true;
|
|
276
|
+
registerNineRouterProvider(pi, config, models);
|
|
277
|
+
} catch (err) {
|
|
278
|
+
// Do not register an empty/broken provider on startup. Leaving the provider
|
|
279
|
+
// absent is safer for built-in DM providers and model selection. Commands
|
|
280
|
+
// remain available so the user can configure/reload 9router later.
|
|
281
|
+
unregisterNineRouterProvider(pi);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// ---------------------------------------------------------------------------
|
|
285
|
+
// Session start: rehydrate config from session
|
|
286
|
+
// ---------------------------------------------------------------------------
|
|
287
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
288
|
+
const restored = loadConfigFromSession(ctx);
|
|
289
|
+
if (!loadConfigFromDisk() && restored) {
|
|
290
|
+
// Migrate old session-persisted config to the new user-wide config file.
|
|
291
|
+
config = restored;
|
|
292
|
+
persistConfig(pi, config);
|
|
293
|
+
try {
|
|
294
|
+
const models = await fetchModels(config, ctx.signal);
|
|
295
|
+
discoveredModels = models;
|
|
296
|
+
isConnected = true;
|
|
297
|
+
registerNineRouterProvider(pi, config, models);
|
|
298
|
+
} catch (err) {
|
|
299
|
+
isConnected = false;
|
|
300
|
+
console.error("[dm-9router-ext] Failed to refresh migrated config:", err);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (isConnected && discoveredModels.length > 0) {
|
|
305
|
+
ctx.ui.notify(
|
|
306
|
+
`9router connected — ${discoveredModels.length} models available`,
|
|
307
|
+
"info",
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// ---------------------------------------------------------------------------
|
|
313
|
+
// Detect routed model from response headers
|
|
314
|
+
// ---------------------------------------------------------------------------
|
|
315
|
+
pi.on("after_provider_response", (event) => {
|
|
316
|
+
if (event.status >= 400 || activeProvider !== "9router") {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
for (const header of ROUTING_HEADERS) {
|
|
321
|
+
const value = event.headers[header];
|
|
322
|
+
if (value && typeof value === "string") {
|
|
323
|
+
lastRoutedModel = value;
|
|
324
|
+
pi.appendEntry(CUSTOM_TYPE_LAST_ROUTE, {
|
|
325
|
+
model: value,
|
|
326
|
+
timestamp: Date.now(),
|
|
327
|
+
});
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// ---------------------------------------------------------------------------
|
|
334
|
+
// Clear routing info when model changes
|
|
335
|
+
// ---------------------------------------------------------------------------
|
|
336
|
+
pi.on("model_select", async (event) => {
|
|
337
|
+
activeProvider = event.model.provider;
|
|
338
|
+
if (event.model.provider !== "9router") {
|
|
339
|
+
lastRoutedModel = undefined;
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// ---------------------------------------------------------------------------
|
|
344
|
+
// Command: /9router-status
|
|
345
|
+
// ---------------------------------------------------------------------------
|
|
346
|
+
pi.registerCommand("9router-status", {
|
|
347
|
+
description: "Show 9router connection status and configuration",
|
|
348
|
+
handler: async (_args, ctx) => {
|
|
349
|
+
const test = await testConnection(config, ctx.signal);
|
|
350
|
+
const lines: string[] = [
|
|
351
|
+
`🔗 9router Status`,
|
|
352
|
+
``,
|
|
353
|
+
`Base URL: ${config.baseUrl}`,
|
|
354
|
+
`API Key: ${config.apiKey ? maskApiKey(config.apiKey) : "not set"}`,
|
|
355
|
+
`Connection: ${test.ok ? "🟢 connected" : `🔴 ${test.error || "disconnected"}`}`,
|
|
356
|
+
`Models: ${discoveredModels.length} available`,
|
|
357
|
+
];
|
|
358
|
+
|
|
359
|
+
if (lastRoutedModel) {
|
|
360
|
+
lines.push(`Last routed: ${lastRoutedModel}`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const combos = discoveredModels.filter((m) => m.owned_by === "combo");
|
|
364
|
+
const regular = discoveredModels.filter((m) => m.owned_by !== "combo");
|
|
365
|
+
if (regular.length > 0) {
|
|
366
|
+
lines.push(``, `Regular models: ${regular.length}`);
|
|
367
|
+
}
|
|
368
|
+
if (combos.length > 0) {
|
|
369
|
+
lines.push(`Combos: ${combos.length}`);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
ctx.ui.notify(lines.join("\n"), test.ok ? "info" : "warning");
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// ---------------------------------------------------------------------------
|
|
377
|
+
// Command: /9router-models
|
|
378
|
+
// ---------------------------------------------------------------------------
|
|
379
|
+
pi.registerCommand("9router-models", {
|
|
380
|
+
description: "Browse 9router available models and combos",
|
|
381
|
+
handler: async (_args, ctx) => {
|
|
382
|
+
if (discoveredModels.length === 0) {
|
|
383
|
+
ctx.ui.notify(
|
|
384
|
+
"No 9router models discovered. Check connection with /9router-status",
|
|
385
|
+
"warning",
|
|
386
|
+
);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const items = discoveredModels.map((m) => {
|
|
391
|
+
const isCombo = m.owned_by === "combo";
|
|
392
|
+
return {
|
|
393
|
+
value: m.id,
|
|
394
|
+
label: isCombo ? `🔀 ${m.id}` : m.id,
|
|
395
|
+
};
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
const selected = await ctx.ui.select(
|
|
399
|
+
"Select a 9router model to use:",
|
|
400
|
+
items.map((i) => i.label),
|
|
401
|
+
);
|
|
402
|
+
if (!selected) return;
|
|
403
|
+
|
|
404
|
+
const modelId = items.find((i) => i.label === selected)?.value;
|
|
405
|
+
if (!modelId) return;
|
|
406
|
+
|
|
407
|
+
const fullModelId = `9router/${modelId}`;
|
|
408
|
+
ctx.ui.notify(`Switching to ${fullModelId}...`, "info");
|
|
409
|
+
pi.sendUserMessage(`/model ${fullModelId}`, { deliverAs: "followUp" });
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// ---------------------------------------------------------------------------
|
|
414
|
+
// Command: /9router-config
|
|
415
|
+
// ---------------------------------------------------------------------------
|
|
416
|
+
pi.registerCommand("9router-config", {
|
|
417
|
+
description: "Configure 9router base URL and API key",
|
|
418
|
+
handler: async (_args, ctx) => {
|
|
419
|
+
// Show current config first
|
|
420
|
+
const test = await testConnection(config, ctx.signal);
|
|
421
|
+
const currentStatus = test.ok ? "🟢 connected" : "🔴 disconnected";
|
|
422
|
+
const currentApiKeyDisplay = config.apiKey ? "●●●●●●●● (set)" : "not set";
|
|
423
|
+
|
|
424
|
+
const currentLines = [
|
|
425
|
+
"Current config:",
|
|
426
|
+
` Base URL: ${config.baseUrl}`,
|
|
427
|
+
` API Key: ${config.apiKey ? maskApiKey(config.apiKey) : "not set"}`,
|
|
428
|
+
` Status: ${currentStatus}`,
|
|
429
|
+
"",
|
|
430
|
+
"Enter new values (press Enter to keep current):",
|
|
431
|
+
].join("\n");
|
|
432
|
+
|
|
433
|
+
ctx.ui.notify(currentLines, "info");
|
|
434
|
+
const newBaseUrl = await ctx.ui.input("Base URL", config.baseUrl);
|
|
435
|
+
if (newBaseUrl === undefined) return; // cancelled
|
|
436
|
+
|
|
437
|
+
const newApiKey = await ctx.ui.input(
|
|
438
|
+
"API key (press Enter to keep current, leave blank to remove):",
|
|
439
|
+
config.apiKey || "",
|
|
440
|
+
);
|
|
441
|
+
if (newApiKey === undefined) return; // cancelled
|
|
442
|
+
|
|
443
|
+
config = {
|
|
444
|
+
baseUrl: normalizeBaseUrl(newBaseUrl),
|
|
445
|
+
apiKey: newApiKey.trim() || undefined,
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
persistConfig(pi, config);
|
|
449
|
+
|
|
450
|
+
// Try to refresh models and re-register provider
|
|
451
|
+
try {
|
|
452
|
+
const models = await fetchModels(config, ctx.signal);
|
|
453
|
+
discoveredModels = models;
|
|
454
|
+
isConnected = true;
|
|
455
|
+
|
|
456
|
+
registerNineRouterProvider(pi, config, models);
|
|
457
|
+
|
|
458
|
+
ctx.ui.notify(
|
|
459
|
+
`9router updated — ${models.length} models at ${config.baseUrl}`,
|
|
460
|
+
"info",
|
|
461
|
+
);
|
|
462
|
+
} catch (err) {
|
|
463
|
+
isConnected = false;
|
|
464
|
+
unregisterNineRouterProvider(pi);
|
|
465
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
466
|
+
ctx.ui.notify(`Failed to connect: ${msg}`, "error");
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// ---------------------------------------------------------------------------
|
|
472
|
+
// Command: /9router-reload
|
|
473
|
+
// ---------------------------------------------------------------------------
|
|
474
|
+
pi.registerCommand("9router-reload", {
|
|
475
|
+
description: "Reload models from 9router",
|
|
476
|
+
handler: async (_args, ctx) => {
|
|
477
|
+
try {
|
|
478
|
+
const models = await fetchModels(config, ctx.signal);
|
|
479
|
+
discoveredModels = models;
|
|
480
|
+
isConnected = true;
|
|
481
|
+
|
|
482
|
+
registerNineRouterProvider(pi, config, models);
|
|
483
|
+
|
|
484
|
+
ctx.ui.notify(
|
|
485
|
+
`9router reloaded — ${models.length} models`,
|
|
486
|
+
"info",
|
|
487
|
+
);
|
|
488
|
+
} catch (err) {
|
|
489
|
+
isConnected = false;
|
|
490
|
+
unregisterNineRouterProvider(pi);
|
|
491
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
492
|
+
ctx.ui.notify(`Reload failed: ${msg}`, "error");
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// ---------------------------------------------------------------------------
|
|
498
|
+
// Tool: ninerouter_status
|
|
499
|
+
// ---------------------------------------------------------------------------
|
|
500
|
+
pi.registerTool({
|
|
501
|
+
name: "ninerouter_status",
|
|
502
|
+
label: "9router Status",
|
|
503
|
+
description:
|
|
504
|
+
"Check 9router connection status and list available models",
|
|
505
|
+
parameters: Type.Object({}),
|
|
506
|
+
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
507
|
+
const test = await testConnection(config, ctx.signal);
|
|
508
|
+
const combos = discoveredModels.filter((m) => m.owned_by === "combo");
|
|
509
|
+
const regular = discoveredModels.filter((m) => m.owned_by !== "combo");
|
|
510
|
+
|
|
511
|
+
return {
|
|
512
|
+
content: [
|
|
513
|
+
{
|
|
514
|
+
type: "text",
|
|
515
|
+
text: [
|
|
516
|
+
`9router: ${test.ok ? "connected" : `disconnected (${test.error})`}`,
|
|
517
|
+
`Base URL: ${config.baseUrl}`,
|
|
518
|
+
`Total models: ${discoveredModels.length}`,
|
|
519
|
+
` Regular: ${regular.length}`,
|
|
520
|
+
` Combos: ${combos.length}`,
|
|
521
|
+
lastRoutedModel
|
|
522
|
+
? `Last routed model: ${lastRoutedModel}`
|
|
523
|
+
: "",
|
|
524
|
+
]
|
|
525
|
+
.filter(Boolean)
|
|
526
|
+
.join("\n"),
|
|
527
|
+
},
|
|
528
|
+
],
|
|
529
|
+
details: {
|
|
530
|
+
connected: test.ok,
|
|
531
|
+
baseUrl: config.baseUrl,
|
|
532
|
+
modelCount: discoveredModels.length,
|
|
533
|
+
regularCount: regular.length,
|
|
534
|
+
comboCount: combos.length,
|
|
535
|
+
lastRoutedModel,
|
|
536
|
+
models: discoveredModels.map((m) => m.id),
|
|
537
|
+
},
|
|
538
|
+
};
|
|
539
|
+
},
|
|
540
|
+
});
|
|
541
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noEmit": true,
|
|
8
|
+
"allowImportingTsExtensions": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"types": [
|
|
11
|
+
"node"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
"include": [
|
|
15
|
+
"src/**/*.ts"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -4006,9 +4006,9 @@
|
|
|
4006
4006
|
"license": "0BSD"
|
|
4007
4007
|
},
|
|
4008
4008
|
"node_modules/typebox": {
|
|
4009
|
-
"version": "1.2.
|
|
4010
|
-
"resolved": "https://registry.npmjs.org/typebox/-/typebox-1.2.
|
|
4011
|
-
"integrity": "sha512-
|
|
4009
|
+
"version": "1.2.1",
|
|
4010
|
+
"resolved": "https://registry.npmjs.org/typebox/-/typebox-1.2.1.tgz",
|
|
4011
|
+
"integrity": "sha512-0upGv6+mxJR7/Wc7yoxjc/U6SjOk2aNDNzbihYacSHh+JfOsf28IJ8ggW4/3tRlDKfbInvEDPVneEywjOWYCzw==",
|
|
4012
4012
|
"dev": true,
|
|
4013
4013
|
"license": "MIT"
|
|
4014
4014
|
},
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"node_modules/typebox": {
|
|
8
|
-
"version": "1.2.
|
|
9
|
-
"resolved": "https://registry.npmjs.org/typebox/-/typebox-1.2.
|
|
10
|
-
"integrity": "sha512-
|
|
8
|
+
"version": "1.2.1",
|
|
9
|
+
"resolved": "https://registry.npmjs.org/typebox/-/typebox-1.2.1.tgz",
|
|
10
|
+
"integrity": "sha512-0upGv6+mxJR7/Wc7yoxjc/U6SjOk2aNDNzbihYacSHh+JfOsf28IJ8ggW4/3tRlDKfbInvEDPVneEywjOWYCzw==",
|
|
11
11
|
"license": "MIT"
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -309,8 +309,8 @@ export type TPatternBigIntMapping<Input extends '-?(?:0|[1-9][0-9]*)n'> = (T.TBi
|
|
|
309
309
|
export declare function PatternBigIntMapping(input: '-?(?:0|[1-9][0-9]*)n'): unknown;
|
|
310
310
|
export type TPatternStringMapping<Input extends '.*'> = (T.TString);
|
|
311
311
|
export declare function PatternStringMapping(input: '.*'): unknown;
|
|
312
|
-
export type TPatternNumberMapping<Input extends '-?(?:0|[1-9][0-9]*)(
|
|
313
|
-
export declare function PatternNumberMapping(input: '-?(?:0|[1-9][0-9]*)(
|
|
312
|
+
export type TPatternNumberMapping<Input extends '-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?'> = (T.TNumber);
|
|
313
|
+
export declare function PatternNumberMapping(input: '-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?'): unknown;
|
|
314
314
|
export type TPatternIntegerMapping<Input extends '-?(?:0|[1-9][0-9]*)'> = (T.TInteger);
|
|
315
315
|
export declare function PatternIntegerMapping(input: '-?(?:0|[1-9][0-9]*)'): unknown;
|
|
316
316
|
export type TPatternNeverMapping<Input extends '(?!)'> = (T.TNever);
|
|
@@ -106,10 +106,10 @@ export type TJsonArray<Input extends string> = (Token.TConst<'[', Input> extends
|
|
|
106
106
|
export type TJson<Input extends string> = (TJsonNumber<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TJsonBoolean<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TJsonString<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TJsonNull<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TJsonObject<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TJsonArray<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : []) extends [infer _0 extends unknown, infer Input extends string] ? [S.TJsonMapping<_0>, Input] : [];
|
|
107
107
|
export type TPatternBigInt<Input extends string> = Token.TConst<'-?(?:0|[1-9][0-9]*)n', Input> extends [infer _0 extends '-?(?:0|[1-9][0-9]*)n', infer Input extends string] ? [S.TPatternBigIntMapping<_0>, Input] : [];
|
|
108
108
|
export type TPatternString<Input extends string> = Token.TConst<'.*', Input> extends [infer _0 extends '.*', infer Input extends string] ? [S.TPatternStringMapping<_0>, Input] : [];
|
|
109
|
-
export type TPatternNumber<Input extends string> = Token.TConst<'-?(?:0|[1-9][0-9]*)(
|
|
109
|
+
export type TPatternNumber<Input extends string> = Token.TConst<'-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?', Input> extends [infer _0 extends '-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?', infer Input extends string] ? [S.TPatternNumberMapping<_0>, Input] : [];
|
|
110
110
|
export type TPatternInteger<Input extends string> = Token.TConst<'-?(?:0|[1-9][0-9]*)', Input> extends [infer _0 extends '-?(?:0|[1-9][0-9]*)', infer Input extends string] ? [S.TPatternIntegerMapping<_0>, Input] : [];
|
|
111
111
|
export type TPatternNever<Input extends string> = Token.TConst<'(?!)', Input> extends [infer _0 extends '(?!)', infer Input extends string] ? [S.TPatternNeverMapping<_0>, Input] : [];
|
|
112
|
-
export type TPatternText<Input extends string> = Token.TUntil_1<['-?(?:0|[1-9][0-9]*)n', '.*', '-?(?:0|[1-9][0-9]*)(
|
|
112
|
+
export type TPatternText<Input extends string> = Token.TUntil_1<['-?(?:0|[1-9][0-9]*)n', '.*', '-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?', '-?(?:0|[1-9][0-9]*)', '(?!)', '(', ')', '$', '|'], Input> extends [infer _0 extends string, infer Input extends string] ? [S.TPatternTextMapping<_0>, Input] : [];
|
|
113
113
|
export type TPatternBase<Input extends string> = (TPatternBigInt<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TPatternString<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TPatternNumber<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TPatternInteger<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TPatternNever<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TPatternGroup<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TPatternText<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : []) extends [infer _0 extends unknown, infer Input extends string] ? [S.TPatternBaseMapping<_0>, Input] : [];
|
|
114
114
|
export type TPatternGroup<Input extends string> = (Token.TConst<'(', Input> extends [infer _0, infer Input extends string] ? (TPatternBody<Input> extends [infer _1, infer Input extends string] ? (Token.TConst<')', Input> extends [infer _2, infer Input extends string] ? [[_0, _1, _2], Input] : []) : []) : []) extends [infer _0 extends [unknown, unknown, unknown], infer Input extends string] ? [S.TPatternGroupMapping<_0>, Input] : [];
|
|
115
115
|
export type TPatternUnion<Input extends string> = ((TPatternTerm<Input> extends [infer _0, infer Input extends string] ? (Token.TConst<'|', Input> extends [infer _1, infer Input extends string] ? (TPatternUnion<Input> extends [infer _2, infer Input extends string] ? [[_0, _1, _2], Input] : []) : []) : []) extends [infer _0, infer Input extends string] ? [_0, Input] : (TPatternTerm<Input> extends [infer _0, infer Input extends string] ? [[_0], Input] : []) extends [infer _0, infer Input extends string] ? [_0, Input] : [[], Input] extends [infer _0, infer Input extends string] ? [_0, Input] : []) extends [infer _0 extends [unknown, unknown, unknown] | [unknown] | [], infer Input extends string] ? [S.TPatternUnionMapping<_0>, Input] : [];
|
|
@@ -110,10 +110,10 @@ export const JsonArray = (input) => If(If(Token.Const('[', input), ([_0, input])
|
|
|
110
110
|
export const Json = (input) => If(If(JsonNumber(input), ([_0, input]) => [_0, input], () => If(JsonBoolean(input), ([_0, input]) => [_0, input], () => If(JsonString(input), ([_0, input]) => [_0, input], () => If(JsonNull(input), ([_0, input]) => [_0, input], () => If(JsonObject(input), ([_0, input]) => [_0, input], () => If(JsonArray(input), ([_0, input]) => [_0, input], () => [])))))), ([_0, input]) => [S.JsonMapping(_0), input]);
|
|
111
111
|
export const PatternBigInt = (input) => If(Token.Const('-?(?:0|[1-9][0-9]*)n', input), ([_0, input]) => [S.PatternBigIntMapping(_0), input]);
|
|
112
112
|
export const PatternString = (input) => If(Token.Const('.*', input), ([_0, input]) => [S.PatternStringMapping(_0), input]);
|
|
113
|
-
export const PatternNumber = (input) => If(Token.Const('-?(?:0|[1-9][0-9]*)(
|
|
113
|
+
export const PatternNumber = (input) => If(Token.Const('-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?', input), ([_0, input]) => [S.PatternNumberMapping(_0), input]);
|
|
114
114
|
export const PatternInteger = (input) => If(Token.Const('-?(?:0|[1-9][0-9]*)', input), ([_0, input]) => [S.PatternIntegerMapping(_0), input]);
|
|
115
115
|
export const PatternNever = (input) => If(Token.Const('(?!)', input), ([_0, input]) => [S.PatternNeverMapping(_0), input]);
|
|
116
|
-
export const PatternText = (input) => If(Token.Until_1(['-?(?:0|[1-9][0-9]*)n', '.*', '-?(?:0|[1-9][0-9]*)(
|
|
116
|
+
export const PatternText = (input) => If(Token.Until_1(['-?(?:0|[1-9][0-9]*)n', '.*', '-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?', '-?(?:0|[1-9][0-9]*)', '(?!)', '(', ')', '$', '|'], input), ([_0, input]) => [S.PatternTextMapping(_0), input]);
|
|
117
117
|
export const PatternBase = (input) => If(If(PatternBigInt(input), ([_0, input]) => [_0, input], () => If(PatternString(input), ([_0, input]) => [_0, input], () => If(PatternNumber(input), ([_0, input]) => [_0, input], () => If(PatternInteger(input), ([_0, input]) => [_0, input], () => If(PatternNever(input), ([_0, input]) => [_0, input], () => If(PatternGroup(input), ([_0, input]) => [_0, input], () => If(PatternText(input), ([_0, input]) => [_0, input], () => []))))))), ([_0, input]) => [S.PatternBaseMapping(_0), input]);
|
|
118
118
|
export const PatternGroup = (input) => If(If(Token.Const('(', input), ([_0, input]) => If(PatternBody(input), ([_1, input]) => If(Token.Const(')', input), ([_2, input]) => [[_0, _1, _2], input]))), ([_0, input]) => [S.PatternGroupMapping(_0), input]);
|
|
119
119
|
export const PatternUnion = (input) => If(If(If(PatternTerm(input), ([_0, input]) => If(Token.Const('|', input), ([_1, input]) => If(PatternUnion(input), ([_2, input]) => [[_0, _1, _2], input]))), ([_0, input]) => [_0, input], () => If(If(PatternTerm(input), ([_0, input]) => [[_0], input]), ([_0, input]) => [_0, input], () => If([[], input], ([_0, input]) => [_0, input], () => []))), ([_0, input]) => [S.PatternUnionMapping(_0), input]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type TSchema, type TNumberOptions } from './schema.mjs';
|
|
2
|
-
export declare const NumberPattern = "-?(?:0|[1-9][0-9]*)(
|
|
2
|
+
export declare const NumberPattern = "-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?";
|
|
3
3
|
export type StaticNumber = number;
|
|
4
4
|
/** Represents a Number type. */
|
|
5
5
|
export interface TNumber extends TSchema {
|
|
@@ -4,7 +4,7 @@ import { IsKind } from './schema.mjs';
|
|
|
4
4
|
// ------------------------------------------------------------------
|
|
5
5
|
// Pattern
|
|
6
6
|
// ------------------------------------------------------------------
|
|
7
|
-
export const NumberPattern = '-?(?:0|[1-9][0-9]*)(
|
|
7
|
+
export const NumberPattern = '-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?';
|
|
8
8
|
// ------------------------------------------------------------------
|
|
9
9
|
// Factory
|
|
10
10
|
// ------------------------------------------------------------------
|
|
@@ -14,7 +14,7 @@ export type TStringKey = typeof StringKey;
|
|
|
14
14
|
export type TIntegerKey = typeof IntegerKey;
|
|
15
15
|
export type TNumberKey = typeof NumberKey;
|
|
16
16
|
export declare const IntegerKey = "^-?(?:0|[1-9][0-9]*)$";
|
|
17
|
-
export declare const NumberKey = "^-?(?:0|[1-9][0-9]*)(
|
|
17
|
+
export declare const NumberKey = "^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?$";
|
|
18
18
|
export declare const StringKey = "^.*$";
|
|
19
19
|
export interface TRecord<Key extends string = string, Value extends TSchema = TSchema> extends TSchema {
|
|
20
20
|
'~kind': 'Record';
|