Commit Graph
1082 Commits
Author SHA1 Message Date
digua cbc8728f41 fix(electron): resolve model download proxy from session
Make desktop proxy saves await Electron session proxy application before model download proxy resolution.

Resolve HTTP, HTTPS, and SOCKS system proxy rules for semantic index model downloads while preserving DIRECT precedence.
2026-06-26 09:56:32 +08:00
digua f304146c8f fix(semantic-index): honor proxy for local model downloads
Pass a model download proxy through the semantic index worker and local embedding provider so local Hugging Face downloads use the effective proxy.

Restart idle local workers when the proxy changes before preload, build, or search calls, and initialize app logging inside the worker process for preload diagnostics.
2026-06-26 09:56:32 +08:00
digua e5954d8fb1 feat(chat-record): polish viewer controls and highlights 2026-06-25 23:18:11 +08:00
digua 851166188d fix(import): harden push and pull import guards
Combine the PR review fixes for push import validation, deduplication, system sender handling, and pull import lock scoping into one reviewable change.
2026-06-25 21:45:16 +08:00
diguaandClaude Sonnet 4.6 4694d83729 fix(electron): align Desktop API port with CLI, auto-fallback on conflict, update docs
- Change default port from 5200 to 3110 in sync config-manager, Vue store,
  and settings UI to match CLI DEFAULT_API_PORT
- Desktop API server now tries up to 10 consecutive ports when preferred
  port is occupied (e.g. CLI already running); reports actual bound port
- Scope Desktop import route lock to session (Set<string>) matching the
  push-importer fix
- Docs: remove unimplemented JSONL/X-Dry-Run claims, fix port references,
  replace platform-specific examples with generic ones

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 21:45:16 +08:00
diguaandClaude Sonnet 4.6 4e2130b2d7 fix(import): address review findings in push import API and pipeline
- Reject path-traversal sessionIds (SAFE_SESSION_ID_RE)
- Reject X-Dry-Run header with 400 instead of silently writing
- Propagate DataDirCompatibilityError as 409 instead of swallowing to 500
- Scope concurrency lock to session instead of process-wide boolean
- Fix hash dedup hole: load hashes for ALL existing messages, not just
  those without platformMessageId
- Fix backfill segment index: detect when written timestamps predate the
  existing tail and use full generateSessionIndex rebuild instead of
  incremental; apply same fix to shared incremental-importer used by
  Desktop and pull sync
- Validate member platformId, message content/platformMessageId types, and
  option enum values in validatePayload before any writes
- Use writeParseResultToDb in fullImport to populate member_name_history;
  pre-deduplicate messages before delegating and auto-create minimal members
  for senders absent from the members list
- Add content-hash to existingKeys when accepting a pmid-keyed row so a
  same-content no-pmid copy later in the same batch is caught

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 21:45:16 +08:00
diguaandClaude Sonnet 4.6 3442110c08 feat(ui): hide sidebar scrollbar by default, show on hover
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 21:45:16 +08:00
digua 75a10c9fed fix(sync): refresh sessions after background pull 2026-06-25 21:45:16 +08:00
diguaandClaude Sonnet 4.6 1209e2ce4b refactor(import): migrate CLI sync/pull path to shared streaming importer
Removes the legacy apps/cli/src/import/importer.ts (hand-rolled FTS, dedup,
session-ID generation) and apps/cli/src/import/chatlab-reader.ts (buffered
in-memory JSON/JSONL parser). DirectImporter in sync/adapters.ts now calls
streamImport / incrementalImport from stream-import.ts, which use the
@openchatlab/parser streaming pipeline and node-runtime shared logic.

Changes:
- Add sessionId? to StreamImportOptions so callers can fix the target session
  ID instead of auto-generating one (needed by pull engine full-import path)
- Rewrite DirectImporter.incrementalImportFile and fullImportFile to use
  the shared importers; map IncrementalImportResult fields to ImportResult
- Remove legacy exports from apps/cli/src/import/index.ts
- Rewrite importer.test.ts to use streamImport + a temp ChatLab JSON file
  (same two data-dir compatibility gate assertions as before)
