19 Commits

Author SHA1 Message Date
185e9f2287 bump cyclopts dep version
add ToC to README.
add Shell Completion section to README

patch bump
2026-03-23 23:22:16 +00:00
372baa7804 upd pre-commit config 2026-03-21 14:22:26 +00:00
91f267c52b bump min vban-cmd version 2026-03-20 09:31:20 +00:00
f944c65f10 reduce min python version 2026-03-20 09:30:34 +00:00
37b8c387d9 upd lockfile 2026-03-14 10:16:44 +00:00
02f96ddf62 bump vban-cmd dep version 2026-03-07 15:56:02 +00:00
411234aeb3 upd lockfile 2026-03-07 00:05:26 +00:00
2ca89515be add command lock/unlock
patch bump
2026-03-05 20:12:36 +00:00
6cd92a2975 add bus label/gain commands
patch bump
2026-03-05 20:05:00 +00:00
36a8ec124a add strip label command
patch bump
2026-03-05 20:04:40 +00:00
4dd01a0e99 add missing help messages
patch bump
2026-03-05 19:59:32 +00:00
cd5b8b0636 upd lockfile 2026-03-03 20:29:04 +00:00
77e21617af increase subheading size 2026-03-03 20:05:26 +00:00
5f448fa48e add Strip Denoiser to README 2026-03-03 16:29:44 +00:00
7eb8dc0bb6 patch bump 2026-03-03 15:55:42 +00:00
81b3841241 annotate optional args as Optional. 2026-03-03 15:13:47 +00:00
6e6a60e9bd remove meta launchers for comp, gate, denoiser. 2026-03-03 15:13:29 +00:00
a2ae240605 upd call to vban_cmd
minor bump
2026-03-02 23:27:11 +00:00
18ed3ea7fb bump development status classifier 2026-03-02 22:11:30 +00:00
14 changed files with 270 additions and 115 deletions

View File

@@ -1,4 +1,11 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
rev: 0.10.7

View File

