@indigoai-us/hq-cli 5.48.0 → 5.49.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.
@@ -24,6 +24,23 @@ jobs:
24
24
  pnpm-version: "10"
25
25
  node-version: ${{ matrix.node-version }}
26
26
  run-build: true
27
- run-lint: false
27
+ run-lint: true
28
28
  run-coverage: true
29
29
  secrets: inherit
30
+
31
+ e2e:
32
+ name: e2e (node 22)
33
+ runs-on: ubuntu-latest
34
+ timeout-minutes: 10
35
+ steps:
36
+ - uses: actions/checkout@v4
37
+ - uses: pnpm/action-setup@v4
38
+ with:
39
+ version: "10"
40
+ - uses: actions/setup-node@v4
41
+ with:
42
+ node-version: "22"
43
+ cache: pnpm
44
+ - run: pnpm install --frozen-lockfile
45
+ - run: pnpm build
46
+ - run: pnpm test:e2e
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f1986062-db39-5448-a8fa-3bb6be32c58b")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ae9ed929-9821-5f8b-bbbe-8f7b6105f5ad")}catch(e){}}();
3
3
  import chalk from "chalk";
4
4
  import { ensureCognitoToken } from "../utils/cognito-session.js";
5
5
  import { vaultApiFetch, getCompanyUid } from "../utils/vault-api.js";
@@ -246,8 +246,15 @@ export function registerMeetingsCommand(program) {
246
246
  }
247
247
  const where = companyId === "unknown" ? "unattributed (personal)" : companyId;
248
248
  console.log(chalk.green(`\n✓ Meeting ${chalk.cyan(meetingId)} attributed to ${chalk.bold(where)}.`));
249
- if (data && data.appliedToSeries)
249
+ if (typeof data.occurrencesUpdated === "number" && data.occurrencesUpdated > 1) {
250
+ console.log(chalk.dim(` Applied to ${data.occurrencesUpdated} meetings in this recurring series (including past occurrences).`));
251
+ }
252
+ else if (data.appliedToSeries) {
250
253
  console.log(chalk.dim(" Future occurrences of this recurring series will inherit this attribution."));
254
+ }
255
+ if (typeof data.refiledCount === "number" && data.refiledCount > 0) {
256
+ console.log(chalk.dim(` Refiled ${data.refiledCount} transcript${data.refiledCount === 1 ? "" : "s"} into the new company vault.`));
257
+ }
251
258
  console.log();
252
259
  }
253
260
  catch (err) {
@@ -419,4 +426,4 @@ export function registerMeetingsCommand(program) {
419
426
  });
420
427
  }
421
428
  //# sourceMappingURL=meetings.js.map
422
- //# debugId=f1986062-db39-5448-a8fa-3bb6be32c58b
429
+ //# debugId=ae9ed929-9821-5f8b-bbbe-8f7b6105f5ad
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * HQ CLI - Module management, package management, and cloud sync for HQ
4
4
  */
5
5
 
6
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="aeb143c7-7e19-5999-b6cb-790fc79034cc")}catch(e){}}();
6
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="60f911e0-ddd5-5a2d-91c7-18ba47457b57")}catch(e){}}();
7
7
  import { Command } from "commander";
8
8
  import { initSentry, Sentry } from "./sentry.js";
9
9
  import { registerAddCommand } from "./commands/add.js";
@@ -180,8 +180,10 @@ registerRescueCommand(program);
180
180
  process.exitCode = 1;
181
181
  }
182
182
  finally {
183
+ // Release health: finalize the per-run session before the flush.
184
+ Sentry.endSession();
183
185
  await Promise.allSettled([refreshVersionCache(), Sentry.flush(2000)]);
184
186
  }
185
187
  })();
186
188
  //# sourceMappingURL=index.js.map
187
- //# debugId=aeb143c7-7e19-5999-b6cb-790fc79034cc
189
+ //# debugId=60f911e0-ddd5-5a2d-91c7-18ba47457b57
package/dist/sentry.js CHANGED
@@ -1,10 +1,11 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="80ca2b2d-c649-5e88-9bc6-69a20fa4aa51")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a260ea42-f6d8-52d0-90c6-6e586de5efd6")}catch(e){}}();
3
3
  import * as Sentry from "@sentry/node";
4
4
  import { BUNDLED_DSN } from "./sentry-dsn.generated.js";
5
5
  import { beforeSend } from "./sentry-before-send.js";
