Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/conformance-repoint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"seamless-cli": patch
---

Point the conformance harness at the seamless-templates monorepo. `seamless verify` now resolves the React web template from `../seamless-templates/templates/web/react-vite` by default (still overridable with `SEAMLESS_REACT_DIR`), and the reusable `verify-conformance.yml` workflow checks out `seamless-templates` (input `templates-ref`) instead of the standalone starter repo.
5 changes: 5 additions & 0 deletions .changeset/registry-driven-templates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"seamless-cli": minor
---

Scaffold web and api starters from the seamless-templates registry instead of hardcoded per-framework generators. The CLI now reads the registry to build its prompts, downloads the selected templates from the templates monorepo at a pinned ref, and applies each template's env contract. Adding a new framework is a templates-repo change, not a CLI change. Set SEAMLESS_TEMPLATES_DIR to scaffold from a local checkout, or SEAMLESS_TEMPLATES_REF to pin a different ref.
12 changes: 6 additions & 6 deletions .github/workflows/verify-conformance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ on:
react-ref:
type: string
default: ''
starter-ref:
templates-ref:
type: string
default: ''

Expand Down Expand Up @@ -64,12 +64,12 @@ jobs:
ref: ${{ inputs.react-ref }}
path: seamless-auth-react

- name: Checkout seamless-auth-starter-react
- name: Checkout seamless-templates
uses: actions/checkout@v4
with:
repository: fells-code/seamless-auth-starter-react
ref: ${{ inputs.starter-ref }}
path: seamless-auth-starter-react
repository: fells-code/seamless-templates
ref: ${{ inputs.templates-ref }}
path: seamless-templates

- uses: actions/setup-node@v4
with:
Expand Down Expand Up @@ -107,7 +107,7 @@ jobs:
SEAMLESS_API_DIR: ${{ github.workspace }}/seamless-auth-api
SEAMLESS_SERVER_DIR: ${{ github.workspace }}/seamless-auth-server
SEAMLESS_REACT_SDK_DIR: ${{ github.workspace }}/seamless-auth-react
SEAMLESS_REACT_DIR: ${{ github.workspace }}/seamless-auth-starter-react
SEAMLESS_REACT_DIR: ${{ github.workspace }}/seamless-templates/templates/web/react-vite
run: node dist/index.js verify ${{ inputs.local && '--local' || '' }}

- name: Upload conformance report
Expand Down
22 changes: 15 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,14 @@ The entry point is [src/index.ts](src/index.ts), which dispatches to a command m

## Commands

- **init** ([src/commands/init.ts](src/commands/init.ts)) scaffolds a project from the generators in
`src/generators/*` (frontend, backend, auth, docker, config), driven by `src/prompts/`.
- **init** ([src/commands/init.ts](src/commands/init.ts)) scaffolds a project, driven by
`src/prompts/`. The web and api starters come from the registry-driven template source
([src/core/templates.ts](src/core/templates.ts)): it reads `registry.json` from the
`fells-code/seamless-templates` monorepo (pinned by `SEAMLESS_TEMPLATES_REF` in
[src/core/images.ts](src/core/images.ts)), downloads the selected templates, and applies each
template's `template.json` env contract. The auth, docker, and config pieces are still generated
locally in `src/generators/*`. Override the template source for development with
`SEAMLESS_TEMPLATES_DIR` (a local checkout) or `SEAMLESS_TEMPLATES_REF` (a different ref).
- **check** health-checks a running stack.
- **bootstrap-admin** mints the first admin invite.
- **verify** ([src/commands/verify.ts](src/commands/verify.ts)) runs the conformance harness (below).
Expand All @@ -49,14 +55,14 @@ Modes and sibling repos:
default uses the published packages.
- The sibling repos are resolved relative to this repo, overridable with `SEAMLESS_API_DIR`,
`SEAMLESS_SERVER_DIR`, `SEAMLESS_REACT_SDK_DIR` (the React SDK), and `SEAMLESS_REACT_DIR` (the
starter app).
`react-vite` web template, defaulting to `../seamless-templates/templates/web/react-vite`).
- Useful flags: `--api-only`, `--no-react`, `--filter <grep>`, `--keep-up`.

## Important Folders

