feat: migrate to DSH 0.1.5-rc.2 with TypeScript refactor, official slot footer and codex selector
This commit is contained in:
+1287
-4436
File diff suppressed because one or more lines are too long
+27
-97
@@ -1,22 +1,17 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
export const name = 'dsh-plugin-model-enhancer';
|
||||
export const inject = ['webServer'];
|
||||
|
||||
const name = "dsh-plugin-model-enhancer";
|
||||
const inject = ["webServer"];
|
||||
let cachedData = null;
|
||||
let lastFetchedAt = 0;
|
||||
const CACHE_TTL_MS = 3600 * 1000; // 1 hour
|
||||
|
||||
const CACHE_TTL_MS = 3600 * 1e3;
|
||||
async function getModelsDevData() {
|
||||
const now = Date.now();
|
||||
if (cachedData && now - lastFetchedAt < CACHE_TTL_MS) {
|
||||
return cachedData;
|
||||
}
|
||||
try {
|
||||
const res = await fetch('https://models.dev/api.json', {
|
||||
const res = await fetch("https://models.dev/api.json", {
|
||||
headers: {
|
||||
'User-Agent': 'DSH-Model-Enhancer/1.0.0'
|
||||
"User-Agent": "DSH-Model-Enhancer/1.0.0"
|
||||
}
|
||||
});
|
||||
if (!res.ok) {
|
||||
@@ -28,114 +23,49 @@ async function getModelsDevData() {
|
||||
return json;
|
||||
} catch (err) {
|
||||
if (cachedData) {
|
||||
// Fallback to stale cache if network fails
|
||||
return cachedData;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export function apply(ctx) {
|
||||
// 1. Models.dev Cache Proxy
|
||||
function apply(ctx) {
|
||||
ctx.effect(() => {
|
||||
return ctx.webServer.register({
|
||||
kind: 'exact',
|
||||
path: '/api/models-dev/models',
|
||||
kind: "exact",
|
||||
path: "/api/models-dev/models",
|
||||
handler: async (req, res) => {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
||||
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
||||
if (req.method === "OPTIONS") {
|
||||
res.writeHead(204);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method !== 'GET') {
|
||||
res.writeHead(405, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Method Not Allowed' }));
|
||||
if (req.method !== "GET") {
|
||||
res.writeHead(405, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ error: "Method Not Allowed" }));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await getModelsDevData();
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json',
|
||||
'Cache-Control': 'public, max-age=3600'
|
||||
"Content-Type": "application/json",
|
||||
"Cache-Control": "public, max-age=3600"
|
||||
});
|
||||
res.end(JSON.stringify(data));
|
||||
} catch (err) {
|
||||
ctx.logger?.error?.('[model-enhancer] Error fetching models.dev:', err);
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: err.message || 'Internal Server Error' }));
|
||||
ctx.logger?.error?.("[model-enhancer] Error fetching models.dev:", err);
|
||||
res.writeHead(500, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ error: err.message || "Internal Server Error" }));
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 'dsh-plugin-model-enhancer: webServer proxy route');
|
||||
|
||||
// 2. Binary / Large Document File Upload to Workspace Endpoint
|
||||
ctx.effect(() => {
|
||||
return ctx.webServer.register({
|
||||
kind: 'exact',
|
||||
path: '/api/model-enhancer/upload',
|
||||
handler: async (req, res) => {
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Filename');
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.writeHead(204);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method !== 'POST') {
|
||||
res.writeHead(405, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Method Not Allowed' }));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const urlObj = new URL(req.url, 'http://127.0.0.1');
|
||||
const rawName = req.headers['x-filename'] || urlObj.searchParams.get('filename') || `upload_${Date.now()}.bin`;
|
||||
const decodedName = decodeURIComponent(rawName);
|
||||
const safeName = path.basename(decodedName);
|
||||
|
||||
const uploadDir = path.join(process.cwd(), 'uploads');
|
||||
fs.mkdirSync(uploadDir, { recursive: true });
|
||||
|
||||
const targetPath = path.join(uploadDir, safeName);
|
||||
const writeStream = fs.createWriteStream(targetPath);
|
||||
|
||||
let totalBytes = 0;
|
||||
req.on('data', (chunk) => {
|
||||
totalBytes += chunk.length;
|
||||
});
|
||||
|
||||
req.pipe(writeStream);
|
||||
|
||||
writeStream.on('finish', () => {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({
|
||||
ok: true,
|
||||
filename: safeName,
|
||||
path: path.join('uploads', safeName),
|
||||
size: totalBytes
|
||||
}));
|
||||
});
|
||||
|
||||
writeStream.on('error', (err) => {
|
||||
ctx.logger?.error?.('[model-enhancer] Upload stream error:', err);
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: err.message || 'File write error' }));
|
||||
});
|
||||
} catch (err) {
|
||||
ctx.logger?.error?.('[model-enhancer] Upload handler error:', err);
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: err.message || 'Upload processing error' }));
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 'dsh-plugin-model-enhancer: workspace file upload endpoint');
|
||||
}, "dsh-plugin-model-enhancer: webServer proxy route");
|
||||
}
|
||||
export {
|
||||
apply,
|
||||
getModelsDevData,
|
||||
inject,
|
||||
name
|
||||
};
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
export interface CodexModelSelectProps {
|
||||
directory: {
|
||||
subscribe: (listener: () => void) => () => void;
|
||||
getSnapshot: () => any;
|
||||
};
|
||||
load: () => void;
|
||||
select: (selection: {
|
||||
provider: string;
|
||||
model: string;
|
||||
reasoningEffort?: string;
|
||||
}) => Promise<boolean>;
|
||||
available: boolean;
|
||||
locked?: boolean;
|
||||
}
|
||||
export declare function CodexModelSelect(props: CodexModelSelectProps): React.DetailedReactHTMLElement<{
|
||||
ref: React.RefObject<HTMLDivElement>;
|
||||
style: {
|
||||
position: "relative";
|
||||
display: "inline-flex";
|
||||
alignItems: "center";
|
||||
};
|
||||
}, HTMLDivElement>;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
export interface ModelsFooterSettingsProps {
|
||||
ctx: any;
|
||||
}
|
||||
export declare function ModelsFooterSettings({ ctx }: ModelsFooterSettingsProps): React.DetailedReactHTMLElement<{
|
||||
className: string;
|
||||
}, HTMLElement>;
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
export interface ProviderCardExtrasProps {
|
||||
provider: {
|
||||
provider: string;
|
||||
displayName: string;
|
||||
settingsNs: string;
|
||||
settingsPath: readonly string[];
|
||||
active: boolean;
|
||||
declared?: boolean;
|
||||
error?: string;
|
||||
};
|
||||
configured: boolean;
|
||||
keyConfigured: boolean;
|
||||
ctx: any;
|
||||
}
|
||||
export declare function ProviderCardExtras({ provider, configured, keyConfigured, ctx }: ProviderCardExtrasProps): React.DetailedReactHTMLElement<{
|
||||
style: {
|
||||
padding: string;
|
||||
marginTop: string;
|
||||
background: string;
|
||||
borderRadius: string;
|
||||
border: string;
|
||||
display: "flex";
|
||||
alignItems: "center";
|
||||
justifyContent: "space-between";
|
||||
gap: string;
|
||||
};
|
||||
}, HTMLElement>;
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
export interface TranslationMap {
|
||||
[key: string]: string;
|
||||
}
|
||||
export declare const I18N_DICT: {
|
||||
zh: TranslationMap;
|
||||
en: TranslationMap;
|
||||
};
|
||||
export declare function isZhLocale(): boolean;
|
||||
export declare function getTranslation(key: string, isZh?: boolean, params?: Record<string, any>): string;
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
export declare const name = "dsh-plugin-model-enhancer";
|
||||
export declare const inject: string[];
|
||||
export declare function apply(ctx: any): void;
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
export interface DevModelInfo {
|
||||
providerKey: string;
|
||||
providerName: string;
|
||||
id: string;
|
||||
rawKey: string;
|
||||
name?: string;
|
||||
limit?: {
|
||||
context?: number;
|
||||
output?: number;
|
||||
};
|
||||
modalities?: {
|
||||
input?: string[];
|
||||
output?: string[];
|
||||
};
|
||||
reasoning?: boolean;
|
||||
reasoning_options?: Array<{
|
||||
type?: string;
|
||||
values?: string[];
|
||||
}>;
|
||||
[key: string]: any;
|
||||
}
|
||||
export declare const THINKING_LEVELS_ALL: string[];
|
||||
export declare function fetchAllModelsDev(): Promise<DevModelInfo[]>;
|
||||
export declare function normModelKey(key: string): string;
|
||||
export declare function findDevModel(queryId: string, allList: DevModelInfo[]): DevModelInfo | null;
|
||||
Vendored
+11
File diff suppressed because one or more lines are too long
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
export declare const name = "dsh-plugin-model-enhancer";
|
||||
export declare const inject: string[];
|
||||
export declare function getModelsDevData(): Promise<any>;
|
||||
export declare function apply(ctx: any): void;
|
||||
Reference in New Issue
Block a user