tanmi-dock 0.4.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +302 -0
- package/dist/commands/clean.d.ts +10 -0
- package/dist/commands/clean.d.ts.map +1 -0
- package/dist/commands/clean.js +215 -0
- package/dist/commands/clean.js.map +1 -0
- package/dist/commands/config.d.ts +10 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +105 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/doctor.d.ts +7 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +127 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +153 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/link.d.ts +7 -0
- package/dist/commands/link.d.ts.map +1 -0
- package/dist/commands/link.js +522 -0
- package/dist/commands/link.js.map +1 -0
- package/dist/commands/migrate.d.ts +7 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +137 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/projects.d.ts +7 -0
- package/dist/commands/projects.d.ts.map +1 -0
- package/dist/commands/projects.js +126 -0
- package/dist/commands/projects.js.map +1 -0
- package/dist/commands/repair.d.ts +7 -0
- package/dist/commands/repair.d.ts.map +1 -0
- package/dist/commands/repair.js +256 -0
- package/dist/commands/repair.js.map +1 -0
- package/dist/commands/status.d.ts +7 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +172 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/unlink.d.ts +7 -0
- package/dist/commands/unlink.d.ts.map +1 -0
- package/dist/commands/unlink.js +104 -0
- package/dist/commands/unlink.js.map +1 -0
- package/dist/commands/verify.d.ts +7 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +190 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/core/codepac.d.ts +79 -0
- package/dist/core/codepac.d.ts.map +1 -0
- package/dist/core/codepac.js +188 -0
- package/dist/core/codepac.js.map +1 -0
- package/dist/core/config.d.ts +70 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +210 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/guard.d.ts +18 -0
- package/dist/core/guard.d.ts.map +1 -0
- package/dist/core/guard.js +82 -0
- package/dist/core/guard.js.map +1 -0
- package/dist/core/linker.d.ts +76 -0
- package/dist/core/linker.d.ts.map +1 -0
- package/dist/core/linker.js +263 -0
- package/dist/core/linker.js.map +1 -0
- package/dist/core/parser.d.ts +43 -0
- package/dist/core/parser.d.ts.map +1 -0
- package/dist/core/parser.js +133 -0
- package/dist/core/parser.js.map +1 -0
- package/dist/core/platform.d.ts +100 -0
- package/dist/core/platform.d.ts.map +1 -0
- package/dist/core/platform.js +193 -0
- package/dist/core/platform.js.map +1 -0
- package/dist/core/registry.d.ts +181 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +443 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/store.d.ts +77 -0
- package/dist/core/store.d.ts.map +1 -0
- package/dist/core/store.js +226 -0
- package/dist/core/store.js.map +1 -0
- package/dist/core/transaction.d.ts +106 -0
- package/dist/core/transaction.d.ts.map +1 -0
- package/dist/core/transaction.js +295 -0
- package/dist/core/transaction.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +84 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +174 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +47 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/disk.d.ts +57 -0
- package/dist/utils/disk.d.ts.map +1 -0
- package/dist/utils/disk.js +313 -0
- package/dist/utils/disk.js.map +1 -0
- package/dist/utils/exit-codes.d.ts +42 -0
- package/dist/utils/exit-codes.d.ts.map +1 -0
- package/dist/utils/exit-codes.js +91 -0
- package/dist/utils/exit-codes.js.map +1 -0
- package/dist/utils/fs-utils.d.ts +44 -0
- package/dist/utils/fs-utils.d.ts.map +1 -0
- package/dist/utils/fs-utils.js +107 -0
- package/dist/utils/fs-utils.js.map +1 -0
- package/dist/utils/global-lock.d.ts +28 -0
- package/dist/utils/global-lock.d.ts.map +1 -0
- package/dist/utils/global-lock.js +89 -0
- package/dist/utils/global-lock.js.map +1 -0
- package/dist/utils/lock.d.ts +26 -0
- package/dist/utils/lock.d.ts.map +1 -0
- package/dist/utils/lock.js +68 -0
- package/dist/utils/lock.js.map +1 -0
- package/dist/utils/logger.d.ts +98 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +190 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/prompt.d.ts +32 -0
- package/dist/utils/prompt.d.ts.map +1 -0
- package/dist/utils/prompt.js +131 -0
- package/dist/utils/prompt.js.map +1 -0
- package/package.json +73 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { createInitCommand } from './commands/init.js';
|
|
4
|
+
import { createLinkCommand } from './commands/link.js';
|
|
5
|
+
import { createStatusCommand } from './commands/status.js';
|
|
6
|
+
import { createProjectsCommand } from './commands/projects.js';
|
|
7
|
+
import { createCleanCommand } from './commands/clean.js';
|
|
8
|
+
import { createUnlinkCommand } from './commands/unlink.js';
|
|
9
|
+
import { createConfigCommand } from './commands/config.js';
|
|
10
|
+
import { createMigrateCommand } from './commands/migrate.js';
|
|
11
|
+
import { createDoctorCommand } from './commands/doctor.js';
|
|
12
|
+
import { createVerifyCommand } from './commands/verify.js';
|
|
13
|
+
import { createRepairCommand } from './commands/repair.js';
|
|
14
|
+
import { Transaction } from './core/transaction.js';
|
|
15
|
+
// 信号处理:优雅退出
|
|
16
|
+
let isShuttingDown = false;
|
|
17
|
+
async function gracefulShutdown(signal, exitCode) {
|
|
18
|
+
if (isShuttingDown)
|
|
19
|
+
return;
|
|
20
|
+
isShuttingDown = true;
|
|
21
|
+
console.log(`\n[info] 收到 ${signal} 信号,正在清理...`);
|
|
22
|
+
try {
|
|
23
|
+
const pending = await Transaction.findPending();
|
|
24
|
+
if (pending) {
|
|
25
|
+
console.log('[info] 正在回滚未完成事务...');
|
|
26
|
+
const errors = await pending.rollback();
|
|
27
|
+
if (errors.length > 0) {
|
|
28
|
+
console.error('[warn] 部分回滚失败:');
|
|
29
|
+
errors.forEach((e) => console.error(` - ${e}`));
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
console.log('[ok] 事务已回滚');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
console.error('[err] 清理失败:', err.message);
|
|
38
|
+
}
|
|
39
|
+
process.exit(exitCode);
|
|
40
|
+
}
|
|
41
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT', 130));
|
|
42
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM', 143));
|
|
43
|
+
// 全局异常捕获
|
|
44
|
+
process.on('uncaughtException', (err) => {
|
|
45
|
+
console.error('[err] 发生未预期错误:', err.message);
|
|
46
|
+
if (process.env.DEBUG) {
|
|
47
|
+
console.error(err.stack);
|
|
48
|
+
}
|
|
49
|
+
console.error('[hint] 如问题持续,请运行 tanmi-dock doctor 诊断');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
});
|
|
52
|
+
process.on('unhandledRejection', (reason) => {
|
|
53
|
+
console.error('[err] 未处理的 Promise 拒绝:', reason);
|
|
54
|
+
if (process.env.DEBUG) {
|
|
55
|
+
console.error(reason);
|
|
56
|
+
}
|
|
57
|
+
process.exit(1);
|
|
58
|
+
});
|
|
59
|
+
const program = new Command();
|
|
60
|
+
program
|
|
61
|
+
.name('tanmi-dock')
|
|
62
|
+
.description('集中型第三方库链接管理工具')
|
|
63
|
+
.version('0.1.0')
|
|
64
|
+
.option('-v, --verbose', '输出详细信息')
|
|
65
|
+
.hook('preAction', (thisCommand) => {
|
|
66
|
+
const opts = thisCommand.optsWithGlobals();
|
|
67
|
+
if (opts.verbose) {
|
|
68
|
+
process.env.VERBOSE = '1';
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
// 注册命令
|
|
72
|
+
program.addCommand(createInitCommand());
|
|
73
|
+
program.addCommand(createLinkCommand());
|
|
74
|
+
program.addCommand(createStatusCommand());
|
|
75
|
+
program.addCommand(createProjectsCommand());
|
|
76
|
+
program.addCommand(createCleanCommand());
|
|
77
|
+
program.addCommand(createUnlinkCommand());
|
|
78
|
+
program.addCommand(createConfigCommand());
|
|
79
|
+
program.addCommand(createMigrateCommand());
|
|
80
|
+
program.addCommand(createDoctorCommand());
|
|
81
|
+
program.addCommand(createVerifyCommand());
|
|
82
|
+
program.addCommand(createRepairCommand());
|
|
83
|
+
program.parse();
|
|
84
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TanmiDock 核心类型定义
|
|
3
|
+
*/
|
|
4
|
+
/** 当前配置版本 */
|
|
5
|
+
export declare const CURRENT_CONFIG_VERSION = "1.1.0";
|
|
6
|
+
/** 最低支持版本 */
|
|
7
|
+
export declare const MIN_SUPPORTED_VERSION = "1.0.0";
|
|
8
|
+
/**
|
|
9
|
+
* 全局配置
|
|
10
|
+
*/
|
|
11
|
+
export interface DockConfig {
|
|
12
|
+
version: string;
|
|
13
|
+
initialized: boolean;
|
|
14
|
+
storePath: string;
|
|
15
|
+
cleanStrategy: CleanStrategy;
|
|
16
|
+
unusedDays: number;
|
|
17
|
+
maxStoreSize?: number;
|
|
18
|
+
autoDownload: boolean;
|
|
19
|
+
}
|
|
20
|
+
export type CleanStrategy = 'unreferenced' | 'unused' | 'manual';
|
|
21
|
+
/**
|
|
22
|
+
* 默认配置
|
|
23
|
+
*/
|
|
24
|
+
export declare const DEFAULT_CONFIG: Omit<DockConfig, 'storePath'>;
|
|
25
|
+
/**
|
|
26
|
+
* 注册表
|
|
27
|
+
*/
|
|
28
|
+
export interface Registry {
|
|
29
|
+
version: string;
|
|
30
|
+
projects: Record<string, ProjectInfo>;
|
|
31
|
+
libraries: Record<string, LibraryInfo>;
|
|
32
|
+
stores: Record<string, StoreEntry>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 项目信息
|
|
36
|
+
*/
|
|
37
|
+
export interface ProjectInfo {
|
|
38
|
+
path: string;
|
|
39
|
+
configPath: string;
|
|
40
|
+
lastLinked: string;
|
|
41
|
+
platforms: string[];
|
|
42
|
+
dependencies: DependencyRef[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 依赖引用
|
|
46
|
+
*/
|
|
47
|
+
export interface DependencyRef {
|
|
48
|
+
libName: string;
|
|
49
|
+
commit: string;
|
|
50
|
+
platform: string;
|
|
51
|
+
linkedPath: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 库信息 (旧版,保留兼容)
|
|
55
|
+
* @deprecated 使用 StoreEntry 替代
|
|
56
|
+
*/
|
|
57
|
+
export interface LibraryInfo {
|
|
58
|
+
libName: string;
|
|
59
|
+
commit: string;
|
|
60
|
+
branch: string;
|
|
61
|
+
url: string;
|
|
62
|
+
platforms: string[];
|
|
63
|
+
size: number;
|
|
64
|
+
referencedBy: string[];
|
|
65
|
+
createdAt: string;
|
|
66
|
+
lastAccess: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Store 条目 (新版,按平台存储)
|
|
70
|
+
* key 格式: lib:commit:platform
|
|
71
|
+
*/
|
|
72
|
+
export interface StoreEntry {
|
|
73
|
+
libName: string;
|
|
74
|
+
commit: string;
|
|
75
|
+
platform: string;
|
|
76
|
+
branch: string;
|
|
77
|
+
url: string;
|
|
78
|
+
size: number;
|
|
79
|
+
usedBy: string[];
|
|
80
|
+
unlinkedAt?: number;
|
|
81
|
+
createdAt: string;
|
|
82
|
+
lastAccess: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 空注册表
|
|
86
|
+
*/
|
|
87
|
+
export declare const EMPTY_REGISTRY: Registry;
|
|
88
|
+
/**
|
|
89
|
+
* codepac-dep.json 配置
|
|
90
|
+
*/
|
|
91
|
+
export interface CodepacDep {
|
|
92
|
+
version: string;
|
|
93
|
+
vars?: Record<string, string>;
|
|
94
|
+
repos: {
|
|
95
|
+
common: RepoConfig[];
|
|
96
|
+
};
|
|
97
|
+
actions?: {
|
|
98
|
+
common: ActionConfig[];
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 仓库配置
|
|
103
|
+
*/
|
|
104
|
+
export interface RepoConfig {
|
|
105
|
+
url: string;
|
|
106
|
+
commit: string;
|
|
107
|
+
branch: string;
|
|
108
|
+
dir: string;
|
|
109
|
+
sparse?: object | string;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 动作配置
|
|
113
|
+
*/
|
|
114
|
+
export interface ActionConfig {
|
|
115
|
+
command: string;
|
|
116
|
+
dir: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 解析后的依赖
|
|
120
|
+
*/
|
|
121
|
+
export interface ParsedDependency {
|
|
122
|
+
libName: string;
|
|
123
|
+
commit: string;
|
|
124
|
+
branch: string;
|
|
125
|
+
url: string;
|
|
126
|
+
sparse?: object | string;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 依赖分类状态
|
|
130
|
+
*/
|
|
131
|
+
export declare enum DependencyStatus {
|
|
132
|
+
/** Store 有,项目链接正确 */
|
|
133
|
+
LINKED = "LINKED",
|
|
134
|
+
/** Store 有,项目链接错误 */
|
|
135
|
+
RELINK = "RELINK",
|
|
136
|
+
/** Store 有,项目是目录 */
|
|
137
|
+
REPLACE = "REPLACE",
|
|
138
|
+
/** Store 没有,项目有目录 */
|
|
139
|
+
ABSORB = "ABSORB",
|
|
140
|
+
/** Store 没有,项目也没有 */
|
|
141
|
+
MISSING = "MISSING",
|
|
142
|
+
/** Store 有,项目没有 */
|
|
143
|
+
LINK_NEW = "LINK_NEW"
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 分类后的依赖
|
|
147
|
+
*/
|
|
148
|
+
export interface ClassifiedDependency {
|
|
149
|
+
dependency: ParsedDependency;
|
|
150
|
+
status: DependencyStatus;
|
|
151
|
+
localPath: string;
|
|
152
|
+
storePath: string;
|
|
153
|
+
}
|
|
154
|
+
export type Platform = 'mac' | 'win';
|
|
155
|
+
/**
|
|
156
|
+
* 磁盘信息
|
|
157
|
+
*/
|
|
158
|
+
export interface DiskInfo {
|
|
159
|
+
path: string;
|
|
160
|
+
label?: string;
|
|
161
|
+
total: number;
|
|
162
|
+
free: number;
|
|
163
|
+
isSystem: boolean;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* 初始化状态
|
|
167
|
+
*/
|
|
168
|
+
export interface InitStatus {
|
|
169
|
+
initialized: boolean;
|
|
170
|
+
configExists: boolean;
|
|
171
|
+
storePathExists: boolean;
|
|
172
|
+
storePath?: string;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TanmiDock 核心类型定义
|
|
3
|
+
*/
|
|
4
|
+
// ============ 版本常量 ============
|
|
5
|
+
/** 当前配置版本 */
|
|
6
|
+
export const CURRENT_CONFIG_VERSION = '1.1.0';
|
|
7
|
+
/** 最低支持版本 */
|
|
8
|
+
export const MIN_SUPPORTED_VERSION = '1.0.0';
|
|
9
|
+
/**
|
|
10
|
+
* 默认配置
|
|
11
|
+
*/
|
|
12
|
+
export const DEFAULT_CONFIG = {
|
|
13
|
+
version: '1.0.0',
|
|
14
|
+
initialized: false,
|
|
15
|
+
cleanStrategy: 'unreferenced',
|
|
16
|
+
unusedDays: 30,
|
|
17
|
+
autoDownload: true,
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* 空注册表
|
|
21
|
+
*/
|
|
22
|
+
export const EMPTY_REGISTRY = {
|
|
23
|
+
version: '1.0.0',
|
|
24
|
+
projects: {},
|
|
25
|
+
libraries: {},
|
|
26
|
+
stores: {},
|
|
27
|
+
};
|
|
28
|
+
// ============ 依赖状态 ============
|
|
29
|
+
/**
|
|
30
|
+
* 依赖分类状态
|
|
31
|
+
*/
|
|
32
|
+
export var DependencyStatus;
|
|
33
|
+
(function (DependencyStatus) {
|
|
34
|
+
/** Store 有,项目链接正确 */
|
|
35
|
+
DependencyStatus["LINKED"] = "LINKED";
|
|
36
|
+
/** Store 有,项目链接错误 */
|
|
37
|
+
DependencyStatus["RELINK"] = "RELINK";
|
|
38
|
+
/** Store 有,项目是目录 */
|
|
39
|
+
DependencyStatus["REPLACE"] = "REPLACE";
|
|
40
|
+
/** Store 没有,项目有目录 */
|
|
41
|
+
DependencyStatus["ABSORB"] = "ABSORB";
|
|
42
|
+
/** Store 没有,项目也没有 */
|
|
43
|
+
DependencyStatus["MISSING"] = "MISSING";
|
|
44
|
+
/** Store 有,项目没有 */
|
|
45
|
+
DependencyStatus["LINK_NEW"] = "LINK_NEW";
|
|
46
|
+
})(DependencyStatus || (DependencyStatus = {}));
|
|
47
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,iCAAiC;AAEjC,aAAa;AACb,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAAO,CAAC;AAC9C,aAAa;AACb,MAAM,CAAC,MAAM,qBAAqB,GAAG,OAAO,CAAC;AAmB7C;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAkC;IAC3D,OAAO,EAAE,OAAO;IAChB,WAAW,EAAE,KAAK;IAClB,aAAa,EAAE,cAAc;IAC7B,UAAU,EAAE,EAAE;IACd,YAAY,EAAE,IAAI;CACnB,CAAC;AAoEF;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAa;IACtC,OAAO,EAAE,OAAO;IAChB,QAAQ,EAAE,EAAE;IACZ,SAAS,EAAE,EAAE;IACb,MAAM,EAAE,EAAE;CACX,CAAC;AAgDF,iCAAiC;AAEjC;;GAEG;AACH,MAAM,CAAN,IAAY,gBAaX;AAbD,WAAY,gBAAgB;IAC1B,qBAAqB;IACrB,qCAAiB,CAAA;IACjB,qBAAqB;IACrB,qCAAiB,CAAA;IACjB,oBAAoB;IACpB,uCAAmB,CAAA;IACnB,qBAAqB;IACrB,qCAAiB,CAAA;IACjB,qBAAqB;IACrB,uCAAmB,CAAA;IACnB,mBAAmB;IACnB,yCAAqB,CAAA;AACvB,CAAC,EAbW,gBAAgB,KAAhB,gBAAgB,QAa3B"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { DiskInfo } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* 获取所有磁盘信息
|
|
4
|
+
*/
|
|
5
|
+
export declare function getDiskInfo(): Promise<DiskInfo[]>;
|
|
6
|
+
/**
|
|
7
|
+
* 格式化文件大小
|
|
8
|
+
*/
|
|
9
|
+
export declare function formatSize(bytes: number): string;
|
|
10
|
+
/**
|
|
11
|
+
* 解析大小字符串为字节数
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseSize(sizeStr: string): number;
|
|
14
|
+
/**
|
|
15
|
+
* 检查路径所在磁盘是否有足够空间
|
|
16
|
+
*/
|
|
17
|
+
export declare function hasEnoughSpace(targetPath: string, requiredBytes: number): Promise<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* 获取路径所在磁盘的可用空间
|
|
20
|
+
*/
|
|
21
|
+
export declare function getFreeSpace(targetPath: string): Promise<number>;
|
|
22
|
+
/**
|
|
23
|
+
* 磁盘空间检查结果
|
|
24
|
+
*/
|
|
25
|
+
export interface DiskSpaceCheckResult {
|
|
26
|
+
sufficient: boolean;
|
|
27
|
+
available: number;
|
|
28
|
+
required: number;
|
|
29
|
+
safetyMargin: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 检查磁盘空间是否充足
|
|
33
|
+
* @param targetPath 目标路径
|
|
34
|
+
* @param requiredBytes 需要的字节数
|
|
35
|
+
* @param safetyMarginGB 安全余量(GB),默认 1GB
|
|
36
|
+
*/
|
|
37
|
+
export declare function checkDiskSpace(targetPath: string, requiredBytes: number, safetyMarginGB?: number): Promise<DiskSpaceCheckResult>;
|
|
38
|
+
/**
|
|
39
|
+
* 获取默认 Store 路径建议
|
|
40
|
+
*/
|
|
41
|
+
export declare function getDefaultStorePaths(): Promise<Array<{
|
|
42
|
+
path: string;
|
|
43
|
+
label: string;
|
|
44
|
+
free: number;
|
|
45
|
+
recommended: boolean;
|
|
46
|
+
}>>;
|
|
47
|
+
declare const _default: {
|
|
48
|
+
getDiskInfo: typeof getDiskInfo;
|
|
49
|
+
formatSize: typeof formatSize;
|
|
50
|
+
parseSize: typeof parseSize;
|
|
51
|
+
hasEnoughSpace: typeof hasEnoughSpace;
|
|
52
|
+
getFreeSpace: typeof getFreeSpace;
|
|
53
|
+
checkDiskSpace: typeof checkDiskSpace;
|
|
54
|
+
getDefaultStorePaths: typeof getDefaultStorePaths;
|
|
55
|
+
};
|
|
56
|
+
export default _default;
|
|
57
|
+
//# sourceMappingURL=disk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disk.d.ts","sourceRoot":"","sources":["../../src/utils/disk.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAIlD;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAKvD;AAmMD;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAShD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAgBjD;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAKhG;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGtE;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,cAAc,SAAI,GACjB,OAAO,CAAC,oBAAoB,CAAC,CAsB/B;AAED;;GAEG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CACnD,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,CAC3E,CAuCA;;;;;;;;;;AAED,wBAQE"}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 磁盘空间检测工具
|
|
3
|
+
* - macOS: 读取 / 和 /Volumes/*
|
|
4
|
+
* - Windows: 读取 C:\, D:\ 等盘符
|
|
5
|
+
*/
|
|
6
|
+
import { exec } from 'child_process';
|
|
7
|
+
import { promisify } from 'util';
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import os from 'os';
|
|
11
|
+
import { isWindows } from '../core/platform.js';
|
|
12
|
+
const execAsync = promisify(exec);
|
|
13
|
+
/**
|
|
14
|
+
* 获取所有磁盘信息
|
|
15
|
+
*/
|
|
16
|
+
export async function getDiskInfo() {
|
|
17
|
+
if (isWindows()) {
|
|
18
|
+
return getWindowsDisks();
|
|
19
|
+
}
|
|
20
|
+
return getMacDisks();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* macOS: 获取磁盘信息
|
|
24
|
+
*/
|
|
25
|
+
async function getMacDisks() {
|
|
26
|
+
const disks = [];
|
|
27
|
+
// 获取根分区
|
|
28
|
+
try {
|
|
29
|
+
const rootInfo = await getDiskUsage('/');
|
|
30
|
+
if (rootInfo) {
|
|
31
|
+
disks.push({
|
|
32
|
+
path: '/',
|
|
33
|
+
label: '系统盘',
|
|
34
|
+
total: rootInfo.total,
|
|
35
|
+
free: rootInfo.free,
|
|
36
|
+
isSystem: true,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// 忽略错误
|
|
42
|
+
}
|
|
43
|
+
// 获取 /Volumes 下的其他卷
|
|
44
|
+
try {
|
|
45
|
+
const volumes = await fs.readdir('/Volumes');
|
|
46
|
+
for (const volume of volumes) {
|
|
47
|
+
const volumePath = path.join('/Volumes', volume);
|
|
48
|
+
// 跳过系统盘链接
|
|
49
|
+
try {
|
|
50
|
+
const stat = await fs.lstat(volumePath);
|
|
51
|
+
if (stat.isSymbolicLink())
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const info = await getDiskUsage(volumePath);
|
|
59
|
+
if (info) {
|
|
60
|
+
disks.push({
|
|
61
|
+
path: volumePath,
|
|
62
|
+
label: volume,
|
|
63
|
+
total: info.total,
|
|
64
|
+
free: info.free,
|
|
65
|
+
isSystem: false,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// 忽略无法访问的卷
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// /Volumes 不存在或无法访问
|
|
76
|
+
}
|
|
77
|
+
return disks;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Windows: 获取磁盘信息
|
|
81
|
+
*/
|
|
82
|
+
async function getWindowsDisks() {
|
|
83
|
+
const disks = [];
|
|
84
|
+
try {
|
|
85
|
+
// 使用 wmic 获取磁盘信息
|
|
86
|
+
const { stdout } = await execAsync('wmic logicaldisk get caption,freespace,size,volumename /format:csv', { encoding: 'utf8' });
|
|
87
|
+
const lines = stdout.trim().split('\n').slice(1); // 跳过表头
|
|
88
|
+
for (const line of lines) {
|
|
89
|
+
const parts = line.trim().split(',');
|
|
90
|
+
if (parts.length < 5)
|
|
91
|
+
continue;
|
|
92
|
+
const [, caption, freeSpace, size, volumeName] = parts;
|
|
93
|
+
if (!caption || !size)
|
|
94
|
+
continue;
|
|
95
|
+
const drivePath = caption + '\\';
|
|
96
|
+
const total = parseInt(size, 10);
|
|
97
|
+
const free = parseInt(freeSpace, 10) || 0;
|
|
98
|
+
// C: 通常是系统盘
|
|
99
|
+
const isSystem = caption.toUpperCase() === 'C:';
|
|
100
|
+
disks.push({
|
|
101
|
+
path: drivePath,
|
|
102
|
+
label: volumeName || (isSystem ? '系统盘' : '本地磁盘'),
|
|
103
|
+
total: isNaN(total) ? 0 : total,
|
|
104
|
+
free: isNaN(free) ? 0 : free,
|
|
105
|
+
isSystem,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// wmic 命令失败,尝试备用方法
|
|
111
|
+
try {
|
|
112
|
+
const drives = ['C:', 'D:', 'E:', 'F:', 'G:', 'H:'];
|
|
113
|
+
for (const drive of drives) {
|
|
114
|
+
try {
|
|
115
|
+
const info = await getDiskUsage(drive + '\\');
|
|
116
|
+
if (info) {
|
|
117
|
+
disks.push({
|
|
118
|
+
path: drive + '\\',
|
|
119
|
+
label: drive === 'C:' ? '系统盘' : '本地磁盘',
|
|
120
|
+
total: info.total,
|
|
121
|
+
free: info.free,
|
|
122
|
+
isSystem: drive === 'C:',
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// 磁盘不存在
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// 忽略错误
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return disks;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* 获取指定路径的磁盘使用情况
|
|
139
|
+
*/
|
|
140
|
+
async function getDiskUsage(targetPath) {
|
|
141
|
+
if (isWindows()) {
|
|
142
|
+
return getWindowsDiskUsage(targetPath);
|
|
143
|
+
}
|
|
144
|
+
return getMacDiskUsage(targetPath);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* macOS: 使用 df 命令获取磁盘使用情况
|
|
148
|
+
*/
|
|
149
|
+
async function getMacDiskUsage(targetPath) {
|
|
150
|
+
try {
|
|
151
|
+
const { stdout } = await execAsync(`df -k "${targetPath}"`, { encoding: 'utf8' });
|
|
152
|
+
const lines = stdout.trim().split('\n');
|
|
153
|
+
if (lines.length < 2)
|
|
154
|
+
return null;
|
|
155
|
+
// 解析 df 输出: Filesystem 1K-blocks Used Available Use% Mounted
|
|
156
|
+
const parts = lines[1].split(/\s+/);
|
|
157
|
+
if (parts.length < 4)
|
|
158
|
+
return null;
|
|
159
|
+
const totalBlocks = parseInt(parts[1], 10);
|
|
160
|
+
const availableBlocks = parseInt(parts[3], 10);
|
|
161
|
+
return {
|
|
162
|
+
total: totalBlocks * 1024,
|
|
163
|
+
free: availableBlocks * 1024,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Windows: 获取磁盘使用情况
|
|
172
|
+
*/
|
|
173
|
+
async function getWindowsDiskUsage(targetPath) {
|
|
174
|
+
try {
|
|
175
|
+
// 提取盘符
|
|
176
|
+
const drive = targetPath.match(/^([A-Za-z]:)/)?.[1];
|
|
177
|
+
if (!drive)
|
|
178
|
+
return null;
|
|
179
|
+
const { stdout } = await execAsync(`wmic logicaldisk where "caption='${drive}'" get freespace,size /format:csv`, { encoding: 'utf8' });
|
|
180
|
+
const lines = stdout.trim().split('\n').slice(1);
|
|
181
|
+
if (lines.length === 0)
|
|
182
|
+
return null;
|
|
183
|
+
const parts = lines[0].trim().split(',');
|
|
184
|
+
if (parts.length < 3)
|
|
185
|
+
return null;
|
|
186
|
+
const [, freeSpace, size] = parts;
|
|
187
|
+
return {
|
|
188
|
+
total: parseInt(size, 10) || 0,
|
|
189
|
+
free: parseInt(freeSpace, 10) || 0,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* 格式化文件大小
|
|
198
|
+
*/
|
|
199
|
+
export function formatSize(bytes) {
|
|
200
|
+
if (bytes === 0)
|
|
201
|
+
return '0 B';
|
|
202
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
203
|
+
const k = 1024;
|
|
204
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
205
|
+
const size = bytes / Math.pow(k, i);
|
|
206
|
+
return `${size.toFixed(1)} ${units[i]}`;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* 解析大小字符串为字节数
|
|
210
|
+
*/
|
|
211
|
+
export function parseSize(sizeStr) {
|
|
212
|
+
const match = sizeStr.match(/^([\d.]+)\s*(B|KB|MB|GB|TB)?$/i);
|
|
213
|
+
if (!match)
|
|
214
|
+
return 0;
|
|
215
|
+
const value = parseFloat(match[1]);
|
|
216
|
+
const unit = (match[2] || 'B').toUpperCase();
|
|
217
|
+
const units = {
|
|
218
|
+
B: 1,
|
|
219
|
+
KB: 1024,
|
|
220
|
+
MB: 1024 * 1024,
|
|
221
|
+
GB: 1024 * 1024 * 1024,
|
|
222
|
+
TB: 1024 * 1024 * 1024 * 1024,
|
|
223
|
+
};
|
|
224
|
+
return Math.floor(value * (units[unit] || 1));
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* 检查路径所在磁盘是否有足够空间
|
|
228
|
+
*/
|
|
229
|
+
export async function hasEnoughSpace(targetPath, requiredBytes) {
|
|
230
|
+
const usage = await getDiskUsage(targetPath);
|
|
231
|
+
if (!usage)
|
|
232
|
+
return true; // 无法获取时假设足够
|
|
233
|
+
return usage.free >= requiredBytes;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 获取路径所在磁盘的可用空间
|
|
237
|
+
*/
|
|
238
|
+
export async function getFreeSpace(targetPath) {
|
|
239
|
+
const usage = await getDiskUsage(targetPath);
|
|
240
|
+
return usage?.free ?? 0;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* 检查磁盘空间是否充足
|
|
244
|
+
* @param targetPath 目标路径
|
|
245
|
+
* @param requiredBytes 需要的字节数
|
|
246
|
+
* @param safetyMarginGB 安全余量(GB),默认 1GB
|
|
247
|
+
*/
|
|
248
|
+
export async function checkDiskSpace(targetPath, requiredBytes, safetyMarginGB = 1) {
|
|
249
|
+
const safetyMargin = safetyMarginGB * 1024 * 1024 * 1024;
|
|
250
|
+
const available = await getFreeSpace(targetPath);
|
|
251
|
+
// 如果无法获取磁盘信息(available 为 0),放行操作
|
|
252
|
+
if (available === 0) {
|
|
253
|
+
return {
|
|
254
|
+
sufficient: true,
|
|
255
|
+
available: 0,
|
|
256
|
+
required: requiredBytes,
|
|
257
|
+
safetyMargin,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
const sufficient = available > requiredBytes + safetyMargin;
|
|
261
|
+
return {
|
|
262
|
+
sufficient,
|
|
263
|
+
available,
|
|
264
|
+
required: requiredBytes,
|
|
265
|
+
safetyMargin,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* 获取默认 Store 路径建议
|
|
270
|
+
*/
|
|
271
|
+
export async function getDefaultStorePaths() {
|
|
272
|
+
const disks = await getDiskInfo();
|
|
273
|
+
const home = os.homedir();
|
|
274
|
+
const suggestions = [];
|
|
275
|
+
// 默认路径: ~/.tanmi-dock/store
|
|
276
|
+
const defaultPath = path.join(home, '.tanmi-dock', 'store');
|
|
277
|
+
const homeDisk = disks.find((d) => defaultPath.startsWith(d.path) || d.isSystem);
|
|
278
|
+
suggestions.push({
|
|
279
|
+
path: defaultPath,
|
|
280
|
+
label: '默认位置',
|
|
281
|
+
free: homeDisk?.free ?? 0,
|
|
282
|
+
recommended: false,
|
|
283
|
+
});
|
|
284
|
+
// 非系统盘的建议
|
|
285
|
+
const nonSystemDisks = disks.filter((d) => !d.isSystem && d.free > 10 * 1024 * 1024 * 1024); // 至少 10GB
|
|
286
|
+
for (const disk of nonSystemDisks) {
|
|
287
|
+
const storePath = path.join(disk.path, '.tanmi-dock', 'store');
|
|
288
|
+
suggestions.push({
|
|
289
|
+
path: storePath,
|
|
290
|
+
label: disk.label || disk.path,
|
|
291
|
+
free: disk.free,
|
|
292
|
+
recommended: true, // 非系统盘通常空间更大
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
// 按可用空间排序,推荐的排前面
|
|
296
|
+
suggestions.sort((a, b) => {
|
|
297
|
+
if (a.recommended !== b.recommended) {
|
|
298
|
+
return a.recommended ? -1 : 1;
|
|
299
|
+
}
|
|
300
|
+
return b.free - a.free;
|
|
301
|
+
});
|
|
302
|
+
return suggestions;
|
|
303
|
+
}
|
|
304
|
+
export default {
|
|
305
|
+
getDiskInfo,
|
|
306
|
+
formatSize,
|
|
307
|
+
parseSize,
|
|
308
|
+
hasEnoughSpace,
|
|
309
|
+
getFreeSpace,
|
|
310
|
+
checkDiskSpace,
|
|
311
|
+
getDefaultStorePaths,
|
|
312
|
+
};
|
|
313
|
+
//# sourceMappingURL=disk.js.map
|