- Delete importer.ts and chatlab-reader.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 21:45:16 +08:00
diguaandClaude Sonnet 4.6 0f1ea43a11 refactor(electron): remove redundant generateIncrementalSessions calls after incremental import
Session index is now built inside the shared incremental-importer, so the
explicit post-import worker.generateIncrementalSessions(sessionId) calls in
the Desktop layer were no-ops. Remove all four call sites.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 21:45:16 +08:00
diguaandClaude Sonnet 4.6 467222b68e docs: fix port 5200→3110, replace platform examples, add quick-test curl
- Correct default port from 5200 to 3110 (matches DEFAULT_API_PORT in config)
- Replace platform-specific example identifiers with generic ones to avoid
  trademark concerns; update session IDs, member IDs, and groupId format
- Add a copyable quick-test curl snippet to the import protocol doc (cn + en)
- Expose lastPlatformMessageId and importedAt in sessions/:id doc example
- Remove brand-name references from platform field descriptions in
  chatlab-format.md (kept valid platform identifiers in the enum table)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 21:45:16 +08:00
diguaandClaude Sonnet 4.6 ced4ed16bd feat(import): auto-generate session index after all import paths
Session index (segment/message_context tables) was previously generated
inconsistently: CLI used postImportHook, Desktop relied on a separate
explicit RPC call, and the CLI sync/pull legacy importer never built it.

Move generateSessionIndex / generateIncrementalSessionIndex into the
shared importers (streaming-importer.ts, incremental-importer.ts) so
every caller gets it automatically. Remove the now-redundant calls from
CLI's postImportHook. Also add the calls to the legacy importer.ts used
by CLI sync/pull, which had no session index at all.

Incremental imports use generateIncrementalSessionIndex, which preserves
existing AI summaries. Full imports call generateSessionIndex only on
newly created sessions where no summaries exist.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 21:45:16 +08:00
diguaandClaude Sonnet 4.6 2b0013f615 fix(api): expose lastPlatformMessageId and importedAt in GET /api/v1/sessions/:id
Both fields are documented in chatlab-api.md but were missing from the
response. lastPlatformMessageId is used as the incremental import boundary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 21:45:16 +08:00
diguaandClaude Sonnet 4.6 419b3002c4 feat(import): add POST /api/v1/imports/:sessionId push import API
Implements the Push Import protocol documented in chatlab-import.md.
Creates a new session on first call, appends and deduplicates on
subsequent calls (platformMessageId preferred, content hash fallback).
Adds IMPORT_IN_PROGRESS (409) and IMPORT_FAILED (500) error codes.
Includes a global concurrency lock and incremental session index
generation after each write.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 21:45:16 +08:00
digua a2158cccc4 release: v0.28.0 v0.28.0 2026-06-25 00:12:53 +08:00
diguaandClaude Sonnet 4.6 22771eea37 fix(import): add stream-chain and stream-json to lockfile
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 00:11:49 +08:00
diguaandClaude Sonnet 4.6 984fe3df3b fix(import): declare stream-json/stream-chain deps and stop multiChat scan fallthrough
- stream-json and stream-chain were used directly in GoogleChatTakeoutResolver
  but not declared in node-runtime's package.json; fixes module resolution
  failure in non-hoisted installs
