Skip to content

feat(storage): support configurable runtime output directory#2127

Open
yiliang114 wants to merge 2 commits intoQwenLM:mainfrom
yiliang114:feat/configurable-runtime-output-dir
Open

feat(storage): support configurable runtime output directory#2127
yiliang114 wants to merge 2 commits intoQwenLM:mainfrom
yiliang114:feat/configurable-runtime-output-dir

Conversation

@yiliang114
Copy link
Collaborator

TLDR

Support configurable runtime output directory, allowing users to redirect runtime output (temp files, debug logs, session data, todos, insights, etc.) to a custom directory via the advanced.runtimeOutputDir setting or the QWEN_RUNTIME_DIR environment variable, while config files (settings.json, oauth, installation_id, etc.) remain at ~/.qwen.

This is an initial implementation for issue #2014, providing flexible runtime output path management for enterprise Agent deployment scenarios.

Dive Deeper

Core Design

  1. Storage class extensions (packages/core/src/config/storage.ts):

    • Added setRuntimeBaseDir() / getRuntimeBaseDir() static methods
    • Supports tilde (~) expansion and relative path resolution (with optional cwd base directory)
    • Priority: QWEN_RUNTIME_DIR env var > setRuntimeBaseDir() value > getGlobalQwenDir() default
    • Uses AsyncLocalStorage to implement runWithRuntimeBaseDir() for isolating runtime paths across concurrent ACP sessions
  2. Path classification:

    • Runtime paths (follow getRuntimeBaseDir()): tmp/, debug/, ide/, projects/, history/, todos/, insights/
    • Config paths (pinned to ~/.qwen): settings.json, oauth_creds.json, installation_id, google_accounts.json, mcp-oauth-tokens.json, commands/, memory.md, bin/, skills/
  3. Settings integration (packages/cli/src/config/settingsSchema.ts + config.ts):

    • Added runtimeOutputDir string setting under the advanced category
    • loadCliConfig() calls Storage.setRuntimeBaseDir() during initialization
  4. ACP session isolation (packages/cli/src/acp-integration/session/Session.ts):

    • Session snapshots the current runtimeBaseDir at construction time
    • prompt() wraps execution in Storage.runWithRuntimeBaseDir() to ensure concurrent sessions have isolated paths
  5. Adapted modules:

    • StaticInsightGenerator: insight output directory
    • insightCommand: projects directory lookup
    • todoWrite: todo file read/write

Test Coverage

  • storage.test.ts: Path resolution, env var priority, tilde expansion, relative paths, null/undefined/empty string reset, runtime path methods, config path stability, AsyncLocalStorage concurrent isolation
  • config.test.ts: loadCliConfig runtimeOutputDir settings integration
  • Session.test.ts: ACP session runWithRuntimeBaseDir invocation verification
  • StaticInsightGenerator.test.ts: Insight output uses runtime directory
  • insightCommand.test.ts: Insight command uses runtime directory
  • todoWrite.test.ts: Todo read/write uses runtime directory

Reviewer Test Plan

  1. Pull the branch and run npm run test to confirm all tests pass
  2. Add to ~/.qwen/settings.json:
    { "advanced": { "runtimeOutputDir": "/tmp/qwen-runtime-test" } }
  3. Start qwen-code and verify debug logs, todo files, etc. are written under /tmp/qwen-runtime-test/
  4. Set env var QWEN_RUNTIME_DIR=/tmp/qwen-env-test and verify the env var takes priority over the setting
  5. Verify ~/.qwen/settings.json and other config files are unaffected
  6. Remove the custom config and verify fallback to the default ~/.qwen directory

Testing Matrix

🍏 🪟 🐧
npm run
npx
Docker
Podman - -
Seatbelt - -

Linked issues / bugs

Closes #2014

…2014)

Add `advanced.runtimeOutputDir` setting and `QWEN_RUNTIME_DIR` env var
to redirect runtime output (temp files, debug logs, session data, todos,
insights) to a custom directory while keeping config files at ~/.qwen.

- Introduce `Storage.setRuntimeBaseDir()` / `getRuntimeBaseDir()` with
  tilde expansion and relative path resolution
- Add `AsyncLocalStorage`-based `runWithRuntimeBaseDir()` for concurrent
  session isolation in ACP integration
- Update all runtime path methods to use `getRuntimeBaseDir()` instead
  of `getGlobalQwenDir()` (temp, debug, ide, projects, history dirs)
- Config paths (settings, oauth, installation_id, etc.) remain pinned
  to `~/.qwen` regardless of runtime dir configuration
- Add comprehensive tests covering path resolution, env var priority,
  async context isolation, and config path stability
@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

📋 Review Summary

This PR introduces a configurable runtime output directory feature, allowing users to redirect temporary files, debug logs, and other runtime data to a custom location via settings or environment variables. The implementation is well-architected with proper path resolution, tilde expansion, and async context isolation for concurrent sessions.

🔍 General Feedback

  • The implementation follows a clean architecture with the Storage class serving as the central point for managing runtime paths
  • Comprehensive test coverage is provided for all the new functionality including path resolution, environment variable priority, and async context isolation
  • Good separation between runtime paths (which can be redirected) and config paths (which remain at ~/.qwen)
  • The AsyncLocalStorage implementation for concurrent session isolation is well-designed

🎯 Specific Feedback

🟡 High

  • File: packages/core/src/config/storage.ts:112 - The resolveRuntimeBaseDir method has a potential security concern where relative paths could lead to directory traversal if untrusted input is accepted. Consider adding validation to ensure resolved paths remain within expected boundaries.
  • File: packages/core/src/config/storage.ts:148 - The priority order (QWEN_RUNTIME_DIR env var > setRuntimeBaseDir() value > getGlobalQwenDir()) is well documented, but consider adding a warning log when the environment variable overrides the settings file value to alert users of potential configuration conflicts.

🟢 Medium

  • File: packages/cli/src/acp-integration/session/Session.ts:140 - The prompt method is wrapped in Storage.runWithRuntimeBaseDir, which is great for isolation, but consider adding error handling around this call to catch any issues with the context switching.
  • File: packages/core/src/config/storage.ts:106 - The documentation for setRuntimeBaseDir mentions passing the project root as cwd for relative path resolution, but there's no validation to ensure the resulting path is secure. Consider adding path validation to prevent access to unintended directories.

🔵 Low

  • File: packages/cli/src/config/settingsSchema.ts:108 - Consider adding a more descriptive title and description for the runtimeOutputDir setting to help users understand its purpose and usage.
  • File: packages/core/src/config/storage.ts:131 - The comment mentions that config paths remain at ~/.qwen regardless of runtime dir configuration, which is good. Consider adding a brief explanation in the README or documentation about which paths are affected by this setting versus which remain constant.

✅ Highlights

  • Excellent test coverage with dedicated test suites for runtime base directory functionality, path resolution, and async context isolation
  • Smart implementation of AsyncLocalStorage for concurrent session isolation in ACP integration
  • Proper separation of concerns with runtime paths vs config paths
  • Good use of tilde expansion and relative path resolution with configurable base directory
  • Well-documented code with clear JSDoc comments explaining the priority order and behavior

@yiliang114 yiliang114 requested a review from DragonnZhang as a code owner March 6, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Support standalone structured error log output for external monitoring integration

1 participant