mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
The picker listed presets alphabetically by id, so the shipped set read cordis, minimal, standard — reverse order of capability. A preset may now declare `order` in its metadata; the shipped three declare 1/2/3 and read standard, minimal, cordis. A preset that declares none sorts behind those that do, then by id, so authored presets stay stable. Editing had nowhere good to live. Inside a card it was squeezed into a ~268px column; hanging off the end of the grid it was orphaned from the card it edits. It now replaces the list: a back link, what is being edited, and the form at full width. One thing on screen at a time, which is what the form's height wanted all along. Cards in different grid rows sized independently, so a short description made a short card. `grid-auto-rows: 1fr` makes every row the same height. The trust badge lost its pill when the card CSS was rewritten, and `In use` never had one; both are tags now. Icon labels moved from `title` to a drawn tooltip — the native one waits about a second, which reads as nothing happening.
106 lines
4.1 KiB
TypeScript
106 lines
4.1 KiB
TypeScript
/**
|
|
* A preset's display metadata: the name and description a picker shows.
|
|
*
|
|
* It lives in its own file because the composition is a top-level list of
|
|
* plugin rows — YAML cannot carry sibling keys beside it, and faking a
|
|
* metadata row would hand the Loader something to load. Keeping it separate
|
|
* also keeps the composition exactly what its name says: a Cordis file the
|
|
* loader owns and the cordis preset can author.
|
|
*
|
|
* The file carries display text ONLY. `id` is the directory name and `trust`
|
|
* comes from the root a preset was discovered under, so neither is writable
|
|
* here — otherwise a locally authored preset could claim to be a shipped one.
|
|
*
|
|
* Every read failure degrades to no metadata. A preset whose display text is
|
|
* missing, malformed, or unreadable still mounts: presentation is not a
|
|
* capability, and a broken name must never become an agent that cannot start.
|
|
* @module @deepseek-ai/dsh-agent-presets/metadata
|
|
*/
|
|
|
|
import { readFile } from 'node:fs/promises'
|
|
import { join } from 'node:path'
|
|
import yaml from 'js-yaml'
|
|
|
|
/** The optional display-metadata file beside a preset's composition. */
|
|
export const METADATA_FILE = 'preset.yml'
|
|
|
|
/** Display text a preset may publish about itself. */
|
|
export interface PresetMetadata {
|
|
/** Human-facing name; falls back to the preset id when absent. */
|
|
readonly name?: string
|
|
/** One sentence on what this preset is for. */
|
|
readonly description?: string
|
|
/**
|
|
* Position within its group; lower comes first. A preset that declares
|
|
* none sorts after every preset that does, then by id — so the shipped set
|
|
* can read in capability order while authored ones stay alphabetical.
|
|
*/
|
|
readonly order?: number
|
|
}
|
|
|
|
/** A non-empty trimmed string, or undefined for anything else. */
|
|
function text(value: unknown): string | undefined {
|
|
if (typeof value !== 'string') return undefined
|
|
const trimmed = value.trim()
|
|
return trimmed === '' ? undefined : trimmed
|
|
}
|
|
|
|
/**
|
|
* Read one preset directory's display metadata.
|
|
*
|
|
* Absent, unparsable, and wrongly-shaped files are all the same answer —
|
|
* empty metadata — because the caller renders a picker, not a diagnostic.
|
|
* @param directory - the preset directory.
|
|
* @returns the display text the preset published, possibly empty.
|
|
*/
|
|
export async function readPresetMetadata(directory: string): Promise<PresetMetadata> {
|
|
let raw: string
|
|
try {
|
|
raw = await readFile(join(directory, METADATA_FILE), 'utf8')
|
|
} catch {
|
|
// Absent is the common case: metadata is optional and most presets,
|
|
// including every one authored by duplicating another, carry none.
|
|
return {}
|
|
}
|
|
let parsed: unknown
|
|
try {
|
|
parsed = yaml.load(raw)
|
|
} catch {
|
|
// Malformed display text is not worth failing discovery over; the picker
|
|
// falls back to the id, and the composition still mounts.
|
|
return {}
|
|
}
|
|
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {}
|
|
const record = parsed as Record<string, unknown>
|
|
const name = text(record.name)
|
|
const description = text(record.description)
|
|
const order = typeof record.order === 'number' && Number.isFinite(record.order)
|
|
? record.order
|
|
: undefined
|
|
return {
|
|
...name === undefined ? {} : { name },
|
|
...description === undefined ? {} : { description },
|
|
...order === undefined ? {} : { order },
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render display metadata as the file's contents.
|
|
*
|
|
* Absent fields are omitted rather than written empty, so a preset with no
|
|
* description does not ship a key that reads as an intentional blank.
|
|
* @param metadata - the display text to store.
|
|
* @returns the YAML document, or undefined when there is nothing to store.
|
|
*/
|
|
export function renderPresetMetadata(metadata: PresetMetadata): string | undefined {
|
|
const name = text(metadata.name)
|
|
const description = text(metadata.description)
|
|
const { order } = metadata
|
|
if (name === undefined && description === undefined && order === undefined) return undefined
|
|
return yaml.dump({
|
|
...name === undefined ? {} : { name },
|
|
...description === undefined ? {} : { description },
|
|
...order === undefined ? {} : { order },
|
|
}, { lineWidth: -1 })
|
|
}
|