Adds an advanced checkbox on the GitHub issue provider that, when enabled,
drops the hardcoded `is:issue` filter in both backlog auto-import and
in-provider search. Lets users sync PRs (assigned, authored, or
review-requested) into their backlog with a query like
`state:open is:pull-request (assignee:@me OR review-requested:@me)`.
Default is off, existing behavior is unchanged.
* feat: add plugin data reinit api
* test(plugins): stub data init in bridge spec
* test(plugins): stub data init in counter bridge spec
---------
Co-authored-by: Aamer Akhter <aamer_akhter@users.noreply.bitbucket.org>
packages/vite-plugin/package.json was bumped to vite ^6.4.2 in
4b9bd23e56 but dependent plugin lockfiles still referenced ^6.0.0.
npm install during the e2e build brought them in sync and hoisted
vitest's nested esbuild binaries, dropping ~500 redundant entries.
Avoids the operations table entirely (sync_devices + users only) so it
returns in milliseconds even when operations grows past 1 GB. Useful when
the standard active-users command is too slow on large prod DBs.
encodeURIComponent leaves ( and ) intact per RFC 3986, but GitHub's
search API treats them as grouping operators that must be percent-encoded,
returning HTTP 422 on queries like "(author:@me OR assignee:@me)".
The in-repo provider was fixed in 960330dd56 but the fix didn't carry
over when GitHub moved to a plugin. Reapply via a small encodeGithubQuery
helper used at both call sites.
The "just 1ms over max clock drift boundary" test captured Date.now()
once in the test and again inside uploadOps. If the second sample
advanced by >=1ms, the op was within the drift window from the
service's perspective, clamping never fired, and toBeLessThan failed
with equal values.
Freeze time with vi.useFakeTimers/setSystemTime so both samples
match, and tighten the assertion to the exact clamped value.
The showUsage query ran two correlated subqueries over the full
operations table once per user, each calling pg_column_size(payload)
on every row, then sorted all users before LIMIT 20. On a 1.85 GB
operations table with 3.6k users this effectively never returned.
Aggregate per-user size and count in a single CTE pass, then join.
One sequential scan instead of scans × users.
* fix(issue): prevent crash from orphan issueProviderId (#7135)
The Jira image-headers effect in task-detail-panel subscribed to
selectIssueProviderById without an error handler, so a task with an
issueProviderId pointing at a deleted provider (e.g. after sync
convergence where taskIdsToUnlink didn't cover all local tasks)
propagated the selector throw to Zone.js as a crash dialog. Wrap the
inner selector observable in catchError that logs and falls back to
of(null); the downstream jiraCfg?.isEnabled guard handles the fallback.
Also drop IssueLog.log(issueProviderKey, issueProvider) from the
throwing variant of the selector: providers may carry credentials
(host, token, apiKey) and IssueLog history is exportable.
* fix(focus-mode): sync tray countdown with in-app timer during breaks
Tray title was rebuilt from a cached currentFocusSessionTime that only
refreshed when CURRENT_TASK_UPDATED fired. addTimeSpent is gated on an
active current task, so during focus-mode breaks or task-less focus
sessions the cache froze while the in-app timer kept ticking.
Add the tick action to taskChangeElectron$ so the cache refreshes every
second whenever the focus timer is running.
Fixes#7278
* fix(ci): restore GitHub Actions SHA pins undone by 0e9218bd68
Commit 0e9218bd68 silently reverted PR #7212 (github-actions-minor group
bump) along with its stated sync/client-id work. This restores the 15
workflow files to their pre-revert state.
Actions restored to newer pinned SHAs:
- actions/upload-artifact v7.0.0 -> v7.0.1
- step-security/harden-runner v2.16.1 -> v2.17.0
- softprops/action-gh-release v2.6.1 -> v3.0.0
- signpath/github-action-submit-signing-request v2.0 -> v2.1
- anthropics/claude-code-action v1.0.89 -> v1.0.93
- docker/build-push-action v7.0.0 -> v7.1.0
- easingthemes/ssh-deploy v5.1.1 -> v6.0.3
* fix: restore i18n, UI, and docs work undone by 0e9218bd68
Commit 0e9218bd68 silently reverted the following work alongside its stated
sync/client-id changes. Files where later master commits (fec7b25f23, etc.)
already re-applied the reverted work are intentionally left untouched.
Restored:
- #7232 docs/long-term-plans/location-based-reminders.md (513 lines)
- #7199 Romanian i18n phase 3 (ro.json + ro-md.json, ~1168 lines)
- #7049 Polish translation improvements
- #7143 planner component styling (4 scss files)
- #7211 add-task-bar preserve time estimate when typing title
- #7208 task.reducer roll-up estimates for added subtasks
- #6767 focus-mode pomodoro reset button
- #7205 plugin-dev github-issue-provider TOKEN description
- #7231 mobile-bottom-nav FAB fix
- a4fe03272 iOS keyboard accessory bar (global-theme + dialog-fullscreen-markdown)
- 309670db3 ShortSyntaxEffects undefined guard
- 667a7986f Dropbox PKCE auth comment/behavior
* fix(electron): restore electron + e2e work undone by 0e9218bd68
Commit 0e9218bd68 silently reverted the following electron/e2e work.
Files already re-fixed by later master commits are left as-is:
- e2e/tests/sync/supersync-archive-conflict.spec.ts (de33234976 + 191d129ff3)
Restored:
- e8a3e156eb fix(electron): Linux autostart IDB backing-store recovery
(re-adds electron/clear-stale-idb-locks.ts + start-app.ts wiring)
- 5ce78a5b63 fix(electron): macOS Cmd+Q / Dock > Quit hang
(setIsQuiting + before-quit delegate to close-handler)
- 46e0fa2d01 fix(sync): FILE_SYNC_LIST_FILES IPC contract
(electronAPI.d.ts + local-file-sync + preload + ipc-events)
- ea1ef16307 fix(android): session-only SAF permissions on OEM devices
- 8865dc0a50 test(e2e): supersync parallel-worker stampede guard
(SUPERSYNC_SERVER_HEALTHY env-var fallback + goto retry loop)
- af7c7687e2 test(e2e): block WS-triggered downloads in non-WS specs
- 265b44db5d test(e2e): premature waitForURL on daily-summary
(this is literally the fix the bad commit's message claimed to add)
Conflict resolutions:
- e2e/utils/supersync-helpers.ts: kept the refined getDoneTaskElement
checks from d64014d086 (later than c558bcab5e) while restoring the
goto retry loop from 8865dc0a50.
- electron/start-app.ts: unioned imports (setIsQuiting +
clearStaleLevelDbLocks from theirs, fs from ours).
* fix(sync): restore sync-core work undone by 0e9218bd68
Commit 0e9218bd68 silently reverted parts of several sync fixes. Most
sync-core reverts have already been re-addressed differently on master
by later commits (1f5184f6e7, 05cd875dd6, 09f5ced2c9, 7df43358ab,
d9158d6adb, 32dbc95ed9, 8c3b08e016, f89fe1ebc3) — those files are
intentionally left untouched to avoid reverting master's newer work.
This PR restores only the pieces that are genuinely still missing:
- e8a3e156eb fix(electron): IDB backing-store autoreload (in-app piece)
operation-log-hydrator.service.ts + .spec.ts (the electron/clear-
stale-idb-locks.ts piece was restored in the prior commit)
Plus three low-risk documentation/cleanup restorations:
- operation-sync.util.ts — add "Nextcloud" to isFileBasedProvider JSDoc
- dropbox.ts — restore improved _getRedirectUri JSDoc (667a7986fb)
- dialog-get-and-enter-auth-code.component.ts — restore isNativePlatform
comment explaining why manual code entry flow is used (667a7986fb)
- file-adapter.interface.ts — remove stray "// NEW" comment (46e0fa2d01)
Intentionally NOT restored (master's newer work covers or supersedes):
- sync-trigger.service.ts / sync.effects.ts (05cd875dd6)
- sync-wrapper.service.ts (1f5184f6e7)
- sync-errors.ts (1f5184f6e7 re-added LegacySyncFormatDetectedError)
- file-based-sync-adapter.service.ts + spec (1f5184f6e7 + d9158d6adb)
- file-based-sync.types.ts (1f5184f6e7)
- operation-log.const.ts (32dbc95ed9 bumped IDB_OPEN_RETRIES to 5)
- dialog-sync-initial-cfg.component.ts (f89fe1ebc3)
* Implement task parsing with sub-tasks
Added a new function to parse tasks with sub-tasks from a text input, improving task management capabilities. Updated the task addition logic to utilize this new parsing method.
Should solve #7183
* Enhance task parsing and user information
Key Changes Summary:
✅ Critical Fix#1 - 4-space indentation: Detects minimum indent and normalizes relative to it
✅ Critical Fix#2 - Mixed input: Plain-text lines no longer silently dropped; warning shown
✅ Critical Fix#3 - Deeply-nested items: Now flattened to sub-task level with user notification
✅ Minor - Sub-task dueDay: Documented that it inherits from parent (not passed independently)
✅ Suggestion - UI/UX: Updated placeholder text to show expected format with examples
* fix indentation calculation and missing isBullet: true
* fix(brain-dump-plugin): keep sub-tasks when plain-text lines interrupt
Previously, a plain-text line between a bullet and its indented sub-task
caused the sub-task to be silently dropped: the outer loop advanced past
the plain-text entry but the inner look-ahead had already exited at
indent 0, so the following indented bullet was never attached to the
parent and was rejected by the outer loop's else branch.
The sub-task scan now skips over plain-text lines while still breaking
at the next top-level bullet, so indented bullets attach to the previous
parent regardless of interleaved plain text. The plain-text line itself
is still emitted as its own top-level task by the outer loop.
Also drops dead code:
- findMinimumIndent() was declared but never called.
- dueDay on sub-task payload was forwarded but silently discarded by the
plugin bridge (sub-tasks inherit the parent's date).
And de-duplicates the "Note: ... Note: ..." prefix in the deeply-nested
warning snack.
---------
Co-authored-by: Adnoh <git@rotzefull.de>
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
* test(e2e): update Show/hide task panel button label
The TOGGLE_DETAIL_PANEL translation was renamed from "Show/Hide
additional info" to "Show/hide task panel" in dd789d2, but e2e
selectors still referenced the old string, causing every test that
opens the task detail panel to time out.
https://claude.ai/code/session_011mX4Pr24S42svmA5j7fRux
* 18.2.1
---------
Co-authored-by: Claude <noreply@anthropic.com>
- Separate JsonParseError and SyncDataCorruptedError handlers in sync wrapper
- Handle corrupted remote data gracefully instead of throwing
- Add getOrGenerateClientId() as unified entry point, eliminating dual injection
of ClientIdService + CLIENT_ID_PROVIDER in snapshot-upload, file-based-encryption,
and sync-hydration services
- Use crypto.getRandomValues() instead of Math.random() for client ID generation
- Warn user when stored client ID is invalid and must be regenerated
- Fix flaky e2e supersync tests (premature waitForURL match on daily-summary URL)
The super-sync-server pretest script only ran `prisma generate` but
didn't build the @sp/shared-schema workspace dependency. This caused
15 test suites (and 20+ individual tests) to fail with "Failed to
resolve entry for package @sp/shared-schema" because the dist/ output
didn't exist.
https://claude.ai/code/session_01EsRx9RENAEdsNUqMQZqjGN
Co-authored-by: Claude <noreply@anthropic.com>
* fix(sync): prevent magic link prefetch failures and improve auth resilience
- Make /magic-login prefetch-immune: GET now renders a confirmation page
instead of consuming the single-use token. A "Log In" button triggers
POST /api/login/magic-link/verify which does the actual verification.
This prevents email client link prefetchers (Outlook SafeLinks, Gmail)
from consuming magic link tokens before users click them.
- Increase rate limits on auth endpoints for shared-IP scenarios (e.g.
university NAT). Global: 100→500, register: 10→50, login: 5→30,
verify: 10→50, magic-login page: 10→50.
- Add trustProxy to Fastify so req.ip uses X-Forwarded-For behind
reverse proxies instead of collapsing all clients to one rate bucket.
- Clear stale SuperSync auth tokens after 3 consecutive AuthFailSPErrors
instead of never clearing them. Prevents persistent failure loops where
an invalid token keeps retrying indefinitely until app reinstall.
- Improve registration-to-login UX messaging to emphasize email
verification is required before login links will work.
https://claude.ai/code/session_01WkbLzvhguQwRDtk7ht38aY
* chore: update package-lock.json from npm install
https://claude.ai/code/session_01WkbLzvhguQwRDtk7ht38aY
* fix(sync): address review feedback for auth resilience
- Remove conflicting style.css link from magic-login page
- Change trustProxy from true to 1 (trust single hop only)
- Increase /login/magic-link rate limit from 30 to 50 per 15min
- Delete dead magic-login-redirect.js (replaced by magic-login-confirm.js)
- Reset auth failure counter on non-auth errors (truly consecutive)
- Add test for counter reset on non-auth error between auth errors
https://claude.ai/code/session_01WkbLzvhguQwRDtk7ht38aY
* fix(sync): increase passkey rate limits and fix CSP inline script violations
- Increase passkey endpoint rate limits to match magic-link equivalents (50/15min)
- Extract inline scripts from /reset-password and /recover-passkey pages
into external JS files to comply with CSP scriptSrc: ["'self'"]
- Remove dead safeJsonForScript helper (no longer needed)
- Increase /recover-passkey page rate limit to 50/15min for consistency
https://claude.ai/code/session_01WkbLzvhguQwRDtk7ht38aY
* fix(sync): update security tests for escapeHtml and fix recover/passkey rate limit
- Update server-security.spec.ts: tests now check for escapeHtml output
(<, ") instead of safeJsonForScript output (\u003c), matching
the new data-token attribute approach
- Increase /recover/passkey rate limit from 30 to 50 per 15min to match
/login/magic-link (both send email with a secret link)
https://claude.ai/code/session_01WkbLzvhguQwRDtk7ht38aY
* fix(sync): reset auth failure counter after successful download
Move the counter reset from the final InSync return to right after
downloadRemoteOps() succeeds. This ensures early returns (cancelled,
LWW pending, payload rejected) also break the consecutive-failure
chain, since a successful download confirms auth is working.
https://claude.ai/code/session_01WkbLzvhguQwRDtk7ht38aY
---------
Co-authored-by: Claude <noreply@anthropic.com>
When a full-state op (SYNC_IMPORT/BACKUP_IMPORT) arrived from another
client, mergeRemoteOpClocks reset the local clock to a "minimal" form
but only preserved the current client's counter from the incoming op's
clock. If the current client had issued ops (e.g. GLOBAL_CONFIG) not
reflected in the incoming full-state op's clock, its counter was dropped,
causing subsequent ops to reuse the same counter value. Downstream clients
then saw these ops as EQUAL (duplicate) and skipped them silently.
Fix: take max(mergedClock[clientId], currentClock[clientId]) when
rebuilding the clock after a full-state op reset.
Also add __SP_E2E_BLOCK_WS_DOWNLOAD flag to WsTriggeredDownloadService
to allow E2E tests to block automatic WS-triggered downloads during
concurrent conflict scenarios.
Fix archive conflict test by blocking WS downloads on Client A during
the concurrent edit phase so it doesn't auto-receive B's rename via
WebSocket before archiving (restoring the intended conflict scenario).
Fix LWW singleton test to assert convergence rather than specific winner.
Fix renameTask helper to avoid Playwright/Angular re-render races.
Fix shepherd.js import paths that broke the Angular dev server build.
Previously, `npx prisma@5.22.0 migrate deploy` in CMD downloaded prisma
from npm on every container start, making startup slow and dependent on
network availability. Now prisma is installed in the image at build time.
Also increases health check start_period from 10s to 30s to give
migrations time to complete before Docker starts counting failures.
* feat(sync): add Helm chart for SuperSync Kubernetes deployment
Includes Deployment, StatefulSet (PostgreSQL), Service, Ingress,
ConfigMap, Secret, HPA, PDB, NetworkPolicy, and test templates.
Supports both bundled PostgreSQL and external database configurations.
* feat(sync): add WebSocket push notifications for near-realtime sync
Server: Fastify WebSocket plugin with connection manager, app-level
heartbeat (30s), debounced notifications, and per-user routing.
Client: WebSocket service with exponential backoff reconnection,
WS-triggered download service, and reduced polling when connected.
* fix(sync): improve WebSocket error handling and reactivity
Make syncInterval$ reactive to WS connection state, fix Set/Map
mutation during heartbeat iteration, add error handling to WS route
handler, separate JSON parsing from message handling, detect auth
failures in WS-triggered downloads, and add logging to all empty
catch blocks.
* fix(sync): address PR review findings for WebSocket and Helm
Fix race condition in WS-triggered download pipeline by moving
isSyncInProgress filter after debounce and adding guard in
_downloadOps. Add logging to remaining empty catch blocks, fix
missing $NODE_IP in Helm NOTES.txt, and correct inaccurate
comments in values.yaml and ws-triggered-download.service.ts.
* fix(sync): add rate limiting to WebSocket upgrade endpoint
Limit WS connection attempts to 10 per minute per IP to prevent
connection flooding, matching the rate-limit pattern used by other
sync endpoints.
* fix(sync): extract shared constants, fix debounce correctness, replace deprecated toPromise
Extract CLIENT_ID_REGEX and MAX_CLIENT_ID_LENGTH into sync.const.ts
to prevent drift between sync.routes.ts and websocket.routes.ts.
Fix debounce in notifyNewOps to accumulate excluded client IDs across
rapid calls from different clients, preventing self-notifications.
Replace deprecated toPromise() with firstValueFrom in connectWebSocket
and _sync methods. Add .catch() to reconnect path and logging to
silent early returns in _downloadOps.
* fix(build): restore correct package-lock.json and fix upstream lint errors
* revert: restore upstream HTML formatting to match CI prettier config
* fix(sync): use DownloadOutcome discriminated union in WsTriggeredDownloadService
* fix(sync): narrow TokenVerificationResult before accessing userId
* fix(sync): await async getProviderById in connectWebSocket
Missing await caused the Promise object to be cast to
SuperSyncProvider, making getWebSocketParams undefined.
* feat(sync): add SEED_USERS env var to create verified users on startup
For self-hosted single-user setups: set SEED_USERS=email1,email2 to
create verified users on boot and log their access tokens. Skips
existing users. Removes need for SMTP/magic link registration.
Also fix Dockerfile to use npm install instead of npm ci for lockfile
compatibility.
* fix(sync): address PR review feedback for Helm chart and server
Security:
- Remove seed-users.ts (logged full JWT tokens to stdout)
- Add fail assertions for missing jwtSecret and postgresql.password
- Add smtp.user/smtp.password values fields
- Add from: selector to NetworkPolicy ingress
- Add egress rule for external database when postgresql.enabled=false
Correctness:
- Fix PostgreSQL StatefulSet indentation for non-persistent mode
- Fix NOTES.txt panic on empty tls list (use len instead of index)
- Restore npm ci in Dockerfile by using node:24-alpine (ships npm 11)
- Add Recreate deployment strategy when using RWO PVC
Operational:
- Add fail guard preventing HPA maxReplicas > 1 (in-memory WS state)
- Fix PDB to use maxUnavailable instead of minAvailable
- Add WebSocket ingress timeout annotation examples
- Add Prisma db push init container for schema migrations
- Default serviceAccount.automount to false
- Add Chart.yaml maintainers, home, sources metadata
* feat(sync): add ALLOWED_EMAILS env var to restrict registration
Supports fully qualified emails (user@example.com) and domain
wildcards (*@example.com). When unset, all emails are allowed.
Applied to all three registration endpoints (passkey options,
passkey verify, magic link).
* fix(sync): exempt health endpoint from rate limiting
Kubernetes liveness/readiness probes hit /health every 5-15s,
exhausting the global rate limit (100 req/15min) and causing
429 responses that trigger container restarts.
* fix(sync): harden WebSocket, Helm chart, and sync services from multi-agent review
- Pin prisma@5.22.0 in init container and use migrate deploy instead of db push
- Add per-user WebSocket connection limit (max 10) to prevent resource exhaustion
- Add replicaCount > 1 fail guard in deployment template (in-memory WS state)
- Set maxPayload: 1024 on WebSocket plugin (only pong messages expected)
- Remove premature IN_SYNC status from download-only WS-triggered sync
- Fix double removeConnection on error+close events (let close handle cleanup)
- DRY debounce logic in notifyNewOps and store latestSeq on pending entry
- Remove dead fromClientId from NewOpsNotification and WsMessage interfaces
- Add takeUntilDestroyed to _wsProviderCleanup subscription
- Simplify email-allowlist.ts to eager init (eliminate mutable state)
- Guard connectWebSocket at call site for non-SuperSync providers
- Add distinctUntilChanged to syncInterval$ to prevent unnecessary resets
- Add container-level securityContext to PostgreSQL StatefulSet
- Fix HPA maxReplicas default to 1 (matches single-replica constraint)
* fix(sync): restore migration in Dockerfile CMD for non-Helm Docker users
Helm users get migration via init container (runs first, CMD becomes no-op).
Docker-compose/plain Docker users get automatic migration back on startup.
* fix(boards): add missing drag delay for touch and extract shared signal
Add cdkDragStartDelay to board-panel drag items, which was missing
entirely. Extract the repeated `isTouchActive() ? DRAG_DELAY_FOR_TOUCH : 0`
expression into a shared `dragDelayForTouch` computed signal and refactor
all 8 components to use it.
* test(sync): add comprehensive WebSocket test coverage
Add unit tests for the new WebSocket real-time sync notification pipeline:
- WebSocketConnectionService (server): connection lifecycle, max-per-user
limits, notification debouncing, heartbeat, graceful shutdown (13 tests)
- WebSocket routes validation: token/clientId validation, close codes,
error handling, validation ordering (18 tests)
- SuperSyncWebSocketService (frontend): reconnection with exponential
backoff, heartbeat, disconnect cleanup, URL conversion (6 tests)
- WsTriggeredDownloadService: auth error handling, pipeline resilience,
start() idempotency (3 tests)
- SyncWrapperService: connectWebSocket guards for non-SuperSync, null
params, and already-connected cases (3 tests)
- E2E realtime push: verifies WS-triggered download between two clients
Quality fixes:
- Replace waitForTimeout with syncAndWait in E2E
- Add explanatory comment for microtask flushing in sync-wrapper spec
- Use getter for isSyncInProgress mock in ws-triggered-download spec
---------
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
- Thread syncImportReason field from operation creation through compact
codec, sync providers, server database, and back to receiving clients
- Constrain server Zod schema to z.enum() matching client SyncImportReason
type for defense-in-depth validation
- Use !== undefined in codec for consistency with entityId/entityIds
- Rename piggybackedImport/incomingSyncImport to piggybackedFullStateOp/
incomingFullStateOp for accuracy
Add a CalDAV Calendar plugin that syncs tasks with calendar events via
the CalDAV/WebDAV protocol, supporting self-hosted servers like Nextcloud.
Plugin features:
- CalDAV PROPFIND/REPORT for calendar discovery and event fetching
- iCal parsing and serialization with RFC 5545 compliance
- Two-way sync with field mappings (title, notes, dates, duration)
- Time-block integration for auto-creating calendar events
- Multi-calendar support with compound IDs
Host-side changes:
- Add generic request() method to PluginHttp for WebDAV methods
- Add allowPrivateNetwork manifest flag (bundled plugins only)
- Add dynamic loadOptions support for config field dropdowns
- Add backfill effect for existing scheduled tasks
- Generify translation keys (LOAD_OPTIONS instead of LOAD_CALENDARS)
Security:
- SSRF protection via origin validation in resolveHref
- allowPrivateNetwork gated by _isPluginBundled check
- XML parse error detection in CalDAV response parsing
- Description rendered as plain text (not markdown)
Correctness:
- TZID conversion uses formatToParts (no local timezone contamination)
- modifyICalEvent scoped to VEVENT block (preserves VTIMEZONE)
- responseType: 'text' on all CalDAV PUT/DELETE calls
- CR characters handled in iCal text escaping
- Trailing CRLF added to modifyICalEvent output per RFC 5545
- Fix taskIdToGcalEventId collision by hex-encoding nanoid bytes
- Add timeBlock API to plugin interface, move Google Calendar-specific
logic into the plugin, make TimeBlockSyncEffects provider-agnostic
- Use isPluginIssueProvider() in task selectors
- Replace console.error with Log.err, add i18n for time-block errors
- Use 1-hour default when rescheduling all-day event to timed slot
- Apply config migration in dialog for legacy single-calendar configs
- Remove planTaskForDay/moveBeforeTask from deleteOnUnschedule$
- Rename icalEvents$ to calendarEvents$
- Make issueProviderKey required on CalendarIntegrationEvent
- Parameterize loadAllCalendars/loadWritableCalendars
- Replace deprecated unescape() with TextEncoder in iCal util
- Fix btoa() non-ASCII throw in getIssueLink
- Fix timezone bug in all-day reschedule date parsing
- Deep-clone pluginConfig before mutating in migration dialog
- Add showIf property to PluginFormField so fields can depend on
another config value being truthy
- Show timeBlockCalendarId only when isAutoTimeBlock is enabled
- Improve wording for auto time blocking and calendar field descriptions
Add Google Calendar as a plugin-based calendar provider with OAuth 2.0
authentication, multi-calendar support, and event management.
- Plugin fetches events from selected read calendars, merged into the
existing calendar integration pipeline
- Context menus on planner and schedule views for calendar events:
open link, reschedule, create as task, hide forever, delete
- Reschedule opens date/time picker and updates event via plugin API,
handling both timed and all-day events correctly
- Delete with confirmation dialog
- CalendarEventActionsService extracts shared event action logic
- HiddenCalendarEventsService for permanent event hiding
- triggerRefresh() for immediate UI updates after mutations
- Multi-select config fields for choosing calendars to display
- Fix change detection for plugin select fields in provider dialog
- Electron-safe URL opening with scheme validation
The monitor:all, analyze-storage, and related npm scripts used tsx
which isn't available in the production Docker image. Switch to
node dist/scripts/*.js and add :dev variants for local development.
The production container runs as non-root user `supersync` without tsx
(dev dependency excluded via --omit=dev). Analysis and monitor-all
commands failed because they tried to use tsx/npx tsx which couldn't
write to node_modules. All scripts are already compiled to dist/ during
the Docker build, so use those directly.
Google rejects custom scheme redirect URIs that don't match the
platform's app identifier. Use the Android applicationId
(com.superproductivity.superproductivity) on Android and the iOS
bundle ID (com.super-productivity.app) on iOS, with single-slash
URI format per Google's documentation.
- Add iosClientId to OAuthFlowConfig and plugin API types
- Add iOS OAuth client ID to Google Calendar plugin
- Select client ID per platform (Android/iOS/Desktop) in bridge service
- Add Android package name intent filter in AndroidManifest
- Accept both URI schemes in OAuth callback handler
- Fix redirect handler to use IS_NATIVE_PLATFORM consistently
Google rejects Desktop OAuth client IDs when the redirect URI is a
custom scheme (as used on mobile via Capacitor), returning a 400 error.
Add mobileClientId to OAuthFlowConfig so plugins can specify a separate
Android/iOS client ID that authenticates via app signing instead of a
client secret. On native platforms, the bridge service automatically
uses the mobile client ID with PKCE only.
Also fix getRedirectUri() to return the custom scheme for all native
platforms (not just Android WebView), and align inline types in the
dialog component and plugin declaration with OAuthFlowConfig.
When a user adds a subtask by editing the markdown file directly, the
MD→SP sync creates the task which triggers SP hooks, which trigger
SP→MD sync, which writes the file, which the file watcher detects,
creating a rapid sync oscillation loop that crashes the app.
Three-layer fix:
1. Stop file watcher for the entire MD→SP sync duration and cancel
pending SP→MD debounce timers
2. Add a cooldown after MD→SP sync (set before and after in finally
block) to suppress SP hooks that fire as side-effects
3. Simplify lazySetInterval with async/await and a stopped flag to
prevent zombie rescheduling when cancelled during async callbacks
Also fixes a file watcher race condition where polling could start
before initial mtime was resolved.
Fixes#6021
* fix(plugin-bridge): enhance task update logic and validation for project movement
* fix(plugin-bridge): include changes in task update event payload
* feat(RuleEditor): add tests for rule editing functionality and enhance action handling
* test(RuleEditor): add comprehensive tests for rule editing and condition handling
* feat(RuleRegistry): enhance rule validation and add support for advanced conditions and actions
* feat(types): extend condition and action types for enhanced automation capabilities
* feat(conditions): add regex support for title conditions and enhance checks
* feat(actions): add ActionMoveToProject to move tasks between projects
* feat(ActionDialog, ActionInput): add support for moveToProject action and enhance input handling
* feat(ConditionDialog, ConditionInput): add support for titleStartsWith and weekdayIs conditions, enhance regex handling
* feat(styles): add new input-with-toggle and field-error styles for improved layout and error handling
* feat(automation): add titleStartsWith condition and enhance task event handling
* feat(automations): add delete task action
add a new `deleteTask` action to the automations plugin
- register the action in the automation runtime
- expose it in the rule editor UI
- restrict it to task-based triggers
- validate and persist rules using `deleteTask`
- add focused tests for runtime behavior, validation, persistence, and UI
* feat(automations): remove false trigger workaround
remove the temporary taskCreated fallback after confirming the
reported trigger bug was a mistaken assumption
* fix(automations): address PR review feedback
- Add regex pattern length cap (200 chars) to mitigate ReDoS risk
- Use project ID instead of title as option value to prevent duplicate name collisions
- Replace dynamic import('rxjs') with static import for firstValueFrom
- Replace any[] with proper types for projects/tags props
- Replace changes?: any with Record<string, unknown> in TaskEvent
- Use createMemo + <Show> for regexError to avoid double reactive computation
- Remove noisy/inconsistent debug logging from automation-manager
- Remove verbose intermediate log from moveToProject action
- Clean up mock: remove unused moveTaskToProject, restore PluginAPI type
- Prefer ID lookup over title in ActionMoveToProject
* fix(automations): harden regex, use IDs for conditions, add validation and tests
- Add dangerous-pattern heuristic to reject nested quantifiers (e.g. (a+)+$)
that cause catastrophic backtracking, supplementing the length cap
- Switch projectIs/hasTag condition dropdowns to store IDs instead of titles
to survive project/tag renames (with title fallback for backward compat)
- Disable ActionDialog Save button when value is empty (except deleteTask)
- Fix webhook test to actually validate payload sanitization
- Add test for HTML escaping in ActionDisplayDialog
- Add tests for regex length cap and dangerous-pattern rejection
- Add tests for ConditionWeekdayIs (7 test cases covering full names,
abbreviations, comma-separated lists, case insensitivity, edge cases)
* test(automations): build dangerous regex pattern dynamically to avoid CodeQL flag
The test intentionally uses a catastrophic-backtracking pattern to verify
our safety heuristic rejects it. Build it via string concatenation so
CodeQL's static analysis doesn't flag the test itself.
* fix(automations): addTag lookup by ID, widen ReDoS heuristic to catch {n,}
- ActionAddTag now looks up tags by ID first (with title fallback),
consistent with all other condition/action lookups
- Extend DANGEROUS_REGEX_PATTERN to also detect {n,} quantifiers
inside nested groups (e.g. (a{2,})+) which also cause backtracking
---------
Co-authored-by: Johannes Millan <johannes.millan@gmail.com>
- Pin Caddy image to 2.11-alpine to prevent breaking changes from
floating tags
- Validate Caddyfile syntax before deploying to catch config errors
early
- Check all container states after startup to detect crashes before
waiting on the HTTPS health check
- Show logs from all services on failure, not just supersync