- when detectFormat returns multiChat but scanMultiChatFile returns empty,
  the code fell through to single-file import and could silently import the
  first chat; now returns early with an error message in both cases

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 18:10:30 +08:00
digua fa55b6ee13 docs: add Google Chat to supported platforms in docs 2026-06-24 18:10:30 +08:00
diguaandClaude Sonnet 4.6 55acb5c848 test(import): add integration test for localized Google Chat archives
Verify end-to-end parsing of localized archive structures and update
semantic-index route fake to match current service interface.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 18:10:30 +08:00
diguaandClaude Sonnet 4.6 aff59c7503 feat(import): wire Google Chat archive sources into CLI, Electron and frontend
Add archive reader, Google Chat resolver and source manager in node-runtime.
Expose prepared archive sources via CLI HTTP route and Electron IPC.
Add frontend service adapters and an import selection flow with i18n support
for all four locales. Fix rescanning on repeated archive imports.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 18:10:30 +08:00
diguaandClaude Sonnet 4.6 7ea95d5c74 feat(parser): parse Google Chat takeout conversations
Add streaming zip reader dependency (fflate) and a new parser for Google
Chat takeout conversation files. Handles localized message types, reactions,
topic threads, and deduplication of multi-file exports.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 18:10:30 +08:00
diguaandClaude Sonnet 4.6 2dbe569f2a fix(logging): fix four review issues in unified logger
- Front-end error reporter was posting to /_web/_web/logs/report (double
  base URL prefix); fixed to /logs/report
- uncaughtException/unhandledRejection handlers in CLI and Electron did not
  exit after logging, overriding Node's default crash behavior; added
  process.exit(1)
- NodePathProvider constructor in run() and chatlab status triggered
  migrateFromElectronIfNeeded() as a side effect; added getSystemLogsDir()
  to get the system logs path without resolving user data
- formatData() was called outside write()'s try/catch, so errors with
  circular cause objects could escape appLogger.error(); moved inside

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 15:28:09 +08:00
diguaandClaude Opus 4.8 7c21d1c879 feat(logging): add unified app logger with file rotation and global error capture
Build a shared appLogger (node-runtime) that all three runtimes write to a
single rolling logs/app.log (10 MB → rename to app.old.log). Replaces the
four independent hand-written loggers (app.log/api.log Electron wrappers)
with a unified entry point while leaving AiLogger/perf-logger intact.

Key additions:
- packages/node-runtime/src/logging/app-logger.ts: initAppLogger/appLogger
  singleton, INFO threshold by default (CHATLAB_LOG_LEVEL to override),
  atomic rename rotation, Error data auto-extracts message+stack
- Three-runtime init: Electron main, CLI Web (http/index.ts), CLI (run())
- Global crash capture: uncaughtException + unhandledRejection on all three
  Node runtimes now land in app.log scope 'crash'
- Fastify setErrorHandler fallback logs scope 'http' on CLI Web and Electron
  internal server for unexpected 5xx errors
- Frontend: window.onerror + unhandledrejection + Vue errorHandler feed into
  POST /_web/logs/report (scope 'web') with dedup+throttle
- AGENTS.md: logging rules section requiring key-path logs on new/changed features

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 15:28:09 +08:00
diguaandClaude Opus 4.8 4f22231189 fix(semantic-index): fix four bugs found in PR review
- SemanticIndexSessionModal called act('disable') and referenced the
  removed i18n key action.disable after the rename to remove; update
  both the action call and i18n key.

- setConfig triggered preload even when saved.enabled is false; guard
  preload behind saved.enabled so disabling the feature does not
  download the local embedding model.

- worker-client setConfig returned early when transport was null, so
  preload never ran in a fresh Electron session after model selection;
  start the worker for local+enabled configs to ensure preload executes.

- rebuild() called computeDbPathHash(sessionId) directly instead of
  resolveHash(), creating a duplicate state row for sessions stored
  under a legacy full-dbPath hash; use resolveHash() and pass the
  resolved hash to enableParams() to update the existing record.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 14:41:33 +08:00
diguaandClaude Opus 4.8 c241fa0502 feat(semantic-index): replace disable with remove; fix legacy hash; cap list height
Replace "停用" (disable, deferred cleanup) with "移除" (remove, immediate):
- Cancels any active build job
- Deletes vector chunks immediately (store.deleteByDbPathHash)
- Removes the state record entirely (stateStore.remove)
Applies to both the per-session button and the picker's deselect flow.

Fix legacy hash mismatch: old records were stored with a hash computed from
the full dbPath; current code hashes only the sessionId basename. Add
resolveHash() which falls back to a full-table scan by dbPath basename so
all operations (build/pause/cancel/remove/status/search) can find legacy
records regardless of which hash scheme was used.

