@levr-one/cli 0.3.1 → 0.4.0

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
@@ -1,13 +1,15 @@
1
1
  # @levr-one/cli
2
2
 
3
3
  The command-line interface for [Levr](https://www.levr.one). The binary is
4
- `levr`, and it does two jobs:
4
+ `levr`, and it does three jobs:
5
5
 
6
6
  - **Connect your AI tools** — `levr mcp add` wires the Levr MCP server into
7
7
  the AI clients on your machine (Claude Desktop, Claude Code, Cursor,
8
8
  Windsurf, Zed) with one command.
9
9
  - **Push test results** — `levr push` uploads results from any terminal or CI
10
10
  pipeline.
11
+ - **Import test cases** — `levr import` brings existing cases in from CSV,
12
+ Excel, JSON, or Google Sheets.
11
13
 
12
14
  ## Install
13
15
 
@@ -129,6 +131,69 @@ the source to the workspace's default team, or pass `--team-id` to link it to a
129
131
  specific team. Subsequent pushes with the same `--source` route to that team
130
132
  automatically. Source names are normalized (lowercased, trimmed).
131
133
 
134
+ ## Import test cases
135
+
136
+ ```bash
137
+ levr import <file> --team-id <uuid> [options]
138
+ ```
139
+
140
+ Brings existing test cases into Levr from CSV, Excel (`.xlsx`), JSON, or a
141
+ public Google Sheet. It runs in two phases: **preview** proposes a mapping from
142
+ your columns onto the Levr test-case schema (exact and fuzzy matching, with an
143
+ LLM assist for whatever is left over), you review and adjust it, then **commit**
144
+ writes the folders, tests, steps, and preconditions.
145
+
146
+ In a terminal, unmapped and low-confidence columns are walked with a picker. For
147
+ scripts and CI, pass `--yes` — and `--map` or `--mapping-file` to pin the
148
+ mapping so a replay can't drift.
149
+
150
+ **One column must map to `test_name`.** Interactive runs ask for it;
151
+ non-interactive runs exit 1 rather than import unnamed cases.
152
+
153
+ **Examples:**
154
+
155
+ ```bash
156
+ # Interactive import from a CSV export
157
+ levr import ./testrail-export.csv --team-id <uuid>
158
+
159
+ # Excel, with one column pinned up front
160
+ levr import ./cases.xlsx --team-id <uuid> --map "Title=test_name"
161
+
162
+ # A public Google Sheet, no prompts
163
+ levr import --sheets-url "https://docs.google.com/spreadsheets/d/..." --team-id <uuid> --yes
164
+
165
+ # Save the confirmed mapping once, then replay it in CI
166
+ levr import ./cases.csv --team-id <uuid> --save-mapping mapping.json
167
+ levr import ./cases.csv --team-id <uuid> --mapping-file mapping.json --yes
168
+ ```
169
+
170
+ **Flags:**
171
+
172
+ | Flag | Alias | Description |
173
+ | ----------------------- | ----- | --------------------------------------------------------------------------------- |
174
+ | `--team-id <uuid>` | `-t` | Team the imported test cases belong to (required) |
175
+ | `--workspace-id <uuid>` | `-w` | Workspace ID (required for multi-workspace JWT auth) |
176
+ | `--sheets-url <url>` | | Public Google Sheets URL, used instead of a file argument |
177
+ | `--format <type>` | `-f` | Source format: `csv`, `xlsx`, `json` (auto-detected from the filename if omitted) |
178
+ | `--map <pair>` | `-m` | Column override, repeatable — three forms, below |
179
+ | `--mapping-file <path>` | | JSON file holding a saved confirmed mapping (from `--save-mapping`) |
180
+ | `--save-mapping <path>` | | Write the confirmed mapping to this JSON file for CI replay |
181
+ | `--yes` | `-y` | Accept the mapping without prompts (required for non-TTY runs) |
182
+ | `--verbose` | `-v` | Show detailed output |
183
+
184
+ `--map` accepts three forms:
185
+
186
+ | Form | Effect |
187
+ | ------------------------------- | ------------------------------------------------------------------------------ |
188
+ | `"Source Column=target_field"` | Map the column onto that Levr field |
189
+ | `"Source Column="` | Drop the column (empty target) |
190
+ | `"Source Column=labels:prefix"` | Import each value as the label `prefix:value`, keeping the column's provenance |
191
+
192
+ ```bash
193
+ # Keep a TestRail "State" column as state:Draft / state:Approved labels
194
+ levr import ./cases.csv --team-id <uuid> --map "State=labels:state"
195
+ ```
196
+
132
197
  ## CI/CD integration
133
198
 
134
199
  ### GitHub Actions
package/dist/cli.js CHANGED
@@ -109,7 +109,7 @@ Examples:
109
109
  aliases: { y: "yes" }
110
110
  },
111
111
  loader: async () => {
112
- const { mcpAddHandler } = await import("./addHandler-boWTd9yY.js");
112
+ const { mcpAddHandler } = await import("./addHandler-DFtvzTci.js");
113
113
  return mcpAddHandler;
114
114
  }
115
115
  });
@@ -147,7 +147,7 @@ Examples:
147
147
  aliases: { d: "device-code" }
148
148
  },
149
149
  loader: async () => {
150
- const { loginHandler } = await import("./loginHandler-Dfg89AD2.js");
150
+ const { loginHandler } = await import("./loginHandler-D9k-tQLX.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-DljfUcC2.js");
189
+ const { statusHandler } = await import("./statusHandler-DuVZJMtw.js");
190
190
  return statusHandler;
191
191
  }
192
192
  });
@@ -297,11 +297,123 @@ Examples:
297
297
  }
298
298
  },
299
299
  loader: async () => {
300
- const { pushHandler } = await import("./pushHandler-CC709aNJ.js");
300
+ const { pushHandler } = await import("./pushHandler-COg5WbGS.js");
301
301
  return pushHandler;
302
302
  }
303
303
  });
304
304
 
