The GHCR Docker build was failing on `npm ci` because package-lock.json
was out of sync with package.json (the lock was generated with npm 11 /
Node 24, while the build image node:23-slim ships npm 10.9, which rejects
the inconsistent optional-dependency entries for esbuild/webpack/picomatch).
Regenerating the lock with npm 10.9 unmasked two further pre-existing
build failures, both caused by `next build` running under the Dockerfile's
NODE_ENV=production (which makes `npm ci` omit devDependencies):
- @types/imap and @types/mailparser were in devDependencies, so they were
absent during the production build and the type-check failed on the
mailparser/imap-simple imports. Moved them to dependencies, matching the
existing convention already used for @types/mime-types and @types/sharp.
- vitest.config.ts and the *.test.ts files import the vitest devDependency,
which next build type-checked and could not resolve. Excluded them from
tsconfig so the production build does not type-check test files.
Verified by building the image end-to-end (linux/amd64) with node:23-slim:
npm ci, npm run build, and image export all succeed.
* feat: initial email impl
* feat: IMAP email ingest (builds on the scaffold) (#100)
* chore: add imap-simple, mailparser, vitest
* feat: AES-256-GCM helpers for email credentials
* feat: extract ingestUnsortedFile helper, reuse in upload action
* chore: gitignore .worktrees/
* feat: email-sync types and pure attachment/search filters
* feat: imap-simple + mailparser client wrapper
* feat: email sync orchestration with UID watermark + status persistence
* feat: encrypt email credentials at rest, add UID/addedAt fields
* feat: real IMAP test-connection, scoped sync-now, thin cron entry
* docs: update email app README to match real IMAP/encryption/UID behavior
* fix: nest SINCE search criteria and guard missing addedAt for first-run sync
* fix: show last-sync time and error detail from sync in server card
* fix: skip storage recompute when no attachments ingested
Avoids an ENOENT crash on first sync when the user's uploads dir does not exist yet and nothing was ingested; this was also masking the real per-server error. Adds regression tests for the guard.
* feat: configurable initial-grab window (fetch-since date)
First sync is bounded by a user-chosen 'Fetch emails since' date instead of the server's addedAt; blank = entire mailbox (IMAP ALL). The UID watermark takes over after the first run.
* fix: add missing @langchain/core dependency
@langchain/core is only a peer dep of the @langchain/* packages and was not installed on a clean npm install, breaking the build (e.g. /unsorted via ai/analyze).
* fix: harden email sync — UID dedup guard, locked status write, graceful decrypt, scrypt memo
Addresses review findings: skip messages at/below the UID watermark (defends against the IMAP `n:*` re-fetch quirk); lock the app_data row with SELECT FOR UPDATE so concurrent cron/manual syncs can't clobber each other; return a friendly error when a stored password can't be decrypted (e.g. after BETTER_AUTH_SECRET rotation) and document the coupling; memoize the scrypt-derived key.
* feat: enforce per-server syncInterval on cron; skip non-Buffer attachments
The cron now honors each server's syncInterval (manual Sync Now bypasses the throttle), so the configured interval is no longer ignored. Attachments whose parsed content is not a Buffer are skipped instead of throwing on .length. Adds throttle regression tests.
* refactor: remove dead lastProcessedMessageId field; clarify cron throttle in README
lastProcessedMessageId was superseded by the lastProcessedUid watermark and never read; dropped from the type and form state. README now describes the per-server interval as an app-level throttle (manual Sync Now bypasses).
* feat(email): UI-selectable sync frequency + working cron heartbeat
Replace the per-server sync-interval number input with a dropdown of
presets (15m/30m/hourly/6h/12h/daily). Switch the stored unit from hours
to minutes and update the throttle accordingly.
Make the cron actually run: heartbeat now fires every 5 minutes as the
resolution floor while each mailbox's UI frequency gates real fetches.
Propagate env into cron jobs via /etc/cron.env (cron strips the
environment) and add BETTER_AUTH_SECRET to the email-sync service in the
dev/build compose files so stored passwords can be decrypted.
* fix(email): reset Add Server dialog to provider selection on close
Radix's onOpenChange only toggled isOpen, so closing the dialog via Esc,
overlay click, or the X left the step/selectedProvider state intact.
Reopening then jumped straight to the previous provider's config form
instead of the provider-selection screen. Route every close through
handleClose() to reset the step.
---------
Co-authored-by: Evgenii Burmakin <Freika@users.noreply.github.com>
* feat: add transaction deduplication with side-by-side comparison modal
* feat: implement three-way duplicate resolution logic
- Add Row-Level check (Merchant/Total/Currency/Date) to Transaction model.
- Implement side-by-side comparison modal with Older/Newer/Both options.
- Update AI and CSV workflows to intercept duplicates.
Closes#92
* fix: include currency code and fallback in transaction deduplication check
Closes#92
* refactor(transactions): separate deduplication logic from creation model
- Extracted duplicate checking into a dedicated `findDuplicateTransaction` method in `models/transactions.ts`
- Restored `createTransaction` to a pure, atomic database insertion to remove unexpected side effects
- Updated all server actions (createTransactionAction, saveInvoice, saveFile, saveTransactions) to orchestrate the duplication check before calling the creation model
Addresses reviewer feedback to separate concerns and maintain clean model methods.
Use UPLOAD_PATH as base and verify resolved path stays within it,
preventing arbitrary file deletion via malicious DB path values.
Co-authored-by: OpenClaw Agent <agent@openclaw.ai>
Support Ollama, LM Studio, vLLM, and any OpenAI-compatible API
via a configurable base URL. Reuses ChatOpenAI with custom baseURL,
no new dependencies. Local models use direct JSON parsing instead
of withStructuredOutput since many don't support function calling.
Co-authored-by: FasterOP <7832832+mmplisskin@useres.noreply.github.com>
Removes the generateId: false setting from Better Auth config that was causing UUID generation errors when creating OTP verification records.
According to Better Auth documentation, generateId: false should only be used for auto-increment ID schemas. For UUID-based schemas, Better Auth should generate UUIDs using crypto.randomUUID().
This fixes the 'Failed to send the code' error during OTP email verification.
- Add calcNetTotalPerCurrency function to calculate signed totals (income positive, expenses negative)
- Update transaction list footer to display both net total and turnover
- Use semantic HTML markup (<dl>, <dt>, <dd>) for better accessibility
- Add color coding: green for positive net, red for negative net
- Maintain turnover calculation for total transaction volume
Fixes issue where transaction totals did not respect transaction type (income/expense)
* fix: add IDs and ARIA labels to custom field forms
- Add id propagation to FormInput/FormTextarea based on name prop for proper label association
- Enhance FormSelect with hidden input for form submission, aria-labelledby for labeling, and controlled/uncontrolled state management
- Add aria-label attributes to inline inputs/selects/checkboxes in CrudTable settings editor
These changes improve accessibility for screen readers and ensure custom field forms are fully navigable via keyboard. Also critical for AI browsers like Comet that rely on semantic HTML/ARIA for form parsing and automation.
* fix: add aria-labels to CRUD table action buttons
- Add aria-label to edit buttons: 'Edit [item name]'
- Add aria-label to delete buttons: 'Delete [item name]'
- Add aria-label to save/cancel buttons in edit/add modes
- Add aria-label to 'Add New' button
Fixes unlabeled icon buttons that were inaccessible to screen readers and AI browsers.
* feat: add google provider
* fix: default for google model
* feat: multiple providers
* fix: defaults from env for login form
* fix: add mistral to env files
* chore: delete unused code
* chore: revert database url to original
* fix: render default value for api key from env on server
* fix: type errors during compilation
---------
Co-authored-by: Vasily Zubarev <me@vas3k.ru>