## Summary
In `UserDialog`, duplicate-email errors (`UserWithEmailAlreadyExists`,
`ContactChannelAlreadyUsedForAuthBySomeoneElse`) were shown via
`window.alert()`. Replaced with a controlled `DesignDialog` using
`WarningCircleIcon`, so the error appears as a styled modal instead of a
native browser popup.
Link to Devin session:
https://app.devin.ai/sessions/99d73091c47a4a58b33d8724df5a7ce8
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Replaced `window.alert()` in the user create/edit flow with a controlled
`DesignDialog` for duplicate-email errors (`UserWithEmailAlreadyExists`,
`ContactChannelAlreadyUsedForAuthBySomeoneElse`). Uses
`WarningCircleIcon` with an OK action, preserves form data via
`prevent-close-and-prevent-reset`, clears dialog state when the form
closes, and fixes a max-statements-per-line lint warning.
<sup>Written for commit 274fcb8e9b.
Summary will update on new commits.</sup>
<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1680?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
---------
Co-authored-by: vedanta.gawande <vedanta.gawande@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Armaan Jain <84474476+Developing-Gamer@users.noreply.github.com>
- Refactored the onboarding logic to differentiate between local emulator and development environments for OAuth provider configuration.
- Introduced a new mock for public environment variables in tests to enhance test coverage.
- Added a test case to ensure the onboarding process correctly waits for user actions before applying configuration updates.
These changes improve the onboarding experience and ensure proper handling of OAuth settings based on the environment.
## Summary
Two changes to the user data export dialog on the project Users page:
1. The export scope now defaults to **"Export only filtered/searched
users"** instead of "all users".
2. The all-users option label is now **"Export all users in the project
(includes Anonymous)"**.
To keep this scoped to the Users table (the shared export dialog is
reused by other tables), the dialog's default scope is made configurable
rather than changed globally:
- `DataGridExportOptions` gains `defaultScope?: DataGridExportScope`
(defaults to `"all"`).
- The dialog initializes `useState(exportOptions?.defaultScope ??
"all")`.
- `user-table.tsx` passes `defaultScope: "filtered"` and the updated
`allScopeLabel`.
Other tables (teams, transactions, emails) are unaffected — they keep
the `"all"` default.
Link to Devin session:
https://app.devin.ai/sessions/4996678b2b944090b6eef2f64f0a62a1
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Default the Users export dialog to filtered scope and clarify that the
"all users" option includes Anonymous; scope resets to the per-table
default only when the dialog reopens, and other tables keep "all".
- **New Features**
- Added `defaultScope` to export options and initialized scope from it;
Users table sets `defaultScope: "filtered"` and updates the all-users
label.
- Reset scope to `defaultScope` only on a closed→open transition to
avoid changing it while the dialog is open.
- **Bug Fixes**
- Stubbed `NODE_ENV` via `vi.stubEnv` in
`apps/backend/src/oauth/ssrf-protection.test.ts` to fix lint and prevent
env mutation.
<sup>Written for commit 3aa670b6d2.
Summary will update on new commits.</sup>
<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1679?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
---------
Co-authored-by: vedanta.gawande <vedanta.gawande@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- config-eval test: assert a malformed file throws a loader error, NOT a
ConfigFileEvalError, so the documented "Failed to load config file" routing
is actually protected (cubic)
- progress-content: guard useElapsedSeconds against the startedAt=0 "not started"
sentinel so the counter shows 0 instead of ~epoch-since-1970 on first paint (vercel)
- config index: clear a cancelled run's captured diff so it can't linger in the
API shape or be replayed by the commit route (greptile observation)
- index/commit route: gate commit_hash advance on committedRef identity so a
mid-run repo re-link can't stamp a foreign commit SHA (cross-repo TOCTOU)
- github-push-dialog: cancel handler now settles the dialog itself instead of
relying on a poll loop that has already exited at awaiting_review
- progress-content: useElapsedSeconds reacts to startedAt changes (fresh anchor)
so a post-mount start time no longer freezes a stale offset
- schema-fields: configAgentRunSchema.id uses .uuid() to match the @db.Uuid column
- tests: cover the SyntaxError config-eval path and the re-link commit-hash case
- Added a new `ConfigAgentRun` model to track the state of configuration agent runs in the database.
- Updated the Prisma schema to include new fields for the `ConfigAgentRun` model, allowing for detailed tracking of run status, timestamps, and associated metadata.
- Introduced new API routes for starting, cancelling, and committing configuration agent runs, improving user interaction and feedback during updates.
- Updated existing routes to utilize the new `run_id` for better tracking and management of agent runs.
- Added a new dependency `diff` to facilitate change tracking in configuration files.
These changes aim to improve the overall functionality and user experience of the configuration agent integration with GitHub.
- Removed outdated comments from `.env.development`, `.eslintrc.cjs`, and `schema.prisma` for clarity.
- Cleaned up import statements in `local-emulator.ts` and `repo-agent.ts` to improve code organization.
- Adjusted import order in `ssrf-protection.ts` and `cli.test.ts` for consistency.
- Updated `init.ts` to streamline imports and enhance readability.
- Minor adjustments in `admin-interface.ts` and `schema-fields.ts` to maintain code quality.
These changes aim to enhance maintainability and readability across the codebase.
- Added support for a new shared backend package in the pnpm workspace.
- Updated the Prisma schema to include a new field for tracking the latest config agent run state.
- Refactored config agent scripts for improved clarity and functionality, including renaming the build image script.
- Removed obsolete scripts related to linking projects to GitHub and seeding config tests.
- Introduced a new API route to retrieve the state of the most recent config agent run, enhancing user feedback during updates.
Co-Authored-By: mantra <mantra@stack-auth.com>
## Problem
In **preview mode**, clicking the user profile → **Account settings**
logs the user out and strands them on a sign-in page they can't get
past.
Root cause is a combination of three things:
- Preview mode uses an **in-memory** token store (`tokenStore:
"memory"`), so any full page reload wipes the session.
- The account-settings menu item calls
`app.redirectToAccountSettings()`, whose `redirectTo*` helpers fall
through to `window.location.assign` on the client — a **hard reload**.
- The `/handler/account-settings` route lives **outside** the
`(protected)` layout that performs the preview auto-login, so nothing
re-mints the preview session after the reload.
Net effect: hard nav → in-memory session wiped → `useUser({ or:
'redirect' })` finds no user → redirect to sign-in, with no way back in
(preview credentials are a throwaway UUID).
In normal (cookie) mode this is harmless because the cookie survives the
reload — the bug is preview-specific.
## Fix
Soft-navigate from the menu item using `app.useNavigate()` (a
`router.push` wrapper under the `"nextjs"` redirect method) instead of
`redirectToAccountSettings()`. A soft client-side navigation does not
re-evaluate the JS bundle, so the module-singleton app instance and its
in-memory token store stay alive.
The fix is scoped to the single dashboard call site rather than the
shared SDK `redirectToAccountSettings()` method, which ships to all
customers and intentionally hard-navigates for cross-domain / sign-out
flows.
## Note / follow-up
This covers navigation **from within the app**, which is the only in-app
entry point to account settings. It does **not** cover a hard refresh or
a direct link to `/handler/account-settings` in preview mode — those
still wipe the in-memory session because the handler route has no
auto-login. A fuller root-cause fix would bring the preview auto-login
(or the `(protected)` layout) to the handler route.
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes preview-mode logout when opening Account settings by switching the
dashboard menu to a soft client-side nav using `app.useNavigate()`
instead of a hard reload. This preserves the in-memory session.
- **Bug Fixes**
- Soft-navigate to `/handler/account-settings` via `app.useNavigate()`
to avoid wiping the preview token.
- Change scoped to the dashboard menu; `redirectToAccountSettings()` in
the SDK remains unchanged.
<sup>Written for commit 0e0c3a1bab.
Summary will update on new commits.</sup>
<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1670?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* The “Account settings” menu now opens with smooth in-app navigation
instead of a full page reload, making the experience faster and more
seamless.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
- Introduced a new API route for committing changes after user review, allowing the agent to keep the sandbox alive for inspection before finalizing updates.
- Enhanced the existing applyConfigUpdate function to transition to an awaiting review state, storing the diff for user visibility.
- Added progress tracking and stage reporting for the config agent run, improving user feedback during the update process.
- Updated the dashboard to reflect the new review stages and provide a more interactive experience for managing configuration changes.
Co-Authored-By: mantra <mantra@stack-auth.com>
P0: Strip OAuth token from git origin after clone so LLM agent
never sees credentials (repo-agent.tsx)
P1: Replace raw error.message with safe hardcoded text in API
response and dashboard UI (apply/route.tsx, config-update.tsx)
P1: E2E spike script now requires explicit env vars instead of
falling back to pushing to main (spike-orchestrator-e2e.mts)
P2: Use urlSchema for commit_url (schema-fields.ts)
P2: Return commitSha directly instead of parsing from URL
(repo-agent.tsx, apply/route.tsx)
P2: Support LINK_BRANCH_ID env var (link-project-to-github.ts)
P2: Widen structural fallback regex (config-updater.ts)
P2: Log warning when cancel has no sandboxId (cancel/route.tsx)
P2: Reject arbitrary string config values (config-eval.ts)
Co-Authored-By: mantra <mantra@stack-auth.com>
## Summary
Two related payments/dashboard changes:
1. **Customer page rework** (`/projects/<id>/payments/customers`) —
replaces the old single-customer selector page with **one unified
table** of users, teams, and custom customers, with filtering and
search. Clicking a row opens a customer view that renders the **same
data as the payments tab** on the user/team detail pages, plus a button
to return to the list.
2. **"Create checkout" everywhere** — the existing checkout dialog is
now reachable from every relevant surface (user table, team table,
user/team detail payments tabs, product detail page, product-lines
cards, and the customer detail view), all driven by **one shared
dialog**.
> ℹ️ Screenshots are from a local dev environment (test-mode banner +
dev-tools rail are dev-only chrome).
---
## 1. Customers page rework
A single `DataGrid` lists users + teams + custom customers (custom ones
are derived from transactions, since they aren't otherwise enumerable).
It uses the existing table/filter components: a **Type** filter (All /
Users / Teams / Custom) and quick-search.

Filtering by type — e.g. **Custom** customers surfaced from transaction
history:

Clicking a row opens a **read-only customer view** that is identical to
the payments tab on the user/team detail pages (metrics, products &
subscriptions, transaction history, item balances), with a **"Back to
customers"** button and a **Create checkout** button:

To guarantee the view is identical, the user/team payments tabs and this
page now share one generic `CustomerPaymentsSection` component keyed by
`(customerType, customerId)`.
---
## 2. Create checkout everywhere (one shared dialog)
The single `CreateCheckoutDialog` now supports:
- **Customer pre-selected** → just choose a product (tables, detail
tabs, customer detail view):

- **Locked product + customer selector** → launched from a product, the
product is fixed and you pick the customer:

The customer selector reuses the existing searchable user/team tables
(nested dialog):

Resulting checkout URL:

### Entry points
**Product detail page** — in the ellipsis menu:

**Product-lines** product-card menu:

**User table** row action (team table is analogous):

**User detail payments tab** (and **team detail payments tab**) get a
Create-checkout button in the header:


---
## Implementation notes
- **New SDK method**: `adminApp.createCheckoutUrl({ userId | teamId |
customCustomerId, productId, returnUrl? })` added to the
`@hexclave/template` **server** app (interface + impl). This is what
enables checkout for **custom** customers, which previously had no
checkout path (the old dialog only called
`customer.createCheckoutUrl(...)` on a `ServerUser`/`Team` object).
Regenerate SDKs after pulling (`pnpm -w run generate-sdks`).
- It lives on the server app (not client) deliberately: it targets an
*arbitrary* customer, which is a server/admin capability. The backend
route enforces this — for `client` auth it calls
`ensureClientCanAccessCustomer(...)` ("clients can only create purchase
URLs for their own user or teams they admin"). The client's safe, scoped
path is the existing customer-object method `user.createCheckoutUrl()` /
`team.createCheckoutUrl()`. The new method sits alongside
`grantProduct`/`createItemQuantityChange`/`getItem`, which take the same
`{ userId | teamId | customCustomerId }` shape.
- **New shared components** under
`apps/dashboard/src/components/payments/`:
- `customer-selector.tsx` — `CustomerSelector`, `CustomerTypeSelect`,
`SelectedCustomer`, `customerToMutationOptions` (extracted from the old
customers page).
- `customer-payments-section.tsx` — generic payments view;
`user-payments.tsx` / `team-payments.tsx` are now thin wrappers over it.
- `CreateCheckoutDialog` reworked to take a unified `customer`
descriptor and the new selector / locked-product props.
## Testing
- `pnpm typecheck` and `pnpm lint` pass.
- Manually verified end-to-end against a seeded local project (test
mode): generated real checkout URLs for a **custom** customer and a
**team**, exercised every entry point above, the type filter
(All/Users/Teams/Custom), search, row → detail → back, and the error
states (e.g. "product already granted", "no products for this customer
type").
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added a unified Customers browser for payments with filter, quick
search, infinite scrolling, and a customer detail view.
* Enabled “Create checkout” directly from product and customer-related
screens (including list/dropdown actions).
* Added streamlined “customer payments” views (metrics, transactions,
product/subscription info, and item balances where available).
* Introduced a flexible customer picker to support user, team, and
custom customers in checkout flows.
* **Bug Fixes**
* Improved checkout validation, dialog open/close behavior, and
empty-state handling when no customer or products are available.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
- Added a script for building a shared config-agent base snapshot, optimizing the warm-boot process for configuration updates.
- Introduced a new script to link existing projects to GitHub repositories without re-seeding, improving workflow efficiency.
- Updated the workflow paths and configuration file names to align with the new Hexclave structure.
- Refactored existing scripts to ensure consistency in configuration paths and enhance overall integration with GitHub.
Co-Authored-By: mantra <mantra@stack-auth.com>
- Added a new script for seeding a local dashboard project linked to a GitHub repository, facilitating end-to-end testing of the config-agent flow.
- Introduced new API routes for preparing and applying configuration updates via the GitHub repo agent, improving the workflow for managing config changes.
- Updated the command hook in settings to provide clearer instructions on handling typecheck and lint failures.
- Refactored the config update logic to ensure seamless integration with the new agent routes.
Co-Authored-By: mantra <mantra@stack-auth.com>
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
Move Node.js-only functions (evalConfigFileContent, tryEvalConfigFileContent,
detectImportPackageFromDir) to new config-eval.ts. This prevents the dashboard
browser build from failing on fs/path/jiti imports.
Dashboard now uses parseStaticConfigLiteral (regex+JSON.parse) instead of
jiti eval for untrusted GitHub-fetched config content, avoiding RCE risk.
Remove type casts in favor of isRecord type guard.
Co-Authored-By: mantra <mantra@stack-auth.com>
Replace parseHexclaveConfigFileContent and evaluateStaticConfigExpression
with jiti-based evalConfigFileContent. Move renderConfigFileContent from
hexclave-config-file.ts to config-rendering.ts alongside the new eval
function.
Removed functions:
- parseHexclaveConfigFileContent (Babel AST walker)
- tryParseHexclaveConfigFileContent
- evaluateStaticConfigExpression
- unwrapStaticConfigExpression
Added jiti dep to @hexclave/shared since config-rendering.ts now uses
jiti.evalModule for runtime evaluation of config file content strings.
Co-Authored-By: mantra <mantra@stack-auth.com>