305
+ //#endregion
306
+ //#region src/commands/import.ts
307
+ const importCommand = buildCommand({
308
+ docs: {
309
+ brief: "Import test cases from CSV, Excel, JSON, or Google Sheets",
310
+ fullDescription: `Two-phase test-case import: preview proposes a column mapping to the
311
+ Levr test-case schema (exact/fuzzy matching with an LLM assist), you review
312
+ and adjust it, then commit writes folders, tests, steps, and preconditions.
313
+
314
+ Interactive mode (default in a terminal) walks unmapped and low-confidence
315
+ columns with a picker. For scripts/CI use --yes and/or --map.
316
+
317
+ Required-field rule: a column must map to test_name or the commit is
318
+ rejected — interactive mode will ask; non-interactive runs exit 1.
319
+
320
+ Examples:
321
+ levr import ./testrail-export.csv --team-id <uuid>
322
+ levr import ./cases.xlsx --team-id <uuid> --map "Title=test_name"
323
+ levr import --sheets-url "https://docs.google.com/spreadsheets/d/..." --team-id <uuid> --yes
324
+ levr import ./cases.csv --team-id <uuid> --save-mapping mapping.json
325
+ levr import ./cases.csv --team-id <uuid> --mapping-file mapping.json --yes # CI replay`
326
+ },
327
+ parameters: {
328
+ positional: {
329
+ kind: "tuple",
330
+ parameters: [{
331
+ parse: String,
332
+ brief: "Path to the source file (.csv, .xlsx, .json)",
333
+ placeholder: "file",
334
+ optional: true
335
+ }]
336
+ },
337
+ flags: {
338
+ "workspace-id": {
339
+ kind: "parsed",
340
+ parse: String,
341
+ brief: "Workspace ID (required for multi-workspace JWT auth)",
342
+ placeholder: "uuid",
343
+ optional: true
344
+ },
345
+ "team-id": {
346
+ kind: "parsed",
347
+ parse: String,
348
+ brief: "Team the imported test cases will belong to",
349
+ placeholder: "uuid",
350
+ optional: false
351
+ },
352
+ "sheets-url": {
353
+ kind: "parsed",
354
+ parse: String,
355
+ brief: "Public Google Sheets URL (instead of a file)",
356
+ placeholder: "url",
357
+ optional: true
358
+ },
359
+ format: {
360
+ kind: "enum",
361
+ values: [
362
+ "csv",
363
+ "xlsx",
364
+ "json"
365
+ ],
366
+ brief: "Source format (auto-detected from the filename if omitted)",
367
+ optional: true
368
+ },
369
+ map: {
370
+ kind: "parsed",
371
+ parse: String,
372
+ brief: "Column override (repeatable): \"Column=target_field\", \"Column=\" to drop, or \"Column=labels:prefix\"",
373
+ placeholder: "pair",
374
+ variadic: true,
375
+ optional: true
376
+ },
377
+ "mapping-file": {
378
+ kind: "parsed",
379
+ parse: String,
380
+ brief: "JSON file with a saved confirmed mapping (from --save-mapping)",
381
+ placeholder: "path",
382
+ optional: true
383
+ },
384
+ "save-mapping": {
385
+ kind: "parsed",
386
+ parse: String,
387
+ brief: "Write the confirmed mapping to this JSON file for CI replay",
388
+ placeholder: "path",
389
+ optional: true
390
+ },
391
+ yes: {
392
+ kind: "boolean",
393
+ default: false,
394
+ brief: "Accept the mapping without prompts (required for non-TTY runs)"
395
+ },
396
+ verbose: {
397
+ kind: "boolean",
398
+ default: false,
399
+ brief: "Show detailed output"
400
+ }
401
+ },
402
+ aliases: {
403
+ w: "workspace-id",
404
+ t: "team-id",
405
+ f: "format",
406
+ m: "map",
407
+ y: "yes",
408
+ v: "verbose"
409
+ }
410
+ },
411
+ loader: async () => {
412
+ const { importHandler } = await import("./importHandler-ChywPYeL.js");
413
+ return importHandler;
414
+ }
415
+ });
416
+
305
417
  //#endregion
306
418
  //#region src/commands/workspace/list.ts
307
419
  const listCommand = buildCommand({
@@ -318,7 +430,7 @@ Examples:
318
430
  },
319
431
  parameters: {},
320
432
  loader: async () => {
321
- const { listHandler } = await import("./listHandler-7DZhE8jn.js");
433
+ const { listHandler } = await import("./listHandler-fK9Mapr3.js");
322
434
  return listHandler;
323
435
  }
324
436
  });
@@ -351,7 +463,7 @@ Examples:
351
463
  flags: {}
352
464
  },
353
465
  loader: async () => {
354
- const { selectHandler } = await import("./selectHandler-nOUsPW9p.js");
466
+ const { selectHandler } = await import("./selectHandler-BzQRF8E7.js");
355
467
  return selectHandler;
356
468
  }
357
469
  });
@@ -368,14 +480,14 @@ Examples:
368
480
  },
369
481
  parameters: {},
370
482
  loader: async () => {
371
- const { currentHandler } = await import("./currentHandler-Bm7GFMkj.js");
483
+ const { currentHandler } = await import("./currentHandler-BD-h0j-S.js");
372
484
  return currentHandler;
373
485
  }
374
486
  });
375
487
 
376
488
  //#endregion
377
489
  //#region package.json
378
- var version = "0.3.1";
490
+ var version = "0.4.0";
379
491
 
380
492
  //#endregion
381
493
  //#region src/app.ts
