virtual-key-mapper 0.1.0__tar.gz
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.
- virtual_key_mapper-0.1.0/LICENSE +202 -0
- virtual_key_mapper-0.1.0/PKG-INFO +450 -0
- virtual_key_mapper-0.1.0/README.md +425 -0
- virtual_key_mapper-0.1.0/pyproject.toml +39 -0
- virtual_key_mapper-0.1.0/setup.cfg +4 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/__init__.py +3 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/__main__.py +3 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/adapters.py +115 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/auth/__init__.py +1 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/auth/forwarded.py +155 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/auth/litellm_oauth.py +404 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/auth/litellm_sso.py +296 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/cli.py +875 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/errors.py +2 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/gateways/__init__.py +1 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/gateways/litellm.py +92 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/integrations/__init__.py +1 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/integrations/claude.py +371 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/integrations/codex.py +190 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/integrations/common.py +115 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/integrations/opencode.py +320 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/labels.py +742 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/profiles.py +339 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/registry.py +74 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/repositories.py +263 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/resolve.py +238 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/rules.py +297 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/shells.py +116 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/storage.py +234 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper/transport.py +130 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper.egg-info/PKG-INFO +450 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper.egg-info/SOURCES.txt +56 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper.egg-info/dependency_links.txt +1 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper.egg-info/entry_points.txt +2 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper.egg-info/requires.txt +2 -0
- virtual_key_mapper-0.1.0/src/virtual_key_mapper.egg-info/top_level.txt +1 -0
- virtual_key_mapper-0.1.0/tests/test_adapters.py +174 -0
- virtual_key_mapper-0.1.0/tests/test_agents_cli.py +461 -0
- virtual_key_mapper-0.1.0/tests/test_architecture.py +68 -0
- virtual_key_mapper-0.1.0/tests/test_claude_integration.py +417 -0
- virtual_key_mapper-0.1.0/tests/test_claude_native.py +230 -0
- virtual_key_mapper-0.1.0/tests/test_codex_opencode.py +426 -0
- virtual_key_mapper-0.1.0/tests/test_devcontainer.py +118 -0
- virtual_key_mapper-0.1.0/tests/test_forwarded.py +162 -0
- virtual_key_mapper-0.1.0/tests/test_labels.py +291 -0
- virtual_key_mapper-0.1.0/tests/test_labels_cli.py +572 -0
- virtual_key_mapper-0.1.0/tests/test_oauth.py +377 -0
- virtual_key_mapper-0.1.0/tests/test_parallel_labels.py +106 -0
- virtual_key_mapper-0.1.0/tests/test_profiles.py +174 -0
- virtual_key_mapper-0.1.0/tests/test_release.py +36 -0
- virtual_key_mapper-0.1.0/tests/test_repositories.py +260 -0
- virtual_key_mapper-0.1.0/tests/test_resolve.py +269 -0
- virtual_key_mapper-0.1.0/tests/test_rules.py +297 -0
- virtual_key_mapper-0.1.0/tests/test_sessions.py +553 -0
- virtual_key_mapper-0.1.0/tests/test_storage.py +259 -0
- virtual_key_mapper-0.1.0/tests/test_tags.py +166 -0
- virtual_key_mapper-0.1.0/tests/test_tools_native.py +319 -0
- virtual_key_mapper-0.1.0/tests/test_vault_native.py +137 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright 2026 cbs Corporate Business Solutions Unternehmensberatung GmbH
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: virtual-key-mapper
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Short-lived keys from your company gateway, labelled with the repository you work in, for every tool you use.
|
|
5
|
+
Author: cbs Corporate Business Solutions Unternehmensberatung GmbH
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/cbs-consulting/virtual-key-mapper
|
|
8
|
+
Project-URL: Issues, https://github.com/cbs-consulting/virtual-key-mapper/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/cbs-consulting/virtual-key-mapper/blob/main/CHANGELOG.md
|
|
10
|
+
Keywords: llm,gateway,litellm,sso,virtual-keys,cost-attribution,claude-code,codex,opencode,copilot
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: MacOS
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Topic :: Software Development
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: filelock==4.0.9
|
|
23
|
+
Requires-Dist: keyring==25.7.0
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
<div align="center">
|
|
27
|
+
|
|
28
|
+
<img src="https://raw.githubusercontent.com/cbs-consulting/virtual-key-mapper/main/.github/assets/logo.svg" alt="" width="120" />
|
|
29
|
+
|
|
30
|
+
# cbs Virtual Key Mapper
|
|
31
|
+
|
|
32
|
+
**Short-lived keys from your company gateway, labelled with the repository you work in, for every tool you use.**
|
|
33
|
+
|
|
34
|
+
[](https://pypi.org/project/virtual-key-mapper/)
|
|
35
|
+
[](https://marketplace.visualstudio.com/items?itemName=cbs-consulting.vkm)
|
|
36
|
+
[](LICENSE)
|
|
37
|
+
[](pyproject.toml)
|
|
38
|
+
[](#-what-works-where)
|
|
39
|
+
[](docs/architecture.md#11-gateway-requirements)
|
|
40
|
+
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
`vkm` signs you in to your company's gateway once, with its single sign-on. From then on your
|
|
46
|
+
tools get short-lived keys from it, and every request carries the **labels** of the
|
|
47
|
+
repository you are working in: key/value pairs such as `customer = "acme"` and
|
|
48
|
+
`project = "webshop"`. Spend is attributed by label without anyone pasting API keys into
|
|
49
|
+
config files, and nothing goes out while a required label has no value.
|
|
50
|
+
|
|
51
|
+
`vkm` does not depend on a particular gateway or tool. Gateways, sign-in methods and tools
|
|
52
|
+
are separate adapters ([docs/adapters.md](docs/adapters.md)), so new ones can be added
|
|
53
|
+
without touching the rest.
|
|
54
|
+
|
|
55
|
+
## ✨ What works where
|
|
56
|
+
|
|
57
|
+
### Gateways and sign-in
|
|
58
|
+
|
|
59
|
+
| Gateway | Sign-in | What the gateway receives |
|
|
60
|
+
| --- | --- | --- |
|
|
61
|
+
| [LiteLLM](https://github.com/BerriAI/litellm) | Its native CLI single sign-on in the browser, with team choice. The refresh token stays in the OS vault. | The SSO user and team (from the token), the labels as request tags `x-litellm-tags` (by default one `KEY:VALUE` tag per label, e.g. `customer:acme`), and `x-litellm-spend-logs-metadata` = `{"labels": {"customer": "acme"}, "sources": {"customer": "explicit\|env\|local\|file\|rules"}, "repository": "github.com/org/repo"}` (repository only inside a Git checkout with an origin). |
|
|
62
|
+
| LiteLLM, from a VS Code dev container, SSH or WSL window | `forwarded`: the two VS Code extensions relay short-lived keys from the sign-in on your own computer; the refresh token never leaves it. | The same. |
|
|
63
|
+
|
|
64
|
+
More gateways and sign-in methods are adapters ([docs/adapters.md](docs/adapters.md)).
|
|
65
|
+
|
|
66
|
+
### Tools
|
|
67
|
+
|
|
68
|
+
| Tool | Connected with | Labels | Key renewal | While a required label has no value |
|
|
69
|
+
| --- | --- | --- | --- | --- |
|
|
70
|
+
| Claude Code CLI | `vkm setup claude` | per repository, checked before each prompt | ✅ | the prompt is blocked |
|
|
71
|
+
| Claude Code for VS Code | `vkm setup claude` + `"claudeCode.disableLoginPrompt": true` | per repository, checked before each prompt | ✅ | the prompt is blocked |
|
|
72
|
+
| Codex CLI | `vkm setup codex` + the shell hook | of the folder where Codex starts | ✅ | no key is sent; the gateway refuses |
|
|
73
|
+
| OpenCode v1 | `vkm setup opencode` | per request, of the folder OpenCode runs in | ✅ | the request is blocked |
|
|
74
|
+
| OpenCode v2 | `vkm run opencode` (`setup` refuses v2) | fixed at launch | ❌ key from launch time; restart when it expires | as for `vkm run` |
|
|
75
|
+
| GitHub Copilot Chat in VS Code | the Virtual Key Mapper extension | per workspace folder, shown in the status bar | ✅ | blocked, with an "Assign labels" button that runs `vkm repo bind` |
|
|
76
|
+
| any of the above, one launch | `vkm run [--label KEY=VALUE]... TOOL` | fixed at launch (or the ones you give) | ✅ (OpenCode v2: ❌) | asks in a terminal for each missing label and saves the answers; blocked without a terminal or outside Git |
|
|
77
|
+
| Codex for VS Code | not supported yet | | | |
|
|
78
|
+
| Copilot inline completions | not possible: they always use GitHub's models | | | |
|
|
79
|
+
|
|
80
|
+
### Platforms
|
|
81
|
+
|
|
82
|
+
✅ = tested in CI on every push, with the real tools (pinned in `ci/agents`, plus a weekly
|
|
83
|
+
run with their latest releases) against a local test gateway. ☑️ = supported and checked by
|
|
84
|
+
hand with the [manual checklist](docs/testing.md#manual-checklist).
|
|
85
|
+
|
|
86
|
+
| | Windows | macOS | Linux | Dev container | SSH, WSL |
|
|
87
|
+
| --- | --- | --- | --- | --- | --- |
|
|
88
|
+
| Claude Code CLI, Codex CLI, OpenCode v1 | ✅ (interactive Codex ☑️) | ✅ | ✅ | ✅ Claude Code and Codex, ☑️ OpenCode | ☑️ |
|
|
89
|
+
| Claude Code for VS Code, Copilot Chat, OpenCode v2 | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ |
|
|
90
|
+
| Credential vault | ✅ Credential Manager | ✅ Keychain | ✅ Secret Service (GNOME Keyring), ☑️ KWallet | not needed | not needed |
|
|
91
|
+
| Shell hooks | ✅ PowerShell 7, bash; ☑️ Windows PowerShell 5.1 and PCs that block scripts (`shell-init pwsh --terminal`) | ✅ bash, zsh, fish, PowerShell 7 | ✅ bash, zsh, fish, PowerShell 7 | as on Linux | as on Linux |
|
|
92
|
+
| VS Code extensions | ✅ in a VS Code extension host | ✅ in an extension host, with the relay | ✅ in an extension host, with the relay | ✅ relay in a real dev container | ☑️ |
|
|
93
|
+
|
|
94
|
+
### Choosing labels
|
|
95
|
+
|
|
96
|
+
Each label is resolved on its own, so `customer` can come from a central rule and `project`
|
|
97
|
+
from a committed file. For each key the first source with a value wins:
|
|
98
|
+
|
|
99
|
+
| Source | Who sets it | Applies to |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| `vkm run --label KEY=VALUE` | you | that launch |
|
|
102
|
+
| `VKM_LABEL_<KEY>` (e.g. `VKM_LABEL_CUSTOMER`) | you | every process started with the variable set |
|
|
103
|
+
| `vkm repo bind --label KEY=VALUE` | you | clones of that repository on this computer (same origin, or it as upstream), only for you |
|
|
104
|
+
| a committed `.vkm` file (`vkm repo bind --shared --label KEY=VALUE` writes it at the repository root) | the team | that folder and everything below it, unless a nearer `.vkm` exists |
|
|
105
|
+
| central rules (a JSON file or URL in the profile) | your organisation | repositories matching a pattern, e.g. `github.com/acme-*/**` |
|
|
106
|
+
|
|
107
|
+
Which labels exist and which are required is set by your organisation's central rules
|
|
108
|
+
document or by your profile ([Labels](#-labels)).
|
|
109
|
+
|
|
110
|
+
## 🚀 Quick start
|
|
111
|
+
|
|
112
|
+
Needs Python 3.11 or newer and an OS credential vault.
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
uv tool install virtual-key-mapper # or: pipx install virtual-key-mapper
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Update later with `uv tool upgrade virtual-key-mapper` (or `pipx upgrade virtual-key-mapper`).
|
|
119
|
+
From a checkout instead:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
python -m venv .venv
|
|
123
|
+
.venv/bin/python -m pip install -e . # Windows: .venv\Scripts\python.exe -m pip install -e .
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`vkm` is the installed command. If your machine blocks pip-generated `.exe` launchers (some
|
|
127
|
+
Windows policies do), use `python -m virtual_key_mapper` everywhere instead; it accepts the
|
|
128
|
+
same arguments.
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# 1. Once per machine: a profile for your gateway and your business-unit team, then sign in.
|
|
132
|
+
# If your organisation publishes its label definitions, pass --rules URL instead of --label.
|
|
133
|
+
vkm profile add work --gateway https://llm.company.example --team-id YOUR_TEAM_ID \
|
|
134
|
+
--label customer --required --description "Who the work is billed to"
|
|
135
|
+
vkm login # browser sign-in; choose the same team there
|
|
136
|
+
vkm status # "state": "ready"
|
|
137
|
+
|
|
138
|
+
# 2. Once per machine: connect your tools.
|
|
139
|
+
vkm setup claude --model YOUR_CLAUDE_ALIAS
|
|
140
|
+
vkm setup codex --model YOUR_CODEX_ALIAS
|
|
141
|
+
vkm setup opencode
|
|
142
|
+
|
|
143
|
+
# 3. Once per repository (unless a central rule or a committed .vkm covers it):
|
|
144
|
+
cd ~/src/some-repo
|
|
145
|
+
vkm repo bind --label customer=acme # or plain 'vkm repo bind' to be asked
|
|
146
|
+
vkm repo show
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The gateway address is its root URL (no `/v1`); the team ID is the exact ID from the gateway,
|
|
150
|
+
not its display name. Your sign-in renews itself; sign in again only after logging out, a
|
|
151
|
+
long pause or a change of team membership.
|
|
152
|
+
The gateway must be served at its host root with LiteLLM's `PROXY_BASE_URL` set to that URL
|
|
153
|
+
(see [Gateway requirements](docs/architecture.md#11-gateway-requirements)).
|
|
154
|
+
|
|
155
|
+
## 🧭 Labels
|
|
156
|
+
|
|
157
|
+
A label is a key with a value, such as `customer = "acme"` or `project = "webshop"`. What a
|
|
158
|
+
label means is up to your organisation; `vkm` knows the keys, which of them are required,
|
|
159
|
+
where their values may come from and how the gateway receives them. The full reference is
|
|
160
|
+
[docs/labels.md](docs/labels.md).
|
|
161
|
+
|
|
162
|
+
### Definitions
|
|
163
|
+
|
|
164
|
+
Labels are defined in your organisation's central rules document (`profile add --rules URL|PATH`
|
|
165
|
+
or `profile update NAME --rules ...`) and, for keys the document does not define, in your profile:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
vkm profile update work --label project --description "Project within the customer"
|
|
169
|
+
vkm profile update work --label customer --required --value acme=Acme --value globex=Globex --strict
|
|
170
|
+
vkm profile update work --no-label project
|
|
171
|
+
vkm labels list # the labels in effect, where each is defined, and notices
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
One label per call. `--required`: nothing is sent while it has no value. `--value ID[=NAME]`:
|
|
175
|
+
the choices the picker offers; with `--strict` only those are accepted. `--source NAME`
|
|
176
|
+
(repeatable) limits where values may come from. Keys are 1-32 lowercase letters, digits, `_`
|
|
177
|
+
or `-`, starting with a letter. A profile label for a key the central document defines is
|
|
178
|
+
refused (or, when the document cannot be read at the time, ignored with a notice): the central
|
|
179
|
+
one applies. If no label is defined anywhere, requests carry only the repository, and `setup`,
|
|
180
|
+
`run` and `repo show` say so.
|
|
181
|
+
|
|
182
|
+
### Values
|
|
183
|
+
|
|
184
|
+
For each defined key, the first source with a value wins:
|
|
185
|
+
|
|
186
|
+
1. `--label KEY=VALUE` (for `run` and `repo show`);
|
|
187
|
+
2. the environment variable `VKM_LABEL_<KEY>` (exactly: uppercase, `-` as `_`; empty counts as
|
|
188
|
+
unset; other `VKM_LABEL_*` names are ignored);
|
|
189
|
+
3. your personal binding: `vkm repo bind --label KEY=VALUE`, stored only for you
|
|
190
|
+
(`repositories.json` on this computer), keyed by the repository's `origin`/`upstream`
|
|
191
|
+
address, so every clone on this computer shares it. It keeps keys you do not name; a
|
|
192
|
+
different value for a key already bound needs `--replace`. `repo unbind` removes the
|
|
193
|
+
binding, `repo unbind --label KEY` one key;
|
|
194
|
+
4. the nearest committed `.vkm` file, written with `repo bind --shared --label KEY=VALUE`:
|
|
195
|
+
```toml
|
|
196
|
+
# Labels for AI tool requests in this folder and below (vkm).
|
|
197
|
+
[labels]
|
|
198
|
+
customer = "acme"
|
|
199
|
+
project = "webshop"
|
|
200
|
+
```
|
|
201
|
+
A file in a parent folder such as `~/src/acme/` covers all repositories below it, and the
|
|
202
|
+
nearest file wins as a whole (a nearer file without `project` does not fall through to a
|
|
203
|
+
parent's). Inside your home folder the search stops below home, so a file in home or above
|
|
204
|
+
it never applies (`repo bind --shared` refuses to write one there); for folders outside home,
|
|
205
|
+
such as `/workspaces` in a dev container or another drive on Windows, it goes up to the root
|
|
206
|
+
of the drive;
|
|
207
|
+
5. a central rule (below).
|
|
208
|
+
|
|
209
|
+
`--label KEY=` on the command line, `""` in a `.vkm` and `null` in a rule mean "no value here,
|
|
210
|
+
stop searching". `--label` refuses keys that no definition knows (typos); values for unknown keys
|
|
211
|
+
from other sources are not sent, and `repo show` lists them. Values are 1-256 printable ASCII
|
|
212
|
+
characters without `,`, surrounding spaces or a leading `!` or `&`.
|
|
213
|
+
|
|
214
|
+
### The central rules document
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"version": "2.0",
|
|
219
|
+
"labels": [
|
|
220
|
+
{"key": "customer", "required": true, "description": "Who the work is billed to",
|
|
221
|
+
"values": [{"id": "acme", "name": "Acme"}, {"id": "globex", "name": "Globex"}]},
|
|
222
|
+
{"key": "project", "description": "Project within the customer"}
|
|
223
|
+
],
|
|
224
|
+
"outputs": {"litellm": {"tags": {"format": ["${customer}", "${customer}:${project}"]}}},
|
|
225
|
+
"rules": [
|
|
226
|
+
{"match": "github.com/acme-*/**", "labels": {"customer": "acme"}},
|
|
227
|
+
{"match": "**", "labels": {"project": null}}
|
|
228
|
+
]
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Rules are tried in order, each against `origin`, then `upstream`; each key takes its value from
|
|
233
|
+
the first matching rule that sets it, so a catch-all below can still supply another label.
|
|
234
|
+
`**` spans path segments, `*`/`?` stay within one. Write the repository as `repo show` prints it
|
|
235
|
+
(lowercase, `%20` decoded to a space). `outputs` are grouped by gateway type, so one document
|
|
236
|
+
can serve profiles on different gateways. A URL is cached for an hour; if a fetch fails or a
|
|
237
|
+
newly published document is invalid, the last good copy is used.
|
|
238
|
+
|
|
239
|
+
### Required labels
|
|
240
|
+
|
|
241
|
+
A required label without a value blocks every request with a message starting
|
|
242
|
+
`Required labels have no value:`, naming the keys and the ways to set them. `vkm run` and
|
|
243
|
+
`vkm repo bind` in a terminal, and "Assign labels" in VS Code, ask for each missing one and save
|
|
244
|
+
the answers as your personal binding. The picker offers the label's `values`, else the
|
|
245
|
+
gateway's catalogue, else accepts any valid typed value without signing in (`vkm labels values KEY`
|
|
246
|
+
shows which). `vkm repo bind` asks only for labels a personal binding (with `--shared`: a `.vkm`
|
|
247
|
+
file) may set.
|
|
248
|
+
`repo show` tells you each label's value and where it came from.
|
|
249
|
+
|
|
250
|
+
### What the gateway receives
|
|
251
|
+
|
|
252
|
+
For LiteLLM, by default:
|
|
253
|
+
|
|
254
|
+
```text
|
|
255
|
+
x-litellm-tags: customer:acme,project:webshop
|
|
256
|
+
x-litellm-spend-logs-metadata: {"labels":{"customer":"acme","project":"webshop"},"repository":"github.com/acme-shop/web","sources":{"customer":"rules","project":"file"}}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
The tags format can be changed in the central document (it wins) or in the profile;
|
|
260
|
+
the metadata can be switched off:
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
vkm profile update work --output 'tags=${customer}' --output 'tags=${customer}:${project}'
|
|
264
|
+
vkm profile update work --output-off metadata
|
|
265
|
+
vkm profile update work --output-default tags
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
An entry that uses an optional label without a value is left out; `$$` is a literal `$`.
|
|
269
|
+
Formats that could make one label's value look like another's are refused, as is any setup in
|
|
270
|
+
which a required label reaches no output. The rules are in
|
|
271
|
+
[docs/labels.md](docs/labels.md#3-outputs).
|
|
272
|
+
|
|
273
|
+
Labels are attribution, not access control: enforce allowed values on the gateway if you need
|
|
274
|
+
that. Gateway settings that break or leak attribution (tag routing, client-side tag rejection,
|
|
275
|
+
header forwarding) and why the picker needs `values` in the central document are listed in
|
|
276
|
+
[Gateway requirements](docs/architecture.md#11-gateway-requirements).
|
|
277
|
+
|
|
278
|
+
## 🧩 Tools
|
|
279
|
+
|
|
280
|
+
### Claude Code (CLI and the official VS Code extension)
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
vkm setup claude [--model ALIAS] [--replace] [--remove]
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Writes `~/.claude/settings.json` (or `$CLAUDE_CONFIG_DIR`): an `apiKeyHelper` that runs
|
|
287
|
+
`vkm token`, the gateway address, and a prompt hook. Before each prompt the hook
|
|
288
|
+
resolves the labels, writes the resulting headers to the repository's `.claude/settings.local.json`
|
|
289
|
+
(excluded from Git) and asks you once to send the prompt again; prompts are blocked while a
|
|
290
|
+
required label has no value.
|
|
291
|
+
For the VS Code extension also set `"claudeCode.disableLoginPrompt": true` in your VS Code
|
|
292
|
+
user settings. `--remove` undoes exactly what setup wrote.
|
|
293
|
+
|
|
294
|
+
### Codex CLI
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
vkm setup codex [--model ALIAS] [--remove]
|
|
298
|
+
eval "$(vkm shell-init bash)" # add to ~/.bashrc; also zsh, fish, pwsh
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
On Windows, add `vkm shell-init pwsh | Out-String | Invoke-Expression` to your PowerShell
|
|
302
|
+
`$PROFILE`. Where the execution policy blocks scripts (common on company PCs, "running scripts is
|
|
303
|
+
disabled on this system"), a profile cannot load; run `vkm shell-init pwsh --terminal`
|
|
304
|
+
instead and paste its command line into Windows Terminal (Settings → your PowerShell profile →
|
|
305
|
+
Command line) or its snippet into your VS Code user settings. Check in a new terminal:
|
|
306
|
+
`Get-Command codex` shows `Function`.
|
|
307
|
+
|
|
308
|
+
Codex reads the label headers from environment variables that the shell hook sets per directory.
|
|
309
|
+
Without them, or while a required label has no value, Codex gets no credential and the gateway refuses the request. The hook also makes
|
|
310
|
+
`codex` start with `--no-daemon`: Codex's shared background daemon would keep the variables of the
|
|
311
|
+
terminal that started it. If one is running from before, stop it once: `codex app-server daemon stop`.
|
|
312
|
+
|
|
313
|
+
### OpenCode
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
vkm setup opencode [--model ALIAS] [--remove]
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Adds a provider with the gateway's models and a small plugin to
|
|
320
|
+
`~/.config/opencode/opencode.json`; the plugin adds a fresh credential and the folder's
|
|
321
|
+
label headers to every request, and blocks requests while a required label has no value. OpenCode v2 is supported only through `vkm run opencode`.
|
|
322
|
+
|
|
323
|
+
### One-off launches
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
vkm run [--label KEY=VALUE]... [--model ALIAS] claude|codex|opencode [-- TOOL ARGUMENTS]
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Starts the tool with child-only configuration, without changing your setup; each `--label`
|
|
330
|
+
sets that key for that process (`--label KEY=` for no value), the others resolve as usual. In a
|
|
331
|
+
terminal it asks for missing required labels and saves the answers as your personal binding.
|
|
332
|
+
|
|
333
|
+
### GitHub Copilot Chat (VS Code)
|
|
334
|
+
|
|
335
|
+
Install **Virtual Key Mapper for Copilot Chat** (`cbs-consulting.vkm`) from the Extensions view,
|
|
336
|
+
the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=cbs-consulting.vkm).
|
|
337
|
+
For editors without the Marketplace (Cursor, VSCodium), install `vkm.vsix` from the
|
|
338
|
+
[latest release](https://github.com/cbs-consulting/virtual-key-mapper/releases/latest)
|
|
339
|
+
(Extensions → ··· → Install from VSIX):
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
code --install-extension cbs-consulting.vkm
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Set `vkm.command` in your VS Code user settings to how the CLI runs, e.g.
|
|
346
|
+
`["/path/to/.venv/bin/python", "-m", "virtual_key_mapper"]`. Pick a "Virtual Key Mapper"
|
|
347
|
+
model in Copilot Chat; the status bar shows the folder's label values (`acme · webshop`),
|
|
348
|
+
missing required labels in warning style, and `no labels` when none are defined; its tooltip
|
|
349
|
+
lists each label with its description and source. If you use VS Code profiles,
|
|
350
|
+
install the extension in the profile you work in. Inline completions and next-edit
|
|
351
|
+
suggestions always use GitHub's models and are not attributed.
|
|
352
|
+
|
|
353
|
+
## 🐳 Dev Containers, SSH and WSL
|
|
354
|
+
|
|
355
|
+
The container gets short-lived tokens from the sign-in on your own computer; nothing
|
|
356
|
+
long-lived is stored inside it.
|
|
357
|
+
|
|
358
|
+
1. Local VS Code: install **Virtual Key Mapper: local sign-in** (`cbs-consulting.vkm-local`) and set `vkm.localCommand` in your user
|
|
359
|
+
settings to the local CLI command. Containers get tokens only for your local default
|
|
360
|
+
profile; list others in `vkm.localProfiles` (user settings) to allow them.
|
|
361
|
+
2. In the container: install the CLI, e.g.
|
|
362
|
+
`uv tool install --python 3.12 virtual-key-mapper`, and install `cbs-consulting.vkm` into the
|
|
363
|
+
container (VS Code offers "Install in Dev Container"; or list it under
|
|
364
|
+
`customizations.vscode.extensions` in `devcontainer.json`). Reload the window.
|
|
365
|
+
3. The extension creates a container profile that uses your local sign-in
|
|
366
|
+
(`vkm profile list` shows `forwarded`), with your local profile's own label definitions and
|
|
367
|
+
output overrides and its central rules URL. Do not run `login` in the container.
|
|
368
|
+
|
|
369
|
+
All tools work in the container as described above. Central rules given as a local file cannot
|
|
370
|
+
be copied; the extension then warns that the organisation's label definitions are missing in the
|
|
371
|
+
container (set them with `vkm profile update NAME --rules URL|PATH` there). Personal `repo bind`
|
|
372
|
+
bindings made inside a container live in the container.
|
|
373
|
+
|
|
374
|
+
## 🗂️ Where things are stored
|
|
375
|
+
|
|
376
|
+
| What | Where |
|
|
377
|
+
| --- | --- |
|
|
378
|
+
| Profiles and their labels, personal label bindings, rules cache, tool state | `%LOCALAPPDATA%\virtual-key-mapper` or `~/.config/virtual-key-mapper` (`--home` or `VKM_HOME` to change) |
|
|
379
|
+
| Sign-in (access and refresh token) | the OS credential vault only; never in files |
|
|
380
|
+
| Claude / Codex / OpenCode configuration | each tool's own user configuration, changed only by `setup` |
|
|
381
|
+
|
|
382
|
+
## ⌨️ Commands
|
|
383
|
+
|
|
384
|
+
`profile add|list|show|use|update`, `login`, `status`, `token [--json]`, `logout`,
|
|
385
|
+
`repo show|bind|unbind|list`, `labels list|values`, `setup claude|codex|opencode`, `run`,
|
|
386
|
+
`shell-init`, and `claude guard` (the hook). `vkm COMMAND --help` explains each.
|
|
387
|
+
|
|
388
|
+
## 📌 Releases
|
|
389
|
+
|
|
390
|
+
| What | Where | Name |
|
|
391
|
+
| --- | --- | --- |
|
|
392
|
+
| CLI | [PyPI](https://pypi.org/project/virtual-key-mapper/) | `virtual-key-mapper` (command `vkm`) |
|
|
393
|
+
| Copilot Chat extension | [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=cbs-consulting.vkm), `vkm.vsix` on each [GitHub release](https://github.com/cbs-consulting/virtual-key-mapper/releases) | `cbs-consulting.vkm` |
|
|
394
|
+
| Local sign-in extension | [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=cbs-consulting.vkm-local), `vkm-local.vsix` on each GitHub release | `cbs-consulting.vkm-local` |
|
|
395
|
+
|
|
396
|
+
All three share one version. Releases are made by the "Release" workflow in the Actions tab, which
|
|
397
|
+
|
|
398
|
+
1. runs the Python and extension tests on the commit to release and builds everything,
|
|
399
|
+
2. requires a `## VERSION - DATE` entry in [`CHANGELOG.md`](CHANGELOG.md),
|
|
400
|
+
3. waits for a maintainer to approve the `release` environment,
|
|
401
|
+
4. publishes the package to PyPI (trusted publishing: no token in the repository) and the
|
|
402
|
+
GitHub release `vVERSION` with the changelog entry as notes and the built files attached.
|
|
403
|
+
|
|
404
|
+
The extensions are then uploaded to the Visual Studio Marketplace by hand, so no store token is
|
|
405
|
+
stored anywhere: download `vkm.vsix` and `vkm-local.vsix` from the release, open
|
|
406
|
+
[marketplace.visualstudio.com/manage](https://marketplace.visualstudio.com/manage) → publisher
|
|
407
|
+
`cbs-consulting`, and choose **⋯ → Update** for each extension (the first time: **New extension →
|
|
408
|
+
Visual Studio Code**). The Marketplace accepts a package only for the publisher named inside it.
|
|
409
|
+
|
|
410
|
+
Every change goes into the "Unreleased" section of `CHANGELOG.md` in its pull request; the
|
|
411
|
+
"Changelog entry" check fails otherwise, unless the pull request has the label "no changelog".
|
|
412
|
+
To prepare a release, set the same version in `pyproject.toml`, `src/virtual_key_mapper/__init__.py`
|
|
413
|
+
and both `package.json` files (`npm install` in `vscode-extension/` updates the lockfile), and
|
|
414
|
+
rename "Unreleased" to `VERSION - DATE`; the tests check that they agree. The workflow can be run
|
|
415
|
+
again and skips what is already published; its "dry run" option only checks and builds.
|
|
416
|
+
|
|
417
|
+
## 🛠️ Contributing
|
|
418
|
+
|
|
419
|
+
Pull requests need two checks: **Test features** (all CI jobs on Windows, macOS and Linux) and
|
|
420
|
+
**Changelog entry**.
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
.venv/bin/python -m unittest discover -s tests -v
|
|
424
|
+
cd vscode-extension && npm test && npm run test:host # extension unit tests; real VS Code host
|
|
425
|
+
cd vscode-extension/local && npm test
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Live tests run the real tools against a local fake gateway (no model calls, no secrets). Install
|
|
429
|
+
the pinned versions and point the tests at them:
|
|
430
|
+
|
|
431
|
+
```bash
|
|
432
|
+
cd ci/agents && npm ci && cd ../..
|
|
433
|
+
export $(node ci/agents/locate.mjs) # sets VKM_TEST_{CLAUDE,CODEX,OPENCODE}_BINARY
|
|
434
|
+
.venv/bin/python -m unittest discover -s tests -v
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
`VKM_TEST_VAULT=1` also runs `tests/test_vault_native.py` against your real OS vault (uniquely named test entries, removed afterwards).
|
|
438
|
+
|
|
439
|
+
Locally, missing tools and shells skip their tests; CI requires them. CI runs everything on Windows,
|
|
440
|
+
macOS and Linux: the Python suite, the live agents and shell hooks, the real OS vault, both extensions
|
|
441
|
+
in a real VS Code, and a real dev container on Linux; a weekly run tries the newest agent releases.
|
|
442
|
+
Details and the manual checklist: [docs/testing.md](docs/testing.md). How it works and why:
|
|
443
|
+
[docs/architecture.md](docs/architecture.md); extending it: [docs/adapters.md](docs/adapters.md).
|
|
444
|
+
|
|
445
|
+
## 📄 License
|
|
446
|
+
|
|
447
|
+
Copyright © 2026 cbs Corporate Business Solutions Unternehmensberatung GmbH.
|
|
448
|
+
Licensed under the [Apache License 2.0](LICENSE).
|
|
449
|
+
|
|
450
|
+
<sub>LiteLLM is a project of BerriAI. Claude Code is a product of Anthropic, Codex of OpenAI, OpenCode of SST, GitHub Copilot and VS Code of GitHub and Microsoft. This project is not affiliated with or endorsed by any of them.</sub>
|