6
6
  import { beforeBreadcrumb } from "./utils/breadcrumb-buffer.js";
7
7
  import { CLI_VERSION } from "./cli-version.js";
8
+ import { getCachedSentryUser } from "./utils/sentry-identity.js";
8
9
  export function initSentry() {
9
10
  const dsn = BUNDLED_DSN || process.env.SENTRY_DSN;
10
11
  if (!dsn)
@@ -22,7 +23,16 @@ export function initSentry() {
22
23
  beforeSend,
23
24
  beforeBreadcrumb,
24
25
  });
26
+ // Attribute events to the logged-in HQ identity (best-effort; null when not
27
+ // logged in). A CLI process is one user, so global setUser is correct here.
28
+ const user = getCachedSentryUser();
29
+ if (user)
30
+ Sentry.setUser(user);
31
+ // Release health: one session per CLI run. Ended + flushed in index.ts's
32
+ // finally (covers every exit path). A captured error auto-marks the session
33
+ // "errored", so crash-free numbers per release stay accurate.
34
+ Sentry.startSession();
25
35
  }
26
36
  export { Sentry };
27
37
  //# sourceMappingURL=sentry.js.map
28
- //# debugId=80ca2b2d-c649-5e88-9bc6-69a20fa4aa51
38
+ //# debugId=a260ea42-f6d8-52d0-90c6-6e586de5efd6
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Best-effort Sentry user from the cached HQ session, so CLI events are
3
+ * attributed to a user ("users affected" + per-user triage). Returns null when
4
+ * not logged in. A CLI process is single-user, so this is set once at init.
5
+ */
6
+ export declare function getCachedSentryUser(): {
7
+ id?: string;
8
+ email?: string;
9
+ username?: string;
10
+ } | null;
11
+ //# sourceMappingURL=sentry-identity.d.ts.map
@@ -0,0 +1,43 @@
1
+
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="1ca7cb4e-47e2-5cd0-a400-bbdaafa2b0d5")}catch(e){}}();
3
+ import { loadCachedTokens, isMachineIdentity, loadMachineCreds, } from "@indigoai-us/hq-cloud";
4
+ /** Decode the (untrusted) id-token payload — for attribution/display only. */
5
+ function peekIdToken(idToken) {
6
+ try {
7
+ const payload = idToken.split(".")[1];
8
+ if (!payload)
9
+ return {};
10
+ const pad = payload.length % 4 === 0 ? "" : "=".repeat(4 - (payload.length % 4));
11
+ const normalized = payload.replace(/-/g, "+").replace(/_/g, "/") + pad;
12
+ const decoded = JSON.parse(Buffer.from(normalized, "base64").toString("utf-8"));
13
+ return { email: decoded.email, sub: decoded.sub };
14
+ }
15
+ catch {
16
+ return {};
17
+ }
18
+ }
19
+ /**
20
+ * Best-effort Sentry user from the cached HQ session, so CLI events are
21
+ * attributed to a user ("users affected" + per-user triage). Returns null when
22
+ * not logged in. A CLI process is single-user, so this is set once at init.
23
+ */
24
+ export function getCachedSentryUser() {
25
+ try {
26
+ if (isMachineIdentity()) {
27
+ const username = loadMachineCreds()?.username;
28
+ return username ? { id: username, username } : null;
29
+ }
30
+ const cached = loadCachedTokens();
31
+ if (!cached)
32
+ return null;
33
+ const { email, sub } = peekIdToken(cached.idToken);
34
+ if (!sub && !email)
35
+ return null;
36
+ return { id: sub, email };
37
+ }
38
+ catch {
39
+ return null;
40
+ }
41
+ }
42
+ //# sourceMappingURL=sentry-identity.js.map
43
+ //# debugId=1ca7cb4e-47e2-5cd0-a400-bbdaafa2b0d5
@@ -0,0 +1,93 @@
1
+ import { mkdtemp, rm } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import path from "node:path";
4
+ import { spawn } from "node:child_process";
5
+ import { afterEach, describe, expect, it } from "vitest";
6
+ import { version } from "../package.json";
7
+
8
+ const cliEntry = path.resolve("dist/index.js");
9
+ const tempDirs: string[] = [];
10
+
11
+ interface CliResult {
12
+ code: number | null;
13
+ stdout: string;
14
+ stderr: string;
15
+ }
16
+
17
+ function runHq(args: string[], options: { cwd?: string; env?: NodeJS.ProcessEnv } = {}) {
18
+ return new Promise<CliResult>((resolve, reject) => {
19
+ const child = spawn(process.execPath, [cliEntry, ...args], {
20
+ cwd: options.cwd,
21
+ env: {
22
+ ...process.env,
23
+ HQ_NO_UPDATE_CHECK: "1",
24
+ ...options.env,
25
+ },
26
+ stdio: ["ignore", "pipe", "pipe"],
27
+ });
28
+
29
+ let stdout = "";
30
+ let stderr = "";
31
+ child.stdout.setEncoding("utf8");
32
+ child.stderr.setEncoding("utf8");
33
+ child.stdout.on("data", (chunk) => {
34
+ stdout += chunk;
35
+ });
36
+ child.stderr.on("data", (chunk) => {
37
+ stderr += chunk;
38
+ });
39
+ child.on("error", reject);
40
+ child.on("close", (code) => resolve({ code, stdout, stderr }));
41
+ });
42
+ }
43
+
44
+ async function makeTempDir(prefix: string) {
45
+ const dir = await mkdtemp(path.join(tmpdir(), prefix));
46
+ tempDirs.push(dir);
47
+ return dir;
48
+ }
49
+
50
+ afterEach(async () => {
51
+ await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })));
52
+ });
53
+
54
+ describe("built hq CLI", () => {
55
+ it("prints help from the built entrypoint", async () => {
56
+ const result = await runHq(["--help"]);
57
+
58
+ expect(result.code).toBe(0);
59
+ expect(result.stderr).toBe("");
60
+ expect(result.stdout).toContain("Usage: hq [options] [command]");
61
+ expect(result.stdout).toContain("HQ management CLI");
62
+ expect(result.stdout).toContain("whoami");
63
+ });
64
+
65
+ it("prints the package version from the built entrypoint", async () => {
66
+ const result = await runHq(["--version"]);
67
+
68
+ expect(result.code).toBe(0);
69
+ expect(result.stderr).toBe("");
70
+ expect(result.stdout.trim()).toBe(version);
71
+ });
72
+
73
+ it("reports logged-out state without network or credentials", async () => {
74
+ const home = await makeTempDir("hq-cli-e2e-home-");
75
+ const cwd = await makeTempDir("hq-cli-e2e-cwd-");
76
+
77
+ const result = await runHq(["whoami"], {
78
+ cwd,
79
+ env: {
80
+ HOME: home,
81
+ USERPROFILE: home,
82
+ XDG_CONFIG_HOME: path.join(home, ".config"),
83
+ XDG_CACHE_HOME: path.join(home, ".cache"),
84
+ HQ_MACHINE_IDENTITY: undefined,
85
+ HQ_MACHINE_CREDS: undefined,
86
+ },
87
+ });
88
+
89
+ expect(result.code).toBe(0);
90
+ expect(result.stderr).toBe("");
91
+ expect(result.stdout.trim()).toBe("Not logged in. Run 'hq login' to authenticate.");
92
+ });
93
+ });
@@ -0,0 +1,37 @@
1
+ import js from "@eslint/js";
2
+ import tseslint from "typescript-eslint";
3
+
4
+ const sourceFiles = ["src/**/*.ts"];
5
+ const recommended = tseslint
6
+ .config(js.configs.recommended, ...tseslint.configs.recommended)
7
+ .map((config) => ({
8
+ ...config,
9
+ files: sourceFiles,
10
+ }));
11
+
12
+ export default [
13
+ {
14
+ ignores: ["dist/**", "generated/**", "node_modules/**"],
15
+ },
16
+ ...recommended,
17
+ {
18
+ files: sourceFiles,
19
+ rules: {
20
+ "no-undef": "off",
21
+ "no-useless-assignment": "warn",
22
+ "no-useless-escape": "warn",
23
+ "prefer-const": "warn",
24
+ "preserve-caught-error": "off",
25
+ "@typescript-eslint/no-explicit-any": "warn",
26
+ "@typescript-eslint/no-require-imports": "warn",
27
+ "@typescript-eslint/no-unused-vars": [
28
+ "warn",
29
+ {
30
+ argsIgnorePattern: "^_",
31
+ caughtErrorsIgnorePattern: "^_",
32
+ varsIgnorePattern: "^_",
33
+ },
34
+ ],
35
+ },
36
+ },
37
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.48.0",
3
+ "version": "5.49.0",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -10,7 +10,9 @@
10
10
  "scripts": {
11
11
  "build": "node scripts/generate-dsn.mjs && tsc",
12
12
  "typecheck": "tsc --noEmit",
13
+ "lint": "eslint .",
13
14
  "test": "vitest run",
15
+ "test:e2e": "vitest run --config vitest.e2e.config.ts",
14
16
  "coverage": "vitest run --coverage --coverage.reporter=text-summary --coverage.reporter=json-summary",
15
17
  "vitest": "vitest",
16
18
  "clean": "rm -rf dist"
@@ -29,11 +31,14 @@
29
31
  },
30
32
  "devDependencies": {
31
33
  "@aws-sdk/client-s3": "^3.1049.0",
34
+ "@eslint/js": "^10.0.1",
32
35
  "@types/js-yaml": "^4.0.9",
33
36
  "@types/node": "^22.0.0",
34
37
  "@types/semver": "^7.5.8",
35
38
  "@vitest/coverage-v8": "4.1.6",
39
+ "eslint": "^10.5.0",
36
40
  "typescript": "^5.7.0",
41
+ "typescript-eslint": "^8.61.1",
37
42
  "vitest": "^4.1.2"
38
43
  },
39
44
  "repository": {
@@ -160,4 +160,68 @@ describe("meetings set-company", () => {
160
160
 
161
161
  expect(logSpy).toHaveBeenCalledWith(JSON.stringify(body, null, 2));
162
162
  });
163
+
164
+ it("prints recurring series and refiled transcript counts from the response", async () => {
165
+ vi.mocked(vaultApiFetch).mockResolvedValueOnce(
166
+ jsonRes({
167
+ ok: true,
168
+ meetingId: "meeting-123456",
169
+ companyId: "cmp_acme",
170
+ seriesKey: "series-123",
171
+ appliedToSeries: true,
172
+ occurrencesUpdated: 3,
173
+ refiled: true,
174
+ refiledCount: 2,
175
+ }),
176
+ );
177
+
178
+ const program = buildProgram();
179
+ await program.parseAsync([
180
+ "node",
181
+ "hq",
182
+ "meetings",
183
+ "set-company",
184
+ "meeting-123456",
185
+ "--company",
186
+ "acme",
187
+ ]);
188
+
189
+ expect(logSpy).toHaveBeenCalledWith(
190
+ expect.stringContaining("Applied to 3 meetings in this recurring series"),
191
+ );
192
+ expect(logSpy).toHaveBeenCalledWith(
193
+ expect.stringContaining("Refiled 2 transcripts into the new company vault."),
194
+ );
195
+ });
196
+
197
+ it("does not print multi-meeting or refile copy for a single occurrence without refiles", async () => {
198
+ vi.mocked(vaultApiFetch).mockResolvedValueOnce(
199
+ jsonRes({
200
+ ok: true,
201
+ meetingId: "meeting-123456",
202
+ companyId: "cmp_acme",
203
+ seriesKey: null,
204
+ appliedToSeries: false,
205
+ occurrencesUpdated: 1,
206
+ refiled: false,
207
+ refiledCount: 0,
208
+ }),
209
+ );
210
+
211
+ const program = buildProgram();
212
+ await program.parseAsync([
213
+ "node",
214
+ "hq",
215
+ "meetings",
216
+ "set-company",
217
+ "meeting-123456",
218
+ "--company",
219
+ "acme",
220
+ ]);
221
+
222
+ const output = logSpy.mock.calls.map(([line]) => String(line)).join("\n");
223
+ expect(output).not.toContain("Applied to 1 meetings");
224
+ expect(output).not.toContain("Refiled 0 transcripts");
225
+ expect(output).not.toContain("Future occurrences of this recurring series will inherit this attribution.");
226
+ });
163
227
  });