@@ -404,6 +516,7 @@ const routes = buildRouteMap({
404
516
  auth: authRoutes,
405
517
  workspace: workspaceRoutes,
406
518
  push: pushCommand,
519
+ import: importCommand,
407
520
  install: buildInstallCommand("levr", { bash: "levr __complete" }),
408
521
  uninstall: buildUninstallCommand("levr", { bash: true })
409
522
  },
@@ -0,0 +1,285 @@
1
+ import { getApiUrl } from "./env-NxtzJJPk.js";
2
+ import { configureClient, testCaseImportCommitV1, testCaseImportPreviewV1 } from "./sdk-client-DyRTezC5.js";
3
+ import "./workspace-store-4hfvsEHS.js";
4
+ import { resolveWorkspace } from "./resolve-workspace-CO8d3IBz.js";
5
+ import "./token-refresh-Cz-FqDtC.js";
6
+ import { resolveToken } from "./resolve-token-Dq8cAuiu.js";
7
+ import { createReadStream } from "node:fs";
8
+ import { basename } from "node:path";
9
+ import chalk from "chalk";
10
+ import { readFile, writeFile } from "node:fs/promises";
11
+ import { createInterface } from "node:readline/promises";
12
+ import ora from "ora";
13
+
14
+ //#region src/utils/import-mapping.ts
15
+ const IMPORT_TARGETS = [
16
+ "row_type",
17
+ "test_id",
18
+ "test_key",
19
+ "folder_id",
20
+ "folder_path",
21
+ "folder_name",
22
+ "test_name",
23
+ "test_description",
24
+ "test_type",
25
+ "case_type_id",
26
+ "case_type_name",
27
+ "test_priority",
28
+ "estimate",
29
+ "is_automated",
30
+ "assignee_email",
31
+ "labels",
32
+ "attachment_filenames",
33
+ "data_set_names",
34
+ "sequence",
35
+ "item_id",
36
+ "method",
37
+ "expected_result",
38
+ "keyword",
39
+ "shared_step_id",
40
+ "shared_precondition_id",
41
+ "data_table",
42
+ "steps",
43
+ "preconditions"
44
+ ];
45
+ const REQUIRED_TARGETS = ["test_name"];
46
+ /**
47
+ * Parse a --map value: "Source Column=target", "Source Column=" (drop),
48
+ * or "Source Column=labels:prefix" (import-as-labels: each value becomes
49
+ * the label "prefix:value", preserving the column's provenance).
50
+ */
51
+ function parseMapFlag(pair) {
52
+ const separator = pair.indexOf("=");
53
+ if (separator <= 0) throw new Error(`Invalid --map "${pair}" — expected "Source Column=target_field" (or "Source Column=" to drop, or "Source Column=labels:prefix" to import values as prefixed labels).`);
54
+ const column = pair.slice(0, separator).trim();
55
+ const raw = pair.slice(separator + 1).trim();
56
+ if (raw === "") return {
57
+ column,
58
+ target: null
59
+ };
60
+ if (raw.startsWith("labels:")) {
61
+ const labelPrefix = raw.slice(7).trim();
62
+ if (!labelPrefix) throw new Error(`Invalid --map "${pair}" — "labels:" needs a prefix (e.g. "State=labels:state").`);
63
+ return {
64
+ column,
65
+ target: "labels",
66
+ labelPrefix
67
+ };
68
+ }
69
+ const target = IMPORT_TARGETS.find((t) => t === raw);
70
+ if (!target) throw new Error(`Invalid --map target "${raw}". Valid targets: ${IMPORT_TARGETS.join(", ")} (labels also accepts "labels:prefix")`);
71
+ return {
72
+ column,
73
+ target
74
+ };
75
+ }
76
+ /** Apply overrides (from --map / --mapping-file) onto the proposed mapping. */
77
+ function applyOverrides(mapping, overrides) {
78
+ const result = mapping.map((entry) => ({ ...entry }));
79
+ for (const override of overrides) {
80
+ const entry = result.find((m) => m.originalName === override.column);
81
+ if (!entry) throw new Error(`--map column "${override.column}" not found in the file. Columns: ${result.map((m) => m.originalName).join(", ")}`);
82
+ entry.targetProperty = override.target;
83
+ entry.matchType = override.target ? "manual" : "unmapped";
84
+ entry.confidence = override.target ? 1 : 0;
85
+ entry.labelPrefix = override.labelPrefix;
86
+ }
87
+ return result;
88
+ }
89
+ /** Required targets not covered by any mapped column. */
90
+ function missingRequired(mapping) {
91
+ const covered = new Set(mapping.map((m) => m.targetProperty).filter(Boolean));
92
+ return REQUIRED_TARGETS.filter((t) => !covered.has(t));
93
+ }
94
+ /** Columns worth walking interactively: unmapped, or LLM guesses < 0.7. */
95
+ function columnsNeedingReview(mapping) {
96
+ return mapping.filter((m) => m.targetProperty === null || m.matchType === "llm" && m.confidence < .7);
97
+ }
98
+
99
+ //#endregion
100
+ //#region src/commands/importHandler.ts
101
+ const MATCH_COLORS = {
102
+ exact: chalk.green,
103
+ prefix: chalk.green,
104
+ preset: chalk.green,
105
+ fuzzy: chalk.yellow,
106
+ llm: chalk.magenta,
107
+ unmapped: chalk.red,
108
+ manual: chalk.green
109
+ };
110
+ /** Stream the file into a Blob — no readFileSync, no sync stall. */
111
+ async function fileToBlob(path$1) {
112
+ const chunks = [];
113
+ for await (const chunk of createReadStream(path$1)) chunks.push(chunk);
114
+ return new Blob(chunks);
115
+ }
116
+ function printMapping(logger, mapping) {
117
+ const width = Math.max(...mapping.map((m) => m.originalName.length), 13);
118
+ logger.info("");
119
+ logger.info(chalk.bold(` ${"SOURCE COLUMN".padEnd(width)} → ${"TARGET".padEnd(24)} MATCH`));
120
+ for (const m of mapping) {
121
+ const color = MATCH_COLORS[m.matchType] ?? chalk.white;
122
+ const target = m.targetProperty ?? "— unmapped —";
123
+ const badge = m.matchType === "unmapped" ? "unmapped" : `${m.matchType} ${Math.round(m.confidence * 100)}%`;
124
+ logger.info(` ${m.originalName.padEnd(width)} → ${target.padEnd(24)} ${color(badge)}`);
125
+ }
126
+ logger.info("");
127
+ }
128
+ async function pickTarget(rl, logger, column) {
129
+ logger.info(chalk.bold(`"${column.originalName}" is ${column.targetProperty ? `mapped to ${column.targetProperty} (low confidence)` : "unmapped"}.`));
130
+ const columns = 3;
131
+ const cellWidth = 26;
132
+ for (let i = 0; i < IMPORT_TARGETS.length; i += columns) logger.info(" " + IMPORT_TARGETS.slice(i, i + columns).map((t, j) => `${String(i + j + 1).padStart(2)}) ${t}`.padEnd(cellWidth)).join(""));
133
+ for (;;) {
134
+ const answer = (await rl.question("Target number or name (blank = skip/drop this column): ")).trim();
135
+ if (answer === "") return null;
136
+ const byNumber = IMPORT_TARGETS[Number(answer) - 1];
137
+ if (byNumber && String(Number(answer)) === answer) return byNumber;
138
+ const byName = IMPORT_TARGETS.find((t) => t === answer.toLowerCase());
139
+ if (byName) return byName;
140
+ logger.info(chalk.red(` "${answer}" is not a valid target — try again.`));
141
+ }
142
+ }
143
+ async function importHandler(flags, file) {
144
+ if (flags.verbose) this.logger.setVerbose(true);
145
+ if (!file && !flags["sheets-url"]) {
146
+ this.logger.error("Provide a file path or --sheets-url.");
147
+ this.process.exitCode = 1;
148
+ return;
149
+ }
150
+ if (file && flags["sheets-url"]) {
151
+ this.logger.error("Provide a file OR --sheets-url, not both.");
152
+ this.process.exitCode = 1;
153
+ return;
154
+ }
155
+ let auth;
156
+ try {
157
+ auth = await resolveToken();
158
+ } catch (err) {
159
+ this.logger.error(err instanceof Error ? err.message : "Authentication failed.");
160
+ this.process.exitCode = 1;
161
+ return;
162
+ }
163
+ configureClient(auth);
164
+ if (flags.verbose) {
165
+ this.logger.debug(`Auth: ${auth.type.toUpperCase()}`);
166
+ this.logger.debug(`API: ${getApiUrl()}`);
167
+ }
168
+ if (auth.type === "jwt") {
169
+ if (!await resolveWorkspace.call(this, flags["workspace-id"])) {
170
+ this.process.exitCode = 1;
171
+ return;
172
+ }
173
+ }
174
+ const spinner = ora("Uploading and analyzing the file…").start();
175
+ let preview;
176
+ try {
177
+ const body = {
178
+ team_id: flags["team-id"],
179
+ format: flags.format,
180
+ sheets_url: flags["sheets-url"]
181
+ };
182
+ if (file) {
183
+ const blob = await fileToBlob(file);
184
+ body["file"] = new File([blob], basename(file));
185
+ }
186
+ const result = await testCaseImportPreviewV1({
187
+ body,
188
+ requestValidator: void 0
189
+ });
190
+ if (result.error || !result.data) throw new Error(result.error?.message ?? `Preview failed (HTTP ${result.response?.status ?? "?"}).`);
191
+ preview = result.data;
192
+ spinner.succeed(`${preview.filename} (${preview.format}) — ${preview.row_count} data rows detected`);
193
+ } catch (err) {
194
+ spinner.fail(err instanceof Error ? err.message : "Preview failed.");
195
+ this.process.exitCode = 1;
196
+ return;
197
+ }
198
+ let mapping;
199
+ try {
200
+ const overrides = (flags.map ?? []).map(parseMapFlag);
201
+ if (flags["mapping-file"]) {
202
+ const saved = JSON.parse(await readFile(flags["mapping-file"], "utf8"));
203
+ overrides.push(...saved);
204
+ }
205
+ mapping = applyOverrides(preview.proposed_mapping, overrides);
206
+ } catch (err) {
207
+ this.logger.error(err instanceof Error ? err.message : String(err));
208
+ this.process.exitCode = 1;
209
+ return;
210
+ }
211
+ printMapping(this.logger, mapping);
212
+ if (!flags.yes && process.stdin.isTTY === true) {
213
+ const rl = createInterface({
214
+ input: process.stdin,
215
+ output: process.stdout
216
+ });
217
+ try {
218
+ for (const column of columnsNeedingReview(mapping)) {
219
+ const target = await pickTarget(rl, this.logger, column);
220
+ mapping = applyOverrides(mapping, [{
221
+ column: column.originalName,
222
+ target
223
+ }]);
224
+ }
225
+ while (missingRequired(mapping).length > 0) {
226
+ this.logger.info(chalk.red(`Required field(s) uncovered: ${missingRequired(mapping).join(", ")}. Every test case needs a name.`));
227
+ const source = (await rl.question(`Which source column holds the test name? (${mapping.map((m) => m.originalName).join(", ")}): `)).trim();
228
+ const entry = mapping.find((m) => m.originalName === source);
229
+ if (!entry) {
230
+ this.logger.info(chalk.red(` "${source}" is not a column — try again.`));
231
+ continue;
232
+ }
233
+ mapping = applyOverrides(mapping, [{
234
+ column: entry.originalName,
235
+ target: "test_name"
236
+ }]);
237
+ }
238
+ printMapping(this.logger, mapping);
239
+ const confirm = (await rl.question("Commit this import? [y/N]: ")).trim().toLowerCase();
240
+ if (confirm !== "y" && confirm !== "yes") {
241
+ this.logger.info("Aborted — nothing was written.");
242
+ return;
243
+ }
244
+ } finally {
245
+ rl.close();
246
+ }
247
+ } else if (missingRequired(mapping).length > 0) {
248
+ this.logger.error(`Required field(s) uncovered: ${missingRequired(mapping).join(", ")}. Map them with --map "Source Column=test_name" (non-interactive runs cannot prompt).`);
249
+ this.process.exitCode = 1;
250
+ return;
251
+ }
252
+ if (flags["save-mapping"]) {
253
+ await writeFile(flags["save-mapping"], JSON.stringify(mapping.map((m) => ({
254
+ column: m.originalName,
255
+ target: m.targetProperty
256
+ })), null, 2) + "\n");
257
+ this.logger.info(`Confirmed mapping saved to ${flags["save-mapping"]}`);
258
+ }
259
+ const commitSpinner = ora("Committing import…").start();
260
+ let outcome;
261
+ try {
262
+ const result = await testCaseImportCommitV1({
263
+ body: {
264
+ token: preview.token,
265
+ confirmed_mapping: mapping
266
+ },
267
+ requestValidator: void 0
268
+ });
269
+ if (result.error || !result.data) throw new Error(result.error?.message ?? `Commit failed (HTTP ${result.response?.status ?? "?"}).`);
270
+ outcome = result.data;
271
+ } catch (err) {
272
+ commitSpinner.fail(err instanceof Error ? err.message : "Commit failed.");
273
+ this.process.exitCode = 1;
274
+ return;
275
+ }
276
+ const { stats } = outcome;
277
+ if (outcome.status === "failed") commitSpinner.fail(`Import failed — 0 of ${stats.rows_processed} rows imported.`);
278
+ else commitSpinner.succeed(`Import ${outcome.status.replace(/_/g, " ")}: ${stats.tests_created} tests, ${stats.folders_created} folders, ${stats.steps_created} steps, ${stats.preconditions_created} preconditions (${stats.rows_failed} rows failed)`);
279
+ for (const warning of outcome.warnings) this.logger.info(chalk.yellow(` warning: ${warning.message}`));
280
+ for (const error of outcome.errors) this.logger.info(chalk.red(` error: ${error.message}`));
281
+ if (outcome.status === "failed") this.process.exitCode = 1;
282
+ }
283
+
284
+ //#endregion
285
+ export { importHandler };
@@ -1,8 +1,8 @@
1
1
  import "./env-NxtzJJPk.js";
2
- import { authGetSitesV1, configureClient } from "./sdk-client-BWrIbeUg.js";
2
+ import { authGetSitesV1, configureClient } from "./sdk-client-DyRTezC5.js";
3
3
  import { loadWorkspace } from "./workspace-store-4hfvsEHS.js";
4
4
  import "./token-refresh-Cz-FqDtC.js";
5
- import { resolveToken } from "./resolve-token-BRPrqdG-.js";
5
+ import { resolveToken } from "./resolve-token-Dq8cAuiu.js";
6
6
 
7
7
  //#region src/commands/workspace/listHandler.ts
8
8
  async function listHandler() {
@@ -1,7 +1,7 @@
1
1
  import { CLI_CLIENT_ID, getApiUrl, getAuthUrl, setSessionApiUrl, writeCredentials } from "./env-NxtzJJPk.js";
2
- import { configureClient } from "./sdk-client-BWrIbeUg.js";
2
+ import { configureClient } from "./sdk-client-DyRTezC5.js";
3
3
  import "./workspace-store-4hfvsEHS.js";
4
- import { autoSelectWorkspace } from "./resolve-workspace-vJZku4FK.js";
4
+ import { autoSelectWorkspace } from "./resolve-workspace-CO8d3IBz.js";
5
5
  import chalk from "chalk";
6
6
  import { createHash, randomBytes } from "node:crypto";
7
7
  import { createServer } from "node:http";
@@ -1,9 +1,9 @@
1
1
  import { getApiUrl, getAutomationSourceIdOverride, getSourceOverride, getTeamId } from "./env-NxtzJJPk.js";
2
- import { client, configureClient, uploadAutomationIngest, uploadImport } from "./sdk-client-BWrIbeUg.js";
2
+ import { client, configureClient, uploadAutomationIngest, uploadImport } from "./sdk-client-DyRTezC5.js";
3
3
  import "./workspace-store-4hfvsEHS.js";
4
- import { resolveWorkspace } from "./resolve-workspace-vJZku4FK.js";
4
+ import { resolveWorkspace } from "./resolve-workspace-CO8d3IBz.js";
5
5
  import "./token-refresh-Cz-FqDtC.js";
6
- import { resolveToken } from "./resolve-token-BRPrqdG-.js";
6
+ import { resolveToken } from "./resolve-token-Dq8cAuiu.js";
7
7
  import { readFileSync, statSync } from "node:fs";
8
8
  import { basename } from "node:path";
9
9
  import ora from "ora";
@@ -1,4 +1,4 @@
1
- import { authGetSitesV1 } from "./sdk-client-BWrIbeUg.js";
1
+ import { authGetSitesV1 } from "./sdk-client-DyRTezC5.js";
2
2
  import { clearWorkspace, loadWorkspace, saveWorkspace } from "./workspace-store-4hfvsEHS.js";
3
3
 
4
4
  //#region src/workspace/resolve-workspace.ts
@@ -11504,7 +11504,6 @@ const zExplorationFindAllV1Data = z.object({
11504
11504
  "filter.name": z.array(z.string()).optional(),
11505
11505
  "filter.key": z.array(z.string()).optional(),
11506
11506
  "filter.is_complete": z.array(z.string()).optional(),
11507
- "filter.milestone_id": z.array(z.string()).optional(),
11508
11507
  "filter.status_id": z.array(z.string()).optional(),
11509
11508
  "filter.project_id": z.array(z.string()).optional(),
11510
11509
  "filter.team_id": z.array(z.string()).optional(),
@@ -12757,7 +12756,7 @@ const zFolderWithChildrenDto = z.object({
12757
12756
  epoch: z.number(),
12758
12757
  virtual: z.unknown().optional(),
12759
12758
  is_root: z.boolean().optional(),
12760
- assigned_to_tester: z.string().optional(),
12759
+ assignee_id: z.string().optional(),
12761
12760
  folder_path: z.string().optional(),
12762
12761
  children: z.array(z.union([
12763
12762
  z.object({
@@ -12774,7 +12773,7 @@ const zFolderWithChildrenDto = z.object({
12774
12773
  epoch: z.number(),
12775
12774
  virtual: z.unknown().optional(),
12776
12775
  is_root: z.boolean().optional(),
12777
- assigned_to_tester: z.string().optional(),
12776
+ assignee_id: z.string().optional(),
12778
12777
  folder_path: z.string().optional()
12779
12778
  }),
12780
12779
  z.object({
@@ -12793,7 +12792,7 @@ const zFolderWithChildrenDto = z.object({
12793
12792
  updated_by: z.string(),
12794
12793
  epoch: z.number(),
12795
12794
  key: z.number(),
12796
- assigned_to_tester: z.string().optional(),
12795
+ assignee_id: z.string().optional(),
12797
12796
  estimate: z.number().optional(),
12798
12797
  test_type: z.enum([
12799
12798
  "manual",
@@ -12853,7 +12852,7 @@ const zFolderNodeDto = z.object({
12853
12852
  epoch: z.number(),
12854
12853
  virtual: z.unknown(),
12855
12854
  is_root: z.boolean().nullable(),
12856
- assigned_to_tester: z.string().nullable(),
12855
+ assignee_id: z.string().nullable(),
12857
12856
  folder_path: z.string().optional()
12858
12857
  });
12859
12858
  const zFolderChildrenResponseDto = z.object({
@@ -12872,7 +12871,7 @@ const zFolderChildrenResponseDto = z.object({
12872
12871
  epoch: z.number(),
12873
12872
  virtual: z.unknown(),
12874
12873
  is_root: z.boolean().nullable(),
12875
- assigned_to_tester: z.string().nullable(),
12874
+ assignee_id: z.string().nullable(),
12876
12875
  folder_path: z.string().optional()
12877
12876
  }),
12878
12877
  z.object({
@@ -12891,7 +12890,7 @@ const zFolderChildrenResponseDto = z.object({
12891
12890
  updated_by: z.string(),
12892
12891
  epoch: z.number(),
12893
12892
  key: z.number(),
12894
- assigned_to_tester: z.string().optional(),
12893
+ assignee_id: z.string().optional(),
12895
12894
  estimate: z.number().optional(),
12896
12895
  test_type: z.enum([
12897
12896
  "manual",
@@ -12959,7 +12958,7 @@ const zBulkFolderOperationDto = z.object({
12959
12958
  name: z.string(),
12960
12959
  description: z.string().optional(),
12961
12960
  virtual: z.unknown().optional(),
12962
- assigned_to_tester: z.string().optional(),
12961
+ assignee_id: z.string().optional(),
12963
12962
  parent_id: z.string().optional(),
12964
12963
  sequence: z.string().optional(),
12965
12964
  labels: z.array(z.string()).optional(),
@@ -12969,7 +12968,7 @@ const zBulkFolderOperationDto = z.object({
12969
12968
  name: z.string().optional(),
12970
12969
  description: z.string().optional(),
12971
12970
  virtual: z.unknown().optional(),
12972
- assigned_to_tester: z.string().optional(),
12971
+ assignee_id: z.string().optional(),
12973
12972
  parent_id: z.string().optional(),
12974
12973
  sequence: z.string().optional(),
12975
12974
  add_labels: z.array(z.string()).optional(),
@@ -12995,7 +12994,7 @@ const zBulkFolderResponseDto = z.object({
12995
12994
  epoch: z.number(),
12996
12995
  virtual: z.unknown(),
12997
12996
  is_root: z.boolean().nullable(),
12998
- assigned_to_tester: z.string().nullable(),
12997
+ assignee_id: z.string().nullable(),
12999
12998
  folder_path: z.string().optional()
13000
12999
  }).optional(),
13001
13000
  error: z.string().optional(),
@@ -13012,7 +13011,7 @@ const zCreateFolderDto = z.object({
13012
13011
  name: z.string(),
13013
13012
  description: z.string().optional(),
13014
13013
  virtual: z.unknown().optional(),
13015
- assigned_to_tester: z.string().optional(),
13014
+ assignee_id: z.string().optional(),
13016
13015
  parent_id: z.string().optional(),
13017
13016
  sequence: z.string().optional(),
13018
13017
  labels: z.array(z.string()).optional(),
@@ -13023,7 +13022,7 @@ const zUpdateFolderDto = z.object({
13023
13022
  name: z.string().optional(),
13024
13023
  description: z.string().optional(),
13025
13024
  virtual: z.unknown().optional(),
13026
- assigned_to_tester: z.string().optional(),
13025
+ assignee_id: z.string().optional(),
13027
13026
  parent_id: z.string().optional(),
13028
13027
  sequence: z.string().optional(),
13029
13028
  add_labels: z.array(z.string()).optional(),
@@ -13042,7 +13041,7 @@ const zFolderFindAllV1Data = z.object({
13042
13041
  "filter.updated_at": z.array(z.string()).optional(),
13043
13042
  "filter.updated_by": z.array(z.string()).optional(),
13044
13043
  "filter.is_root": z.array(z.string()).optional(),
13045
- "filter.assigned_to_tester": z.array(z.string()).optional(),
13044
+ "filter.assignee_id": z.array(z.string()).optional(),
13046
13045
  "filter.folder_path": z.array(z.string()).optional(),
13047
13046
  "sortBy": z.array(z.enum([
13048
13047
  "id:ASC",
@@ -13918,6 +13917,7 @@ const zGitHubAppClientCreateTeamAndMapV1Data = z.object({
13918
13917
 
13919
13918
  //#endregion
13920
13919
  //#region ../sdk/dist/gen/git-hub-sync-bridge/zod.js
13920
+ const zGitHubSyncBridgeOutboundAckV1Data = z.object({ url: z.literal("/v1/internal/github/outbound-ack") });
13921
13921
  const zGitHubSyncBridgeTransitionIssueV1Data = z.object({ url: z.literal("/v1/internal/github/issue/transition") });
13922
13922
  const zGitHubSyncBridgeSyncIssueV1Data = z.object({ url: z.literal("/v1/internal/github/issue/sync") });
13923
13923
  const zGitHubSyncBridgeSyncIssueCommentV1Data = z.object({ url: z.literal("/v1/internal/github/issue-comment/sync") });
@@ -14060,11 +14060,6 @@ 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(""),
14068
14063
  tq_issue_id: z.string(),
14069
14064
  github_repository_id: z.string(),
14070
14065
  team_id: z.string()
@@ -14100,11 +14095,6 @@ const zUpdateGithubIssueLinkDto = z.object({
14100
14095
  sync_direction: z.string().optional().describe(""),
14101
14096
  origin: z.string().optional().describe(""),
14102
14097
  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(""),
14108
14098
  tq_issue_id: z.string().optional(),
14109
14099
  github_repository_id: z.string().optional(),
14110
14100
  team_id: z.string().optional()
@@ -14155,6 +14145,7 @@ const zGithubIssueLinkFindAllV1Data = z.object({
14155
14145
  "filter.sync_direction": z.array(z.string()).optional(),
14156
14146
  "filter.origin": z.array(z.string()).optional(),
14157
14147
  "filter.role": z.array(z.string()).optional(),
14148
+ "filter.sync_status": z.array(z.string()).optional(),
14158
14149
  "filter.tq_issue_id": z.array(z.string()).optional(),
14159
14150
  "filter.github_repository_id": z.array(z.string()).optional(),
14160
14151
  "filter.team_id": z.array(z.string()).optional(),
@@ -14174,6 +14165,8 @@ const zGithubIssueLinkFindAllV1Data = z.object({
14174
14165
  "origin:DESC",
14175
14166
  "role:ASC",
14176
14167
  "role:DESC",
14168
+ "sync_status:ASC",
14169
+ "sync_status:DESC",
14177
14170
  "created_at:ASC",
14178
14171
  "created_at:DESC",
14179
14172
  "updated_at:ASC",
@@ -17189,6 +17182,7 @@ const zIssueViewRestoreV1Data = z.object({
17189
17182
 
17190
17183
  //#endregion
17191
17184
  //#region ../sdk/dist/gen/jira/zod.js
17185
+ const zJiraStatusV1Data = z.object({ url: z.literal("/v1/jira/status") });
17192
17186
  const zJiraLinkV1Data = z.object({ url: z.literal("/v1/jira/link") });
17193
17187
  const zJiraInstallationsV1Data = z.object({
17194
17188
  query: z.object({ "panelNonce": z.string().optional() }).optional(),
@@ -23859,6 +23853,7 @@ const zRunNodeDto = z.object({
23859
23853
  test_scope_id: z.string().nullable(),
23860
23854
  cycle_id: z.string().nullable(),
23861
23855
  parent_run_id: z.string().nullable(),
23856
+ original_parent_run_id: z.string().nullable(),
23862
23857
  name: z.string(),
23863
23858
  key: z.number().optional(),
23864
23859
  run_type: z.enum([
@@ -23968,6 +23963,7 @@ const zRunWithContentDto = z.object({
23968
23963
  test_scope_id: z.string().nullable(),
23969
23964
  cycle_id: z.string().nullable(),
23970
23965
  parent_run_id: z.string().nullable(),
23966
+ original_parent_run_id: z.string().nullable(),
23971
23967
  name: z.string(),
23972
23968
  key: z.number().optional(),
23973
23969
  run_type: z.enum([
@@ -27618,7 +27614,7 @@ const zTestResponseDto = z.object({
27618
27614
  priority: z.number().optional(),
27619
27615
  estimate: z.number().nullable().optional().describe(""),
27620
27616
  data: z.unknown().optional().describe(""),
27621
- assigned_to_tester: z.string().nullable().optional().describe(""),
27617
+ assignee_id: z.string().nullable().optional().describe(""),
27622
27618
  is_automated: z.boolean().optional().describe(""),
27623
27619
  key: z.number().optional().describe(""),
27624
27620
  identifier: z.string().optional().describe(""),
@@ -27687,7 +27683,7 @@ const zCreateTestDto = z.object({
27687
27683
  priority: z.number().optional().describe(""),
27688
27684
  estimate: z.number().nullable().optional().describe(""),
27689
27685
  data: z.unknown().optional().describe(""),
27690
- assigned_to_tester: z.string().nullable().optional().describe(""),
27686
+ assignee_id: z.string().nullable().optional().describe(""),
27691
27687
  is_automated: z.boolean().optional().describe(""),
27692
27688
  key: z.number().optional().describe(""),
27693
27689
  identifier: z.string().optional().describe(""),
@@ -27724,7 +27720,7 @@ const zUpdateTestDto = z.object({
27724
27720
  is_automated: z.boolean().optional(),
27725
27721
  to_automate: z.boolean().optional(),
27726
27722
  state_mask: z.number().optional(),
27727
- assigned_to_tester: z.string().optional(),
27723
+ assignee_id: z.string().optional(),
27728
27724
  data: z.object({
27729
27725
  description: z.string().optional(),
27730
27726
  shared_precondition_id: z.string().nullable().optional(),
@@ -27784,7 +27780,6 @@ const zUpdateTestDto = z.object({
27784
27780
  add_labels: z.array(z.string()).optional(),
27785
27781
  remove_labels: z.array(z.string()).optional()
27786
27782
  });
27787
- const zExportTestsDto = z.object({ ids: z.array(z.string()).describe("") });
27788
27783
  const zTestBulkOperationDto = z.object({
27789
27784
  op: z.enum([
27790
27785
  "create",
@@ -27815,6 +27810,7 @@ const zBulkTestResponseDto = z.object({
27815
27810
  savepoints_used: z.number().describe("")
27816
27811
  }).describe("")
27817
27812
  });
27813
+ const zExportTestsDto = z.object({ ids: z.array(z.string()).describe("") });
27818
27814
  const zTestFindAllV1Data = z.object({
27819
27815
  query: z.object({
27820
27816
  "page": z.number().optional(),
@@ -27824,10 +27820,11 @@ const zTestFindAllV1Data = z.object({
27824
27820
  "filter.name": z.array(z.string()).optional(),
27825
27821
  "filter.priority": z.array(z.string()).optional(),
27826
27822
  "filter.test_type": z.array(z.string()).optional(),
27823
+ "filter.case_type_id": z.array(z.string()).optional(),
27827
27824
  "filter.is_automated": z.array(z.string()).optional(),
27828
27825
  "filter.to_automate": z.array(z.string()).optional(),
27829
27826
  "filter.state_mask": z.array(z.string()).optional(),
27830
- "filter.assigned_to_tester": z.array(z.string()).optional(),
27827
+ "filter.assignee_id": z.array(z.string()).optional(),
27831
27828
  "filter.estimate": z.array(z.string()).optional(),
27832
27829
  "filter.created_at": z.array(z.string()).optional(),
27833
27830
  "filter.created_by": z.array(z.string()).optional(),
@@ -27884,14 +27881,14 @@ const zTestRemoveV1Data = z.object({
27884
27881
  path: z.object({ "testId": z.string() }),
27885
27882
  url: z.literal("/v1/test/{testId}")
27886
27883
  });
27887
- const zTestExportTestsV1Data = z.object({
27888
- body: zExportTestsDto,
27889
- url: z.literal("/v1/test/export")
27890
- });
27891
27884
  const zTestBulkOperationV1Data = z.object({
27892
27885
  body: zBulkTestRequestDto,
27893
27886
  url: z.literal("/v1/test/bulk")
27894
27887
  });
27888
+ const zTestExportTestsV1Data = z.object({
27889
+ body: zExportTestsDto,
27890
+ url: z.literal("/v1/test/export")
27891
+ });
27895
27892
 
27896
27893
  //#endregion
27897
27894
  //#region ../sdk/dist/gen/test-attachment/zod.js
@@ -28000,6 +27997,311 @@ const zTestAttachmentRemoveV1Data = z.object({
28000
27997
  url: z.literal("/v1/test/{testId}/attachment/{attachmentId}")
28001
27998
  });
28002
27999
 
28000
+ //#endregion
28001
+ //#region ../sdk/dist/gen/test-case-import/functions.js
28002
+ /**
28003
+ * Uploads (or references) a CSV/Excel/JSON file or a public Google Sheets URL, proposes a column mapping to the test-case schema with per-column confidence, and stages the source for a later commit. Writes NO tests/folders — the returned token is presented to POST /v1/test-case-import/commit after the user reviews and confirms the mapping. Sample rows are capped at 20; row_count reports the full source total. Staged previews expire after 1 hour.
28004
+ */
28005
+ const testCaseImportPreviewV1 = (options) => {
28006
+ return (options?.client ?? client).post({
28007
+ security: [{
28008
+ scheme: "bearer",
28009
+ type: "http"
28010
+ }],
28011
+ url: "/v1/test-case-import/preview",
28012
+ bodySerializer: (body) => {
28013
+ const fd = new FormData();
28014
+ if (body && typeof body === "object") for (const [k, v] of Object.entries(body)) {
28015
+ if (v == null) continue;
28016
+ fd.append(k, v instanceof Blob ? v : String(v));
28017
+ }
28018
+ return fd;
28019
+ },
28020
+ ...options
28021
+ });
28022
+ };
28023
+ /**
28024
+ * Re-parses the staged source through the user-confirmed column mapping and writes folders (auto-created as needed), tests, steps, and preconditions in bulk chunks. Malformed rows are reported as warnings/errors — the batch never aborts. Rejects: 403 cross-workspace token, 409 concurrent or repeated commit / expired preview, 422 when a required target field is unmapped.
28025
+ */
28026
+ const testCaseImportCommitV1 = (options) => {
28027
+ return (options?.client ?? client).post({
28028
+ security: [{
28029
+ scheme: "bearer",
28030
+ type: "http"
28031
+ }],
28032
+ url: "/v1/test-case-import/commit",
28033
+ ...options
28034
+ });
28035
+ };
28036
+
28037
+ //#endregion
28038
+ //#region ../sdk/dist/gen/test-case-import/zod.js
28039
+ const zPreviewImportDto = z.object({
28040
+ team_id: z.string().describe(""),
28041
+ format: z.enum([
28042
+ "csv",
28043
+ "xlsx",
28044
+ "sheets",
28045
+ "json"
28046
+ ]).optional().describe(""),
28047
+ file_id: z.string().optional().describe(""),
28048
+ sheets_url: z.string().optional().describe("")
28049
+ });
28050
+ const zPreviewImportResponseDto = z.object({
28051
+ token: z.string(),
28052
+ filename: z.string(),
28053
+ format: z.enum([
28054
+ "csv",
28055
+ "xlsx",
28056
+ "sheets",
28057
+ "json"
28058
+ ]),
28059
+ proposed_mapping: z.array(z.object({
28060
+ originalName: z.string(),
28061
+ targetProperty: z.enum([
28062
+ "row_type",
28063
+ "test_id",
28064
+ "test_key",
28065
+ "folder_id",
28066
+ "folder_path",
28067
+ "folder_name",
28068
+ "test_name",
28069
+ "test_description",
28070
+ "test_type",
28071
+ "case_type_id",
28072
+ "case_type_name",
28073
+ "test_priority",
28074
+ "estimate",
28075
+ "is_automated",
28076
+ "assignee_email",
28077
+ "labels",
28078
+ "attachment_filenames",
28079
+ "data_set_names",
28080
+ "sequence",
28081
+ "item_id",
28082
+ "method",
28083
+ "expected_result",
28084
+ "keyword",
28085
+ "shared_step_id",
28086
+ "shared_precondition_id",
28087
+ "data_table",
28088
+ "steps",
28089
+ "preconditions"
28090
+ ]).nullable(),
28091
+ matchType: z.enum([
28092
+ "exact",
28093
+ "prefix",
28094
+ "fuzzy",
28095
+ "llm",
28096
+ "unmapped",
28097
+ "manual",
28098
+ "preset"
28099
+ ]),
28100
+ confidence: z.number(),
28101
+ fieldType: z.enum([
28102
+ "string",
28103
+ "number",
28104
+ "boolean",
28105
+ "unknown"
28106
+ ]).optional(),
28107
+ labelPrefix: z.string().optional().describe(""),
28108
+ descriptionHeading: z.string().optional().describe("")
28109
+ })),
28110
+ missing_required: z.array(z.string()),
28111
+ sample_rows: z.array(z.record(z.string(), z.unknown())),
28112
+ row_count: z.number(),
28113
+ expires_at: z.unknown(),
28114
+ advisories: z.array(z.object({
28115
+ column: z.string(),
28116
+ target: z.enum([
28117
+ "row_type",
28118
+ "test_id",
28119
+ "test_key",
28120
+ "folder_id",
28121
+ "folder_path",
28122
+ "folder_name",
28123
+ "test_name",
28124
+ "test_description",
28125
+ "test_type",
28126
+ "case_type_id",
28127
+ "case_type_name",
28128
+ "test_priority",
28129
+ "estimate",
28130
+ "is_automated",
28131
+ "assignee_email",
28132
+ "labels",
28133
+ "attachment_filenames",
28134
+ "data_set_names",
28135
+ "sequence",
28136
+ "item_id",
28137
+ "method",
28138
+ "expected_result",
28139
+ "keyword",
28140
+ "shared_step_id",
28141
+ "shared_precondition_id",
28142
+ "data_table",
28143
+ "steps",
28144
+ "preconditions"
28145
+ ]),
28146
+ message: z.string(),
28147
+ count: z.number(),
28148
+ values: z.array(z.object({
28149
+ value: z.string(),
28150
+ count: z.number()
28151
+ }))
28152
+ })),
28153
+ detected_vendor: z.string().nullable(),
28154
+ assignee_resolution: z.object({
28155
+ column: z.string(),
28156
+ entries: z.array(z.object({
28157
+ sourceValue: z.string(),
28158
+ count: z.number(),
28159
+ kind: z.enum([
28160
+ "existing",
28161
+ "new_email",
28162
+ "unresolved"
28163
+ ]),
28164
+ userId: z.string().optional(),
28165
+ userName: z.string().optional(),
28166
+ suggestedEmail: z.string().optional()
28167
+ }))
28168
+ }).nullable()
28169
+ });
28170
+ const zCommitImportDto = z.object({
28171
+ token: z.string().describe(""),
28172
+ confirmed_mapping: z.array(z.object({
28173
+ originalName: z.string(),
28174
+ targetProperty: z.enum([
28175
+ "row_type",
28176
+ "test_id",
28177
+ "test_key",
28178
+ "folder_id",
28179
+ "folder_path",
28180
+ "folder_name",
28181
+ "test_name",
28182
+ "test_description",
28183
+ "test_type",
28184
+ "case_type_id",
28185
+ "case_type_name",
28186
+ "test_priority",
28187
+ "estimate",
28188
+ "is_automated",
28189
+ "assignee_email",
28190
+ "labels",
28191
+ "attachment_filenames",
28192
+ "data_set_names",
28193
+ "sequence",
28194
+ "item_id",
28195
+ "method",
28196
+ "expected_result",
28197
+ "keyword",
28198
+ "shared_step_id",
28199
+ "shared_precondition_id",
28200
+ "data_table",
28201
+ "steps",
28202
+ "preconditions"
28203
+ ]).nullable(),
28204
+ matchType: z.enum([
28205
+ "exact",
28206
+ "prefix",
28207
+ "fuzzy",
28208
+ "llm",
28209
+ "unmapped",
28210
+ "manual",
28211
+ "preset"
28212
+ ]),
28213
+ confidence: z.number(),
28214
+ fieldType: z.enum([
28215
+ "string",
28216
+ "number",
28217
+ "boolean",
28218
+ "unknown"
28219
+ ]).optional(),
28220
+ labelPrefix: z.string().optional().describe(""),
28221
+ descriptionHeading: z.string().optional().describe("")
28222
+ })).optional().describe(""),
28223
+ assignee_plan: z.object({
28224
+ role: z.enum([
28225
+ "owner",
28226
+ "admin",
28227
+ "member",
28228
+ "guest"
28229
+ ]).optional(),
28230
+ teamIds: z.array(z.string()).optional(),
28231
+ entries: z.array(z.object({
28232
+ sourceValue: z.string(),
28233
+ action: z.enum([
28234
+ "assign",
28235
+ "create",
28236
+ "skip"
28237
+ ]),
28238
+ userId: z.string().optional(),
28239
+ email: z.string().optional()
28240
+ }))
28241
+ }).optional().describe(""),
28242
+ team_ids: z.array(z.string()).optional()
28243
+ });
28244
+ const zTestCaseImportResultDto = z.object({
28245
+ status: z.enum([
28246
+ "completed",
28247
+ "completed_with_warnings",
28248
+ "failed"
28249
+ ]),
28250
+ created_tests: z.array(z.object({
28251
+ id: z.string(),
28252
+ name: z.string()
28253
+ })),
28254
+ stats: z.object({
28255
+ rows_processed: z.number(),
28256
+ tests_created: z.number(),
28257
+ folders_created: z.number(),
28258
+ steps_created: z.number(),
28259
+ preconditions_created: z.number(),
28260
+ rows_failed: z.number(),
28261
+ members_provisioned: z.number().optional()
28262
+ }),
28263
+ warnings: z.array(z.object({
28264
+ type: z.enum([
28265
+ "skipped",
28266
+ "invalid",
28267
+ "unresolved",
28268
+ "write_failed"
28269
+ ]),
28270
+ message: z.string(),
28271
+ row_index: z.number().optional(),
28272
+ column: z.string().optional(),
28273
+ count: z.number().optional(),
28274
+ values: z.array(z.object({
28275
+ value: z.string(),
28276
+ count: z.number()
28277
+ })).optional()
28278
+ })),
28279
+ errors: z.array(z.object({
28280
+ type: z.enum([
28281
+ "skipped",
28282
+ "invalid",
28283
+ "unresolved",
28284
+ "write_failed"
28285
+ ]),
28286
+ message: z.string(),
28287
+ row_index: z.number().optional(),
28288
+ column: z.string().optional(),
28289
+ count: z.number().optional(),
28290
+ values: z.array(z.object({
28291
+ value: z.string(),
28292
+ count: z.number()
28293
+ })).optional()
28294
+ }))
28295
+ });
28296
+ const zTestCaseImportPreviewV1Data = z.object({
28297
+ body: zPreviewImportDto,
28298
+ url: z.literal("/v1/test-case-import/preview")
28299
+ });
28300
+ const zTestCaseImportCommitV1Data = z.object({
28301
+ body: zCommitImportDto,
28302
+ url: z.literal("/v1/test-case-import/commit")
28303
+ });
28304
+
28003
28305
  //#endregion
28004
28306
  //#region ../sdk/dist/gen/test-comment/zod.js
28005
28307
  const zTestCommentFindAllV1Data = z.object({
@@ -30312,7 +30614,10 @@ const zInviteMultiUserDto = z.object({
30312
30614
  workspace_ids: z.array(z.string()).describe(""),
30313
30615
  team_ids_by_workspace: z.record(z.string(), z.array(z.string())).optional().describe("")
30314
30616
  });
30315
- const zAcceptByTokenDto = z.object({ token: z.string().describe("") });
30617
+ const zAcceptByTokenDto = z.object({
30618
+ token: z.string().describe(""),
30619
+ password: z.string().optional().describe("")
30620
+ });
30316
30621
  const zAcceptByTokenResponseDto = z.object({
30317
30622
  access_token: z.string().describe(""),
30318
30623
  user: z.object({
@@ -30746,4 +31051,4 @@ function tryReadMessage(err) {
30746
31051
  }
30747
31052
 
30748
31053
  //#endregion
30749
- export { authGetProfileV1, authGetSitesV1, client, configureClient, uploadAutomationIngest, uploadImport };
31054
+ export { authGetProfileV1, authGetSitesV1, client, configureClient, testCaseImportCommitV1, testCaseImportPreviewV1, uploadAutomationIngest, uploadImport };
@@ -1,8 +1,8 @@
1
1
  import "./env-NxtzJJPk.js";
2
- import { authGetSitesV1, configureClient } from "./sdk-client-BWrIbeUg.js";
2
+ import { authGetSitesV1, configureClient } from "./sdk-client-DyRTezC5.js";
3
3
  import { saveWorkspace } from "./workspace-store-4hfvsEHS.js";
4
4
  import "./token-refresh-Cz-FqDtC.js";
5
- import { resolveToken } from "./resolve-token-BRPrqdG-.js";
5
+ import { resolveToken } from "./resolve-token-Dq8cAuiu.js";
6
6
 
7
7
  //#region src/commands/workspace/selectHandler.ts
8
8
  async function selectHandler(_flags, workspaceId) {
@@ -1,5 +1,5 @@
1
1
  import { getApiUrl, getPatToken, readCredentials } from "./env-NxtzJJPk.js";
2
- import { authGetProfileV1, configureClient } from "./sdk-client-BWrIbeUg.js";
2
+ import { authGetProfileV1, configureClient } from "./sdk-client-DyRTezC5.js";
3
3
  import { isTokenExpired } from "./token-refresh-Cz-FqDtC.js";
4
4
  import chalk from "chalk";
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@levr-one/cli",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "The command-line interface for Levr",
5
5
  "type": "module",
6
6
  "license": "MIT",