mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-17 03:57:18 +00:00
* feat(core): add generic atomicWriteFile and wire into Write/Edit tools The Write and Edit tools used bare fs.writeFile, risking half-written corrupt files on crash or power loss. Both tools' source code contained explicit TODOs noting atomic write as the fix. - Add atomicWriteFile() supporting string/Buffer with flush (fsync), permission preservation, symlink resolution, and EXDEV fallback - Wire StandardFileSystemService.writeTextFile() through atomicWriteFile - Refactor atomicWriteJSON to delegate to atomicWriteFile (adds fsync) - Deduplicate renameWithRetry from runtimeStatus.ts - Add flush:true to writeWithBackupSync for settings writes - Upgrade @types/node to ^22.0.0 (flush option type support) Closes the TODO in write-file.ts:371-385 and edit.ts:487-497. Ref: #4095 (Phase 1) 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(core): address review comments on atomicWriteFile - Fix permission window: separate existingMode from desiredMode so mode is set during writeFile (not just chmod after), eliminating the brief window where tmp file has overly permissive defaults - Fix broken symlink handling: use lstat+readlink instead of realpath to correctly resolve symlinks whose targets don't exist yet, preventing the symlink from being replaced by rename - Add test for writing through a broken symlink 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(core): address wenshao review on atomicWriteFile - Fix Windows bug: use path.isAbsolute() instead of startsWith('/') - Hoist path import to top-level static import - Resolve full symlink chains via loop (handles A→B→C), with ELOOP guard at 40 hops matching POSIX SYMLOOP_MAX - Mask stat.mode with 0o7777 to strip file-type bits - Document EXDEV fallback atomicity loss in JSDoc - Add tests for relative symlinks and multi-level symlink chains 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(test): fix CI failures from atomic write changes - edit.test.ts: mock writeTextFile instead of chmod 444 for write error test — atomic write creates tmp file in same dir, so readonly target no longer triggers a write error - atomicFileWrite.test.ts: skip permission tests on Windows — chmod is a no-op and stat.mode always returns 0o666 * fix(core): address deepseek review on atomicWriteFile - Add try/catch around chmod calls to handle FAT/exFAT filesystems where POSIX permissions are not supported - Add explicit type annotation to lstats variable * fix: restore version numbers to 0.15.11 after rebase * fix(core): resolve relative symlinks through directory symlinks resolveSymlinkChain used path.dirname() to resolve relative symlink targets, which is purely string-based. When intermediate path components are themselves directory symlinks, the result would be wrong (e.g. /a/link/file → ../target resolves to /a/target instead of the kernel-resolved /b/target). Use fs.realpath() on the parent directory to get the kernel-resolved base for relative-target resolution. * fix(test): normalize path separators in directory symlink test Windows readlink returns native separators (backslashes), causing the directory-symlink test to fail on Windows CI. Wrap both sides of the symlink-target comparison with path.normalize. * refactor(core): dedupe write/chmod logic in atomicWriteFile - Extract writeOptions construction and tryChmod helper, removing duplication between the main write path and the EXDEV fallback - Document atomicWriteJSON's symlink-preservation behavior Addresses deepseek review on PR #4096.
121 lines
3.4 KiB
JSON
121 lines
3.4 KiB
JSON
{
|
|
"name": "@qwen-code/qwen-code",
|
|
"version": "0.15.11",
|
|
"description": "Qwen Code",
|
|
"repository": {
|
|
"type": "git",
|
|
"url": "git+https://github.com/QwenLM/qwen-code.git"
|
|
},
|
|
"type": "module",
|
|
"main": "dist/index.js",
|
|
"types": "dist/index.d.ts",
|
|
"bin": {
|
|
"qwen": "dist/index.js"
|
|
},
|
|
"exports": {
|
|
".": {
|
|
"types": "./dist/index.d.ts",
|
|
"import": "./dist/index.js"
|
|
},
|
|
"./export": {
|
|
"types": "./dist/src/export/index.d.ts",
|
|
"import": "./dist/src/export/index.js"
|
|
}
|
|
},
|
|
"scripts": {
|
|
"build": "node ../../scripts/build_package.js",
|
|
"start": "node dist/index.js",
|
|
"debug": "node --inspect-brk dist/index.js",
|
|
"lint": "eslint . --ext .ts,.tsx",
|
|
"format": "prettier --write .",
|
|
"test": "vitest run",
|
|
"test:ci": "vitest run",
|
|
"typecheck": "tsc --noEmit",
|
|
"check-i18n": "tsx ../../scripts/check-i18n.ts"
|
|
},
|
|
"files": [
|
|
"dist"
|
|
],
|
|
"config": {
|
|
"sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.15.11"
|
|
},
|
|
"dependencies": {
|
|
"@agentclientprotocol/sdk": "^0.14.1",
|
|
"@google/genai": "1.30.0",
|
|
"@iarna/toml": "^2.2.5",
|
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
"@qwen-code/channel-base": "file:../channels/base",
|
|
"@qwen-code/channel-dingtalk": "file:../channels/dingtalk",
|
|
"@qwen-code/channel-telegram": "file:../channels/telegram",
|
|
"@qwen-code/channel-weixin": "file:../channels/weixin",
|
|
"@qwen-code/qwen-code-core": "file:../core",
|
|
"@qwen-code/web-templates": "file:../web-templates",
|
|
"@types/update-notifier": "^6.0.8",
|
|
"ansi-regex": "^6.2.2",
|
|
"command-exists": "^1.2.9",
|
|
"comment-json": "^4.2.5",
|
|
"diff": "^7.0.0",
|
|
"dotenv": "^17.1.0",
|
|
"express": "^5.2.1",
|
|
"fzf": "^0.5.2",
|
|
"glob": "^10.5.0",
|
|
"highlight.js": "^11.11.1",
|
|
"ink": "^7.0.3",
|
|
"ink-gradient": "^3.0.0",
|
|
"ink-link": "^4.1.0",
|
|
"ink-spinner": "^5.0.0",
|
|
"lowlight": "^3.3.0",
|
|
"open": "^10.1.2",
|
|
"p-limit": "^7.3.0",
|
|
"prompts": "^2.4.2",
|
|
"react": "^19.2.4",
|
|
"read-package-up": "^11.0.0",
|
|
"shell-quote": "^1.8.3",
|
|
"simple-git": "^3.28.0",
|
|
"string-width": "^7.1.0",
|
|
"strip-ansi": "^7.1.0",
|
|
"strip-json-comments": "^3.1.1",
|
|
"undici": "^6.22.0",
|
|
"update-notifier": "^7.3.1",
|
|
"wrap-ansi": "^10.0.0",
|
|
"yargs": "^17.7.2",
|
|
"zod": "^3.23.8"
|
|
},
|
|
"devDependencies": {
|
|
"@babel/runtime": "^7.27.6",
|
|
"@testing-library/react": "^16.3.0",
|
|
"@types/archiver": "^6.0.3",
|
|
"@types/command-exists": "^1.2.3",
|
|
"@types/diff": "^7.0.2",
|
|
"@types/dotenv": "^6.1.1",
|
|
"@types/express": "^5.0.3",
|
|
"@types/node": "^22.0.0",
|
|
"@types/prompts": "^2.4.9",
|
|
"@types/react": "^19.1.8",
|
|
"@types/react-dom": "^19.1.6",
|
|
"@types/semver": "^7.7.0",
|
|
"@types/shell-quote": "^1.7.5",
|
|
"@types/supertest": "^6.0.3",
|
|
"@types/yargs": "^17.0.32",
|
|
"archiver": "^7.0.1",
|
|
"ink-testing-library": "^4.0.0",
|
|
"jsdom": "^26.1.0",
|
|
"pretty-format": "^30.0.2",
|
|
"react-dom": "^19.1.0",
|
|
"supertest": "^7.2.2",
|
|
"typescript": "^5.3.3",
|
|
"vitest": "^3.1.1"
|
|
},
|
|
"optionalDependencies": {
|
|
"@teddyzhu/clipboard": "^0.0.5",
|
|
"@teddyzhu/clipboard-darwin-arm64": "0.0.5",
|
|
"@teddyzhu/clipboard-darwin-x64": "0.0.5",
|
|
"@teddyzhu/clipboard-linux-arm64-gnu": "0.0.5",
|
|
"@teddyzhu/clipboard-linux-x64-gnu": "0.0.5",
|
|
"@teddyzhu/clipboard-win32-arm64-msvc": "0.0.5",
|
|
"@teddyzhu/clipboard-win32-x64-msvc": "0.0.5"
|
|
},
|
|
"engines": {
|
|
"node": ">=22"
|
|
}
|
|
}
|