@@ -341,6 +341,9 @@ export function registerMeetingsCommand(program: Command): void {
341
341
  if (!res.ok) await handleApiError(res);
342
342
  const data = (await res.json()) as {
343
343
  appliedToSeries?: boolean;
344
+ occurrencesUpdated?: number;
345
+ refiled?: boolean;
346
+ refiledCount?: number;
344
347
  [key: string]: unknown;
345
348
  };
346
349
 
@@ -351,7 +354,14 @@ export function registerMeetingsCommand(program: Command): void {
351
354
 
352
355
  const where = companyId === "unknown" ? "unattributed (personal)" : companyId;
353
356
  console.log(chalk.green(`\n✓ Meeting ${chalk.cyan(meetingId)} attributed to ${chalk.bold(where)}.`));
354
- if (data && data.appliedToSeries) console.log(chalk.dim(" Future occurrences of this recurring series will inherit this attribution."));
357
+ if (typeof data.occurrencesUpdated === "number" && data.occurrencesUpdated > 1) {
358
+ console.log(chalk.dim(` Applied to ${data.occurrencesUpdated} meetings in this recurring series (including past occurrences).`));
359
+ } else if (data.appliedToSeries) {
360
+ console.log(chalk.dim(" Future occurrences of this recurring series will inherit this attribution."));
361
+ }
362
+ if (typeof data.refiledCount === "number" && data.refiledCount > 0) {
363
+ console.log(chalk.dim(` Refiled ${data.refiledCount} transcript${data.refiledCount === 1 ? "" : "s"} into the new company vault.`));
364
+ }
355
365
  console.log();
356
366
  } catch (err) {
357
367
  console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
package/src/index.ts CHANGED
@@ -218,6 +218,8 @@ registerRescueCommand(program);
218
218
  Sentry.captureException(err);
219
219
  process.exitCode = 1;
220
220
  } finally {
221
+ // Release health: finalize the per-run session before the flush.
222
+ Sentry.endSession();
221
223
  await Promise.allSettled([refreshVersionCache(), Sentry.flush(2000)]);
222
224
  }
223
225
  })();
