From a84746339a60bbe8cae5457120df033ee2e1a14d Mon Sep 17 00:00:00 2001 From: Brandon Corbett Date: Wed, 11 Nov 2026 08:12:36 -0500 Subject: [PATCH] fix(verify): explain a missing API checkout instead of naming the npx cache Run from a scaffolded project through npx, verify guessed the API source as a sibling of the CLI's install folder and failed with a path inside the npm cache. The error now says verify is a contributor tool, where to clone the API, and to run seamless check for a generated project. From an npm install it skips the sibling guess unless SEAMLESS_API_DIR is set, and verify --help leads with the same pointer. Closes #367 --- .changeset/verify-contributor-guidance.md | 5 +++++ src/commands/helpTopics.ts | 5 ++++- src/commands/verify.test.ts | 26 ++++++++++++++++++++++- src/commands/verify.ts | 23 ++++++++++++++------ 4 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 .changeset/verify-contributor-guidance.md diff --git a/.changeset/verify-contributor-guidance.md b/.changeset/verify-contributor-guidance.md new file mode 100644 index 0000000..4375e28 --- /dev/null +++ b/.changeset/verify-contributor-guidance.md @@ -0,0 +1,5 @@ +--- +"seamless-cli": patch +--- + +`seamless verify` run without a seamless-auth-api checkout now says it is a contributor tool, where to clone the API, and that `seamless check` is the command for a scaffolded project. From an npx or global install it no longer prints a path inside the npm cache, and `seamless verify --help` says the same up front. diff --git a/src/commands/helpTopics.ts b/src/commands/helpTopics.ts index 04a4af4..e3c08bd 100644 --- a/src/commands/helpTopics.ts +++ b/src/commands/helpTopics.ts @@ -255,7 +255,10 @@ The code replaces any code sent before and expires in 5 minutes. sections: [ { heading: "verify [--local] [--api-only] [--dev] [--filter=] [--keep-up]", - body: `Stand up the auth stack and run the conformance suite across the API and + body: `For Seamless contributors: to check a project made by init, use +seamless check instead. + +Stand up the auth stack and run the conformance suite across the API and the cookie (adapter) paths. Requires Docker. Builds the auth server from a sibling seamless-auth-api checkout (override with SEAMLESS_API_DIR). diff --git a/src/commands/verify.test.ts b/src/commands/verify.test.ts index 0a03246..697c683 100644 --- a/src/commands/verify.test.ts +++ b/src/commands/verify.test.ts @@ -4,7 +4,7 @@ import { execSync } from "child_process"; import fs from "fs"; import { runCommand } from "../core/exec.js"; -import { runVerify } from "./verify.js"; +import { resolveApiDir, runVerify } from "./verify.js"; // Every browser profile, as a clean, a log dump, or a teardown passes them. const ALL_PROFILES = [ @@ -694,6 +694,30 @@ describe("runVerify: setup failures (thrown before the run)", () => { await expect(runVerify([])).rejects.toThrow(/seamless-auth-api/); }); + it("says what verify is for and points at check when the source is missing", async () => { + vi.mocked(fs.existsSync).mockImplementation((p: never) => !String(p).includes("/fake/api")); + await expect(runVerify([])).rejects.toThrow(/contributors[\s\S]*seamless check/); + }); + + it("names no npx cache path when run from an npm install", () => { + delete process.env.SEAMLESS_API_DIR; + const npxRoot = "/home/me/.npm/_npx/abc123/node_modules/seamless-cli"; + expect(() => resolveApiDir(npxRoot)).toThrow(/^No seamless-auth-api source to build/); + expect(() => resolveApiDir(npxRoot)).not.toThrow(/_npx/); + expect(fs.existsSync).not.toHaveBeenCalled(); + }); + + it("uses SEAMLESS_API_DIR even from an npm install", () => { + expect(resolveApiDir("/home/me/.npm/_npx/abc123/node_modules/seamless-cli")).toBe( + "/fake/api", + ); + }); + + it("defaults to a sibling checkout for a linked CLI", () => { + delete process.env.SEAMLESS_API_DIR; + expect(resolveApiDir("/home/me/git/seamless-cli")).toBe("/home/me/git/seamless-auth-api"); + }); + it("aborts when the templates registry is missing", async () => { vi.mocked(fs.existsSync).mockImplementation((p: never) => !String(p).endsWith("registry.json")); await expect(runVerify([])).rejects.toThrow(/templates registry/); diff --git a/src/commands/verify.ts b/src/commands/verify.ts index 62fa517..17ff3a2 100644 --- a/src/commands/verify.ts +++ b/src/commands/verify.ts @@ -59,15 +59,26 @@ function ensureDocker(): void { } } +const API_SOURCE_HELP = + " `seamless verify` is the conformance suite for Seamless contributors. It builds\n" + + " the auth server from a clone of https://github.com/fells-code/seamless-auth-api:\n" + + " clone it and set SEAMLESS_API_DIR to that folder.\n" + + " To check a project made by `seamless init`, run: seamless check"; + // The auth API is built from local source. Defaults to a sibling checkout so a -// linked CLI works without extra config; override with SEAMLESS_API_DIR. -function resolveApiDir(): string { - const candidate = - process.env.SEAMLESS_API_DIR ?? path.resolve(REPO_ROOT, "..", "seamless-auth-api"); +// linked CLI works without extra config; override with SEAMLESS_API_DIR. An npm +// install (npx, global) has no sibling worth guessing: it would sit in the package +// cache, and naming that path only confuses someone who ran verify by mistake. +export function resolveApiDir(cliRoot: string = REPO_ROOT): string { + const fromEnv = process.env.SEAMLESS_API_DIR; + const installedFromNpm = cliRoot.split(path.sep).includes("node_modules"); + if (!fromEnv && installedFromNpm) { + throw new Error(`No seamless-auth-api source to build.\n${API_SOURCE_HELP}`); + } + const candidate = fromEnv ?? path.resolve(cliRoot, "..", "seamless-auth-api"); if (!fs.existsSync(path.join(candidate, "package.json"))) { throw new Error( - `Could not find the seamless-auth-api source at ${candidate}.\n` + - " Set SEAMLESS_API_DIR to its local checkout.", + `Could not find the seamless-auth-api source at ${candidate}.\n${API_SOURCE_HELP}`, ); } return candidate;