Cap the enabled sessions list at max-h-72 with overflow-y-auto so the
settings page does not grow unboundedly with many sessions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 14:41:33 +08:00
diguaandClaude Opus 4.8 d1d1f1a3af fix(semantic-index): prevent double-toggle in conversation picker
The row was wrapped in a <label> which forwards native clicks to the
inner checkbox input. Clicking the UCheckbox visual therefore triggered
toggle() twice (once from UCheckbox's own update:model-value, once from
the label forwarding the click back to the input), causing the selected
state to cancel out and sessions to appear stuck or re-selected.

Replace <label> with <div @click="toggle"> and add @click.stop to
UCheckbox so each interaction path calls toggle() exactly once.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 14:41:33 +08:00
diguaandClaude Opus 4.8 d6896bf09a feat(semantic-index): move model to ai dir and preload on config save
Move local embedding model storage from getCacheDir() to getAiDataDir()
(~/.chatlab/ai/models/semantic-index) so the "clear cache" button in
settings no longer wipes downloaded models.

Also trigger model download immediately in the background when a local
model is configured, instead of waiting for the first index build.
Download status (idle/downloading/ready/error) is tracked in
SemanticIndexService, exposed via the SemanticIndexRuntime interface and
worker RPC, and surfaced in the settings UI as a pulsing indicator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 14:41:33 +08:00
diguaandClaude Opus 4.8 cabb794ff7 feat(semantic-index): remove searchMaxResults user setting, always use default
The "单次检索片段数" input was surfaced in settings but adds noise without
meaningful benefit for most users. Remove the UI control entirely and fix
normalizeConfig to always return SEARCH_MAX_RESULTS_DEFAULT (10), ignoring
any previously saved value so existing custom settings are silently reset to
default on next load.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 14:41:33 +08:00
digua 46fb1cd4ca release: v0.27.2 v0.27.2 2026-06-24 00:02:01 +08:00
diguaandClaude Sonnet 4.6 b3c4866ae7 fix(ai): fix type error in llm-config-store tests for authProfile fixture
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 23:35:53 +08:00
digua d84a6632a1 fix(ai): preserve auth profiles during cleanup 2026-06-23 22:58:22 +08:00
digua 651c7f936f fix(analytics): retry daily activity after failed posts 2026-06-23 22:58:22 +08:00
digua 43e6ffdb81 fix(cli): include analytics key in release build 2026-06-23 22:58:22 +08:00
digua d49b1b8472 fix(web): honor analytics opt-out state 2026-06-23 22:58:22 +08:00
digua 9171cec5de fix(ai): keep shared auth profiles referenced 2026-06-23 22:58:22 +08:00
digua aaea90d2fc refactor(analytics): drop legacy settings migration 2026-06-23 22:58:22 +08:00
digua da1432a1d3 fix(semantic-index): package local embedding runtime deps 2026-06-23 22:58:22 +08:00
diguaandClaude Sonnet 4.6 c8cccbefd4 feat(analytics): track CLI web daily active users via shared AnalyticsService
- Extract shared AnalyticsService to packages/node-runtime using direct
  Aptabase HTTP API (removes @aptabase/electron dependency)
- Add POST /_web/telemetry/track route for generic event reporting
- Desktop: migrate analytics.json from userData to systemDir (~/.chatlab/),
  trigger daily active from renderer via new IPC so user-selected locale is used
- CLI: inject APTABASE_APP_KEY at build time, create AnalyticsService on
  server start, report app_active with platform: cli-web
- Both platforms report user-selected locale via platform service abstraction

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 22:58:22 +08:00
diguaandClaude Sonnet 4.6 46066d7a5f fix(ai): clean up old auth profile when rename + key change shifts profile name
When a model service is renamed and its API key changed simultaneously,
onApiKeyCreated derives a new profile name from the new service name and
writes a new auth profile, but the old profile (keyed by the old name)
was never deleted, leaving an orphaned credential on disk.

Capture the old authProfile before applying updates. If onApiKeyCreated
returns a different profile name, call onApiKeyDeleted on the old config
to trigger cleanup before persisting the new profile.