package/src/sentry.ts CHANGED
@@ -3,6 +3,7 @@ import { BUNDLED_DSN } from "./sentry-dsn.generated.js";
3
3
  import { beforeSend } from "./sentry-before-send.js";
4
4
  import { beforeBreadcrumb } from "./utils/breadcrumb-buffer.js";
5
5
  import { CLI_VERSION } from "./cli-version.js";
6
+ import { getCachedSentryUser } from "./utils/sentry-identity.js";
6
7
 
7
8
  export function initSentry(): void {
8
9
  const dsn = BUNDLED_DSN || process.env.SENTRY_DSN;
@@ -20,6 +21,14 @@ export function initSentry(): void {
20
21
  beforeSend,
21
22
  beforeBreadcrumb,
22
23
  });
24
+ // Attribute events to the logged-in HQ identity (best-effort; null when not
25
+ // logged in). A CLI process is one user, so global setUser is correct here.
26
+ const user = getCachedSentryUser();
27
+ if (user) Sentry.setUser(user);
28
+ // Release health: one session per CLI run. Ended + flushed in index.ts's
29
+ // finally (covers every exit path). A captured error auto-marks the session
30
+ // "errored", so crash-free numbers per release stay accurate.
31
+ Sentry.startSession();
23
32
  }
24
33
 
