@dsh-enhanced/dsh-rsi-cli 0.1.48
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 +231 -0
- package/bin/dsh-rsi.js +11 -0
- package/lib/backup.d.ts +16 -0
- package/lib/backup.d.ts.map +1 -0
- package/lib/backup.js +27 -0
- package/lib/backup.js.map +1 -0
- package/lib/diagnose.d.ts +33 -0
- package/lib/diagnose.d.ts.map +1 -0
- package/lib/diagnose.js +145 -0
- package/lib/diagnose.js.map +1 -0
- package/lib/index.d.ts +46 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +592 -0
- package/lib/index.js.map +1 -0
- package/lib/install.d.ts +33 -0
- package/lib/install.d.ts.map +1 -0
- package/lib/install.js +64 -0
- package/lib/install.js.map +1 -0
- package/lib/paths.d.ts +61 -0
- package/lib/paths.d.ts.map +1 -0
- package/lib/paths.js +168 -0
- package/lib/paths.js.map +1 -0
- package/lib/process-guard.d.ts +51 -0
- package/lib/process-guard.d.ts.map +1 -0
- package/lib/process-guard.js +143 -0
- package/lib/process-guard.js.map +1 -0
- package/lib/purge.d.ts +58 -0
- package/lib/purge.d.ts.map +1 -0
- package/lib/purge.js +204 -0
- package/lib/purge.js.map +1 -0
- package/lib/run.d.ts +27 -0
- package/lib/run.d.ts.map +1 -0
- package/lib/run.js +53 -0
- package/lib/run.js.map +1 -0
- package/lib/secrets.d.ts +22 -0
- package/lib/secrets.d.ts.map +1 -0
- package/lib/secrets.js +107 -0
- package/lib/secrets.js.map +1 -0
- package/lib/services.d.ts +89 -0
- package/lib/services.d.ts.map +1 -0
- package/lib/services.js +339 -0
- package/lib/services.js.map +1 -0
- package/lib/update.d.ts +59 -0
- package/lib/update.d.ts.map +1 -0
- package/lib/update.js +160 -0
- package/lib/update.js.map +1 -0
- package/lib/version.d.ts +2 -0
- package/lib/version.d.ts.map +1 -0
- package/lib/version.js +2 -0
- package/lib/version.js.map +1 -0
- package/lib/weburl.d.ts +36 -0
- package/lib/weburl.d.ts.map +1 -0
- package/lib/weburl.js +106 -0
- package/lib/weburl.js.map +1 -0
- package/package.json +54 -0
package/lib/purge.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { readdir, rm } from 'node:fs/promises';
|
|
2
|
+
import { basename, dirname, join } from 'node:path';
|
|
3
|
+
import { createHomeBackup, verifyBackup } from "./backup.js";
|
|
4
|
+
import { backupArchivePath, collectLocalCheckouts, listLifecycleResiduals, listProfiles, listProfilePlugins, profileLogPaths, HOME_LOCK_SUFFIX, TRANSACTION_SUFFIX, } from "./paths.js";
|
|
5
|
+
import { deleteCredentialLocator, scanCredentialLocators } from "./secrets.js";
|
|
6
|
+
import { findRunningProfiles, defaultRunner } from "./run.js";
|
|
7
|
+
import { stopManagedService } from "./services.js";
|
|
8
|
+
import { scanRunningProfiles, terminatePids } from "./process-guard.js";
|
|
9
|
+
export class PurgeError extends Error {
|
|
10
|
+
}
|
|
11
|
+
function utcStamp() {
|
|
12
|
+
return new Date().toISOString().replaceAll(/[-:.]/gu, '').replace(/\.\d{3}Z$/u, 'Z');
|
|
13
|
+
}
|
|
14
|
+
async function listDirectoryOrEmpty(path) {
|
|
15
|
+
try {
|
|
16
|
+
return await readdir(path);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
if (error.code === 'ENOENT')
|
|
20
|
+
return [];
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 彻底卸载(purge)编排:
|
|
26
|
+
* 1. 进程静止检查(活动 host 拒绝执行,不代用户 kill)
|
|
27
|
+
* 2. 停用并注销受管常驻服务(fail-closed:停服失败则不进入删除阶段)
|
|
28
|
+
* 3. tar.gz 备份整个 DSH home(默认开启)
|
|
29
|
+
* 4. 删除 profile/整个 home 及生命周期残留
|
|
30
|
+
* 5. 清理外部凭据库条目(--keep-keychain 保留)
|
|
31
|
+
* 6. 可选卸载全局 @deepseek-ai/dsh host(仅全量模式允许)
|
|
32
|
+
*
|
|
33
|
+
* local checkout 形态:只随 profile 目录移除其中的符号链接,checkout 源码绝不动。
|
|
34
|
+
*/
|
|
35
|
+
export async function runPurge(options) {
|
|
36
|
+
const platform = options.platform ?? process.platform;
|
|
37
|
+
if (platform !== 'darwin' && platform !== 'linux') {
|
|
38
|
+
throw new PurgeError(`dsh-rsi purge 仅支持 macOS 与 Linux,当前平台:${platform}`);
|
|
39
|
+
}
|
|
40
|
+
if (options.profile !== undefined && options.removeHost) {
|
|
41
|
+
throw new PurgeError('--remove-host 只能在全量 purge(不指定 --profile)时使用,避免影响其它 profile 的全局 host');
|
|
42
|
+
}
|
|
43
|
+
const runner = options.runner ?? defaultRunner;
|
|
44
|
+
const allProfiles = await listProfiles(options.dshHome);
|
|
45
|
+
const scope = options.profile === undefined ? 'full' : 'profile';
|
|
46
|
+
if (scope === 'profile' && !allProfiles.includes(options.profile)) {
|
|
47
|
+
throw new PurgeError(`profile 不存在:${options.profile}(DSH home:${options.dshHome})`);
|
|
48
|
+
}
|
|
49
|
+
const targetProfiles = scope === 'full' ? allProfiles : [options.profile];
|
|
50
|
+
// 1. 先停用并注销受管常驻服务(fail-closed:停服失败则不进入删除阶段)。
|
|
51
|
+
// 受管 host 是「本应自动停掉」的进程,旧逻辑把它和临时进程一起当成阻塞,
|
|
52
|
+
// 导致用户明明可以自动停服却被卡在入口。
|
|
53
|
+
const skippedServiceFiles = [];
|
|
54
|
+
const serviceErrors = [];
|
|
55
|
+
for (const profile of targetProfiles) {
|
|
56
|
+
const outcome = await stopManagedService(platform, options.home, profile, runner, options.dryRun);
|
|
57
|
+
skippedServiceFiles.push(...outcome.skipped);
|
|
58
|
+
for (const message of outcome.errors)
|
|
59
|
+
serviceErrors.push(`[${profile}] ${message}`);
|
|
60
|
+
}
|
|
61
|
+
if (serviceErrors.length > 0)
|
|
62
|
+
throw new PurgeError(`受管服务停用失败,已中止(未删除任何数据):\n${serviceErrors.join('\n')}`);
|
|
63
|
+
// 2. 停服后复检残留进程。受管 host 此时应已消失;剩下的要么是可证明归属本 home
|
|
64
|
+
// 的临时 dsh/plugin 命令(proven,可终止),要么是无法证明归属的进程(foreign,
|
|
65
|
+
// fail-closed,绝不自动杀)。
|
|
66
|
+
const guard = await scanRunningProfiles(options.dshHome, targetProfiles, runner);
|
|
67
|
+
if (guard.error !== undefined) {
|
|
68
|
+
throw new PurgeError(`无法检查运行中的 profile 进程:${guard.error}`);
|
|
69
|
+
}
|
|
70
|
+
const foreign = guard.processes.filter(p => p.owner === 'foreign');
|
|
71
|
+
const proven = guard.processes.filter(p => p.owner === 'proven');
|
|
72
|
+
const foreignListing = foreign.map(p => ` PID ${p.pid}: ${p.commandLine}\n ${p.evidence.join('\n ')}`).join('\n');
|
|
73
|
+
if (foreign.length > 0) {
|
|
74
|
+
throw new PurgeError(`发现无法证明归属本 DSH home 的进程,为避免误杀已中止(请手工确认后再 purge):\n${foreignListing}`);
|
|
75
|
+
}
|
|
76
|
+
const terminatedPids = [];
|
|
77
|
+
const failedPids = [];
|
|
78
|
+
if (proven.length > 0) {
|
|
79
|
+
const listing = proven.map(p => ` PID ${p.pid}: ${p.commandLine}\n ${p.evidence.join('\n ')}`).join('\n');
|
|
80
|
+
if (options.dryRun) {
|
|
81
|
+
// dry-run 只列出,不终止。
|
|
82
|
+
}
|
|
83
|
+
else if (options.assumeYes) {
|
|
84
|
+
const outcome = await terminatePids(proven.map(p => p.pid), runner);
|
|
85
|
+
if (outcome.failed.length > 0) {
|
|
86
|
+
throw new PurgeError(`以下残留进程未能安全终止(身份已变化或 KILL 后仍在),已中止:${outcome.failed.join(', ')}\n${listing}`);
|
|
87
|
+
}
|
|
88
|
+
terminatedPids.push(...proven.map(p => p.pid));
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
throw new PurgeError(`发现以下指向本 DSH home 的残留进程(可能是正在执行的 dsh plugin 操作)。` +
|
|
92
|
+
`purge 将删除它们正在使用的目录,请先确认其已结束,或加 --yes 自动终止:\n${listing}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// 兼容旧的整体静止检查:在进程守卫之外再做一次 pgrep 汇总(无 proven/foreign 区分时用)。
|
|
96
|
+
const { active, error } = findRunningProfiles(targetProfiles, runner);
|
|
97
|
+
if (error !== undefined)
|
|
98
|
+
throw new PurgeError(`无法检查运行中的 profile 进程:${error}`);
|
|
99
|
+
if (active.length > 0 && proven.length === 0 && foreign.length === 0) {
|
|
100
|
+
const listing = active.map(process_ => ` PID ${process_.pid}: ${process_.commandLine}`).join('\n');
|
|
101
|
+
throw new PurgeError(`以下 profile host 仍在运行,请先停用服务后再 purge(不代你结束进程):\n${listing}`);
|
|
102
|
+
}
|
|
103
|
+
const report = {
|
|
104
|
+
scope,
|
|
105
|
+
profiles: targetProfiles,
|
|
106
|
+
serviceProfiles: targetProfiles,
|
|
107
|
+
removedPaths: [],
|
|
108
|
+
removedCredentials: [],
|
|
109
|
+
credentialErrors: [],
|
|
110
|
+
skippedServiceFiles,
|
|
111
|
+
serviceErrors,
|
|
112
|
+
keptCheckouts: [],
|
|
113
|
+
hostRemoved: false,
|
|
114
|
+
dryRun: options.dryRun,
|
|
115
|
+
terminatedPids,
|
|
116
|
+
failedPids,
|
|
117
|
+
foreignProcesses: foreign.map(p => p.commandLine),
|
|
118
|
+
};
|
|
119
|
+
// 3. 备份(单 profile 也备份整个 home:其中包含该 profile 全部数据与凭据落盘)
|
|
120
|
+
if (options.backup) {
|
|
121
|
+
const stamp = options.stamp?.() ?? utcStamp();
|
|
122
|
+
const archivePath = backupArchivePath(options.home, stamp);
|
|
123
|
+
if (!options.dryRun) {
|
|
124
|
+
createHomeBackup(options.dshHome, archivePath, runner);
|
|
125
|
+
const bytes = await verifyBackup(archivePath);
|
|
126
|
+
report.backup = { archivePath, bytes };
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
report.backup = { archivePath, bytes: 0 };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// 4. 外部凭据库条目必须在删除文件前扫描(journal 位于 DSH home 内)。
|
|
133
|
+
const locators = options.keepKeychain
|
|
134
|
+
? []
|
|
135
|
+
: await scanCredentialLocators(options.dshHome, scope === 'full' ? undefined : [options.profile]);
|
|
136
|
+
// 5. 删除文件
|
|
137
|
+
if (scope === 'full') {
|
|
138
|
+
// 收集 local checkout(删除前读取符号链接,仅用于报告)
|
|
139
|
+
for (const profile of allProfiles) {
|
|
140
|
+
const plugins = await listProfilePlugins(options.dshHome, profile);
|
|
141
|
+
report.keptCheckouts.push(...collectLocalCheckouts(plugins));
|
|
142
|
+
}
|
|
143
|
+
report.keptCheckouts = [...new Set(report.keptCheckouts)].sort();
|
|
144
|
+
const parentEntries = await listDirectoryOrEmpty(dirname(options.dshHome));
|
|
145
|
+
const tmpEntries = await listDirectoryOrEmpty('/tmp').catch(() => []);
|
|
146
|
+
const residuals = await listLifecycleResiduals(options.dshHome, parentEntries, tmpEntries);
|
|
147
|
+
const targets = [options.dshHome, ...residuals];
|
|
148
|
+
report.removedPaths.push(...targets);
|
|
149
|
+
if (!options.dryRun) {
|
|
150
|
+
for (const target of targets)
|
|
151
|
+
await rm(target, { recursive: true, force: true });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
const profilePath = join(options.dshHome, 'profiles', options.profile);
|
|
156
|
+
const plugins = await listProfilePlugins(options.dshHome, options.profile);
|
|
157
|
+
report.keptCheckouts = collectLocalCheckouts(plugins);
|
|
158
|
+
const targets = [profilePath, ...profileLogPaths(options.dshHome, options.profile)];
|
|
159
|
+
// 单 profile 只清理归属它的失败诊断目录;事务目录可能涉及多 profile,不动。
|
|
160
|
+
const parentEntries = await listDirectoryOrEmpty(dirname(options.dshHome));
|
|
161
|
+
const failedPrefix = `${basename(options.dshHome)}${TRANSACTION_SUFFIX}.failed-${options.profile}-`;
|
|
162
|
+
for (const entry of parentEntries) {
|
|
163
|
+
if (entry.startsWith(failedPrefix))
|
|
164
|
+
targets.push(join(dirname(options.dshHome), entry));
|
|
165
|
+
}
|
|
166
|
+
report.removedPaths.push(...targets);
|
|
167
|
+
if (!options.dryRun) {
|
|
168
|
+
for (const target of targets)
|
|
169
|
+
await rm(target, { recursive: true, force: true });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// 删除后再执行外部凭据库清理(journal 已随备份保存;条目以预先扫描的 locators 为准)。
|
|
173
|
+
for (const locator of locators) {
|
|
174
|
+
const label = `${locator.provider}:${locator.service} (account=${locator.account})`;
|
|
175
|
+
if (options.dryRun) {
|
|
176
|
+
report.removedCredentials.push(label);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const result = deleteCredentialLocator(locator, runner);
|
|
180
|
+
if (result.ok)
|
|
181
|
+
report.removedCredentials.push(label);
|
|
182
|
+
else
|
|
183
|
+
report.credentialErrors.push(`${label}: ${result.detail ?? '删除失败'}`);
|
|
184
|
+
}
|
|
185
|
+
if (report.credentialErrors.length > 0) {
|
|
186
|
+
throw new PurgeError(`文件已删除,但以下凭据条目清理失败,请手工删除:\n${report.credentialErrors.join('\n')}`);
|
|
187
|
+
}
|
|
188
|
+
// 6. 全局 host
|
|
189
|
+
if (options.removeHost) {
|
|
190
|
+
const prefixResult = runner('npm', ['prefix', '-g']);
|
|
191
|
+
report.hostPrefix = prefixResult.stdout.trim() || prefixResult.stderr.trim();
|
|
192
|
+
report.hostRemoved = true;
|
|
193
|
+
if (!options.dryRun) {
|
|
194
|
+
const uninstall = runner('npm', ['uninstall', '-g', '@deepseek-ai/dsh']);
|
|
195
|
+
if (uninstall.status !== 0) {
|
|
196
|
+
throw new PurgeError(`卸载全局 @deepseek-ai/dsh 失败(前缀 ${report.hostPrefix}):${uninstall.stderr.trim()}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return report;
|
|
201
|
+
}
|
|
202
|
+
/** 仅用于外部模块需要时引用(保持路径常量单一来源)。 */
|
|
203
|
+
export { HOME_LOCK_SUFFIX, TRANSACTION_SUFFIX };
|
|
204
|
+
//# sourceMappingURL=purge.js.map
|
package/lib/purge.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"purge.js","sourceRoot":"","sources":["../src/purge.ts"],"names":[],"mappings":"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"}
|
package/lib/run.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface CommandResult {
|
|
2
|
+
status: number | null;
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
}
|
|
6
|
+
export type CommandRunner = (command: string, args: readonly string[]) => CommandResult;
|
|
7
|
+
/** 默认命令执行器:直接 spawn,不经 shell(参数数组天然无注入面)。 */
|
|
8
|
+
export declare const defaultRunner: CommandRunner;
|
|
9
|
+
export interface RunningProcess {
|
|
10
|
+
pid: number;
|
|
11
|
+
commandLine: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 进程静止检查:查找仍在运行的受管 profile host。
|
|
15
|
+
*
|
|
16
|
+
* 先用 `pgrep -f <pattern>`(只取 PID),pattern 为 `--profile <profile>`;
|
|
17
|
+
* 命令行再用 `ps -p <pid> -o command=` 读取(procps/BSD 均支持),
|
|
18
|
+
* 因为部分环境 pgrep -l 会打印线程 comm(如 MainThread)而非完整命令行。
|
|
19
|
+
* dsh-rsi purge 自身命令行也可能带该参数,因此把命令行中含 dsh-rsi / dsh-rsi-cli
|
|
20
|
+
* (旧包名 @dsh-enhanced/rsi-cli 的全局路径同样包含 rsi-cli)的匹配剔除。
|
|
21
|
+
* pgrep 无匹配时退出码 1(属正常);其它非零按执行失败上抛,由调用方 fail-closed。
|
|
22
|
+
*/
|
|
23
|
+
export declare function findRunningProfiles(profiles: readonly string[], runner?: CommandRunner): {
|
|
24
|
+
active: RunningProcess[];
|
|
25
|
+
error?: string;
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=run.d.ts.map
|
package/lib/run.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,aAAa,CAAA;AAEvF,6CAA6C;AAC7C,eAAO,MAAM,aAAa,EAAE,aAO3B,CAAA;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;CACpB;AAMD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,MAAM,GAAE,aAA6B,GACpC;IAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAmB9C"}
|
package/lib/run.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
/** 默认命令执行器:直接 spawn,不经 shell(参数数组天然无注入面)。 */
|
|
3
|
+
export const defaultRunner = (command, args) => {
|
|
4
|
+
const result = spawnSync(command, [...args], { maxBuffer: 16 * 1024 * 1024 });
|
|
5
|
+
return {
|
|
6
|
+
status: result.status,
|
|
7
|
+
stdout: result.stdout?.toString() ?? '',
|
|
8
|
+
stderr: result.stderr?.toString() ?? '',
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
function escapeRegex(value) {
|
|
12
|
+
return value.replaceAll(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 进程静止检查:查找仍在运行的受管 profile host。
|
|
16
|
+
*
|
|
17
|
+
* 先用 `pgrep -f <pattern>`(只取 PID),pattern 为 `--profile <profile>`;
|
|
18
|
+
* 命令行再用 `ps -p <pid> -o command=` 读取(procps/BSD 均支持),
|
|
19
|
+
* 因为部分环境 pgrep -l 会打印线程 comm(如 MainThread)而非完整命令行。
|
|
20
|
+
* dsh-rsi purge 自身命令行也可能带该参数,因此把命令行中含 dsh-rsi / dsh-rsi-cli
|
|
21
|
+
* (旧包名 @dsh-enhanced/rsi-cli 的全局路径同样包含 rsi-cli)的匹配剔除。
|
|
22
|
+
* pgrep 无匹配时退出码 1(属正常);其它非零按执行失败上抛,由调用方 fail-closed。
|
|
23
|
+
*/
|
|
24
|
+
export function findRunningProfiles(profiles, runner = defaultRunner) {
|
|
25
|
+
const active = [];
|
|
26
|
+
for (const profile of profiles) {
|
|
27
|
+
// `[-]-profile` 等价匹配字面量 `--profile`,但模式本身不以 - 开头,
|
|
28
|
+
// Linux procps 与 macOS BSD pgrep 都不会把它当成选项(无需依赖 -- 分隔符)。
|
|
29
|
+
const pattern = `[-]-profile ${escapeRegex(profile)}`;
|
|
30
|
+
const result = runner('pgrep', ['-f', pattern]);
|
|
31
|
+
if (result.status !== 0 && result.status !== 1) {
|
|
32
|
+
return { active: [], error: result.stderr.trim() || `pgrep 退出码 ${result.status}` };
|
|
33
|
+
}
|
|
34
|
+
for (const line of result.stdout.split('\n')) {
|
|
35
|
+
const pid = Number(line.trim());
|
|
36
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
37
|
+
continue;
|
|
38
|
+
const commandLine = readCommandLine(pid, runner);
|
|
39
|
+
if (commandLine.includes('dsh-rsi') || commandLine.includes('rsi-cli') || commandLine.includes('purge.sh'))
|
|
40
|
+
continue;
|
|
41
|
+
active.push({ pid, commandLine });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { active };
|
|
45
|
+
}
|
|
46
|
+
/** 跨平台读取单个 PID 的完整命令行;读取失败时回退占位文本。 */
|
|
47
|
+
function readCommandLine(pid, runner) {
|
|
48
|
+
const shown = runner('ps', ['-p', String(pid), '-o', 'command=']);
|
|
49
|
+
if (shown.status !== 0)
|
|
50
|
+
return `PID ${pid}(命令行不可读)`;
|
|
51
|
+
return shown.stdout.trim() || `PID ${pid}`;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=run.js.map
|
package/lib/run.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAyB,MAAM,oBAAoB,CAAA;AAUrE,6CAA6C;AAC7C,MAAM,CAAC,MAAM,aAAa,GAAkB,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE;IAC5D,MAAM,MAAM,GAA6B,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC,CAAA;IACvG,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;QACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;KACxC,CAAA;AACH,CAAC,CAAA;AAOD,SAAS,WAAW,CAAC,KAAa;IAChC,OAAO,KAAK,CAAC,UAAU,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAA;AACzD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAA2B,EAC3B,SAAwB,aAAa;IAErC,MAAM,MAAM,GAAqB,EAAE,CAAA;IACnC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,kDAAkD;QAClD,yDAAyD;QACzD,MAAM,OAAO,GAAG,eAAe,WAAW,CAAC,OAAO,CAAC,EAAE,CAAA;QACrD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;QAC/C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/C,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,aAAa,MAAM,CAAC,MAAM,EAAE,EAAE,CAAA;QACpF,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;YAC/B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;gBAAE,SAAQ;YAChD,MAAM,WAAW,GAAG,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;YAChD,IAAI,WAAW,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC;gBAAE,SAAQ;YACpH,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAA;QACnC,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,CAAA;AACnB,CAAC;AAED,sCAAsC;AACtC,SAAS,eAAe,CAAC,GAAW,EAAE,MAAqB;IACzD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAA;IACjE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,GAAG,UAAU,CAAA;IACnD,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,OAAO,GAAG,EAAE,CAAA;AAC5C,CAAC"}
|
package/lib/secrets.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { CommandRunner } from './run.ts';
|
|
2
|
+
export type { CommandRunner };
|
|
3
|
+
export interface CredentialLocator {
|
|
4
|
+
provider: 'macos-keychain' | 'linux-secret-service';
|
|
5
|
+
service: string;
|
|
6
|
+
account: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 从 DSH home 内的 setup journal / cleanup 记录反查外部凭据库条目。
|
|
10
|
+
*
|
|
11
|
+
* 不使用 macOS `security dump-keychain`(逐条访问会弹系统授权框),
|
|
12
|
+
* 也不依赖 secret-tool 枚举(libsecret 无通配查询);
|
|
13
|
+
* setup 写入 keychain/secret-service 的每个 service/account 都在 journal 里有权威记录。
|
|
14
|
+
* home 内的 linux-protected-file / windows-dpapi 凭据随 DSH home 一起删除,无需外部清理。
|
|
15
|
+
*/
|
|
16
|
+
export declare function scanCredentialLocators(dshHome: string, profiles?: readonly string[]): Promise<CredentialLocator[]>;
|
|
17
|
+
/** 删除一条 keychain/secret-service 条目;条目不存在视为成功。 */
|
|
18
|
+
export declare function deleteCredentialLocator(locator: CredentialLocator, runner: CommandRunner): {
|
|
19
|
+
ok: boolean;
|
|
20
|
+
detail?: string;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAG7C,YAAY,EAAE,aAAa,EAAE,CAAA;AAE7B,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,gBAAgB,GAAG,sBAAsB,CAAA;IACnD,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB;AASD;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,GAC3B,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAsB9B;AA8CD,iDAAiD;AACjD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,aAAa,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAU3H"}
|
package/lib/secrets.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { CREDENTIAL_SERVICE_PREFIX, profilesDirectory } from "./paths.js";
|
|
4
|
+
/**
|
|
5
|
+
* lark-channel setup 落盘的凭据记录文件名(setup.ts:1637-1642)。
|
|
6
|
+
* journal 记录当前 locator;credential-cleanup 记录轮换时待清理的旧 locator。
|
|
7
|
+
* 两者内容均为 JSON,凭据项位于顶层 locators[]。
|
|
8
|
+
*/
|
|
9
|
+
const JOURNAL_SUFFIXES = ['.lark-setup.journal.json', '.lark-credential-cleanup.json'];
|
|
10
|
+
/**
|
|
11
|
+
* 从 DSH home 内的 setup journal / cleanup 记录反查外部凭据库条目。
|
|
12
|
+
*
|
|
13
|
+
* 不使用 macOS `security dump-keychain`(逐条访问会弹系统授权框),
|
|
14
|
+
* 也不依赖 secret-tool 枚举(libsecret 无通配查询);
|
|
15
|
+
* setup 写入 keychain/secret-service 的每个 service/account 都在 journal 里有权威记录。
|
|
16
|
+
* home 内的 linux-protected-file / windows-dpapi 凭据随 DSH home 一起删除,无需外部清理。
|
|
17
|
+
*/
|
|
18
|
+
export async function scanCredentialLocators(dshHome, profiles) {
|
|
19
|
+
const profileDirectories = profiles ?? await readProfilesOrEmpty(dshHome);
|
|
20
|
+
const wanted = profiles === undefined ? undefined : new Set(profiles);
|
|
21
|
+
const found = new Map();
|
|
22
|
+
for (const profile of profileDirectories) {
|
|
23
|
+
if (wanted !== undefined && !wanted.has(profile))
|
|
24
|
+
continue;
|
|
25
|
+
const directory = profilesDirectory(dshHome);
|
|
26
|
+
for (const file of await readFilesOrEmpty(join(directory, profile))) {
|
|
27
|
+
if (!JOURNAL_SUFFIXES.some(suffix => file.endsWith(suffix)))
|
|
28
|
+
continue;
|
|
29
|
+
const text = await readFile(join(directory, profile, file), 'utf8').catch(() => undefined);
|
|
30
|
+
if (text === undefined)
|
|
31
|
+
continue;
|
|
32
|
+
for (const locator of parseLocators(text)) {
|
|
33
|
+
if (!locator.service.startsWith(CREDENTIAL_SERVICE_PREFIX))
|
|
34
|
+
continue;
|
|
35
|
+
// 单 profile 模式只清理归属该 profile 的条目;
|
|
36
|
+
// setup-preflight(dsh/lark/setup-preflight/...)等无 profile 归属项仅全量清理时删除。
|
|
37
|
+
if (wanted !== undefined && !locator.service.startsWith(`dsh/lark/${profile}/`))
|
|
38
|
+
continue;
|
|
39
|
+
found.set(`${locator.provider}\0${locator.service}\0${locator.account}`, locator);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return [...found.values()].sort((a, b) => a.provider.localeCompare(b.provider) || a.service.localeCompare(b.service) || a.account.localeCompare(b.account));
|
|
44
|
+
}
|
|
45
|
+
async function readProfilesOrEmpty(dshHome) {
|
|
46
|
+
try {
|
|
47
|
+
return await readdir(profilesDirectory(dshHome));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function readFilesOrEmpty(directory) {
|
|
54
|
+
try {
|
|
55
|
+
return await readdir(directory);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function parseLocators(text) {
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = JSON.parse(text);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
const raw = Array.isArray(parsed)
|
|
70
|
+
? parsed
|
|
71
|
+
: typeof parsed === 'object' && parsed !== null && Array.isArray(parsed.locators)
|
|
72
|
+
? parsed.locators
|
|
73
|
+
: [];
|
|
74
|
+
const result = [];
|
|
75
|
+
for (const entry of raw) {
|
|
76
|
+
if (typeof entry !== 'object' || entry === null)
|
|
77
|
+
continue;
|
|
78
|
+
const record = entry;
|
|
79
|
+
if ((record.provider !== 'macos-keychain' && record.provider !== 'linux-secret-service')
|
|
80
|
+
|| typeof record.service !== 'string' || typeof record.account !== 'string')
|
|
81
|
+
continue;
|
|
82
|
+
if (!record.service.startsWith(CREDENTIAL_SERVICE_PREFIX))
|
|
83
|
+
continue;
|
|
84
|
+
result.push({
|
|
85
|
+
provider: record.provider,
|
|
86
|
+
service: record.service,
|
|
87
|
+
account: record.account,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
/** 删除一条 keychain/secret-service 条目;条目不存在视为成功。 */
|
|
93
|
+
export function deleteCredentialLocator(locator, runner) {
|
|
94
|
+
const result = locator.provider === 'macos-keychain'
|
|
95
|
+
? runner('/usr/bin/security', ['delete-generic-password', '-s', locator.service, '-a', locator.account])
|
|
96
|
+
: runner('/usr/bin/secret-tool', ['clear', 'service', locator.service, 'account', locator.account]);
|
|
97
|
+
if (result.status === 0)
|
|
98
|
+
return { ok: true };
|
|
99
|
+
const stderr = result.stderr?.toString().trim() ?? '';
|
|
100
|
+
// macOS security:条目不存在时退出码 44;secret-tool 无精确稳定码,stderr 含 not found/no such。
|
|
101
|
+
if (locator.provider === 'macos-keychain' && result.status === 44)
|
|
102
|
+
return { ok: true };
|
|
103
|
+
if (/not found|no such|could not be found/iu.test(stderr))
|
|
104
|
+
return { ok: true };
|
|
105
|
+
return { ok: false, detail: stderr || `exit ${result.status ?? 'null'}` };
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { CommandRunner } from './run.ts';
|
|
2
|
+
export interface ServiceStopOutcome {
|
|
3
|
+
profile: string;
|
|
4
|
+
/** 已执行/计划执行的动作(dry-run 下同样填充,供报告展示)。 */
|
|
5
|
+
actions: string[];
|
|
6
|
+
/** 归属不明而跳过、需要人工确认的文件(fail-closed)。 */
|
|
7
|
+
skipped: string[];
|
|
8
|
+
errors: string[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 停用并注销一个 profile 的受管常驻服务。
|
|
12
|
+
* macOS:launchctl bootout + 删 ~/Library/LaunchAgents/<label>.plist。
|
|
13
|
+
* Linux:systemctl --user disable --now + reset-failed + 删受管 unit(及同名 .d)+ daemon-reload。
|
|
14
|
+
* 服务未注册/已停止不报错;任何非预期文件归属问题 fail-closed 只报告不删除。
|
|
15
|
+
*/
|
|
16
|
+
export declare function stopManagedService(platform: NodeJS.Platform, home: string, profile: string, runner: CommandRunner, dryRun: boolean): Promise<ServiceStopOutcome>;
|
|
17
|
+
/** 一次运维动作的结果。exit 供 CLI 决定退出码。 */
|
|
18
|
+
export interface ServiceActionOutcome {
|
|
19
|
+
profile: string;
|
|
20
|
+
/** 实际执行(或 dry-run 下计划执行)的命令描述。 */
|
|
21
|
+
actions: string[];
|
|
22
|
+
/** 面向用户的结论行。 */
|
|
23
|
+
messages: string[];
|
|
24
|
+
errors: string[];
|
|
25
|
+
}
|
|
26
|
+
export type ServiceAction = 'start' | 'stop' | 'restart';
|
|
27
|
+
/** 受管服务的注册与运行状态,以及未注册时的成因判定。 */
|
|
28
|
+
export interface ManagedServiceProcessIds {
|
|
29
|
+
pids: number[];
|
|
30
|
+
errors: string[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Return only the exact MainPID owned by an installer-managed Linux user unit.
|
|
34
|
+
* Other processes with the same --profile remain external and must block lifecycle work.
|
|
35
|
+
*/
|
|
36
|
+
export declare function managedServiceProcessIds(platform: NodeJS.Platform, home: string, profiles: readonly string[], runner?: CommandRunner): Promise<ManagedServiceProcessIds>;
|
|
37
|
+
export interface ManagedServicePresence {
|
|
38
|
+
/** 服务定义文件存在且内容确属受管。 */
|
|
39
|
+
managed: boolean;
|
|
40
|
+
/** 定义文件路径(无论是否受管)。 */
|
|
41
|
+
definitionPath: string;
|
|
42
|
+
/** 文件存在但内容不属受管(人工改写或同名占用)。 */
|
|
43
|
+
foreign: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 检查一个 profile 的受管服务定义是否就绪。
|
|
47
|
+
* 只读文件系统,不执行 systemctl / launchctl。
|
|
48
|
+
*/
|
|
49
|
+
export declare function inspectManagedService(platform: NodeJS.Platform, home: string, profile: string): Promise<ManagedServicePresence>;
|
|
50
|
+
/**
|
|
51
|
+
* 对一个已注册的受管服务执行 start / stop / restart。
|
|
52
|
+
*
|
|
53
|
+
* 前置条件是服务定义文件已存在且确属受管:这个命令只切换运行状态,不承担安装
|
|
54
|
+
* 职责,未注册时返回可复制的 dsh-rsi install 指引而不是隐式注册一个
|
|
55
|
+
* 服务——隐式注册会绕过安装器的归属、路径与凭据校验。
|
|
56
|
+
*
|
|
57
|
+
* macOS 用 `launchctl kickstart -k`(重启)/ `kickstart`(启动)/ `bootout`(停止);
|
|
58
|
+
* 停止后 plist 仍在,所以随后可以再 start,这与 purge 的 bootout+删文件不同。
|
|
59
|
+
* Linux 直接用 `systemctl --user start|stop|restart`。
|
|
60
|
+
*/
|
|
61
|
+
export declare function controlManagedService(platform: NodeJS.Platform, home: string, profile: string, action: ServiceAction, runner?: CommandRunner, dryRun?: boolean): Promise<ServiceActionOutcome>;
|
|
62
|
+
/**
|
|
63
|
+
* 解析运维命令的目标 profile 集合。
|
|
64
|
+
* 显式 --profile 优先;否则取 DSH home 下全部 profile。
|
|
65
|
+
*/
|
|
66
|
+
export declare function resolveTargetProfiles(dshHome: string, profile?: string): Promise<string[]>;
|
|
67
|
+
/** 单个日志文件的尾部读取结果。 */
|
|
68
|
+
export interface LogTail {
|
|
69
|
+
path: string;
|
|
70
|
+
/** 文件不存在或为空时为 true,lines 为空。 */
|
|
71
|
+
empty: boolean;
|
|
72
|
+
lines: string[];
|
|
73
|
+
/** 文件总字节数,供用户判断是否被截断。 */
|
|
74
|
+
bytes: number;
|
|
75
|
+
}
|
|
76
|
+
/** 读取单个日志文件的末尾 N 行,只读末尾有界字节,避免把大日志整体载入内存。 */
|
|
77
|
+
export declare function tailLogFile(path: string, lines: number, maxBytes?: number): Promise<LogTail>;
|
|
78
|
+
export interface ProfileLogs {
|
|
79
|
+
profile: string;
|
|
80
|
+
tails: LogTail[];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 读取若干 profile 的受管 stdout/stderr 日志尾部。
|
|
84
|
+
* `errorOnly` 只取 *-host.error.log。
|
|
85
|
+
*/
|
|
86
|
+
export declare function collectLogs(dshHome: string, profiles: readonly string[], lines: number, errorOnly: boolean): Promise<ProfileLogs[]>;
|
|
87
|
+
export declare function formatLogs(collected: readonly ProfileLogs[], lines: number): string;
|
|
88
|
+
export declare function formatServiceOutcomes(outcomes: readonly ServiceActionOutcome[], action: ServiceAction, dryRun: boolean): string;
|
|
89
|
+
//# sourceMappingURL=services.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../src/services.ts"],"names":[],"mappings":"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"}
|