@@ -5,9 +5,16 @@
---
This CLI is still in an early stage of development with many more things that could be implemented. However, the commands that are implemented should be working without issues.
## Table of Contents
## Install
- [Installation](#installation)
- [Configuration](#configuration)
- [Use](#use)
- [Shell Completion](#shell-completion)
- [License](#license)
## Installation
#### With uv
@@ -65,7 +72,7 @@ vban-cli strip 2 gain -18.7
see `vban-cli strip --help` for more info.
##### Strip EQ
#### Strip EQ
*Usage: vban-cli strip \<index> eq COMMAND [OPTIONS]*
@@ -77,7 +84,7 @@ vban-cli strip 0 eq on true
see `vban-cli strip eq --help` for more info.
##### Strip EQ Cell Command
#### Strip EQ Cell Command
*Usage: vban-cli strip \<index> eq cell \<band> COMMAND [ARGS]*
@@ -93,7 +100,7 @@ vban-cli strip 4 eq cell 5 type 5
see `vban-cli strip eq cell --help` for more info.
##### Strip Comp Command
#### Strip Comp Command
*Usage: vban-cli strip \<index> comp COMMAND [OPTIONS]*
@@ -107,7 +114,7 @@ vban-cli strip 3 comp auto-makeup true
see `vban-cli strip comp --help` for more info.
##### Strip Gate Command
#### Strip Gate Command
*Usage: vban-cli strip \<index> gate COMMAND [OPTIONS]*
@@ -121,7 +128,19 @@ vban-cli strip 5 gate hold 2088.7
see `vban-cli strip comp --help` for more info.
##### Strip Gainlayer Command
#### Strip Denoiser Command
*Usage: vban-cli strip \<index> denoiser COMMAND [OPTIONS]*
examples:
```console
vban-cli strip 4 denoiser knob 3.6
```
see `vban-cli strip denoiser --help` for more info.
#### Strip Gainlayer Command
*Usage: vban-cli strip \<index> gainlayer \<gainlayer_index> COMMAND [OPTIONS] [ARGS]*
@@ -201,6 +220,14 @@ vban-cli sendtext 'Point(ASIO128.IN[1..4],ASIO128.OUT[1]).dBGain = -3.0'
see `vban-cli sendtext --help` for more info.
## Shell Completion
Shell completion scripts are available for *bash*, *zsh*, and *fish*.
```console
vban-cli --install-completion
```
---
## Implementation Notes
@@ -216,8 +243,6 @@ see `vban-cli sendtext --help` for more info.
I've made the effort to set up the basic skeletal structure of the CLI as well as demonstrate how to combine subcommand groups with subcommand groups so more can be implemented, it just needs doing. There may be restrictions on some things however, for example, retrieving values is only possible for parameters [defined in the protocol](https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/3be2c1c36563afbd6df3da8436406c77d2cc1f10/VoicemeeterRemote.h#L787). Setting parameters can be done for anything possible by a string request.
Shell completion scripts are available (for zsh, bash and fish) but they haven't been thoroughly tested.
If there's something missing that you would like to see added the best bet is to submit a PR. You may raise an issue and if it's quick and simple to do I may (or may not) do it.
---

View File

@@ -1,13 +1,13 @@
[project]
name = "vban-cli"
version = "0.11.1"
version = "0.12.6"
description = "A command-line interface for Voicemeeter leveraging VBAN."
readme = "README.md"
license = { text = "LICENSE" }
requires-python = ">=3.13"
dependencies = ["cyclopts>=4.6.0", "loguru>=0.7.3", "vban-cmd>=2.9.0"]
requires-python = ">=3.10"
dependencies = ["cyclopts>=4.10.1", "loguru>=0.7.3", "vban-cmd>=2.10.3"]
classifiers = [
"Development Status :: 3 - Alpha",
"Development Status :: 4 - Beta",
"Programming Language :: Python",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",

View File

@@ -53,7 +53,7 @@ def launcher(
try:
with vban_cmd.api(
vban_config.kind,
ip=vban_config.host,
host=vban_config.host,
port=vban_config.port,
streamname=vban_config.streamname,
disable_rt_listeners=disable_rt_listeners,

View File

@@ -106,3 +106,43 @@ def mode(
app.console.print(ctx.client.bus[index].mode.get())
return
setattr(ctx.client.bus[index].mode, type_, True)
@app.command(name='gain')
def gain(
new_value: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
):
"""Get or set the gain of the specified bus.
Parameters
----------
new_value : float, optional
If provided, sets the gain to this value. If not provided, the current gain is printed.
"""
if new_value is None:
app.console.print(ctx.client.bus[index].gain)
return
ctx.client.bus[index].gain = new_value
@app.command(name='label')
def label(
new_value: Annotated[Optional[str], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
):
"""Get or set the label of the specified bus.
Parameters
----------
new_value : str, optional
If provided, sets the label to this value. If not provided, the current label is printed.
"""
if new_value is None:
app.console.print(ctx.client.bus[index].label)
return
ctx.client.bus[index].label = new_value

View File

@@ -5,7 +5,11 @@ from cyclopts import App, Parameter
from .context import Context
from .help import BaseHelpFormatter
app = App(name='command', help_formatter=BaseHelpFormatter())
app = App(
name='command',
help='Execute commands that perform actions',
help_formatter=BaseHelpFormatter(),
)
@app.command(name='show')
@@ -46,3 +50,23 @@ def restart(
"""Restart the Voicemeeter engine."""
ctx.client.command.restart()
app.console.print('Voicemeeter engine should now be restarting.')
@app.command(name='lock')
def lock(
*,
ctx: Annotated[Context, Parameter(parse=False)],
):
"""Lock the Voicemeeter GUI."""
ctx.client.command.lock = True
app.console.print('Voicemeeter GUI should now be locked.')
@app.command(name='unlock')
def unlock(
*,
ctx: Annotated[Context, Parameter(parse=False)],
):
"""Unlock the Voicemeeter GUI."""
ctx.client.command.lock = False
app.console.print('Voicemeeter GUI should now be unlocked.')

View File

@@ -1,31 +1,20 @@
from typing import Annotated
from typing import Annotated, Optional
from cyclopts import App, Argument, Parameter
from .context import Context
from .help import StripHelpFormatter
app = App(name='comp', help_formatter=StripHelpFormatter())
@app.meta.default
def launcher(
*tokens: Annotated[str, Parameter(show=False, allow_leading_hyphen=True)],
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
):
"""Control the compressor parameters."""
additional_kwargs = {}
command, bound, _ = app.parse_args(tokens)
additional_kwargs['index'] = index
additional_kwargs['ctx'] = ctx
return command(*bound.args, **bound.kwargs, **additional_kwargs)
app = App(
name='comp',
help='Control the compressor settings',
help_formatter=StripHelpFormatter(),
)
@app.command(name='knob')
def knob(
new_knob: Annotated[float, Argument()] = None,
new_knob: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
@@ -34,7 +23,7 @@ def knob(
Parameters
----------
new_knob : int, optional
new_knob : float, optional
If provided, sets the knob to this value. If not provided, the current knob is printed.
"""
if new_knob is None:
@@ -46,7 +35,7 @@ def knob(
@app.command(name='input-gain')
def input_gain(
new_gain: Annotated[float, Argument()] = None,
new_gain: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
@@ -67,7 +56,7 @@ def input_gain(
@app.command(name='ratio')
def ratio(
new_ratio: Annotated[float, Argument()] = None,
new_ratio: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
@@ -88,7 +77,7 @@ def ratio(
@app.command(name='threshold')
def threshold(
new_threshold: Annotated[float, Argument()] = None,
new_threshold: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
@@ -109,7 +98,7 @@ def threshold(
@app.command(name='attack')
def attack(
new_attack: Annotated[float, Argument()] = None,
new_attack: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
@@ -130,7 +119,7 @@ def attack(
@app.command(name='release')
def release(
new_release: Annotated[float, Argument()] = None,
new_release: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
@@ -151,7 +140,7 @@ def release(
@app.command(name='knee')
def knee(
new_knee: Annotated[float, Argument()] = None,
new_knee: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
@@ -172,7 +161,7 @@ def knee(
@app.command(name='output-gain')
def output_gain(
new_gain: Annotated[float, Argument()] = None,
new_gain: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
@@ -193,7 +182,7 @@ def output_gain(
@app.command(name='auto-makeup')
def makeup(
new_makeup: Annotated[bool, Argument()] = None,
new_makeup: Annotated[Optional[bool], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],

View File

@@ -1,31 +1,20 @@
from typing import Annotated
from typing import Annotated, Optional
from cyclopts import App, Argument, Parameter
from .context import Context
from .help import StripHelpFormatter
app = App(name='denoiser', help_formatter=StripHelpFormatter())
@app.meta.default
def launcher(
*tokens: Annotated[str, Parameter(show=False, allow_leading_hyphen=True)],
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
):
"""Control the denoiser parameters."""
additional_kwargs = {}
command, bound, _ = app.parse_args(tokens)
additional_kwargs['index'] = index
additional_kwargs['ctx'] = ctx
return command(*bound.args, **bound.kwargs, **additional_kwargs)
app = App(
name='denoiser',
help='Control the denoiser settings',
help_formatter=StripHelpFormatter(),
)
@app.command(name='knob')
def knob(
new_knob: Annotated[float, Argument()] = None,
new_knob: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
@@ -34,7 +23,7 @@ def knob(
Parameters
----------
new_knob : int, optional
new_knob : float, optional
If provided, sets the knob to this value. If not provided, the current knob is printed.
"""
if new_knob is None:

View File

@@ -1,4 +1,4 @@
from typing import Annotated
from typing import Annotated, Optional
from cyclopts import App, Argument, Parameter
@@ -35,7 +35,7 @@ def launcher(
@app.command(name='on')
def on(
new_state: Annotated[bool, Argument()] = None,
new_state: Annotated[Optional[bool], Argument()] = None,
*,
target: Annotated[object, Parameter(parse=False)],
):
@@ -43,7 +43,7 @@ def on(
Parameters
----------
new_state : bool
new_state : bool, optional
If provided, sets the on state to this value. If not provided, the current on state is printed.
"""
if new_state is None:
@@ -73,7 +73,7 @@ def cell_launcher(
@cell_app.command(name='on')
def cell_on(
new_state: Annotated[bool, Argument()] = None,
new_state: Annotated[Optional[bool], Argument()] = None,
*,
target: Annotated[object, Parameter(parse=False)],
):
@@ -81,7 +81,7 @@ def cell_on(
Parameters
----------
new_state : bool
new_state : bool, optional
If provided, sets the on state to this value. If not provided, the current on state is printed.
"""
if new_state is None:
@@ -93,7 +93,7 @@ def cell_on(
@cell_app.command(name='freq')
def cell_freq(
new_freq: Annotated[float, Argument()] = None,
new_freq: Annotated[Optional[float], Argument()] = None,
*,
target: Annotated[object, Parameter(parse=False)],
):
@@ -101,7 +101,7 @@ def cell_freq(
Parameters
----------
new_freq : float
new_freq : float, optional
If provided, sets the frequency to this value. If not provided, the current frequency is printed.
"""
if new_freq is None:
@@ -113,7 +113,7 @@ def cell_freq(
@cell_app.command(name='gain')
def cell_gain(
new_gain: Annotated[float, Argument()] = None,
new_gain: Annotated[Optional[float], Argument()] = None,
*,
target: Annotated[object, Parameter(parse=False)],
):
@@ -121,7 +121,7 @@ def cell_gain(
Parameters
----------
new_gain : float
new_gain : float, optional
If provided, sets the gain to this value. If not provided, the current gain is printed.
"""
if new_gain is None:
@@ -133,7 +133,7 @@ def cell_gain(
@cell_app.command(name='quality')
def cell_q(
new_q: Annotated[float, Argument()] = None,
new_q: Annotated[Optional[float], Argument()] = None,
*,
target: Annotated[object, Parameter(parse=False)],
):
@@ -141,7 +141,7 @@ def cell_q(
Parameters
----------
new_q : float
new_q : float, optional
If provided, sets the Q to this value. If not provided, the current Q is printed.
"""
if new_q is None:
@@ -153,7 +153,7 @@ def cell_q(
@cell_app.command(name='type')
def cell_type(
new_type: Annotated[int, Argument()] = None,
new_type: Annotated[Optional[int], Argument()] = None,
*,
target: Annotated[object, Parameter(parse=False)],
):
@@ -161,7 +161,7 @@ def cell_type(
Parameters
----------
new_type : int
new_type : int, optional
If provided, sets the type to this value. If not provided, the current type is printed.
"""
if new_type is None:

View File

@@ -1,4 +1,4 @@
from typing import Annotated
from typing import Annotated, Optional
from cyclopts import App, Argument, Parameter
@@ -28,7 +28,7 @@ def launcher(
@app.command(name='level')
def level(
new_level: Annotated[float, Argument()] = None,
new_level: Annotated[Optional[float], Argument()] = None,
*,
strip_index: Annotated[int, Parameter(parse=False)],
gainlayer_index: Annotated[int, Parameter(parse=False)],
@@ -38,7 +38,7 @@ def level(
Parameters
----------
new_level : float
new_level : float, optional
If provided, sets the level to this value. If not provided, the current level is printed.
"""
if new_level is None:

View File

@@ -1,31 +1,20 @@
from typing import Annotated
from typing import Annotated, Optional
from cyclopts import App, Argument, Parameter
from .context import Context
from .help import StripHelpFormatter
app = App(name='gate', help_formatter=StripHelpFormatter())
@app.meta.default
def launcher(
*tokens: Annotated[str, Parameter(show=False, allow_leading_hyphen=True)],
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
):
"""Control the compressor parameters."""
additional_kwargs = {}
command, bound, _ = app.parse_args(tokens)
additional_kwargs['index'] = index
additional_kwargs['ctx'] = ctx
return command(*bound.args, **bound.kwargs, **additional_kwargs)
app = App(
name='gate',
help='Control the gate settings',
help_formatter=StripHelpFormatter(),
)
@app.command(name='knob')
def knob(
new_knob: Annotated[float, Argument()] = None,
new_knob: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
@@ -34,7 +23,7 @@ def knob(
Parameters
----------
new_knob : int, optional
new_knob : float, optional
If provided, sets the knob to this value. If not provided, the current knob is printed.
"""
if new_knob is None:
@@ -46,7 +35,7 @@ def knob(
@app.command(name='threshold')
def threshold(
new_threshold: Annotated[float, Argument()] = None,
new_threshold: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
@@ -67,7 +56,7 @@ def threshold(
@app.command(name='damping-max')
def damping_max(
new_damping_max: Annotated[float, Argument()] = None,
new_damping_max: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
@@ -88,7 +77,7 @@ def damping_max(
@app.command(name='bp-sidechain')
def bp_sidechain(
new_bp_sidechain: Annotated[float, Argument()] = None,
new_bp_sidechain: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
@@ -109,7 +98,7 @@ def bp_sidechain(
@app.command(name='attack')
def attack(
new_attack: Annotated[float, Argument()] = None,
new_attack: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
@@ -130,7 +119,7 @@ def attack(
@app.command(name='hold')
def hold(
new_hold: Annotated[float, Argument()] = None,
new_hold: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
@@ -151,7 +140,7 @@ def hold(
@app.command(name='release')
def release(
new_release: Annotated[float, Argument()] = None,
new_release: Annotated[Optional[float], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],

View File

@@ -7,7 +7,11 @@ from . import validation
from .context import Context
from .help import BaseHelpFormatter
app = App(name='recorder', help_formatter=BaseHelpFormatter())
app = App(
name='recorder',
help='Control the recorder playback and recording',
help_formatter=BaseHelpFormatter(),
)
@app.command(name='play')

View File

@@ -8,9 +8,9 @@ from .help import StripHelpFormatter
app = App(name='strip', help_formatter=StripHelpFormatter())
app.command(eq.app.meta, name='eq')
app.command(comp.app.meta, name='comp')
app.command(gate.app.meta, name='gate')
app.command(denoiser.app.meta, name='denoiser')
app.command(comp.app, name='comp')
app.command(gate.app, name='gate')
app.command(denoiser.app, name='denoiser')
app.command(gainlayer.app.meta, name='gainlayer')
@@ -270,3 +270,23 @@ def b3(
app.console.print(ctx.client.strip[index].B3)
return
ctx.client.strip[index].B3 = new_value
@app.command(name='label')
def label(
new_value: Annotated[Optional[str], Argument()] = None,
*,
index: Annotated[int, Parameter(parse=False)],
ctx: Annotated[Context, Parameter(parse=False)],
):
"""Get or set the label of the specified strip.
Parameters
----------
new_value : str, optional
If provided, sets the label to this value. If not provided, the current label is printed.
"""
if new_value is None:
app.console.print(ctx.client.strip[index].label)
return
ctx.client.strip[index].label = new_value

82
uv.lock generated
View File

@@ -1,6 +1,6 @@
version = 1
revision = 3
requires-python = ">=3.13"
requires-python = ">=3.10"
[[package]]
name = "attrs"
@@ -22,17 +22,19 @@ wheels = [
[[package]]
name = "cyclopts"
version = "4.6.0"
version = "4.10.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "attrs" },
{ name = "docstring-parser" },
{ name = "rich" },
{ name = "rich-rst" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/49/5c/88a4068c660a096bbe87efc5b7c190080c9e86919c36ec5f092cb08d852f/cyclopts-4.6.0.tar.gz", hash = "sha256:483c4704b953ea6da742e8de15972f405d2e748d19a848a4d61595e8e5360ee5", size = 162724, upload-time = "2026-02-23T15:44:49.286Z" }
sdist = { url = "https://files.pythonhosted.org/packages/6c/c4/2ce2ca1451487dc7d59f09334c3fa1182c46cfcf0a2d5f19f9b26d53ac74/cyclopts-4.10.1.tar.gz", hash = "sha256:ad4e4bb90576412d32276b14a76f55d43353753d16217f2c3cd5bdceba7f15a0", size = 166623, upload-time = "2026-03-23T14:43:01.098Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8f/eb/1e8337755a70dc7d7ff10a73dc8f20e9352c9ad6c2256ed863ac95cd3539/cyclopts-4.6.0-py3-none-any.whl", hash = "sha256:0a891cb55bfd79a3cdce024db8987b33316aba11071e5258c21ac12a640ba9f2", size = 200518, upload-time = "2026-02-23T15:44:47.854Z" },
{ url = "https://files.pythonhosted.org/packages/8a/0b/2261922126b2e50c601fe22d7ff5194e0a4d50e654836260c0665e24d862/cyclopts-4.10.1-py3-none-any.whl", hash = "sha256:35f37257139380a386d9fe4475e1e7c87ca7795765ef4f31abba579fcfcb6ecd", size = 204331, upload-time = "2026-03-23T14:43:02.625Z" },
]
[[package]]
@@ -122,9 +124,72 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" },
]
[[package]]
name = "tomli"
version = "2.4.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" },
{ url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" },
{ url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" },
{ url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" },
{ url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" },
{ url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" },
{ url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" },
{ url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" },
{ url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" },
{ url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" },
{ url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" },
{ url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" },
{ url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" },
{ url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" },
{ url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" },
{ url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" },
{ url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" },
{ url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" },
{ url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" },
{ url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" },
{ url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" },
{ url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" },
{ url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" },
{ url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" },
{ url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" },
{ url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" },
{ url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" },
{ url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" },
{ url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" },
{ url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" },
{ url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" },
{ url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" },
{ url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" },
{ url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" },
{ url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" },
{ url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" },
{ url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" },
{ url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" },
{ url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" },
{ url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" },
{ url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" },
{ url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" },
{ url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" },
{ url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" },
{ url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" },
{ url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" },
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
]
[[package]]
name = "vban-cli"
version = "0.11.1"
version = "0.12.6"
source = { editable = "." }
dependencies = [
{ name = "cyclopts" },
@@ -134,15 +199,18 @@ dependencies = [
[package.metadata]
requires-dist = [
{ name = "cyclopts", specifier = ">=4.6.0" },
{ name = "cyclopts", specifier = ">=4.10.1" },
{ name = "loguru", specifier = ">=0.7.3" },
{ name = "vban-cmd", editable = "../vban-cmd-python" },
]
[[package]]
name = "vban-cmd"
version = "2.9.0"
version = "2.10.3"
source = { editable = "../vban-cmd-python" }
dependencies = [
{ name = "tomli", marker = "python_full_version < '3.11'" },
]
[package.metadata]
requires-dist = [{ name = "tomli", marker = "python_full_version < '3.11'", specifier = ">=2.0.1,<3.0" }]