25
34
  export { Sentry };
@@ -0,0 +1,45 @@
1
+ import {
2
+ loadCachedTokens,
3
+ isMachineIdentity,
4
+ loadMachineCreds,
5
+ } from "@indigoai-us/hq-cloud";
6
+
7
+ /** Decode the (untrusted) id-token payload — for attribution/display only. */
8
+ function peekIdToken(idToken: string): { email?: string; sub?: string } {
9
+ try {
10
+ const payload = idToken.split(".")[1];
11
+ if (!payload) return {};
12
+ const pad =
13
+ payload.length % 4 === 0 ? "" : "=".repeat(4 - (payload.length % 4));
14
+ const normalized = payload.replace(/-/g, "+").replace(/_/g, "/") + pad;
15
+ const decoded = JSON.parse(
16
+ Buffer.from(normalized, "base64").toString("utf-8"),
17
+ ) as { email?: string; sub?: string };
18
+ return { email: decoded.email, sub: decoded.sub };
19
+ } catch {
20
+ return {};
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Best-effort Sentry user from the cached HQ session, so CLI events are
26
+ * attributed to a user ("users affected" + per-user triage). Returns null when
27
+ * not logged in. A CLI process is single-user, so this is set once at init.
28
+ */
29
+ export function getCachedSentryUser():
30
+ | { id?: string; email?: string; username?: string }
31
+ | null {
32
+ try {
33
+ if (isMachineIdentity()) {
34
+ const username = loadMachineCreds()?.username;
35
+ return username ? { id: username, username } : null;
36
+ }
37
+ const cached = loadCachedTokens();
38
+ if (!cached) return null;
39
+ const { email, sub } = peekIdToken(cached.idToken);
40
+ if (!sub && !email) return null;
41
+ return { id: sub, email };
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ include: ["e2e/**/*.test.ts"],
6
+ },
7
+ });