@levr-one/cli 0.1.0 → 0.1.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 CHANGED
@@ -149,13 +149,13 @@ withEnv(["LEVR_TOKEN=${LEVR_TOKEN}"]) {
149
149
 
150
150
  All configuration is via environment variables. Flags take precedence.
151
151
 
152
- | Variable | Description | Default |
153
- | --------------- | ---------------------------------------------------------------------------------------- | ----------------------- |
154
- | `LEVR_TOKEN` | Personal Access Token (for CI / headless) | |
155
- | `LEVR_URL` | API base URL | `https://api.levr.one` |
156
- | `LEVR_AUTH_URL` | Auth server URL | `https://auth.levr.one` |
157
- | `LEVR_TEAM_ID` | Default team ID (optional; server resolves from automation source or workspace default) | |
158
- | `LEVR_SOURCE` | Automation source name override (groups imports, remembers team) | |
152
+ | Variable | Description | Default |
153
+ | --------------- | --------------------------------------------------------------------------------------- | ----------------------- |
154
+ | `LEVR_TOKEN` | Personal Access Token (for CI / headless) | |
155
+ | `LEVR_URL` | API base URL | `https://api.levr.one` |
156
+ | `LEVR_AUTH_URL` | Auth server URL | `https://auth.levr.one` |
157
+ | `LEVR_TEAM_ID` | Default team ID (optional; server resolves from automation source or workspace default) | |
158
+ | `LEVR_SOURCE` | Automation source name override (groups imports, remembers team) | |
159
159
 
160
160
  ## Troubleshooting
161
161
 
package/dist/cli.js CHANGED
@@ -1,15 +1,389 @@
1
1
  #!/usr/bin/env node
2
- import { app, buildContext } from "./app-0F0Zyyt7.js";
3
- import { run } from "@stricli/core";
2
+ import { buildApplication, buildCommand, buildRouteMap, proposeCompletions, run, text_en } from "@stricli/core";
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import chalk from "chalk";
7
+ import { buildInstallCommand, buildUninstallCommand } from "@stricli/auto-complete";
4
8
 
9
+ //#region src/utils/logger.ts
10
+ var Logger = class {
11
+ verbose;
12
+ stdout;
13
+ stderr;
14
+ constructor(options) {
15
+ this.verbose = options.verbose ?? false;
16
+ this.stdout = options.stdout ?? process.stdout;
17
+ this.stderr = options.stderr ?? process.stderr;
18
+ }
19
+ info(message) {
20
+ this.stdout.write(`${chalk.blue("info")} ${message}\n`);
21
+ }
22
+ success(message) {
23
+ this.stdout.write(`${chalk.green("ok")} ${message}\n`);
24
+ }
25
+ error(message) {
26
+ this.stderr.write(`${chalk.red("error")} ${message}\n`);
27
+ }
28
+ warning(message) {
29
+ this.stdout.write(`${chalk.yellow("warn")} ${message}\n`);
30
+ }
31
+ debug(message) {
32
+ if (this.verbose) this.stdout.write(`${chalk.gray("debug")} ${message}\n`);
33
+ }
34
+ setVerbose(verbose) {
35
+ this.verbose = verbose;
36
+ }
37
+ };
38
+
39
+ //#endregion
40
+ //#region src/context.ts
41
+ function buildContext(process$1) {
42
+ return {
43
+ process: process$1,
44
+ os,
45
+ fs,
46
+ path,
47
+ logger: new Logger({
48
+ verbose: false,
49
+ stdout: process$1.stdout,
50
+ stderr: process$1.stderr
51
+ })
52
+ };
53
+ }
54
+
55
+ //#endregion
56
+ //#region src/commands/auth/login.ts
57
+ const loginCommand = buildCommand({
58
+ docs: {
59
+ brief: "Authenticate with Levr",
60
+ fullDescription: `Authenticate with Levr using OAuth.
61
+
62
+ By default, opens a browser for PKCE-based authentication.
63
+ For headless environments (SSH, containers), use --device-code
64
+ to authenticate via a code displayed in the terminal.
65
+
66
+ Examples:
67
+ levr auth login # Browser-based PKCE login
68
+ levr auth login --device-code # Device flow for SSH/headless`
69
+ },
70
+ parameters: {
71
+ flags: { "device-code": {
72
+ kind: "boolean",
73
+ default: false,
74
+ brief: "Use device code flow (for SSH/headless environments)"
75
+ } },
76
+ aliases: { d: "device-code" }
77
+ },
78
+ loader: async () => {
79
+ const { loginHandler } = await import("./loginHandler-BnokItPo.js");
80
+ return loginHandler;
81
+ }
82
+ });
83
+
84
+ //#endregion
85
+ //#region src/commands/auth/logout.ts
86
+ const logoutCommand = buildCommand({
87
+ docs: {
88
+ brief: "Log out of Levr",
89
+ fullDescription: `Remove stored credentials.
90
+
91
+ Note: If using LEVR_TOKEN environment variable, it will remain set.
92
+
93
+ Examples:
94
+ levr auth logout`
95
+ },
96
+ parameters: {},
97
+ loader: async () => {
98
+ const { logoutHandler } = await import("./logoutHandler-C3X9rqLP.js");
99
+ return logoutHandler;
100
+ }
101
+ });
102
+
103
+ //#endregion
104
+ //#region src/commands/auth/status.ts
105
+ const statusCommand = buildCommand({
106
+ docs: {
107
+ brief: "Check authentication status",
108
+ fullDescription: `Check the current authentication status.
109
+
110
+ Shows whether you are authenticated, the auth method (PAT or JWT),
111
+ and tests API reachability.
112
+
113
+ Examples:
114
+ levr auth status`
115
+ },
116
+ parameters: {},
117
+ loader: async () => {
118
+ const { statusHandler } = await import("./statusHandler-LfrSN4uh.js");
119
+ return statusHandler;
120
+ }
121
+ });
122
+
123
+ //#endregion
124
+ //#region src/commands/push.ts
125
+ const pushCommand = buildCommand({
126
+ docs: {
127
+ brief: "Push test results to Levr",
128
+ fullDescription: `Upload a test result file to Levr.
129
+
130
+ The backend auto-detects the file format (JUnit XML, Gherkin, Cucumber JSON).
131
+ In CI environments, the automation source name and CI metadata are auto-detected.
132
+
133
+ Team ID is optional. When omitted, the server resolves the team from:
134
+ 1. The existing automation source's team (if --source matches a known source)
135
+ 2. The workspace's default team
136
+
137
+ Examples:
138
+ levr push ./test-results.xml
139
+ levr push ./results.xml --source "backend-unit-tests"
140
+ levr push ./report.json --team-id <uuid> # explicit team
141
+ levr push ./report.json # uses LEVR_TOKEN env var, default team`
142
+ },
143
+ parameters: {
144
+ positional: {
145
+ kind: "tuple",
146
+ parameters: [{
147
+ parse: String,
148
+ brief: "Path to test result file (.xml, .feature, .json)",
149
+ placeholder: "file",
150
+ optional: false
151
+ }]
152
+ },
153
+ flags: {
154
+ "workspace-id": {
155
+ kind: "parsed",
156
+ parse: String,
157
+ brief: "Workspace ID (required for multi-workspace JWT auth)",
158
+ placeholder: "uuid",
159
+ optional: true
160
+ },
161
+ "team-id": {
162
+ kind: "parsed",
163
+ parse: String,
164
+ brief: "Team ID (optional; server resolves default if omitted)",
165
+ placeholder: "uuid",
166
+ optional: true
167
+ },
168
+ source: {
169
+ kind: "parsed",
170
+ parse: String,
171
+ brief: "Automation source name (auto-detected in CI)",
172
+ placeholder: "name",
173
+ optional: true
174
+ },
175
+ "automation-source": {
176
+ kind: "parsed",
177
+ parse: String,
178
+ brief: "Automation source UUID. When set, routes to POST /v1/automation-run/ingest (synchronous, bypasses ImportJob queue) instead of POST /v1/imports.",
179
+ placeholder: "uuid",
180
+ optional: true
181
+ },
182
+ "run-name": {
183
+ kind: "parsed",
184
+ parse: String,
185
+ brief: "Name for the test run",
186
+ placeholder: "name",
187
+ optional: true
188
+ },
189
+ format: {
190
+ kind: "enum",
191
+ values: [
192
+ "junit",
193
+ "gherkin",
194
+ "cucumber-json"
195
+ ],
196
+ brief: "File format (auto-detected if omitted)",
197
+ optional: true
198
+ },
199
+ "parent-folder-id": {
200
+ kind: "parsed",
201
+ parse: String,
202
+ brief: "Destination folder ID",
203
+ placeholder: "uuid",
204
+ optional: true
205
+ },
206
+ "update-mode": {
207
+ kind: "enum",
208
+ values: ["update", "create_new"],
209
+ default: "update",
210
+ brief: "How to handle existing tests"
211
+ },
212
+ verbose: {
213
+ kind: "boolean",
214
+ default: false,
215
+ brief: "Show detailed output"
216
+ }
217
+ },
218
+ aliases: {
219
+ w: "workspace-id",
220
+ t: "team-id",
221
+ s: "source",
222
+ a: "automation-source",
223
+ r: "run-name",
224
+ f: "format",
225
+ v: "verbose"
226
+ }
227
+ },
228
+ loader: async () => {
229
+ const { pushHandler } = await import("./pushHandler-C3DteESD.js");
230
+ return pushHandler;
231
+ }
232
+ });
233
+
234
+ //#endregion
235
+ //#region src/commands/workspace/list.ts
236
+ const listCommand = buildCommand({
237
+ docs: {
238
+ brief: "List available workspaces",
239
+ fullDescription: `List all workspaces you have access to.
240
+
241
+ The current workspace (if selected) is marked with an asterisk (*).
242
+
243
+ Requires JWT authentication (levr auth login).
244
+
245
+ Examples:
246
+ levr workspace list`
247
+ },
248
+ parameters: {},
249
+ loader: async () => {
250
+ const { listHandler } = await import("./listHandler-gfnJbj60.js");
251
+ return listHandler;
252
+ }
253
+ });
254
+
255
+ //#endregion
256
+ //#region src/commands/workspace/select.ts
257
+ const selectCommand = buildCommand({
258
+ docs: {
259
+ brief: "Select a workspace",
260
+ fullDescription: `Select a workspace by ID.
261
+
262
+ The selected workspace is used for all subsequent commands.
263
+ Use 'levr workspace list' to see available workspaces.
264
+
265
+ Requires JWT authentication (levr auth login).
266
+
267
+ Examples:
268
+ levr workspace select <workspace-id>`
269
+ },
270
+ parameters: {
271
+ positional: {
272
+ kind: "tuple",
273
+ parameters: [{
274
+ parse: String,
275
+ brief: "Workspace ID",
276
+ placeholder: "workspace-id",
277
+ optional: false
278
+ }]
279
+ },
280
+ flags: {}
281
+ },
282
+ loader: async () => {
283
+ const { selectHandler } = await import("./selectHandler-BjM4LKYW.js");
284
+ return selectHandler;
285
+ }
286
+ });
287
+
288
+ //#endregion
289
+ //#region src/commands/workspace/current.ts
290
+ const currentCommand = buildCommand({
291
+ docs: {
292
+ brief: "Show current workspace",
293
+ fullDescription: `Show the currently selected workspace.
294
+
295
+ Examples:
296
+ levr workspace current`
297
+ },
298
+ parameters: {},
299
+ loader: async () => {
300
+ const { currentHandler } = await import("./currentHandler-DpWIqCBm.js");
301
+ return currentHandler;
302
+ }
303
+ });
304
+
305
+ //#endregion
306
+ //#region package.json
307
+ var version = "0.1.1";
308
+
309
+ //#endregion
310
+ //#region src/app.ts
311
+ const routes = buildRouteMap({
312
+ routes: {
313
+ auth: buildRouteMap({
314
+ routes: {
315
+ login: loginCommand,
316
+ logout: logoutCommand,
317
+ status: statusCommand
318
+ },
319
+ docs: { brief: "Manage authentication" }
320
+ }),
321
+ workspace: buildRouteMap({
322
+ routes: {
323
+ list: listCommand,
324
+ select: selectCommand,
325
+ current: currentCommand
326
+ },
327
+ docs: { brief: "Manage workspace selection" }
328
+ }),
329
+ push: pushCommand,
330
+ install: buildInstallCommand("levr", { bash: "levr __complete" }),
331
+ uninstall: buildUninstallCommand("levr", { bash: true })
332
+ },
333
+ docs: {
334
+ brief: "The command-line interface for Levr",
335
+ hideRoute: {
336
+ install: true,
337
+ uninstall: true
338
+ }
339
+ }
340
+ });
341
+ const app = buildApplication(routes, {
342
+ name: "levr",
343
+ versionInfo: { currentVersion: version },
344
+ localization: { loadText: () => ({
345
+ ...text_en,
346
+ exceptionWhileParsingArguments: (exc, ansiColor) => {
347
+ const base = text_en.exceptionWhileParsingArguments(exc, ansiColor);
348
+ const hint = "Run `levr <command> --help` for usage information.";
349
+ return ansiColor ? `${base}\n\x1b[2m${hint}\x1b[22m` : `${base}\n${hint}`;
350
+ }
351
+ }) }
352
+ });
353
+
354
+ //#endregion
355
+ //#region src/completion.ts
356
+ /**
357
+ * Compute shell tab-completion suggestions for the hidden `__complete` entrypoint
358
+ * (see src/bin/cli.ts). `levr install` registers a bash function that invokes
359
+ * `levr __complete <COMP_LINE>` on each TAB, so `rawArgs` (process.argv.slice(2))
360
+ * is `['__complete', <targetCommandName>, ...wordsBeingCompleted]`. A COMP_LINE
361
+ * ending in a space means the cursor is on a fresh (empty) word to complete.
362
+ *
363
+ * Never throws — completion must not surface an error to the user's shell.
364
+ */
365
+ async function proposeCompletionLines(rawArgs, compLine, context) {
366
+ const inputs = rawArgs.slice(2);
367
+ if (compLine?.endsWith(" ")) inputs.push("");
368
+ try {
369
+ return (await proposeCompletions(app, inputs, context)).map(({ completion }) => completion);
370
+ } catch {
371
+ return [];
372
+ }
373
+ }
374
+
375
+ //#endregion
5
376
  //#region src/bin/cli.ts
6
- let savedExitCode;
7
- const processProxy = new Proxy(process, { set(target, prop, value) {
8
- if (prop === "exitCode" && typeof value === "number" && value !== 0) savedExitCode = value;
9
- return Reflect.set(target, prop, value);
10
- } });
11
- await run(app, process.argv.slice(2), buildContext(processProxy));
12
- if (savedExitCode !== void 0) process.exitCode = savedExitCode;
377
+ const argv = process.argv.slice(2);
378
+ if (argv[0] === "__complete") for (const line of await proposeCompletionLines(argv, process.env["COMP_LINE"], buildContext(process))) process.stdout.write(`${line}\n`);
379
+ else {
380
+ let savedExitCode;
381
+ await run(app, argv, buildContext(new Proxy(process, { set(target, prop, value) {
382
+ if (prop === "exitCode" && typeof value === "number" && value !== 0) savedExitCode = value;
383
+ return Reflect.set(target, prop, value);
384
+ } })));
385
+ if (savedExitCode !== void 0) process.exitCode = savedExitCode;
386
+ }
13
387
 
14
388
  //#endregion
15
389
  export { };
@@ -1,4 +1,4 @@
1
- import { loadWorkspace } from "./workspace-store-BcyMJAht.js";
1
+ import { loadWorkspace } from "./workspace-store-4hfvsEHS.js";
2
2
 
3
3
  //#region src/commands/workspace/currentHandler.ts
4
4
  function currentHandler() {
@@ -1,8 +1,8 @@
1
- import "./credentials-CfHLkU7k.js";
2
- import { authGetSitesV1, configureClient } from "./sdk-client-DunBmYLR.js";
3
- import { loadWorkspace } from "./workspace-store-BcyMJAht.js";
4
- import "./token-refresh-waF23pyw.js";
5
- import { resolveToken } from "./resolve-token-BL8vL_ok.js";
1
+ import "./credentials-Ciq9mA7N.js";
2
+ import { authGetSitesV1, configureClient } from "./sdk-client-rpUEykDw.js";
3
+ import { loadWorkspace } from "./workspace-store-4hfvsEHS.js";
4
+ import "./token-refresh-Pl6Adngb.js";
5
+ import { resolveToken } from "./resolve-token-CQU55U6J.js";
6
6
 
7
7
  //#region src/commands/workspace/listHandler.ts
8
8
  async function listHandler() {
@@ -1,7 +1,7 @@
1
- import { CLI_CLIENT_ID, getApiUrl, getAuthUrl, writeCredentials } from "./credentials-CfHLkU7k.js";
2
- import { configureClient } from "./sdk-client-DunBmYLR.js";
3
- import "./workspace-store-BcyMJAht.js";
4
- import { autoSelectWorkspace } from "./resolve-workspace-lHEoGPHe.js";
1
+ import { CLI_CLIENT_ID, getApiUrl, getAuthUrl, writeCredentials } from "./credentials-Ciq9mA7N.js";
2
+ import { configureClient } from "./sdk-client-rpUEykDw.js";
3
+ import "./workspace-store-4hfvsEHS.js";
4
+ import { autoSelectWorkspace } from "./resolve-workspace-BPMkU_SK.js";
5
5
  import chalk from "chalk";
6
6
  import { createHash, randomBytes } from "node:crypto";
7
7
  import { createServer } from "node:http";
@@ -1,5 +1,5 @@
1
- import { deleteCredentials, getPatToken } from "./credentials-CfHLkU7k.js";
2
- import { clearWorkspace } from "./workspace-store-BcyMJAht.js";
1
+ import { deleteCredentials, getPatToken } from "./credentials-Ciq9mA7N.js";
2
+ import { clearWorkspace } from "./workspace-store-4hfvsEHS.js";
3
3
 
4
4
  //#region src/commands/auth/logoutHandler.ts
5
5
  function logoutHandler() {
@@ -1,9 +1,9 @@
1
- import { getApiUrl, getAutomationSourceIdOverride, getSourceOverride, getTeamId } from "./credentials-CfHLkU7k.js";
2
- import { client, configureClient, uploadAutomationIngest, uploadImport } from "./sdk-client-DunBmYLR.js";
3
- import "./workspace-store-BcyMJAht.js";
4
- import { resolveWorkspace } from "./resolve-workspace-lHEoGPHe.js";
5
- import "./token-refresh-waF23pyw.js";
6
- import { resolveToken } from "./resolve-token-BL8vL_ok.js";
1
+ import { getApiUrl, getAutomationSourceIdOverride, getSourceOverride, getTeamId } from "./credentials-Ciq9mA7N.js";
2
+ import { client, configureClient, uploadAutomationIngest, uploadImport } from "./sdk-client-rpUEykDw.js";
3
+ import "./workspace-store-4hfvsEHS.js";
4
+ import { resolveWorkspace } from "./resolve-workspace-BPMkU_SK.js";
5
+ import "./token-refresh-Pl6Adngb.js";
6
+ import { resolveToken } from "./resolve-token-CQU55U6J.js";
7
7
  import { readFileSync, statSync } from "node:fs";
8
8
  import { basename } from "node:path";
9
9
  import ora from "ora";
@@ -1,5 +1,5 @@
1
- import { getPatToken, readCredentials } from "./credentials-CfHLkU7k.js";
2
- import { isTokenExpired, refreshToken } from "./token-refresh-waF23pyw.js";
1
+ import { getPatToken, readCredentials } from "./credentials-Ciq9mA7N.js";
2
+ import { isTokenExpired, refreshToken } from "./token-refresh-Pl6Adngb.js";
3
3
 
4
4
  //#region src/auth/resolve-token.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
- import { authGetSitesV1 } from "./sdk-client-DunBmYLR.js";
2
- import { clearWorkspace, loadWorkspace, saveWorkspace } from "./workspace-store-BcyMJAht.js";
1
+ import { authGetSitesV1 } from "./sdk-client-rpUEykDw.js";
2
+ import { clearWorkspace, loadWorkspace, saveWorkspace } from "./workspace-store-4hfvsEHS.js";
3
3
 
4
4
  //#region src/workspace/resolve-workspace.ts
5
5
  async function fetchSites() {
@@ -1,4 +1,4 @@
1
- import { getApiUrl } from "./credentials-CfHLkU7k.js";
1
+ import { getApiUrl } from "./credentials-Ciq9mA7N.js";
2
2
  import { z } from "zod/v3";
3
3
 
4
4
  //#region ../sdk/dist/gen/_common/zod.js
@@ -1,8 +1,8 @@
1
- import "./credentials-CfHLkU7k.js";
2
- import { authGetSitesV1, configureClient } from "./sdk-client-DunBmYLR.js";
3
- import { saveWorkspace } from "./workspace-store-BcyMJAht.js";
4
- import "./token-refresh-waF23pyw.js";
5
- import { resolveToken } from "./resolve-token-BL8vL_ok.js";
1
+ import "./credentials-Ciq9mA7N.js";
2
+ import { authGetSitesV1, configureClient } from "./sdk-client-rpUEykDw.js";
3
+ import { saveWorkspace } from "./workspace-store-4hfvsEHS.js";
4
+ import "./token-refresh-Pl6Adngb.js";
5
+ import { resolveToken } from "./resolve-token-CQU55U6J.js";
6
6
 
7
7
  //#region src/commands/workspace/selectHandler.ts
8
8
  async function selectHandler(_flags, workspaceId) {
@@ -1,6 +1,6 @@
1
- import { getApiUrl, getPatToken, readCredentials } from "./credentials-CfHLkU7k.js";
2
- import { authGetProfileV1, configureClient } from "./sdk-client-DunBmYLR.js";
3
- import { isTokenExpired } from "./token-refresh-waF23pyw.js";
1
+ import { getApiUrl, getPatToken, readCredentials } from "./credentials-Ciq9mA7N.js";
2
+ import { authGetProfileV1, configureClient } from "./sdk-client-rpUEykDw.js";
3
+ import { isTokenExpired } from "./token-refresh-Pl6Adngb.js";
4
4
  import chalk from "chalk";
5
5
 
6
6
  //#region src/commands/auth/statusHandler.ts
@@ -1,4 +1,4 @@
1
- import { CLI_CLIENT_ID, deleteCredentials, getApiUrl, writeCredentials } from "./credentials-CfHLkU7k.js";
1
+ import { CLI_CLIENT_ID, deleteCredentials, getApiUrl, writeCredentials } from "./credentials-Ciq9mA7N.js";
2
2
 
3
3
  //#region src/auth/token-refresh.ts
4
4
  const REFRESH_BUFFER_MS = 300 * 1e3;
package/package.json CHANGED
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "@levr-one/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "The command-line interface for Levr",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "main": "dist/cli.js",
8
8
  "bin": {
9
- "__levr_bash_complete": "dist/bash-complete.js",
10
9
  "levr": "dist/cli.js"
11
10
  },
12
11
  "files": [
@@ -1,350 +0,0 @@
1
- import { buildApplication, buildCommand, buildRouteMap, text_en } from "@stricli/core";
2
- import fs from "node:fs";
3
- import os from "node:os";
4
- import path from "node:path";
5
- import chalk from "chalk";
6
- import { buildInstallCommand, buildUninstallCommand } from "@stricli/auto-complete";
7
-
8
- //#region src/utils/logger.ts
9
- var Logger = class {
10
- verbose;
11
- stdout;
12
- stderr;
13
- constructor(options) {
14
- this.verbose = options.verbose ?? false;
15
- this.stdout = options.stdout ?? process.stdout;
16
- this.stderr = options.stderr ?? process.stderr;
17
- }
18
- info(message) {
19
- this.stdout.write(`${chalk.blue("info")} ${message}\n`);
20
- }
21
- success(message) {
22
- this.stdout.write(`${chalk.green("ok")} ${message}\n`);
23
- }
24
- error(message) {
25
- this.stderr.write(`${chalk.red("error")} ${message}\n`);
26
- }
27
- warning(message) {
28
- this.stdout.write(`${chalk.yellow("warn")} ${message}\n`);
29
- }
30
- debug(message) {
31
- if (this.verbose) this.stdout.write(`${chalk.gray("debug")} ${message}\n`);
32
- }
33
- setVerbose(verbose) {
34
- this.verbose = verbose;
35
- }
36
- };
37
-
38
- //#endregion
39
- //#region src/context.ts
40
- function buildContext(process$1) {
41
- return {
42
- process: process$1,
43
- os,
44
- fs,
45
- path,
46
- logger: new Logger({
47
- verbose: false,
48
- stdout: process$1.stdout,
49
- stderr: process$1.stderr
50
- })
51
- };
52
- }
53
-
54
- //#endregion
55
- //#region src/commands/auth/login.ts
56
- const loginCommand = buildCommand({
57
- docs: {
58
- brief: "Authenticate with Levr",
59
- fullDescription: `Authenticate with Levr using OAuth.
60
-
61
- By default, opens a browser for PKCE-based authentication.
62
- For headless environments (SSH, containers), use --device-code
63
- to authenticate via a code displayed in the terminal.
64
-
65
- Examples:
66
- levr auth login # Browser-based PKCE login
67
- levr auth login --device-code # Device flow for SSH/headless`
68
- },
69
- parameters: {
70
- flags: { "device-code": {
71
- kind: "boolean",
72
- default: false,
73
- brief: "Use device code flow (for SSH/headless environments)"
74
- } },
75
- aliases: { d: "device-code" }
76
- },
77
- loader: async () => {
78
- const { loginHandler } = await import("./loginHandler-C8D6DgjW.js");
79
- return loginHandler;
80
- }
81
- });
82
-
83
- //#endregion
84
- //#region src/commands/auth/logout.ts
85
- const logoutCommand = buildCommand({
86
- docs: {
87
- brief: "Log out of Levr",
88
- fullDescription: `Remove stored credentials.
89
-
90
- Note: If using LEVR_TOKEN environment variable, it will remain set.
91
-
92
- Examples:
93
- levr auth logout`
94
- },
95
- parameters: {},
96
- loader: async () => {
97
- const { logoutHandler } = await import("./logoutHandler-BBtdpxHX.js");
98
- return logoutHandler;
99
- }
100
- });
101
-
102
- //#endregion
103
- //#region src/commands/auth/status.ts
104
- const statusCommand = buildCommand({
105
- docs: {
106
- brief: "Check authentication status",
107
- fullDescription: `Check the current authentication status.
108
-
109
- Shows whether you are authenticated, the auth method (PAT or JWT),
110
- and tests API reachability.
111
-
112
- Examples:
113
- levr auth status`
114
- },
115
- parameters: {},
116
- loader: async () => {
117
- const { statusHandler } = await import("./statusHandler-DQDmEOGI.js");
118
- return statusHandler;
119
- }
120
- });
121
-
122
- //#endregion
123
- //#region src/commands/push.ts
124
- const pushCommand = buildCommand({
125
- docs: {
126
- brief: "Push test results to Levr",
127
- fullDescription: `Upload a test result file to Levr.
128
-
129
- The backend auto-detects the file format (JUnit XML, Gherkin, Cucumber JSON).
130
- In CI environments, the automation source name and CI metadata are auto-detected.
131
-
132
- Team ID is optional. When omitted, the server resolves the team from:
133
- 1. The existing automation source's team (if --source matches a known source)
134
- 2. The workspace's default team
135
-
136
- Examples:
137
- levr push ./test-results.xml
138
- levr push ./results.xml --source "backend-unit-tests"
139
- levr push ./report.json --team-id <uuid> # explicit team
140
- levr push ./report.json # uses LEVR_TOKEN env var, default team`
141
- },
142
- parameters: {
143
- positional: {
144
- kind: "tuple",
145
- parameters: [{
146
- parse: String,
147
- brief: "Path to test result file (.xml, .feature, .json)",
148
- placeholder: "file",
149
- optional: false
150
- }]
151
- },
152
- flags: {
153
- "workspace-id": {
154
- kind: "parsed",
155
- parse: String,
156
- brief: "Workspace ID (required for multi-workspace JWT auth)",
157
- placeholder: "uuid",
158
- optional: true
159
- },
160
- "team-id": {
161
- kind: "parsed",
162
- parse: String,
163
- brief: "Team ID (optional; server resolves default if omitted)",
164
- placeholder: "uuid",
165
- optional: true
166
- },
167
- source: {
168
- kind: "parsed",
169
- parse: String,
170
- brief: "Automation source name (auto-detected in CI)",
171
- placeholder: "name",
172
- optional: true
173
- },
174
- "automation-source": {
175
- kind: "parsed",
176
- parse: String,
177
- brief: "Automation source UUID. When set, routes to POST /v1/automation-run/ingest (synchronous, bypasses ImportJob queue) instead of POST /v1/imports.",
178
- placeholder: "uuid",
179
- optional: true
180
- },
181
- "run-name": {
182
- kind: "parsed",
183
- parse: String,
184
- brief: "Name for the test run",
185
- placeholder: "name",
186
- optional: true
187
- },
188
- format: {
189
- kind: "enum",
190
- values: [
191
- "junit",
192
- "gherkin",
193
- "cucumber-json"
194
- ],
195
- brief: "File format (auto-detected if omitted)",
196
- optional: true
197
- },
198
- "parent-folder-id": {
199
- kind: "parsed",
200
- parse: String,
201
- brief: "Destination folder ID",
202
- placeholder: "uuid",
203
- optional: true
204
- },
205
- "update-mode": {
206
- kind: "enum",
207
- values: ["update", "create_new"],
208
- default: "update",
209
- brief: "How to handle existing tests"
210
- },
211
- verbose: {
212
- kind: "boolean",
213
- default: false,
214
- brief: "Show detailed output"
215
- }
216
- },
217
- aliases: {
218
- w: "workspace-id",
219
- t: "team-id",
220
- s: "source",
221
- a: "automation-source",
222
- r: "run-name",
223
- f: "format",
224
- v: "verbose"
225
- }
226
- },
227
- loader: async () => {
228
- const { pushHandler } = await import("./pushHandler-DkCrbV7y.js");
229
- return pushHandler;
230
- }
231
- });
232
-
233
- //#endregion
234
- //#region src/commands/workspace/list.ts
235
- const listCommand = buildCommand({
236
- docs: {
237
- brief: "List available workspaces",
238
- fullDescription: `List all workspaces you have access to.
239
-
240
- The current workspace (if selected) is marked with an asterisk (*).
241
-
242
- Requires JWT authentication (levr auth login).
243
-
244
- Examples:
245
- levr workspace list`
246
- },
247
- parameters: {},
248
- loader: async () => {
249
- const { listHandler } = await import("./listHandler-BiwIo88i.js");
250
- return listHandler;
251
- }
252
- });
253
-
254
- //#endregion
255
- //#region src/commands/workspace/select.ts
256
- const selectCommand = buildCommand({
257
- docs: {
258
- brief: "Select a workspace",
259
- fullDescription: `Select a workspace by ID.
260
-
261
- The selected workspace is used for all subsequent commands.
262
- Use 'levr workspace list' to see available workspaces.
263
-
264
- Requires JWT authentication (levr auth login).
265
-
266
- Examples:
267
- levr workspace select <workspace-id>`
268
- },
269
- parameters: {
270
- positional: {
271
- kind: "tuple",
272
- parameters: [{
273
- parse: String,
274
- brief: "Workspace ID",
275
- placeholder: "workspace-id",
276
- optional: false
277
- }]
278
- },
279
- flags: {}
280
- },
281
- loader: async () => {
282
- const { selectHandler } = await import("./selectHandler-C2UDAWOl.js");
283
- return selectHandler;
284
- }
285
- });
286
-
287
- //#endregion
288
- //#region src/commands/workspace/current.ts
289
- const currentCommand = buildCommand({
290
- docs: {
291
- brief: "Show current workspace",
292
- fullDescription: `Show the currently selected workspace.
293
-
294
- Examples:
295
- levr workspace current`
296
- },
297
- parameters: {},
298
- loader: async () => {
299
- const { currentHandler } = await import("./currentHandler-oXFeNNgd.js");
300
- return currentHandler;
301
- }
302
- });
303
-
304
- //#endregion
305
- //#region src/app.ts
306
- const routes = buildRouteMap({
307
- routes: {
308
- auth: buildRouteMap({
309
- routes: {
310
- login: loginCommand,
311
- logout: logoutCommand,
312
- status: statusCommand
313
- },
314
- docs: { brief: "Manage authentication" }
315
- }),
316
- workspace: buildRouteMap({
317
- routes: {
318
- list: listCommand,
319
- select: selectCommand,
320
- current: currentCommand
321
- },
322
- docs: { brief: "Manage workspace selection" }
323
- }),
324
- push: pushCommand,
325
- install: buildInstallCommand("levr", { bash: "__levr_bash_complete" }),
326
- uninstall: buildUninstallCommand("levr", { bash: true })
327
- },
328
- docs: {
329
- brief: "The command-line interface for Levr",
330
- hideRoute: {
331
- install: true,
332
- uninstall: true
333
- }
334
- }
335
- });
336
- const app = buildApplication(routes, {
337
- name: "levr",
338
- versionInfo: { currentVersion: "0.1.0" },
339
- localization: { loadText: () => ({
340
- ...text_en,
341
- exceptionWhileParsingArguments: (exc, ansiColor) => {
342
- const base = text_en.exceptionWhileParsingArguments(exc, ansiColor);
343
- const hint = "Run `levr <command> --help` for usage information.";
344
- return ansiColor ? `${base}\n\x1b[2m${hint}\x1b[22m` : `${base}\n${hint}`;
345
- }
346
- }) }
347
- });
348
-
349
- //#endregion
350
- export { app, buildContext };
@@ -1,13 +0,0 @@
1
- #!/usr/bin/env node
2
- import { app, buildContext } from "./app-0F0Zyyt7.js";
3
- import { proposeCompletions } from "@stricli/core";
4
-
5
- //#region src/bin/bash-complete.ts
6
- const inputs = process.argv.slice(3);
7
- if (process.env["COMP_LINE"]?.endsWith(" ")) inputs.push("");
8
- try {
9
- for (const { completion } of await proposeCompletions(app, inputs, buildContext(process))) process.stdout.write(`${completion}\n`);
10
- } catch {}
11
-
12
- //#endregion
13
- export { };