No cleanup is needed for key-only updates (profile name unchanged) or
name-only updates (onApiKeyCreated is not called).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 22:58:22 +08:00
diguaandClaude Sonnet 4.6 04680fc840 fix(ai): delete auth profile when removing a model service config
When deleteConfig was called, the auth profile written to
auth-profiles.json during addConfig was never cleaned up, leaving
orphaned credentials on disk indefinitely.

Add onApiKeyDeleted callback to LLMConfigStoreDeps and call it in
deleteConfig with the removed config so callers can clean up the
corresponding auth profile. Wire up deleteAuthProfile in both the
CLI HTTP server and the Desktop internal API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 22:58:22 +08:00
digua 5bb01966fa release: v0.27.1 v0.27.1 2026-06-23 14:39:53 +08:00
diguaandClaude Sonnet 4.6 35b5e720d4 fix(test): fix type errors in worker-runtime and core-data-provider tests
Cast FakeSemanticIndexRuntime via unknown to satisfy the full
SemanticIndexRuntime interface in the factory return type.

Enable allowImportingTsExtensions in tsconfig.web.json so test files
importing .mts helpers pass vue-tsc type checking.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 00:21:18 +08:00
diguaandClaude Sonnet 4.6 f8079d9031 fix(sync): fold initial empty-page cursor before entering retry loop
When the first small response is empty with hasMore=false and carries
sync.nextSince, the file was deleted before that cursor value was saved.
If retry pages also omit nextSince, nextPullSince stays at the old
lastPullAt and the next poll re-fetches the same completed tail window.

Fold sync0.nextSince into nextPullSince immediately after cleanupTempFile,
before the retry loop, matching the existing pattern for retry pages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 14:18:32 +08:00
diguaandClaude Sonnet 4.6 f69f419192 fix(semantic-index): make insertOne idempotent to survive mid-batch crash
With API batched embeddings, insertChunks() and stateStore.updateProgress()
write to two separate SQLite databases, so they cannot be atomic. If the
worker is terminated between the two writes, the next resume attempts to
re-insert the same chunk_ids and hits the UNIQUE constraint, leaving the
session stuck in 'failed' state.

Use INSERT OR IGNORE on chunk_vector_index and skip the vec-table insert
when changes === 0. Duplicate inserts during resume are now silently
discarded instead of throwing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 14:18:32 +08:00
digua dc1c236cc0 fix: address semantic worker and pull review bugs 2026-06-23 14:18:32 +08:00
digua 434334b203 feat(semantic-index): run local embeddings in lazy worker 2026-06-23 14:18:32 +08:00
digua f14f139ec6 feat(semantic-index): batch API embedding warmup 2026-06-23 14:18:32 +08:00
digua 35c260ac33 fix(sync): repair pull pagination cursor(#237) 2026-06-23 14:18:32 +08:00
diguaandClaude Sonnet 4.6 7df673f62f test: add openTestSqliteDatabase helper to fix native binding mismatch
Centralise the better-sqlite3 native-binding selection for tests in
tests/helpers/sqlite.mts so test files consistently use the CLI-side
binding (apps/cli/native/better_sqlite3.node) rather than the default
one, which may be compiled for a different Node.js ABI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 14:18:32 +08:00
diguaandClaude Sonnet 4.6 39393cf38d refactor: remove dead code and simplify over-engineered abstractions
- Delete NotificationBus interface (no implementations, no callers)
- Delete useAsyncData/useMultipleAsyncData composables (zero import sites)
- Inline CHART_CAPABILITY_SKILL_ID into tool-catalog.ts (was a single-constant file)
- Inline AsyncSqlExecutor into message-query-functions.ts (was a single-interface file)
- Rename tools TimeFilter → ToolTimeRange to eliminate name collision with shared-types
- Shrink useToast wrapper: two interfaces → one type, 40 lines → 17 lines
- Simplify deepMerge: remove recursion, config is at most 1 level deep

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 14:18:32 +08:00