@levr-one/cli 0.3.0 → 0.3.1
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.
- package/README.md +113 -59
- package/dist/cli.js +6 -6
- package/dist/{listHandler-EsB3AeOB.js → listHandler-7DZhE8jn.js} +1 -1
- package/dist/{loginHandler-BfIUQJrC.js → loginHandler-Dfg89AD2.js} +2 -2
- package/dist/{pushHandler-DBBa6hKz.js → pushHandler-CC709aNJ.js} +2 -2
- package/dist/{resolve-workspace-Dj7jgtE3.js → resolve-workspace-vJZku4FK.js} +1 -1
- package/dist/{sdk-client-BeE6mXns.js → sdk-client-BWrIbeUg.js} +15 -1
- package/dist/{selectHandler-XOUHdyea.js → selectHandler-nOUsPW9p.js} +1 -1
- package/dist/{statusHandler-BaQB40t7.js → statusHandler-DljfUcC2.js} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# @levr-one/cli
|
|
2
2
|
|
|
3
|
-
The command-line interface for [Levr](https://www.levr.one).
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
The command-line interface for [Levr](https://www.levr.one). The binary is
|
|
4
|
+
`levr`, and it does two jobs:
|
|
5
|
+
|
|
6
|
+
- **Connect your AI tools** — `levr mcp add` wires the Levr MCP server into
|
|
7
|
+
the AI clients on your machine (Claude Desktop, Claude Code, Cursor,
|
|
8
|
+
Windsurf, Zed) with one command.
|
|
9
|
+
- **Push test results** — `levr push` uploads results from any terminal or CI
|
|
10
|
+
pipeline.
|
|
6
11
|
|
|
7
12
|
## Install
|
|
8
13
|
|
|
@@ -15,82 +20,68 @@ npx @levr-one/cli --help
|
|
|
15
20
|
The package is self-contained — no peer setup required. A global install
|
|
16
21
|
replaces the `levr` bin from the deprecated `@levr-one/setup` package.
|
|
17
22
|
|
|
18
|
-
##
|
|
19
|
-
|
|
20
|
-
Pushing test results needs authentication and nothing else — log in once
|
|
21
|
-
(or set `LEVR_TOKEN` in CI):
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
levr auth login # browser PKCE (or --device-code for SSH/headless)
|
|
25
|
-
levr push ./results.xml
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Using an AI client (Claude Desktop/Code, Cursor, Windsurf, Zed)? Wire the
|
|
29
|
-
Levr MCP server into it with one command — no login needed; the client opens
|
|
30
|
-
a browser to authorize the first time it connects:
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
npx @levr-one/cli mcp add # detect installed clients and pick interactively
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Config edits preserve your existing MCP servers and comments, and re-running
|
|
37
|
-
is a no-op. Use `--all`, `--client <id>`, or `--yes` for non-interactive
|
|
38
|
-
runs and `--dry-run` to preview.
|
|
39
|
-
|
|
40
|
-
### Shell completion (optional)
|
|
23
|
+
## Quick start
|
|
41
24
|
|
|
42
|
-
|
|
25
|
+
**Using an AI client?** Wire the Levr MCP server into it — no login needed;
|
|
26
|
+
the client opens a browser to authorize the first time it connects:
|
|
43
27
|
|
|
44
28
|
```bash
|
|
45
|
-
levr
|
|
46
|
-
levr uninstall # remove it
|
|
29
|
+
npx @levr-one/cli mcp add
|
|
47
30
|
```
|
|
48
31
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
The CLI supports three authentication modes.
|
|
52
|
-
|
|
53
|
-
### Interactive (browser) — default
|
|
32
|
+
**Pushing test results?** Authentication is all you need — log in once (or
|
|
33
|
+
set `LEVR_TOKEN` in CI):
|
|
54
34
|
|
|
55
35
|
```bash
|
|
56
|
-
levr auth login
|
|
36
|
+
levr auth login # browser PKCE (or --device-code for SSH/headless)
|
|
37
|
+
levr push ./results.xml
|
|
57
38
|
```
|
|
58
39
|
|
|
59
|
-
|
|
40
|
+
## Connect AI clients: `levr mcp add`
|
|
60
41
|
|
|
61
|
-
|
|
42
|
+
Detects the MCP-capable clients installed on your machine, lets you pick
|
|
43
|
+
which to set up, and writes the Levr MCP server into each one's config:
|
|
62
44
|
|
|
63
45
|
```bash
|
|
64
|
-
levr
|
|
46
|
+
levr mcp add # detect clients and pick interactively
|
|
47
|
+
levr mcp add --all # set up every detected client
|
|
48
|
+
levr mcp add --dry-run # preview the changes without writing
|
|
49
|
+
levr mcp add --client cursor,zed --yes # non-interactive selection
|
|
65
50
|
```
|
|
66
51
|
|
|
67
|
-
|
|
68
|
-
|
|
52
|
+
What it writes is **credential-free** — a `levr` server entry that connects
|
|
53
|
+
via `mcp-remote`. No token or secret is stored; your client opens the browser
|
|
54
|
+
to authorize with Levr on its first connection. After a run, restart the
|
|
55
|
+
client(s) and authorize once.
|
|
69
56
|
|
|
70
|
-
|
|
57
|
+
**Supported clients:**
|
|
71
58
|
|
|
72
|
-
|
|
73
|
-
|
|
59
|
+
| Client | How it's configured |
|
|
60
|
+
| -------------- | ------------------------------------------------------------------------------ |
|
|
61
|
+
| Claude Desktop | config file (`claude_desktop_config.json`) |
|
|
62
|
+
| Claude Code | prints the `claude mcp add --transport http levr <url>` command for you to run |
|
|
63
|
+
| Cursor | config file (`~/.cursor/mcp.json`) |
|
|
64
|
+
| Windsurf | config file (`~/.codeium/windsurf/mcp_config.json`) |
|
|
65
|
+
| Zed | config file (`settings.json`, `context_servers`) |
|
|
66
|
+
| VS Code, Codex | listed but not yet installable (coming soon) |
|
|
74
67
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### Other auth commands
|
|
68
|
+
Config edits are **safe and repeatable**: existing MCP servers and comments
|
|
69
|
+
in your config files are preserved (jsonc-aware merge), and re-running is a
|
|
70
|
+
no-op that reports "already set up".
|
|
81
71
|
|
|
82
|
-
|
|
83
|
-
levr auth status # show current authentication state
|
|
84
|
-
levr auth logout # clear stored credentials
|
|
85
|
-
```
|
|
72
|
+
**Flags:**
|
|
86
73
|
|
|
87
|
-
|
|
74
|
+
| Flag | Alias | Description |
|
|
75
|
+
| ----------------- | ----- | ----------------------------------------------------- |
|
|
76
|
+
| `--client <id,…>` | | Set up these client ids (comma-separated or repeated) |
|
|
77
|
+
| `--all` | | Set up every detected, installable client |
|
|
78
|
+
| `--yes` | `-y` | Non-interactive; auto-select detected clients |
|
|
79
|
+
| `--dry-run` | | Show the changes without writing |
|
|
80
|
+
| `--url <url>` | | MCP server URL (default derived from the API server) |
|
|
88
81
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
levr workspace current # show the active workspace
|
|
93
|
-
```
|
|
82
|
+
Runs non-interactively whenever `--all`, `--client`, or `--yes` is passed —
|
|
83
|
+
or automatically when not attached to a terminal (CI). Unknown client ids and
|
|
84
|
+
failed writes exit non-zero.
|
|
94
85
|
|
|
95
86
|
## Push test results
|
|
96
87
|
|
|
@@ -168,6 +159,61 @@ withEnv(["LEVR_TOKEN=${LEVR_TOKEN}"]) {
|
|
|
168
159
|
}
|
|
169
160
|
```
|
|
170
161
|
|
|
162
|
+
## Authentication
|
|
163
|
+
|
|
164
|
+
Needed for `push` and `workspace` commands (`mcp add` needs none). Three modes:
|
|
165
|
+
|
|
166
|
+
### Interactive (browser) — default
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
levr auth login
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Opens a browser for PKCE-based OAuth login.
|
|
173
|
+
|
|
174
|
+
### Device code (SSH / headless)
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
levr auth login --device-code
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
A code is displayed in the terminal. Open the provided URL on any device, enter
|
|
181
|
+
the code, and approve.
|
|
182
|
+
|
|
183
|
+
### Personal Access Token (CI/CD)
|
|
184
|
+
|
|
185
|
+
Set the `LEVR_TOKEN` environment variable and the CLI uses it automatically —
|
|
186
|
+
no interactive login:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
export LEVR_TOKEN=<your-personal-access-token>
|
|
190
|
+
levr push ./results.xml
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Other auth commands
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
levr auth status # show current authentication state
|
|
197
|
+
levr auth logout # clear stored credentials
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Workspaces
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
levr workspace list # list the workspaces you belong to
|
|
204
|
+
levr workspace select # choose the active workspace
|
|
205
|
+
levr workspace current # show the active workspace
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Shell completion (optional)
|
|
209
|
+
|
|
210
|
+
Tab-completion is an explicit opt-in step (it is not installed automatically):
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
levr install # add shell completion for the current shell
|
|
214
|
+
levr uninstall # remove it
|
|
215
|
+
```
|
|
216
|
+
|
|
171
217
|
## Configuration
|
|
172
218
|
|
|
173
219
|
All configuration is via environment variables. Flags take precedence.
|
|
@@ -183,6 +229,14 @@ All configuration is via environment variables. Flags take precedence.
|
|
|
183
229
|
|
|
184
230
|
## Troubleshooting
|
|
185
231
|
|
|
232
|
+
**The `levr` server doesn't appear in my AI client after `mcp add`** — Restart
|
|
233
|
+
the client; MCP servers are read at startup. Verify the entry with
|
|
234
|
+
`levr mcp add --dry-run` (it reports "already set up" when the config is in
|
|
235
|
+
place).
|
|
236
|
+
|
|
237
|
+
**My client asks me to authorize Levr** — Expected on the first connection:
|
|
238
|
+
the config is credential-free, so each client authorizes once in the browser.
|
|
239
|
+
|
|
186
240
|
**`Authentication required. Run: levr auth login`** — No valid credentials
|
|
187
241
|
found. Run `levr auth login` or set `LEVR_TOKEN`.
|
|
188
242
|
|
package/dist/cli.js
CHANGED
|
@@ -147,7 +147,7 @@ Examples:
|
|
|
147
147
|
aliases: { d: "device-code" }
|
|
148
148
|
},
|
|
149
149
|
loader: async () => {
|
|
150
|
-
const { loginHandler } = await import("./loginHandler-
|
|
150
|
+
const { loginHandler } = await import("./loginHandler-Dfg89AD2.js");
|
|
151
151
|
return loginHandler;
|
|
152
152
|
}
|
|
153
153
|
});
|
|
@@ -186,7 +186,7 @@ Examples:
|
|
|
186
186
|
},
|
|
187
187
|
parameters: {},
|
|
188
188
|
loader: async () => {
|
|
189
|
-
const { statusHandler } = await import("./statusHandler-
|
|
189
|
+
const { statusHandler } = await import("./statusHandler-DljfUcC2.js");
|
|
190
190
|
return statusHandler;
|
|
191
191
|
}
|
|
192
192
|
});
|
|
@@ -297,7 +297,7 @@ Examples:
|
|
|
297
297
|
}
|
|
298
298
|
},
|
|
299
299
|
loader: async () => {
|
|
300
|
-
const { pushHandler } = await import("./pushHandler-
|
|
300
|
+
const { pushHandler } = await import("./pushHandler-CC709aNJ.js");
|
|
301
301
|
return pushHandler;
|
|
302
302
|
}
|
|
303
303
|
});
|
|
@@ -318,7 +318,7 @@ Examples:
|
|
|
318
318
|
},
|
|
319
319
|
parameters: {},
|
|
320
320
|
loader: async () => {
|
|
321
|
-
const { listHandler } = await import("./listHandler-
|
|
321
|
+
const { listHandler } = await import("./listHandler-7DZhE8jn.js");
|
|
322
322
|
return listHandler;
|
|
323
323
|
}
|
|
324
324
|
});
|
|
@@ -351,7 +351,7 @@ Examples:
|
|
|
351
351
|
flags: {}
|
|
352
352
|
},
|
|
353
353
|
loader: async () => {
|
|
354
|
-
const { selectHandler } = await import("./selectHandler-
|
|
354
|
+
const { selectHandler } = await import("./selectHandler-nOUsPW9p.js");
|
|
355
355
|
return selectHandler;
|
|
356
356
|
}
|
|
357
357
|
});
|
|
@@ -375,7 +375,7 @@ Examples:
|
|
|
375
375
|
|
|
376
376
|
//#endregion
|
|
377
377
|
//#region package.json
|
|
378
|
-
var version = "0.3.
|
|
378
|
+
var version = "0.3.1";
|
|
379
379
|
|
|
380
380
|
//#endregion
|
|
381
381
|
//#region src/app.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./env-NxtzJJPk.js";
|
|
2
|
-
import { authGetSitesV1, configureClient } from "./sdk-client-
|
|
2
|
+
import { authGetSitesV1, configureClient } from "./sdk-client-BWrIbeUg.js";
|
|
3
3
|
import { loadWorkspace } from "./workspace-store-4hfvsEHS.js";
|
|
4
4
|
import "./token-refresh-Cz-FqDtC.js";
|
|
5
5
|
import { resolveToken } from "./resolve-token-BRPrqdG-.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CLI_CLIENT_ID, getApiUrl, getAuthUrl, setSessionApiUrl, writeCredentials } from "./env-NxtzJJPk.js";
|
|
2
|
-
import { configureClient } from "./sdk-client-
|
|
2
|
+
import { configureClient } from "./sdk-client-BWrIbeUg.js";
|
|
3
3
|
import "./workspace-store-4hfvsEHS.js";
|
|
4
|
-
import { autoSelectWorkspace } from "./resolve-workspace-
|
|
4
|
+
import { autoSelectWorkspace } from "./resolve-workspace-vJZku4FK.js";
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
import { createHash, randomBytes } from "node:crypto";
|
|
7
7
|
import { createServer } from "node:http";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getApiUrl, getAutomationSourceIdOverride, getSourceOverride, getTeamId } from "./env-NxtzJJPk.js";
|
|
2
|
-
import { client, configureClient, uploadAutomationIngest, uploadImport } from "./sdk-client-
|
|
2
|
+
import { client, configureClient, uploadAutomationIngest, uploadImport } from "./sdk-client-BWrIbeUg.js";
|
|
3
3
|
import "./workspace-store-4hfvsEHS.js";
|
|
4
|
-
import { resolveWorkspace } from "./resolve-workspace-
|
|
4
|
+
import { resolveWorkspace } from "./resolve-workspace-vJZku4FK.js";
|
|
5
5
|
import "./token-refresh-Cz-FqDtC.js";
|
|
6
6
|
import { resolveToken } from "./resolve-token-BRPrqdG-.js";
|
|
7
7
|
import { readFileSync, statSync } from "node:fs";
|
|
@@ -14060,6 +14060,11 @@ const zCreateGithubIssueLinkDto = z.object({
|
|
|
14060
14060
|
sync_direction: z.string().optional().describe(""),
|
|
14061
14061
|
origin: z.string().optional().describe(""),
|
|
14062
14062
|
role: z.string().optional().describe(""),
|
|
14063
|
+
last_synced_fingerprint: z.string().nullable().optional().describe(""),
|
|
14064
|
+
last_synced_origin: z.string().nullable().optional().describe(""),
|
|
14065
|
+
last_synced_snapshot: z.unknown().optional().describe(""),
|
|
14066
|
+
sync_status: z.string().optional().describe(""),
|
|
14067
|
+
conflict_id: z.string().nullable().optional().describe(""),
|
|
14063
14068
|
tq_issue_id: z.string(),
|
|
14064
14069
|
github_repository_id: z.string(),
|
|
14065
14070
|
team_id: z.string()
|
|
@@ -14071,6 +14076,11 @@ const zResponseGithubIssueLinkDto = z.object({
|
|
|
14071
14076
|
sync_direction: z.string().optional().describe(""),
|
|
14072
14077
|
origin: z.string().optional().describe(""),
|
|
14073
14078
|
role: z.string().optional().describe(""),
|
|
14079
|
+
last_synced_fingerprint: z.string().nullable().optional().describe(""),
|
|
14080
|
+
last_synced_origin: z.string().nullable().optional().describe(""),
|
|
14081
|
+
last_synced_snapshot: z.unknown().optional().describe(""),
|
|
14082
|
+
sync_status: z.string().optional().describe(""),
|
|
14083
|
+
conflict_id: z.string().nullable().optional().describe(""),
|
|
14074
14084
|
tq_issue_id: z.string(),
|
|
14075
14085
|
github_repository_id: z.string(),
|
|
14076
14086
|
team_id: z.string(),
|
|
@@ -14090,6 +14100,11 @@ const zUpdateGithubIssueLinkDto = z.object({
|
|
|
14090
14100
|
sync_direction: z.string().optional().describe(""),
|
|
14091
14101
|
origin: z.string().optional().describe(""),
|
|
14092
14102
|
role: z.string().optional().describe(""),
|
|
14103
|
+
last_synced_fingerprint: z.string().nullable().optional().describe(""),
|
|
14104
|
+
last_synced_origin: z.string().nullable().optional().describe(""),
|
|
14105
|
+
last_synced_snapshot: z.unknown().optional().describe(""),
|
|
14106
|
+
sync_status: z.string().optional().describe(""),
|
|
14107
|
+
conflict_id: z.string().nullable().optional().describe(""),
|
|
14093
14108
|
tq_issue_id: z.string().optional(),
|
|
14094
14109
|
github_repository_id: z.string().optional(),
|
|
14095
14110
|
team_id: z.string().optional()
|
|
@@ -27227,7 +27242,6 @@ const zIngestRequestDto = z.object({
|
|
|
27227
27242
|
force_upsert: z.boolean().optional()
|
|
27228
27243
|
});
|
|
27229
27244
|
const zSyncGetLastSyncIdV1Data = z.object({ url: z.literal("/v1/sync/last-sync-id") });
|
|
27230
|
-
const zSyncResetSyncV1Data = z.object({ url: z.literal("/v1/sync/reset") });
|
|
27231
27245
|
const zSyncBootstrapV1Data = z.object({
|
|
27232
27246
|
body: zBootstrapRequestDto,
|
|
27233
27247
|
url: z.literal("/v1/sync/bootstrap")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./env-NxtzJJPk.js";
|
|
2
|
-
import { authGetSitesV1, configureClient } from "./sdk-client-
|
|
2
|
+
import { authGetSitesV1, configureClient } from "./sdk-client-BWrIbeUg.js";
|
|
3
3
|
import { saveWorkspace } from "./workspace-store-4hfvsEHS.js";
|
|
4
4
|
import "./token-refresh-Cz-FqDtC.js";
|
|
5
5
|
import { resolveToken } from "./resolve-token-BRPrqdG-.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getApiUrl, getPatToken, readCredentials } from "./env-NxtzJJPk.js";
|
|
2
|
-
import { authGetProfileV1, configureClient } from "./sdk-client-
|
|
2
|
+
import { authGetProfileV1, configureClient } from "./sdk-client-BWrIbeUg.js";
|
|
3
3
|
import { isTokenExpired } from "./token-refresh-Cz-FqDtC.js";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
|