Add typed helper functions (lastWrite, lastToolState, providerConfig) to src/renderer/hooks/__tests__/useCodeCli.test.ts and import CodeCliToolState. Replace direct mockSetter.mock.calls accesses with these helpers to simplify and type-safe assertions, improving readability of the tests.
The barrel/module-boundary rules (naming-conventions.md §6.4) were warn-only
while the codebase was being brought into compliance. The full tree is now
barrel-clean (lint reports 0 barrel findings), so promote all seven rules —
no-export-star, index-no-impl, no-index-tsx, named-only, closed, no-nesting,
no-bucket-root — from warn to error to lock the boundary against regressions.
Close the last 14 barrel/closed (§6.4) violations in the knowledge feature: both
v1→v2 migrators deep-imported knowledge internals to rebuild a base's index.sqlite.
Extract the shared open sequence into createKnowledgeIndexStoreAtPath (also adopted
by KnowledgeVectorStoreService, deduping the runtime) and the snapshot assembly into
build{Url,Note}SnapshotFile (adopted by capture*SnapshotFile); add index-count reads
to KnowledgeIndexStore; re-export a curated rebuild surface from the knowledge barrel.
The migrators now import only @main/features/knowledge — the engine internals
(driver/schema/meta/vector index) stay private behind the factory.
Resolve OpenClawService.ts import conflict: adopt upstream's moved
`@main/data/migration/legacyTypes` path and drop the `WindowType` import,
whose only consumer (the OpenClaw_InstallProgress broadcast) was removed on
this branch during the OpenClaw IPC migration.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
Address the review on #16491:
- i18n: restore `settings.models.manage.sync_apply_default_in_use`, net-deleted
from en-us/zh-cn/zh-tw during a rebase yet still referenced by the model-list
"in use as default" delete guard.
- FileStorage.writeFile: chmod an existing file to the requested mode BEFORE
writing, so secret content never lands under the old (world-readable) mode;
log + rethrow on chmod failure rather than falling through.
- cliConfig/dotenv: align parseDotenv with the real dotenv package — keep `\\`
and `\"` literal inside double quotes (only expand `\n`/`\r`), so a hand-written
KEY="C:\\path" no longer silently loses a backslash on rewrite.
- ipc/codeCli: drop the duplicated providerId/model validation; delegate to
CodeCliService.run() as the single source of truth.
- useConfigPanelController: add a per-tool in-flight guard so a rapid re-toggle
can't interleave the sequential, un-locked multi-file config writes.
- CLIIcon: use cn() so the fallback className never concatenates a literal
"undefined".
- OpenClawService: delete the now-unreachable install/installInternal/
installPromise/uninstall/checkInstalled methods and their stale tests/mocks
(install/update routes through CodeCliService -> BinaryManager).
Tests cover each fix: chmod ordering, dotenv round-trip vs real dotenv,
validation single-source + login/providerless exemptions, the in-flight guard on
both toggle branches, and the CLIIcon fallback.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
Every §6.4 `barrel/closed` warning on these six main barrels came from
external code deep-importing internals. Route each consumer through the
barrel, widening a door only with symbols that keep it one cohesive unit,
and trim dead re-exports.
- filesystem: export resolveFilesystemBaseDir
- observability: export the 3 lifecycle services (ClaudeCode/Node/TraceStorage)
- fileProcessing: export the job-result contract (artifact helpers + payload type)
- webSearch: export isPermanentWebSearchConfigError
- knowledge: export KnowledgeVectorStoreService; drop internal-only Lock/Workflow re-exports
- migration/v2: export isSchemaOutOfSyncError; drop dead BaseMigrator/reader re-exports;
fold the gate's redundant versionPolicy deep-imports back through the barrel
Relocate two misplaced concerns the deep imports surfaced:
- isAbortError (generic) -> @main/utils/error, out of webSearch
- legacyTypes (v1 @deprecated) -> data/migration/, out of the v2 barrel; delete dead MistralClientManager
Update v2MigrationGate's barrel mock to match the consolidated import, and
fix a stale migrator-registration path in the migration README.
The remaining knowledge migrator coupling (KnowledgeVectorMigrator /
KnowledgeMigrator) is deferred to a fix-upstream follow-up.
Clear all 8 `closed` deep-import warnings on five directories, applying the
topic-subdir-vs-type-bucket distinction (naming §6.4 / renderer §5):
- hooks/translate, hooks/command, components/ActionTools — cohesive topic
subdirs / component framework: keep the curated door, route the stray deep
imports through it.
- pages/knowledge/hooks — a page-internal type bucket (named `hooks`, not a
topic): drop the barrel per rule 3/§4.8; four consumers deep-import the leaf
hooks (matching the no-door home/notes/paintings siblings).
- components/composer/tokenView — heavy component + light vocab aggregator:
keep the component door, move the token vocabulary out
(tokenView.ts -> composer/chatTokenView.ts) so light consumers no longer
pull the ComposerToken graph through the door.
Retarget the knowledge tests' barrel mocks to the leaf modules.
Fix two barrel/closed violations where Shell and ResourceList deep-imported
CommandId from shared/utils/command/definitions instead of the barrel. The
command/ barrel is a cohesive subsystem (command/keybinding/menu/context-
expression); it stays as-is — only the two stray deep-import paths are corrected.
The `databases/index.ts` was implementation (the Dexie `db` instance and its version schema), not a re-export barrel, tripping `barrel/index-no-impl` (naming-conventions.md §6.4 rule 1). Since this v1 dexie residue is slated for removal, take the lightest §6.4 resolution: git mv the implementation to a named `db.ts` so `databases/` becomes a plain no-barrel container, and repoint consumers to deep-import `@renderer/databases/db`. No new barrel is built and no lint is suppressed; the warning is cleared structurally.
Clean up barrel boundaries per naming §6.4 across four renderer component
directories:
- CodeBlockView -> no-barrel container: a heavy main component plus
independently consumed satellites (ViewMode type, constants config,
HtmlPreviewFrame), with a latent CodeBlockView<->CodeToolbar cycle. Deep
imports become legal; light consumers no longer drag the heavy graph through
a door.
- Preview -> no-barrel container: an aggregator of four heavy, per-view
lazy-loaded diagram previews. Removing the barrel preserves per-view code
splitting -- routing the lazy imports through a door would merge the chunks.
- composer/quickPanel -> add ComposerPanelSymbol to the door; it is part of the
quick-panel public API (used internally by unifiedPanel).
- Sidebar -> add UserAvatar/MiniAppIcon to the door, drop the dead
SidebarTooltip export, route consumers through the door.
Removing the two barrels also required fixing their test deep-import surfaces
(MermaidPreview, CodeBlock vi.mock, Sidebar door mock) that the barrel lint
exempts but that break at runtime.
Additionally:
- Move the co-located ProviderSettings/utils tests into __tests__/ to match the
convention used by every sibling directory.
- Fix a pre-existing AgentComposer test flake: it mocked the dialogs/edit leaf
while the source lazy-imports the barrel, so the lazy import loaded the real
barrel's heavy sibling dialogs and raced findByTestId's timeout under load.
Mock the barrel instead.
Three core barrels, three diagnoses under naming-conventions §6.4:
- paths: remove the barrel — it only re-exported types while the real entry
is application.getPath(). constants.ts and pathRegistry.ts are independent
building blocks deep-imported by their specific consumers (rules 2/3/4).
- lifecycle: keep the barrel (a cohesive framework consumed as a set by 80+
services), route the last deep-import leaks through it, and prune 13
framework-internal machinery exports (resolver, decorator getters, capability
guards) that no consumer touches — tightening the door to its public surface.
- application: remove the barrel and repoint @application directly at
Application.ts. The barrel bundled the hot `application` locator (~218
consumers) with the bootstrap-only serviceRegistry, so every @application
import dragged in the whole service graph — the root of the JobManager-to-
serviceRegistry eval-time cycle. The locator now loads on its own.
Relocate SHUTDOWN_TIMEOUT_MS from an Application static into
lifecycle/constants.ts: a shared shutdown-grace policy honored by both
Application and JobManager, so JobManager no longer imports the Application
class at all.
Docs (paths/README, application/README, application-overview) and per-file
test mocks (vi.mock('@main/core/application') to '@application';
Application.getPath vi.unmock) are updated in lockstep.
Hoist page-shared model-sync logic (modelSync, buildModelListSyncPreview,
providerModelSyncRequirements, modelSyncPreviewTypes) out of the ModelList
component directory into the page-level utils/ and types/ buckets so
ModelList becomes a cleanly-closable door; expose the ModelList health
context through the ModelList barrel; surface MODEL_ENDPOINT_OPTIONS on the
ModelDrawer barrel; and drop the dead ProviderList re-export from the root
barrel. Clears all ProviderSettings barrel warnings.
Selecting CherryIN's agent/deepseek-v4-flash for Gemini CLI showed "unknown
provider" (未知供应商) after enabling it. CLI_TOOL_PROVIDER_MAP allow-lists
CherryIN/DMXAPI as Gemini CLI providers even though neither exposes a
google-generate-content endpoint, and GEMINI_AGGREGATOR_BASE_URLS only had an
override for aihubmix. resolveGeminiBaseUrl fell through to '', so
GOOGLE_GEMINI_BASE_URL was never written to ~/.gemini/.env — not just a
cosmetic mismatch, the CLI tool itself was left pointed at Google's real API
instead of the aggregator. The readback in providerMatching.ts then correctly
flagged the empty base URL as a non-match and surfaced the "unknown provider"
badge.
resolveGeminiBaseUrl now falls back to the provider's default-chat-endpoint
base URL when there's no dedicated Gemini endpoint or override, mirroring the
identical fallback buildCherryinConfig/dmxapiProvider.ts already use for real
chat requests to these same providers — both proxy every protocol off one
shared host, so this fixes CherryIN and DMXAPI generically without a guessed,
hardcoded per-provider URL. new-api (has a native google-generate-content
endpoint) and aihubmix (already overridden) are unaffected.
Checked the other file-configured CLI tools for the same allow-list/resolver
mismatch: none exists — Claude Code/Codex/Qwen/Kimi's provider filters check
the exact endpoint their resolver reads, and OpenCode's resolver dynamically
picks whichever endpoint the provider actually has. Gemini CLI is the only
tool whose filter carries a hardcoded aggregator allow-list independent of
real endpoint presence.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
The src/shared/data/bootConfig/index.ts barrel had zero consumers — every
importer already deep-imports bootConfigSchemas/bootConfigTypes — while
imposing a hand-sync burden against the auto-generated bootConfigSchemas.ts.
Per §6.4 rule 2 (enforced sole entry, or no barrel) and matching the sibling
api/preference/cache no-barrel containers, remove it. Clears 11 barrel/closed
warnings with no consumer changes.
The FileHandle type union moved to src/shared/data/types/file.ts and the
watcher/ directory was flattened to watcher.ts during the naming §6.4
barrel refactor, leaving two dead links caught by docs:check-links.
Ollama's real endpointConfigs only expose an anthropic-messages endpoint
(no openai-chat-completions/openai-responses), so CLI_TOOL_PROVIDER_MAP
lets it be selected as a provider for exactly two file-configured tools:
Claude Code and OpenCode (routed through @ai-sdk/anthropic). OpenCode hit
the same "missing required fields (apiKey/baseUrl)" failure the previous
commit fixed for Claude Code, since assertCliConfigCredentials only
special-cased Claude Code.
Extended the placeholder-token fallback to OpenCode and renamed the shared
constant from OLLAMA_CLAUDE_CODE_AUTH_TOKEN to OLLAMA_PLACEHOLDER_AUTH_TOKEN
now that it backs two tools. Codex/Gemini CLI/Qwen Code/Kimi Code require
openai-chat-completions, openai-responses, or google-generate-content
endpoints that Ollama never exposes, so they can't reach this bug — their
existing API-key checks are correct as-is and were left untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
Batch D of the §6.4 barrel cleanup: an index.ts must be a pure re-export, so
implementation, side effects, and local declarations move to named files.
- i18n: demote to a no-barrel container (index.ts -> resolver.ts); label.ts
stays as a sibling deep-import. The directory holds two orthogonal concerns
(the heavy i18next engine + pure label-key resolvers), so a single barrel
would drag the engine into label's pure consumers -> no barrel per rule 4.
- ipc: keep a cohesive barrel (index.ts -> ipcApi.ts) re-exporting both ipcApi
and useIpcOn -- the request and event halves of one IpcApi surface.
- composer/tools: eliminate import-for-side-effect registration. Each definition
exports its tool; an explicit BUILTIN_COMPOSER_TOOLS array replaces the
module-scope registry Map, and the barrel is dropped.
- transport / tags-Model / migrationV2-i18n: mechanical barrel cleanup.
- import/importers: inline availableImporters into its sole consumer.
- databases (v1 Dexie) is left as-is; it is throwaway code with live v2
consumers and belongs to the dexie-deletion effort.
- runtime: dissolve container barrel; aiSdk and claudeCode each own a door
- channels: single door, internalize security satellite
- types: complete the barrel surface (AiBaseRequest et al.)
The adapters/ root barrel deep-imported leaves inside four sub-barrels
(stream/converters/formatters/factory), tripping both no-nesting and closed.
The sub-barrels were themselves dead — incomplete and imported by nobody: the
root barrel, the factories, and the tests all bypassed them for leaves.
- Delete the four internal sub-barrels; the subdirs become plain folders, so
the factory/formatter/root leaf imports are now legal (no barrel to bypass).
- Curate adapters/index.ts to the real public surface — the two factories plus
the interface/format types actually consumed by proxyStream and errors. The
concrete stream adapters, converters, and formatters have zero external
consumers and stay internal.
- Move openrouter.ts up to apiGateway/, next to its sole consumer
reasoningCache.ts — it is a reasoning-cache type contract, not adapter
machinery — clearing the last closed violation.
- Document openrouter.ts's new home in the api-gateway README tree.
apiGateway barrel warnings: ~38 -> 0.
`assertCliConfigCredentials` unconditionally required an API key for every
CLI tool, so enabling Claude Code with an Ollama provider (which needs no
real credential) failed with "Claude Code config is missing the API key" —
both from the config dialog's save path and from the plain enable-toggle
path, since both funnel through the same `writeCliConfigDraft`.
The in-app agent runtime (agentSessionWarmup.ts) already special-cases this:
Ollama gets a placeholder auth token ('ollama') when no real key is
configured, since Ollama's server doesn't validate credentials but Claude
Code's SDK still requires a non-empty token. That fallback was never ported
to the renderer's external-CLI config injection path. Applied the same
fallback in `resolveContext` and moved the shared constant into
`@shared/utils/provider.ts` so both call sites stay in sync.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
Rewrite streamManager/index.ts to export the full consumed surface (all
listeners, startAgentSessionRun, PersistenceBackend contract, TranslationBackend,
stream types); route all 15 external deep imports through it. Remove the
internal-only context/ and lifecycle/ sub-barrels so internal siblings import
directly. Expose AiStreamRequest/CallOverrides on the ai/types barrel and route
ChannelAdapter through the channels barrel.
Two deep imports remain, blocked by nested sub-barrels in sibling dirs
(runtime/claudeCode, channels/security) — deferred to those dirs' cleanup.
icons/ is a bucket of independently-consumed icon components, not a
cohesive API taken as a set — §6.4 rule 4 (aggregator) + §4.9 (plural
bucket) both point to no barrel. Delete index.ts; consumers deep-import
the specific file, so the 22 pre-existing deep imports become legal and
the ~24 barrel/closed lint warnings clear.
Rename SvgSpinners180Ring.tsx -> LoadingIcon.tsx so the deep import
reads naturally (the barrel had aliased its default as LoadingIcon).
Re-point 18 barrel-mock tests to the deep paths (default-export files
wrap the stub in { default: ... }).
Deferred (flagged, out of scope): splitting the 36KB SvgIcon.tsx
grab-bag; moving miniAppsLogo.ts to utils/.
- Rename components/resource → components/resourceCatalog (align with hooks/resourceCatalog, types/resourceCatalog)
- Demote resourceCatalog/ and dialogs/ to no-barrel containers (§6.4 rule 4); each cohesive unit owns its door
- Sink the 5 selectors into selectors/ behind their own barrel
- Extract cross-cluster shared logic (form state/diff, model filter, constants) to utils/resourceCatalog/
- Split dialogs into per-family barrels (create/edit/delete/detail/import/manage); move shared EditDialogShared scaffolding to dialogs/components
- Route consumers through the family doors, restoring the selectors' create-eager / edit-lazy code-split that a single dialogs door had defeated
- Complete hooks/resourceCatalog barrel (add useResourceCatalogController)
Clears 11 §6.4 rule-2 deep-import warnings; typecheck + 14398 tests green.
Clear all `barrel/index-no-impl` violations in src/main (14 barrels) by
turning each index.ts into a pure re-export or dissolving it:
- Aggregation buckets → drop the barrel, move the assembled object/array/fn
to a named file, deep-import at the sole consumer: data/api/handlers →
apiHandlers.ts, ipc/handlers → ipcHandlers.ts, migrators → migratorRegistry.ts,
params/features → internalFeatures.ts, browser/tools → registry.ts,
builtin → registerBuiltinTools.ts, db/seeding → seederRegistry.ts.
- Single-impl dirs flattened: provider/extensions.ts, provider/cherryai.ts.
- Barrel kept, impl extracted to a named file: i18n/resolver.ts,
ai/types/providerConfig.ts.
- Route subdir flattened to match its flat siblings: routes/knowledge.ts +
routes/knowledgeSchemas.ts.
- runtime: replace the import-time `claudeCode/register` side effect with an
explicit registerRuntimeDrivers() invoked from AgentSessionRuntimeService.onInit,
so runtime/index.ts stays a pure re-export.
- loop: dissolve the non-enforced barrel into loop/types.ts + loop/hookRunner.ts.
Also route DataApiService's apiHandlers import through the data/api barrel,
and expose ClaudeCodeRuntimeDriver via the claudeCode barrel, clearing the two
deep-import warnings this batch touched. Docs and comments updated to the new paths.
Resolve all 25 no-index-tsx violations across components/ and pages/ per
naming-conventions.md §6.4 (the `index` filename is reserved for re-export
barrels, so an `index.tsx` is always a violation).
- Single-file components -> flat `Foo.tsx` (no directory, no barrel):
AppModal, HorizontalScrollContainer, ListItem, MarkdownEditor,
ProviderLogoPicker, Scrollbar, home ChatNavbar, AgentChatNavbar/Tools.
- Multi-file components with private satellites -> named impl + enforced
index.ts barrel (default re-exported as a named export):
EmojiPicker, ErrorDetailModal, WindowControls, LanTransferPopup,
AgentChatNavbar, WebSearch CompressionSettings.
- Route-bound pages -> named file, route deep-imports, no barrel:
paintings (PaintingPage); settings ChannelsSettings, CommonSettings,
FileProcessingSettings, IntegrationSettings, WebSearchSettings; home Tabs
(HomeTabs, whose internals are already deep-imported).
- settings/index.tsx (a styles + SettingsPrimitives passthrough masquerading
as a barrel) -> settingsStyles.ts for the layout class-name constants; drop
the redundant primitives passthrough and repoint 53 consumers straight to
@renderer/components/SettingsPrimitives.
- McpSettings shell index.tsx -> McpSettingsPage.tsx (avoids colliding with
the sibling McpSettings.tsx form).
- TopView index.tsx -> TopView.tsx; toast stays a separately deep-imported
surface (no barrel, since its internals are consumed externally).
- RichEditor index.tsx -> RichEditor.tsx with no barrel: its light satellites
(useRichTextEditorKernel, the Placeholder extension) are consumed standalone
and must not drag the 569-line editor through a barrel.
Consumers, tests, and vi.mock factories updated for the default->named
conversions and path changes. Renderer typecheck + full test suite (14398
tests) green.
src/renderer/pages/code/index.ts was a barrel carrying ~280 lines of
implementation plus a dead `export { default } from './CodeCliPage'`: the
route deep-imports CodeCliPage.tsx, so nothing consumed the barrel default.
This violates naming conventions §6.4 (the index filename is reserved for
re-export-only barrels, and an unenforced/unused door should not exist).
Dissolve the barrel and route the implementation into two named modules by
concern:
- cliTools.ts -> CLI_TOOLS catalog + provider eligibility
(CLI_TOOL_PROVIDER_MAP, isOpenCodeProvider, *_SUPPORTED_PROVIDERS)
- toolEnvironment.ts -> launch env synthesis
(generateToolEnvironment, parseEnvironmentVariables,
getCodeCliApiBaseUrl, ToolEnvironmentConfig)
pages/code/ now has no index.ts; the route entry stays CodeCliPage.tsx, in
line with the majority of sibling page directories. Also drops the dead
LaunchValidationResult export (zero consumers) and the hidden
CodeCliPage -> index -> CodeCliPage import cycle.
Split the test file to match. toolEnvironment.test.ts no longer needs the
four mocks (CodeCliPage, useCodeCli, LoggerService, react-i18next) that
existed only to survive the barrel pulling CodeCliPage's dependency graph
into a pure-function import.
All import sites updated; full lint + 14398 tests green.
Merge the split src/shared/data/types/file/ tree (fileEntry + ref/* + the
relocated FileHandle) into a single src/shared/data/types/file.ts, matching
the one-module-per-domain convention for data-layer types. Co-locating the
FileHandle addressing types with FileEntry also removes the module-eval cycle
that previously forced eslint-disable barrel/closed pragmas.
Rename the v1 metadata modules to legacyFile for symmetry across layers
(shared/data/types/legacyFile.ts and main/utils/legacyFile.ts).
Dissolve the enforced-barrel violations flagged by naming conventions §6.4
across the five file-related paths, replacing parent-door-over-child-door
barrels with flat named re-exports:
- shared/data/types/file -> single flat module
- shared/types/file -> pure vocabulary (drop handle re-export)
- shared/utils/file -> named door only
- main/utils/file -> clean named door, hoist legacyFile out
- main/services/file -> single facade, flatten tree/ and watcher/
All import sites updated; behavior unchanged (14398 tests green, barrel lint 0).
Replace `export *` with explicit named/type re-exports across the renderer,
main, and shared barrels flagged by the naming §6.4 lint, and give five
`named-only` barrels an explicit named export in place of a forwarded bare
`default` (updating their default-import consumers).
Dissolve two mis-classified bucket barrels instead of enumerating them:
- components/CodeToolbar/hooks: a `hooks/` bucket that fed a nested `export *`
from the component root; the root now re-exports the eight tool-hook leaves.
- pages/knowledge/utils: an aggregator over four unrelated topics; consumers
now deep-import the specific error/group/rag/validate module.
Deferred to their own batches (untouched here): the file-quartet barrels
(shared/data/types/file, main/utils/file) and pages/code/index.ts.
no-export-star 51->4, named-only 6->1; no-nesting 18->17, closed 301->299;
no other barrel rule regressed. typecheck (node/web/aicore) and the full test
suite pass.
Dissolve the aggregator and nested barrels under src/renderer/components/chat
that forced deep-import (rule 2) and nesting (rule 3) violations, and split the
resource-list family so its two layers stop sharing one bucket.
Barrels:
- Delete chat/index.ts and messages/index.ts aggregator doors; both become
no-door containers reached by file path.
- Relocate misfiled units to chat-level siblings: flow/ (home topic graph) and
editing/ (MessageEditingContext), which never belonged to message display.
- Keep the cohesive sub-doors: tools/agent (expanded to cover its cross-door
consumers), primitives (export-* replaced with named re-exports), and
resources (drop the variants nesting).
Resource lists (resources -> resourceList):
- Rename the mislabeled resources/ dir to resourceList/; its contents are
uniformly a ResourceList engine, not "resources".
- Move the generic engine into resourceList/base/ (single door) and leave the
concrete entity lists loose at resourceList/ root with no door. resourceList/
is itself a no-door container, so base/ keeps one clean door with no
parent-over-sub-door conflict.
- Route all consumers through the base door or the loose list files and drop
the dead variants/index.ts door.
The public engine API is unchanged; consumer edits are path-only. Verified with
typecheck, the full renderer test suite (5548 tests), and lint (chat subtree
barrel deep-import warnings now 0).
Re-review of the previous CLI-config fix batch (4fc8948) surfaced three
regressions introduced by that batch itself, plus two sinks the secret
redaction still missed:
- dotenv.ts: quoting a value that needed escaping (contains #, quotes, or
leading/trailing whitespace) injected \\ and \" escapes that the real
`dotenv` package (used by gemini-cli to read the file back) never
unescapes — it only re-expands \n/\r inside double-quoted values. This
silently corrupted Windows-style paths (doubled backslashes) and any
value containing a literal double quote. Fixed by preferring a
single-quoted value (read back 100% literally) whenever it has no
embedded single quote, and only escaping \n/\r in the double-quote
fallback.
- redact.ts: the value-matching regex stopped at the first embedded
apostrophe in a double-quoted value, or at an already-broken/empty
quoted pair, leaving the real secret exposed as an unredacted trailing
fragment in both cases. Fixed by matching the rest of the source line
(smol-toml embeds raw source lines verbatim in its own error messages)
instead of a single quoted token.
- Centralized secret redaction inside parseTomlOrThrow/parseJsonOrThrow
themselves (instead of only at the two call sites that remembered to
wrap it), closing two previously-unredacted sinks:
validateCliConfigDraftForWrite (manual config-text editing) and
draftUpdater.ts's direct parse calls.
- Added missing assertCliConfigCredentials coverage for the
missing-base-URL branches of OpenCode/Qwen/Kimi (only the missing-API-key
branch had a test before).
Every fix has a regression test verified to fail without it; the dotenv
and redact tests exercise the real `dotenv`/`smol-toml` packages directly
rather than hand-crafted strings, to catch this class of drift going
forward.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
Addresses a local code review of the CLI-config subsystem covering 18 findings
across 2 CRITICAL, 9 IMPORTANT, and 7 SUGGESTION items:
- Distinguish missing-file from read-error in readExternal/snapshotFile so a
transient read failure (EACCES/EBUSY) no longer gets misread as "file
doesn't exist," which previously caused the build path to wipe unmanaged
config keys and the rollback path to trash real user config files.
- Stop using the MANAGED (full-wipe) key set on incremental build paths for
Gemini/Qwen/Kimi, which was silently deleting fields Cherry has no UI for.
- Quote dotenv values containing `#` or leading/trailing whitespace so
Gemini's .env file survives round-tripping through external parsers.
- Fail closed (instead of silently defaulting) when OpenCode provider
resolution fails, and when Claude Code is enabled without an API key.
- Restore the custom OpenClaw gateway port preference and fix an initial-load
race in the config draft controller that could misclassify a foreign
config as managed before apiKeys finished loading.
- Redact likely secrets (including multiline TOML triple-quoted values and
Bearer tokens) from parser error messages before they reach logs/UI.
- Write CLI config files with 0600 permissions instead of the platform
default, since they contain credentials.
- Avoid a Codex provider display-name collision with the literal "OpenAI"
sentinel used to encode remoteCompaction.
- Remove the dead Codex "Write Common Config" toggle and its dead OpenClaw
IPC routes (check_health/get_channels/check_update/perform_update), which
had no consumers.
- Harden rollback error logging (don't mask the original write error with a
restore error) and null out empty-shell connection objects instead of
treating them as valid foreign configs.
Each fix ships with a regression test verified to fail without its
corresponding change.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
The dashboard-reopen button was icon-only, inconsistent with the
Launch/Stop buttons next to it. Switch it to the same outline
icon+label style and relabel it "Open OpenClaw" instead of the more
generic "Open Dashboard".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
gemini-cli only loads ~/.gemini/.env (where GEMINI_API_KEY lives) for
directories it considers "trusted", which a freshly launched project
folder never is. Even with security.auth.selectedType configured
correctly, this left users staring at an interactive "Enter Gemini API
Key" prompt instead of a working session. Inject gemini-cli's own
GEMINI_CLI_TRUST_WORKSPACE bypass env var when spawning it, scoped to
this one launched session so it doesn't touch the user's global
gemini-cli trust settings.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
Split components/Selector/ into three §4.2-conformant units (flat Selector.tsx,
ModelSelector/ with an enforced door, flat SelectorShell.tsx), relocate the
tools-wide shared primitives (GenericTools, ClickableFilePath, agent tool types)
from tools/agent/ to tools/shared/, lift the message-parts context API from the
blocks shim barrel into the messages door, delete the EditableNumber forwarding
shim, and rename the remaining pure re-export index.tsx barrels to index.ts.
Launching OpenClaw auto-opens its control dashboard as a miniapp, but
that only happens once at launch time. Closing the miniapp window left
no way back in even though the gateway kept running in the background
(the tool card still showed a Stop button). Add an icon button next to
Stop, shown only while the gateway is running, that re-fetches the
dashboard URL and reopens the miniapp.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
gemini-cli and qwen-code both gate API-key auth behind
security.auth.selectedType in settings.json. The Gemini config writer
never set it, so gemini-cli couldn't recognize the configured key even
though provider/model sync succeeded. Qwen wrote it on connect but
never cleared it on disconnect, leaving a stale auth type behind after
removing the provider. Add the missing write for Gemini and a shared
cleanup helper used by both CLIs' disconnect paths.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
schemas/index.ts and errors/index.ts carried real local declarations — the
`ipcRequestSchemas` runtime registry plus composed types, and the `IpcError`
class plus `IpcErrorCode` — yet sat under the index.ts name, tripping
barrel/index-no-impl (§6.4 rule 1: an index must be a pure re-export barrel).
Unlike the api barrel there is no outer barrel to remove — ipc/ is already an
open namespace — so this is a straight rename to named modules.
- schemas/index.ts -> schemas/ipcSchemas.ts; errors/index.ts -> errors/IpcError.ts.
- Repoint all consumers (18 deep imports + one intra-package import), including
directory-form relative imports (./schemas, ../errors) that resolved via Node's
index lookup.
- Sync the ipc docs: rename path references and reword the "no errors/index.ts
aggregation" prose, since errors/ now has no index at all.
The api/index.ts barrel re-exported only infra types yet required domain DTOs
to deep-import schemas/<domain>, colliding with naming §6.4 rule 2
(enforced-or-no-barrel) and accounting for the bulk of barrel/closed lint
violations. Because schema files carry zod runtime values, any aggregating
barrel drags all 24 domains in on a single import — exactly what the barrel
reform removes.
- Remove api/index.ts; api/ is now an open namespace.
- Flatten infra file names: apiTypes/apiErrors/apiPaths -> types/errors/paths;
schemas/index.ts -> schemas/apiSchemas.ts.
- Rewrite all consumers: 71 deep-import path renames + 106 barrel imports split
to their owning module (types/paths/errors), including two dynamic imports and
the tests/__mocks__ fixtures.
- Sync data docs and codify the schemas/ `_xxx.ts` = cross-resource construct
convention in naming-conventions §3.2.
Uninstalling a CLI tool left its previously enabled provider marked as
current, so the config panel still showed it as active for a tool that
was no longer installed. Clear the CLI config and unset the current
provider once removal succeeds.
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
The launch/stop button was hardcoded to text-muted-foreground, only
turning full-opacity on hover. Use text-foreground while idle and
text-destructive while running so the button state is visible at rest.
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
Extract shared isCherryManagedModel / findCherryProviderKey helpers and route CHERRY_-prefixed env filtering through the existing omitKeysByPrefix, replacing duplicated predicates across builders/clear/parser/draftUpdater. Pure refactor; behavior unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
Add an inline `barrel` ESLint plugin (same pattern as the lifecycle plugin) implementing §6.4 rules 1-3: no-export-star; index-no-impl (a barrel is pure re-export — no default/local exports, no side-effect imports, no top-level logic); no-index-tsx (no exceptions — index routes use the flat dot form); named-only; closed (sole-entry boundary, judged against the outermost crossed barrel dir so a nested barrel's index cannot bypass its parent's door); no-nesting; no-bucket-root. Barrel discovery classifies pure re-export index.ts files once at config load; import resolution covers relative specs plus @renderer/@main/@shared/@data, with deliberately-unresolved single-file aliases documented inline. All rules start at warn and tests are exempt by design; a full-src sweep reports 0 errors, with warnings matching the audited migration backlog.
src/main/index.ts → main.ts and src/preload/index.ts → preload.ts, so the process entries stop occupying the barrel-reserved index filename (§6.4) without needing an exemption list. The rename is wired through electron.vite (main build.lib.entry — rollupOptions.input would bypass electron-vite's output-format detection — and the preload input key), package.json main (out/main/main.js), WindowManager's default preload filename, MainWindowService's webview preload path, and DbService's slow-query stack filter, which matches the bundled artifact name. preload.d.ts becomes globals.d.ts: sharing preload.ts's basename makes TypeScript drop the .d.ts as that file's presumed output declaration, and tsgo only applies its global Window augmentation when the declaration sorts before its importer, so the file keeps a distinct, alphabetically-early name. Entry-path mentions in code comments and reference docs follow.
Rename the four directory-form index routes (settings/, settings/mcp/, app/mini-app/, app/paintings/) to TanStack's default-supported flat dot form (settings.index.tsx, …) so no file under routes/ is named index.* and the §6.4 iron rule — the index filename is reserved for barrels — holds across src with no exceptions. URLs and route ids are unchanged; routeTree.gen.ts regenerates with only the four import paths moved. Naming §6.4 drops the routes/ carve-out, §6.6 documents the dot-form token, and the routes READMEs replace a stale bare-index example.
CLI_TOOLS.label was a hardcoded English string rendered directly in the
sidebar. Store an i18n key instead and resolve it with t() at the point
of use, matching the existing convention for provider display names.
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
Merges the inject.ts and draft.ts write pipelines (removing the circular
fallback between them), dedupes small helper functions repeated across
builders/clear/parser/values, unifies sanitizeProviderName between the
renderer and main process to remove a cross-process drift risk, and
consolidates the OpenCode npm mapping and per-tool baseUrl resolution
into single-source-of-truth tables in resolvers.ts.
Signed-off-by: eeee0717 <chentao020717Work@outlook.com>