Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vox",
"version": "3.0.0",
"version": "3.1.0",
"description": "Agent-facing speech to text and text to speech over Alibaba Model Studio.",
"repository": "https://github.com/celados/vox",
"bin": {
Expand All @@ -18,7 +18,7 @@
"build": "bun build src/main.ts --outfile=dist/vox --target=bun --minify && chmod +x dist/vox"
},
"dependencies": {
"@celados/argc": "^10.3.1",
"@celados/argc": "^11.0.0",
"@celados/argc-latch": "^1.0.0",
"@valibot/to-json-schema": "^1.8.0",
"valibot": "^1.5.0",
Expand Down
39 changes: 21 additions & 18 deletions src/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,31 +39,33 @@ test("@schema exposes the dotted command surface", async () => {
const { stdout, stderr, exitCode } = await run(["@schema"]);
expect(exitCode, stderr).toBe(0);
for (const fragment of [
"status()",
"hear(input: {",
"session:",
"list(",
"remove(",
"export(input: {",
"vocab:",
"sync(",
"prune(",
"say(input: {",
"voice:",
"record(",
"cache:",
"### status\n",
"### hear\n",
"### session.*\n",
"### session.list\n",
"### session.remove\n",
"### export\n",
"### vocab.*\n",
"### vocab.sync\n",
"### vocab.prune\n",
"### say\n",
"### voice.*\n",
"### voice.record\n",
"### cache.*\n",
]) {
expect(stdout).toContain(fragment);
}
expect(stdout).not.toContain("dashscope(");
expect(stdout).not.toContain("auth:");
expect(stdout).not.toContain("### dashscope");
expect(stdout).not.toContain("### auth");
expect(stdout).not.toContain("values:");
// Key fields are named once (addGlobalDefs), not repeated per command.
expect(stdout).toContain(
"type DashScopeKey = string | { ref: `env:${string}` | `bw://${string}/${string}` }",
);
expect(stdout).toContain("prune(input: { apiKey?: DashScopeKey;");
expect(stdout).toContain("list(input: { apiKey?: ProviderKey })");
expect(stdout).toContain("{ apiKey?: DashScopeKey; dryRun?: boolean = false }");
expect(stdout).toContain(
"### vocab.list\nList local vocabularies, sync state, and remote quota\n{ apiKey?: ProviderKey }",
);
expect(stdout).toContain("text: string | { ref: `file:${string}` }");
expect(stdout).toContain("env:DASHSCOPE_API_KEY → bw://");
expect(stdout).toContain("env:MIMO_API_KEY → bw://");
Expand Down Expand Up @@ -163,7 +165,8 @@ test("speaker command schemas preserve enrollment, matching settings, and identi
const home = await mkdtemp(join(tmpdir(), "vox-speaker-cli-"));
const schema = await run(["@schema", ".speaker"], home);
expect(schema.exitCode, schema.stderr).toBe(0);
expect(schema.stdout).toContain("enroll(input: { id: string; name: string; file: string;");
expect(schema.stdout).toContain("### speaker.enroll\n");
expect(schema.stdout).toMatch(/\n id: string\n name: string\n file: string\n/);
expect(schema.stdout).toContain("threshold?: number");
const config = await run(["speaker.config", "{ threshold: 0.72, margin: 0.12 }"], home);
expect(config.exitCode, config.stderr).toBe(0);
Expand Down
20 changes: 3 additions & 17 deletions src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,14 @@ const status = c
.meta({
description:
"Preflight: whether each provider key resolves (env, then vault), with sources tried. Never prints key material",
examples: ["vox status"],
})
.input(s(v.object({})));

const hear = c
.meta({
description:
"Transcribe audio into a cached run and Markdown file, optionally identify enrolled speakers",
examples: [
"vox hear --file meeting.m4a",
"vox hear \"{ file: 'lecture.mp3', lang: ['zh'], speakers: true }\"",
],
examples: ["vox hear \"{ file: 'lecture.mp3', lang: ['zh'], speakers: true }\""],
})
.input(
s(
Expand Down Expand Up @@ -54,7 +50,6 @@ const speaker = group(
enroll: c
.meta({
description: "Enroll a named speaker from 3–30 seconds of clean solo audio",
examples: ["vox speaker.enroll \"{ id: 'ethan', name: 'Ethan', file: 'ethan.wav' }\""],
})
.input(
s(
Expand All @@ -81,7 +76,6 @@ const speaker = group(
identify: c
.meta({
description: "Identify speakers in a stored run without uploading or repeating ASR",
examples: ["vox speaker.identify \"{ sid: 'a3f1c2', output: 'meeting.transcript.md' }\""],
})
.input(
s(
Expand All @@ -102,7 +96,6 @@ const session = group(
list: c
.meta({
description: "List run envelopes, newest first. No transcript — use export for content",
examples: ["vox session.list", "vox session.list --file meeting.m4a"],
})
.input(
s(
Expand All @@ -115,7 +108,6 @@ const session = group(
remove: c
.meta({
description: "Delete one run by unique sid prefix, or every run with all",
examples: ["vox session.remove --sid a3f1c2", "vox session.remove --all"],
})
.input(
s(
Expand All @@ -133,10 +125,7 @@ const exportCmd = c
.meta({
description:
"Render a stored run as srt, vtt, md, txt, or json. stdout is the document unless output is set",
examples: [
"vox export --sid a3f1c2 --format md",
"vox export --sid a3f1c2 --format srt --output meeting.srt",
],
examples: ["vox export --sid a3f1c2 --format srt --output meeting.srt"],
})
.input(
s(
Expand All @@ -159,7 +148,7 @@ const vocab = group(
.meta({
description:
"Push local YAML to the server for a target model. hear --vocab syncs on demand",
examples: ["vox vocab.sync --name meeting", "vox vocab.sync --all --model qwen"],
examples: ["vox vocab.sync --all --model qwen"],
})
.input(
s(
Expand All @@ -176,7 +165,6 @@ const vocab = group(
prune: c
.meta({
description: "Delete server-side lists no local YAML claims",
examples: ["vox vocab.prune --dryRun"],
})
.input(s(v.objectAsync({ ...dashscopeAuth, dryRun: v.optional(v.boolean(), false) }))),
},
Expand All @@ -187,7 +175,6 @@ const say = c
description:
"Speak text with TTS. Multi-line text: whole input as YAML with text: |, or a file with text: { ref: 'file:…' }",
examples: [
"vox say --text 'Hello world' --voice Cherry",
"vox say - <<'EOF'\nvoice: Cherry\ntext: |\n First line.\n Slow and clear.\nEOF",
"vox say \"{ text: { ref: 'file:script.txt' }, voice: 'Cherry' }\"",
],
Expand Down Expand Up @@ -219,7 +206,6 @@ const voice = group(
record: c
.meta({
description: "Enroll a cloned voice from a file, or record from the mic on a TTY",
examples: ["vox voice.record --file sample.wav --name myvoice"],
})
.input(
s(
Expand Down
Loading