- [src/commands](src/commands): one file per CLI command
- [src/generators](src/generators): project scaffolding (frontend, backend, auth, docker, config)
- [src/core](src/core): shared helpers (exec, env, fetch, secrets, paths, package manager, output)
- [src/generators](src/generators): locally generated scaffolding (auth, docker, config)
- [src/core](src/core): shared helpers (templates, exec, env, fetch, secrets, paths, package manager, output)
- [src/prompts](src/prompts): interactive setup prompts (`@clack/prompts`)
- [src/utils](src/utils): repo and env-file helpers
- [verify](verify): the conformance harness (shipped with the package)
Expand Down Expand Up @@ -84,7 +90,7 @@ Modes and sibling repos:
## Known Maintenance Traps

- **Sibling-repo branches**: the server's integration branch is `dev` (its `main` lags), so the verify
CI workflow defaults the server checkout to `dev`. The api, react, and starter use `main`.
CI workflow defaults the server checkout to `dev`. The api, react SDK, and seamless-templates use `main`.
- **`--local` needs SDK dependencies**: it builds the server (pnpm) and the React SDK (npm) from source
on the host, so those repos must have their dependencies installed first. CI installs them explicitly.
- **OAuth mock networking**: the in-process mock OIDC is reached by the browser and harness via
Expand All @@ -94,4 +100,6 @@ Modes and sibling repos:
limiter (10 per 15 minutes, hardcoded) bounds adapter / react OTP traffic. Keep specs off it where
possible (for example, magic-link login instead of a second email-OTP round trip).
- **Version pins**: [verify/adapter-app](verify/adapter-app) pins `@seamless-auth/express` and the
starter pins `@seamless-auth/react`. Bump these when new versions publish.
`react-vite` template pins `@seamless-auth/react`. Bump these when new versions publish.
- **Templates ref**: the CLI scaffolds from `seamless-templates` at `SEAMLESS_TEMPLATES_REF`
([src/core/images.ts](src/core/images.ts)); bump it when a new templates release publishes.
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,12 @@ Seamless CLI pulls from the following repositories:
- Seamless Auth API
[https://github.com/fells-code/seamless-auth-api](https://github.com/fells-code/seamless-auth-api)

- Seamless Auth React Starter
[https://github.com/fells-code/seamless-auth-starter-react](https://github.com/fells-code/seamless-auth-starter-react)
- Seamless Templates (the frontend and API starters)
[https://github.com/fells-code/seamless-templates](https://github.com/fells-code/seamless-templates)

- Seamless Auth API Starter
[https://github.com/fells-code/seamless-auth-starter-express](https://github.com/fells-code/seamless-auth-starter-express)

Each project can be used independently, but the CLI connects them into a working system.
The starters live in the templates monorepo and are listed in its registry, so the set of
frameworks the CLI offers grows there. Each project can be used independently, but the CLI connects
them into a working system.

---

Expand Down
68 changes: 49 additions & 19 deletions src/commands/init.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import path from "path";
import fs from "fs";

import { runProjectSetupPrompts } from "../prompts/projectSetup.js";
import { generateReactStarter } from "../generators/frontend/react.js";
import { generateExpressStarter } from "../generators/backend/express.js";
import { generateAuthServer } from "../generators/auth/auth.js";
import { configureApiEnv, configureWebEnv } from "../core/configure.js";
import { generateDockerCompose } from "../generators/docker/docker.js";
import { printSuccessOutput } from "../core/output.js";
import { generateSeamlessConfig } from "../generators/config/config.js";
import {
applyTemplateEnv,
assertCliSupports,
openTemplateSource,
type RegistryEntry,
type TemplateManifest,
} from "../core/templates.js";

const AUTH_SERVER_URL = "http://localhost:5312";
const API_URL = "http://localhost:3000";

export async function runCLI(projectName?: string) {
const cwd = process.cwd();
Expand Down Expand Up @@ -35,14 +43,30 @@ export async function runCLI(projectName?: string) {
return;
}

const answers = await runProjectSetupPrompts();

if (answers.web && answers.webFramework === "react") {
await generateReactStarter({ root });
}
const source = await openTemplateSource();
const answers = await runProjectSetupPrompts(source.registry.templates);

if (answers.api && answers.apiFramework === "express") {
await generateExpressStarter({ root });
const findEntry = (id: string): RegistryEntry => {
const entry = source.registry.templates.find((t) => t.id === id);
if (!entry) {
throw new Error(`Selected template "${id}" is not in the registry.`);
}
return entry;
};

// Resolve the chosen templates, then place their files. Env wiring waits until the
// shared auth config (tokens, key id) exists below.
const selected: { entry: RegistryEntry; manifest: TemplateManifest; dir: string }[] =
[];
for (const id of [answers.webTemplateId, answers.apiTemplateId]) {
const entry = findEntry(id);
const manifest = await source.readManifest(entry);
assertCliSupports(manifest, entry.label);
const dir = path.join(root, manifest.targetDir);

console.log(`Adding ${entry.label} starter...`);
await source.copyInto(entry, dir);
selected.push({ entry, manifest, dir });
}

let sharedConfig: any = {};
Expand All @@ -63,27 +87,33 @@ export async function runCLI(projectName?: string) {
}
}

if (answers.api) {
configureApiEnv(root, sharedConfig);
}
const ctx = {
authServerUrl: AUTH_SERVER_URL,
apiUrl: API_URL,
apiToken: sharedConfig.apiToken,
jwksKid: sharedConfig.kid,
};

if (answers.web) {
configureWebEnv(root);
for (const { manifest, dir } of selected) {
applyTemplateEnv(dir, manifest, ctx);
}

const webEntry = findEntry(answers.webTemplateId);
const apiEntry = findEntry(answers.apiTemplateId);

generateSeamlessConfig(root, {
projectName,
webFramework: answers.webFramework,
apiFramework: answers.apiFramework,
webFramework: webEntry.framework,
apiFramework: apiEntry.framework,
authMode: answers.authMode,
adminMode: answers.adminMode,
});

printSuccessOutput({
projectName,
root,
webFramework: answers.webFramework,
apiFramework: answers.apiFramework,
webFramework: webEntry.framework,
apiFramework: apiEntry.framework,
authMode: answers.authMode,
useDocker: answers.useDocker,
});
Expand Down
13 changes: 8 additions & 5 deletions src/commands/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,19 @@ function resolveApiDir(): string {
return candidate;
}

// The React starter app, served at :5173 and pointed at the adapter. Defaults to
// a sibling checkout; override with SEAMLESS_REACT_DIR. Only needed for browser runs.
// The React web template, served at :5173 and pointed at the adapter. Defaults to the
// react-vite template inside a sibling seamless-templates checkout; override with
// SEAMLESS_REACT_DIR. Only needed for browser runs.
// TODO(#1): resolve this from the registry so every web template is conformance-tested,
// not just react-vite.
function resolveReactDir(): string {
const candidate =
process.env.SEAMLESS_REACT_DIR ??
path.resolve(REPO_ROOT, "..", "seamless-auth-starter-react");
path.resolve(REPO_ROOT, "..", "seamless-templates", "templates", "web", "react-vite");
if (!fs.existsSync(path.join(candidate, "package.json"))) {
throw new Error(
`Could not find seamless-auth-starter-react at ${candidate}.\n` +
" Set SEAMLESS_REACT_DIR to its local checkout, or run with --no-react.",
`Could not find the react-vite web template at ${candidate}.\n` +
" Set SEAMLESS_REACT_DIR to a local template checkout, or run with --no-react.",
);
}
return candidate;
Expand Down
32 changes: 0 additions & 32 deletions src/core/configure.ts

This file was deleted.

7 changes: 7 additions & 0 deletions src/core/images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@ export const SEAMLESS_AUTH_API_IMAGE = `ghcr.io/fells-code/seamless-auth-api:${S
export const SEAMLESS_AUTH_ADMIN_DASHBOARD_VERSION = "v0.1.0";

export const SEAMLESS_AUTH_ADMIN_DASHBOARD_IMAGE = `ghcr.io/fells-code/seamless-auth-admin-dashboard:${SEAMLESS_AUTH_ADMIN_DASHBOARD_VERSION}`;

// The starter templates monorepo the CLI scaffolds from. Pinned to a tag so a given
// CLI version always produces the same project. Override the ref with
// SEAMLESS_TEMPLATES_REF, or point at a local checkout with SEAMLESS_TEMPLATES_DIR.
export const SEAMLESS_TEMPLATES_REPO = "fells-code/seamless-templates";

export const SEAMLESS_TEMPLATES_REF = "v0.1.1";
Loading
Loading