create-principles-disciple 1.132.2 → 1.132.3
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/console/dist/server/routes/update.js +187 -19
- package/console/dist/server/update/mutation-controller.d.ts +18 -0
- package/console/dist/server/update/mutation-controller.js +29 -0
- package/console/dist/server/utils/installed-layout.d.ts +1 -0
- package/console/dist/server/utils/installed-layout.js +2 -0
- package/console/package.json +1 -0
- package/core/dist/runtime-v2/__tests__/architecture-regression.test.js +15 -0
- package/core/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
- package/core/dist/runtime-v2/adapter/__tests__/pd-stream-simple.test.d.ts +2 -0
- package/core/dist/runtime-v2/adapter/__tests__/pd-stream-simple.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/adapter/__tests__/pd-stream-simple.test.js +71 -0
- package/core/dist/runtime-v2/adapter/__tests__/pd-stream-simple.test.js.map +1 -0
- package/core/dist/runtime-v2/adapter/__tests__/pi-ai-http-transport.test.d.ts +2 -0
- package/core/dist/runtime-v2/adapter/__tests__/pi-ai-http-transport.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/adapter/__tests__/pi-ai-http-transport.test.js +219 -0
- package/core/dist/runtime-v2/adapter/__tests__/pi-ai-http-transport.test.js.map +1 -0
- package/core/dist/runtime-v2/adapter/artificer-l2-adapter.d.ts.map +1 -1
- package/core/dist/runtime-v2/adapter/artificer-l2-adapter.js +2 -3
- package/core/dist/runtime-v2/adapter/artificer-l2-adapter.js.map +1 -1
- package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.d.ts +12 -1
- package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.d.ts.map +1 -1
- package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.js +15 -2
- package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.js.map +1 -1
- package/core/dist/runtime-v2/adapter/pi-ai-http-transport.d.ts +50 -0
- package/core/dist/runtime-v2/adapter/pi-ai-http-transport.d.ts.map +1 -0
- package/core/dist/runtime-v2/adapter/pi-ai-http-transport.js +93 -0
- package/core/dist/runtime-v2/adapter/pi-ai-http-transport.js.map +1 -0
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts.map +1 -1
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js +5 -0
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js +152 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-review.d.ts +7 -1
- package/core/dist/runtime-v2/internalization/owner-review.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-review.js +22 -3
- package/core/dist/runtime-v2/internalization/owner-review.js.map +1 -1
- package/core/package.json +3 -2
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +50 -0
- package/dist/installer.js.map +1 -1
- package/dist/update/release-manager-authority.d.ts +69 -0
- package/dist/update/release-manager-authority.d.ts.map +1 -0
- package/dist/update/release-manager-authority.js +125 -0
- package/dist/update/release-manager-authority.js.map +1 -0
- package/dist/utils/workspace-divergence.d.ts +36 -0
- package/dist/utils/workspace-divergence.d.ts.map +1 -0
- package/dist/utils/workspace-divergence.js +87 -0
- package/dist/utils/workspace-divergence.js.map +1 -0
- package/install-layout/dist/index.d.ts +5 -0
- package/install-layout/dist/index.js +1 -0
- package/install-layout/package.json +1 -1
- package/package.json +3 -2
- package/plugin/dist/bundle.js +870 -734
- package/plugin/dist/governance-audit.js +381 -247
- package/plugin/dist/openclaw-sdk.d.ts +3 -0
- package/plugin/dist/rulehost-evidence.js +420 -286
- package/plugin/dist/service/subagent-workflow/types.d.ts +2 -2
- package/plugin/dist/utils/retry.d.ts +1 -1
- package/plugin/dist/utils/workspace-resolver.d.ts +35 -4
- package/plugin/openclaw.plugin.json +1 -1
- package/plugin/package.json +1 -1
- package/release-manager/dist/i18n.d.ts +6 -0
- package/release-manager/dist/i18n.d.ts.map +1 -0
- package/release-manager/dist/i18n.js +234 -0
- package/release-manager/dist/i18n.js.map +1 -0
- package/release-manager/dist/index.d.ts +7 -0
- package/release-manager/dist/index.d.ts.map +1 -0
- package/release-manager/dist/index.js +474 -0
- package/release-manager/dist/index.js.map +1 -0
- package/release-manager/dist/installer.d.ts +185 -0
- package/release-manager/dist/installer.d.ts.map +1 -0
- package/release-manager/dist/installer.js +2305 -0
- package/release-manager/dist/installer.js.map +1 -0
- package/release-manager/dist/installers/codex-host-installer.d.ts +29 -0
- package/release-manager/dist/installers/codex-host-installer.d.ts.map +1 -0
- package/release-manager/dist/installers/codex-host-installer.js +494 -0
- package/release-manager/dist/installers/codex-host-installer.js.map +1 -0
- package/release-manager/dist/installers/index.d.ts +40 -0
- package/release-manager/dist/installers/index.d.ts.map +1 -0
- package/release-manager/dist/installers/index.js +35 -0
- package/release-manager/dist/installers/index.js.map +1 -0
- package/release-manager/dist/installers/openclaw-host-installer.d.ts +18 -0
- package/release-manager/dist/installers/openclaw-host-installer.d.ts.map +1 -0
- package/release-manager/dist/installers/openclaw-host-installer.js +414 -0
- package/release-manager/dist/installers/openclaw-host-installer.js.map +1 -0
- package/release-manager/dist/mvp-config.d.ts +197 -0
- package/release-manager/dist/mvp-config.d.ts.map +1 -0
- package/release-manager/dist/mvp-config.js +509 -0
- package/release-manager/dist/mvp-config.js.map +1 -0
- package/release-manager/dist/prompts.d.ts +30 -0
- package/release-manager/dist/prompts.d.ts.map +1 -0
- package/release-manager/dist/prompts.js +231 -0
- package/release-manager/dist/prompts.js.map +1 -0
- package/release-manager/dist/skill-language.d.ts +32 -0
- package/release-manager/dist/skill-language.d.ts.map +1 -0
- package/release-manager/dist/skill-language.js +107 -0
- package/release-manager/dist/skill-language.js.map +1 -0
- package/release-manager/dist/uninstaller.d.ts +55 -0
- package/release-manager/dist/uninstaller.d.ts.map +1 -0
- package/release-manager/dist/uninstaller.js +409 -0
- package/release-manager/dist/uninstaller.js.map +1 -0
- package/release-manager/dist/update/atomic-file.d.ts +35 -0
- package/release-manager/dist/update/atomic-file.d.ts.map +1 -0
- package/release-manager/dist/update/atomic-file.js +120 -0
- package/release-manager/dist/update/atomic-file.js.map +1 -0
- package/release-manager/dist/update/bootstrap-protocol.d.ts +55 -0
- package/release-manager/dist/update/bootstrap-protocol.d.ts.map +1 -0
- package/release-manager/dist/update/bootstrap-protocol.js +132 -0
- package/release-manager/dist/update/bootstrap-protocol.js.map +1 -0
- package/release-manager/dist/update/channel-metadata.d.ts +28 -0
- package/release-manager/dist/update/channel-metadata.d.ts.map +1 -0
- package/release-manager/dist/update/channel-metadata.js +90 -0
- package/release-manager/dist/update/channel-metadata.js.map +1 -0
- package/release-manager/dist/update/channel-promotion.d.ts +39 -0
- package/release-manager/dist/update/channel-promotion.d.ts.map +1 -0
- package/release-manager/dist/update/channel-promotion.js +55 -0
- package/release-manager/dist/update/channel-promotion.js.map +1 -0
- package/release-manager/dist/update/data-compatibility.d.ts +35 -0
- package/release-manager/dist/update/data-compatibility.d.ts.map +1 -0
- package/release-manager/dist/update/data-compatibility.js +45 -0
- package/release-manager/dist/update/data-compatibility.js.map +1 -0
- package/release-manager/dist/update/install-layout.d.ts +59 -0
- package/release-manager/dist/update/install-layout.d.ts.map +1 -0
- package/release-manager/dist/update/install-layout.js +127 -0
- package/release-manager/dist/update/install-layout.js.map +1 -0
- package/release-manager/dist/update/legacy-migration.d.ts +56 -0
- package/release-manager/dist/update/legacy-migration.d.ts.map +1 -0
- package/release-manager/dist/update/legacy-migration.js +254 -0
- package/release-manager/dist/update/legacy-migration.js.map +1 -0
- package/release-manager/dist/update/product-identity.d.ts +50 -0
- package/release-manager/dist/update/product-identity.d.ts.map +1 -0
- package/release-manager/dist/update/product-identity.js +63 -0
- package/release-manager/dist/update/product-identity.js.map +1 -0
- package/release-manager/dist/update/release-asset-manifest.d.ts +31 -0
- package/release-manager/dist/update/release-asset-manifest.d.ts.map +1 -0
- package/release-manager/dist/update/release-asset-manifest.js +161 -0
- package/release-manager/dist/update/release-asset-manifest.js.map +1 -0
- package/release-manager/dist/update/release-identity.d.ts +33 -0
- package/release-manager/dist/update/release-identity.d.ts.map +1 -0
- package/release-manager/dist/update/release-identity.js +108 -0
- package/release-manager/dist/update/release-identity.js.map +1 -0
- package/release-manager/dist/update/release-manager-authority.d.ts +69 -0
- package/release-manager/dist/update/release-manager-authority.d.ts.map +1 -0
- package/release-manager/dist/update/release-manager-authority.js +125 -0
- package/release-manager/dist/update/release-manager-authority.js.map +1 -0
- package/release-manager/dist/update/release-manager.d.ts +96 -0
- package/release-manager/dist/update/release-manager.d.ts.map +1 -0
- package/release-manager/dist/update/release-manager.js +253 -0
- package/release-manager/dist/update/release-manager.js.map +1 -0
- package/release-manager/dist/update/release-metadata.d.ts +57 -0
- package/release-manager/dist/update/release-metadata.d.ts.map +1 -0
- package/release-manager/dist/update/release-metadata.js +200 -0
- package/release-manager/dist/update/release-metadata.js.map +1 -0
- package/release-manager/dist/update/release-policy.d.ts +51 -0
- package/release-manager/dist/update/release-policy.d.ts.map +1 -0
- package/release-manager/dist/update/release-policy.js +103 -0
- package/release-manager/dist/update/release-policy.js.map +1 -0
- package/release-manager/dist/update/rollback-policy.d.ts +56 -0
- package/release-manager/dist/update/rollback-policy.d.ts.map +1 -0
- package/release-manager/dist/update/rollback-policy.js +61 -0
- package/release-manager/dist/update/rollback-policy.js.map +1 -0
- package/release-manager/dist/update/transaction-journal.d.ts +131 -0
- package/release-manager/dist/update/transaction-journal.d.ts.map +1 -0
- package/release-manager/dist/update/transaction-journal.js +346 -0
- package/release-manager/dist/update/transaction-journal.js.map +1 -0
- package/release-manager/dist/update/trust-metadata.d.ts +55 -0
- package/release-manager/dist/update/trust-metadata.d.ts.map +1 -0
- package/release-manager/dist/update/trust-metadata.js +170 -0
- package/release-manager/dist/update/trust-metadata.js.map +1 -0
- package/release-manager/dist/update/update-history.d.ts +46 -0
- package/release-manager/dist/update/update-history.d.ts.map +1 -0
- package/release-manager/dist/update/update-history.js +123 -0
- package/release-manager/dist/update/update-history.js.map +1 -0
- package/release-manager/dist/utils/config-file-io.d.ts +26 -0
- package/release-manager/dist/utils/config-file-io.d.ts.map +1 -0
- package/release-manager/dist/utils/config-file-io.js +128 -0
- package/release-manager/dist/utils/config-file-io.js.map +1 -0
- package/release-manager/dist/utils/env.d.ts +60 -0
- package/release-manager/dist/utils/env.d.ts.map +1 -0
- package/release-manager/dist/utils/env.js +308 -0
- package/release-manager/dist/utils/env.js.map +1 -0
- package/release-manager/dist/utils/logger.d.ts +15 -0
- package/release-manager/dist/utils/logger.d.ts.map +1 -0
- package/release-manager/dist/utils/logger.js +30 -0
- package/release-manager/dist/utils/logger.js.map +1 -0
- package/release-manager/dist/utils/workspace-divergence.d.ts +36 -0
- package/release-manager/dist/utils/workspace-divergence.d.ts.map +1 -0
- package/release-manager/dist/utils/workspace-divergence.js +87 -0
- package/release-manager/dist/utils/workspace-divergence.js.map +1 -0
- package/release-manager/package.json +78 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uninstaller module
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ Safety principles:
|
|
5
|
+
* 1. Only delete plugin system files (~/.openclaw/extensions/principles-disciple)
|
|
6
|
+
* 2. Only delete plugin config files (~/.openclaw/principles-disciple.json)
|
|
7
|
+
* 3. Never delete any files from user workspace:
|
|
8
|
+
* - MD files (AGENTS.md, SOUL.md, etc.)
|
|
9
|
+
* - Memory files (.principles/ directory)
|
|
10
|
+
* - State files (.state/ directory)
|
|
11
|
+
* - Any user data
|
|
12
|
+
*
|
|
13
|
+
* ADR-0020 §2.3: Multi-host uninstall. Host-side config cleanup (openclaw.json
|
|
14
|
+
* entries, ~/.codex/hooks.json entries, wrapper scripts) is delegated to
|
|
15
|
+
* HostInstaller.uninstall() implementations. Workspace user data is always
|
|
16
|
+
* preserved regardless of host target.
|
|
17
|
+
*/
|
|
18
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
19
|
+
import fse from 'fs-extra';
|
|
20
|
+
import * as path from 'path';
|
|
21
|
+
import * as os from 'os';
|
|
22
|
+
import { confirm } from '@inquirer/prompts';
|
|
23
|
+
import { logger } from './utils/logger.js';
|
|
24
|
+
import { getOpenClawConfigDir, getPluginExtDir, checkOpenClawGateway } from './utils/env.js';
|
|
25
|
+
import { getGlobalShimPaths, getInstalledBinDir, getPdRuntimeDir, getInstallManifestPath, isWindows } from './mvp-config.js';
|
|
26
|
+
import { parseInstallManifest } from '@principles/install-layout';
|
|
27
|
+
import { setLanguage, t, getLanguage } from './i18n.js';
|
|
28
|
+
import { getHostInstallers } from './installers/index.js';
|
|
29
|
+
export function planSharedRuntimeUninstall(host) {
|
|
30
|
+
if (host === 'all') {
|
|
31
|
+
return { removeSharedRuntime: true, remainingHosts: [], manifestHasTarget: true };
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const raw = JSON.parse(readFileSync(getInstallManifestPath(), 'utf8'));
|
|
35
|
+
const parsed = parseInstallManifest(raw);
|
|
36
|
+
if (!parsed.manifest) {
|
|
37
|
+
return {
|
|
38
|
+
removeSharedRuntime: false,
|
|
39
|
+
remainingHosts: [],
|
|
40
|
+
manifestHasTarget: false,
|
|
41
|
+
warning: `${parsed.error ?? 'install_manifest_malformed'}; shared runtime preserved. Re-run uninstall --host all after repairing ~/.pd/install.json.`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const manifestHasTarget = parsed.manifest.hosts.includes(host);
|
|
45
|
+
const remainingHosts = parsed.manifest.hosts.filter(candidate => candidate !== host);
|
|
46
|
+
return { removeSharedRuntime: remainingHosts.length === 0, remainingHosts, manifestHasTarget };
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return {
|
|
50
|
+
removeSharedRuntime: false,
|
|
51
|
+
remainingHosts: [],
|
|
52
|
+
manifestHasTarget: false,
|
|
53
|
+
warning: `install_manifest_unreadable: ${error instanceof Error ? error.message : String(error)}; shared runtime preserved. Re-run uninstall --host all after repairing ~/.pd/install.json.`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function detectAndSetLanguage(lang) {
|
|
58
|
+
setLanguage(lang === 'en' ? 'en' : 'zh');
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check install status across all hosts (OpenClaw + Codex).
|
|
62
|
+
*
|
|
63
|
+
* ADR-0020 §2.3: Detects PD install markers for each registered host.
|
|
64
|
+
* Returns aggregated status so `pd status` / `npx create-principles-disciple
|
|
65
|
+
* status` shows the full picture regardless of which host was selected
|
|
66
|
+
* during install.
|
|
67
|
+
*/
|
|
68
|
+
export function checkInstallStatus(host = 'all') {
|
|
69
|
+
const configDir = getOpenClawConfigDir();
|
|
70
|
+
const lang = getLanguage();
|
|
71
|
+
// OpenClaw paths
|
|
72
|
+
const openclawPaths = [
|
|
73
|
+
{ path: getPluginExtDir(), name: lang === 'zh' ? '插件扩展目录' : 'Plugin extension directory', type: 'dir' },
|
|
74
|
+
{ path: path.join(configDir, 'principles-disciple.json'), name: lang === 'zh' ? 'OpenClaw 配置文件' : 'OpenClaw config file', type: 'file' },
|
|
75
|
+
];
|
|
76
|
+
// Codex paths (ADR-0020).
|
|
77
|
+
// NOTE: ~/.codex/hooks.json is intentionally NOT in this deletion set.
|
|
78
|
+
// It is a shared host config file — CodexHostInstaller.uninstall() removes
|
|
79
|
+
// only PD-owned entries (matched by __pd_marker) from it. If it were listed
|
|
80
|
+
// here, the generic delete loop below would delete the entire file,
|
|
81
|
+
// destroying the user's non-PD Codex hooks. Also, including it would make
|
|
82
|
+
// isInstalled=true for any user who merely has a hooks.json, even if PD
|
|
83
|
+
// was never installed.
|
|
84
|
+
const pdCodexDir = path.join(os.homedir(), '.pd', 'codex');
|
|
85
|
+
const codexPaths = [
|
|
86
|
+
{ path: path.join(pdCodexDir, 'pd-hooks.marker'), name: lang === 'zh' ? 'Codex 安装标记' : 'Codex install marker', type: 'file' },
|
|
87
|
+
{ path: path.join(pdCodexDir, 'pd-hook-entry.cjs'), name: lang === 'zh' ? 'Codex 钩子入口' : 'Codex hook entry script', type: 'file' },
|
|
88
|
+
];
|
|
89
|
+
const allPaths = host === 'openclaw'
|
|
90
|
+
? openclawPaths
|
|
91
|
+
: host === 'codex'
|
|
92
|
+
? codexPaths
|
|
93
|
+
: [...openclawPaths, ...codexPaths];
|
|
94
|
+
const checkedPaths = allPaths.map(p => ({
|
|
95
|
+
exists: existsSync(p.path),
|
|
96
|
+
path: p.path,
|
|
97
|
+
name: p.name,
|
|
98
|
+
type: p.type,
|
|
99
|
+
}));
|
|
100
|
+
const isInstalled = checkedPaths.some(p => p.exists);
|
|
101
|
+
return { isInstalled, paths: checkedPaths };
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* 检查 shim 文件是否由 Principles Disciple 创建
|
|
105
|
+
* 通过读取文件内容,确认其目标指向 PD 安装目录
|
|
106
|
+
*/
|
|
107
|
+
export function isPdOwnedShim(shimPath) {
|
|
108
|
+
try {
|
|
109
|
+
const content = readFileSync(shimPath, 'utf-8');
|
|
110
|
+
const pdBinDir = getInstalledBinDir();
|
|
111
|
+
const pdBinPath = isWindows()
|
|
112
|
+
? path.join(pdBinDir, 'pd.cmd')
|
|
113
|
+
: path.join(pdBinDir, 'pd');
|
|
114
|
+
if (isWindows()) {
|
|
115
|
+
// Windows cmd/ps1 shim 应包含 PD 安装路径
|
|
116
|
+
return content.includes(pdBinDir) || content.includes(pdBinPath);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Unix shim 通常是符号链接或脚本,检查是否指向 PD 安装目录
|
|
120
|
+
return content.includes(pdBinDir) || content.includes(pdBinPath);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Clean up global pd shim files
|
|
129
|
+
* Only deletes shims that were created by Principles Disciple
|
|
130
|
+
*/
|
|
131
|
+
async function removeGlobalPdShim() {
|
|
132
|
+
const removed = [];
|
|
133
|
+
const skipped = [];
|
|
134
|
+
const shimPaths = getGlobalShimPaths();
|
|
135
|
+
if (shimPaths.length === 0) {
|
|
136
|
+
logger.info(t('cannot_detect_npm_global'));
|
|
137
|
+
return { removed, skipped };
|
|
138
|
+
}
|
|
139
|
+
for (const shimPath of shimPaths) {
|
|
140
|
+
if (!existsSync(shimPath)) {
|
|
141
|
+
skipped.push(shimPath);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (!isPdOwnedShim(shimPath)) {
|
|
145
|
+
logger.warn(`${t('global_command_skip_not_owned')}: ${shimPath}`);
|
|
146
|
+
skipped.push(shimPath);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
await fse.remove(shimPath);
|
|
151
|
+
removed.push(shimPath);
|
|
152
|
+
logger.success(`${t('global_command_deleted')}: ${shimPath}`);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
logger.warn(`${t('global_command_delete_failed')}: ${shimPath} - ${error instanceof Error ? error.message : String(error)}`);
|
|
156
|
+
skipped.push(shimPath);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return { removed, skipped };
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get user workspace path (for showing preservation notice)
|
|
163
|
+
*/
|
|
164
|
+
function getWorkspacePath() {
|
|
165
|
+
const configDir = getOpenClawConfigDir();
|
|
166
|
+
const configPath = path.join(configDir, 'principles-disciple.json');
|
|
167
|
+
if (!existsSync(configPath))
|
|
168
|
+
return null;
|
|
169
|
+
// Read and parse JSON directly — require() would execute the file if its
|
|
170
|
+
// extension ever changed, and the path must stay inside the config dir.
|
|
171
|
+
const resolved = path.resolve(configPath);
|
|
172
|
+
if (resolved !== configPath && !resolved.startsWith(configDir + path.sep)) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
177
|
+
if (config && typeof config === 'object' && !Array.isArray(config)) {
|
|
178
|
+
const workspace = Reflect.get(config, 'workspace');
|
|
179
|
+
return typeof workspace === 'string' && workspace.length > 0 ? workspace : null;
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Execute uninstall
|
|
189
|
+
*
|
|
190
|
+
* @param options.force Skip confirmation prompt (dangerous, for scripts only)
|
|
191
|
+
* @param options.host ADR-0020: 'openclaw' | 'codex' | 'all' (default 'all')
|
|
192
|
+
*/
|
|
193
|
+
const REMOVE_RETRY_ATTEMPTS = 3;
|
|
194
|
+
const REMOVE_RETRY_DELAY_MS = 1000;
|
|
195
|
+
async function removeWithRetry(targetPath, _type) {
|
|
196
|
+
for (let attempt = 1; attempt <= REMOVE_RETRY_ATTEMPTS; attempt++) {
|
|
197
|
+
try {
|
|
198
|
+
await fse.remove(targetPath);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
203
|
+
const isLockError = errMsg.includes('EPERM') || errMsg.includes('EBUSY') || errMsg.includes('permission');
|
|
204
|
+
if (!isLockError || attempt === REMOVE_RETRY_ATTEMPTS)
|
|
205
|
+
throw error;
|
|
206
|
+
await new Promise(r => setTimeout(r, REMOVE_RETRY_DELAY_MS * attempt));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
export async function uninstall(options = {}) {
|
|
211
|
+
await detectAndSetLanguage(options.lang);
|
|
212
|
+
const hostTarget = options.host ?? 'all';
|
|
213
|
+
const result = {
|
|
214
|
+
success: false,
|
|
215
|
+
removedDirs: [],
|
|
216
|
+
removedFiles: [],
|
|
217
|
+
removedGlobalShims: [],
|
|
218
|
+
skippedGlobalShims: [],
|
|
219
|
+
preservedPaths: [],
|
|
220
|
+
};
|
|
221
|
+
try {
|
|
222
|
+
const gatewayStatus = hostTarget === 'codex'
|
|
223
|
+
? { isRunning: false, port: undefined, pid: undefined }
|
|
224
|
+
: await checkOpenClawGateway();
|
|
225
|
+
if (gatewayStatus.isRunning) {
|
|
226
|
+
const portInfo = gatewayStatus.port ? ` (port ${gatewayStatus.port})` : '';
|
|
227
|
+
const pidInfo = gatewayStatus.pid ? `, PID ${gatewayStatus.pid}` : '';
|
|
228
|
+
logger.warn(`OpenClaw gateway is running${portInfo}${pidInfo}.`);
|
|
229
|
+
logger.warn(isWindows()
|
|
230
|
+
? '文件可能被占用导致删除失败。建议先关闭 OpenClaw(openclaw gateway stop),然后重新运行卸载。'
|
|
231
|
+
: 'Files may be locked. Stop OpenClaw first (openclaw gateway stop), then re-run uninstall.');
|
|
232
|
+
console.log('');
|
|
233
|
+
}
|
|
234
|
+
// 1. Check install status
|
|
235
|
+
const status = checkInstallStatus(hostTarget);
|
|
236
|
+
const runtimePlan = planSharedRuntimeUninstall(hostTarget);
|
|
237
|
+
if (runtimePlan.warning)
|
|
238
|
+
logger.warn(runtimePlan.warning);
|
|
239
|
+
if (!status.isInstalled && !runtimePlan.manifestHasTarget) {
|
|
240
|
+
logger.warn(t('no_install_detected'));
|
|
241
|
+
result.success = true;
|
|
242
|
+
return result;
|
|
243
|
+
}
|
|
244
|
+
// 2. Get workspace path and show preservation notice
|
|
245
|
+
const workspaceDir = getWorkspacePath();
|
|
246
|
+
console.log('\n');
|
|
247
|
+
logger.warn(t('uninstall_warning_title'));
|
|
248
|
+
console.log(` ${t('uninstall_warning_msg')}`);
|
|
249
|
+
console.log('\n');
|
|
250
|
+
if (workspaceDir && existsSync(workspaceDir)) {
|
|
251
|
+
logger.info(t('workspace_files_preserved'));
|
|
252
|
+
console.log(` 📁 ${t('workspace_dir_label')}: ${workspaceDir}`);
|
|
253
|
+
console.log(` 📄 ${t('md_files_preserved')}`);
|
|
254
|
+
console.log(` 📁 ${t('principles_dir_preserved')}`);
|
|
255
|
+
console.log(` 📁 ${t('state_dir_preserved')}`);
|
|
256
|
+
console.log('\n');
|
|
257
|
+
}
|
|
258
|
+
// 3. Show what will be deleted
|
|
259
|
+
logger.info(t('plugin_files_will_delete'));
|
|
260
|
+
for (const p of status.paths) {
|
|
261
|
+
if (p.exists) {
|
|
262
|
+
const icon = p.type === 'dir' ? '📁' : '📄';
|
|
263
|
+
console.log(` ${icon} ${p.name}: ${p.path}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
console.log('\n');
|
|
267
|
+
// 4. Confirm uninstall (unless --force)
|
|
268
|
+
if (!options.force) {
|
|
269
|
+
const confirmed = await confirm({
|
|
270
|
+
message: t('confirm_uninstall'),
|
|
271
|
+
default: false,
|
|
272
|
+
});
|
|
273
|
+
if (!confirmed) {
|
|
274
|
+
logger.info(t('uninstall_cancelled'));
|
|
275
|
+
result.success = true;
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// 5. Execute deletion (only plugin system files)
|
|
280
|
+
const deleteErrors = [];
|
|
281
|
+
for (const p of status.paths) {
|
|
282
|
+
if (!p.exists)
|
|
283
|
+
continue;
|
|
284
|
+
try {
|
|
285
|
+
await removeWithRetry(p.path, p.type);
|
|
286
|
+
if (p.type === 'dir') {
|
|
287
|
+
result.removedDirs.push(p.path);
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
result.removedFiles.push(p.path);
|
|
291
|
+
}
|
|
292
|
+
logger.success(`${t('deleted')}: ${p.name}`);
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
296
|
+
const isPermError = errMsg.includes('EPERM') || errMsg.includes('permission') || errMsg.includes('access denied') || errMsg.includes('EBUSY');
|
|
297
|
+
if (isPermError) {
|
|
298
|
+
logger.error(`${t('delete_failed')}: ${p.name} - ${errMsg}`);
|
|
299
|
+
logger.warn(isWindows()
|
|
300
|
+
? '文件被占用,请先关闭 OpenClaw(openclaw gateway stop),然后手动删除目录或重新运行卸载。'
|
|
301
|
+
: 'File is locked. Stop OpenClaw first (openclaw gateway stop), then re-run uninstall or delete manually.');
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
logger.error(`${t('delete_failed')}: ${p.name} - ${errMsg}`);
|
|
305
|
+
}
|
|
306
|
+
deleteErrors.push({ name: p.name, error: errMsg });
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// 6. The pd shim belongs to the shared runtime. Keep it while another host
|
|
310
|
+
// remains attached; otherwise a host-scoped uninstall breaks that host.
|
|
311
|
+
if (runtimePlan.removeSharedRuntime) {
|
|
312
|
+
console.log('\n');
|
|
313
|
+
logger.info(t('cleaning_global_commands'));
|
|
314
|
+
const { removed, skipped } = await removeGlobalPdShim();
|
|
315
|
+
result.removedGlobalShims = removed;
|
|
316
|
+
result.skippedGlobalShims = skipped;
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
logger.info('Keeping the shared pd command because another host still uses the runtime.');
|
|
320
|
+
}
|
|
321
|
+
// 6.5. ADR-0020 §2.3: Clean up host-side configs via HostInstallers.
|
|
322
|
+
// Replaces the old cleanupOpenClawConfig() with multi-host delegation.
|
|
323
|
+
const hostInstallers = getHostInstallers(hostTarget);
|
|
324
|
+
for (const installer of hostInstallers) {
|
|
325
|
+
try {
|
|
326
|
+
const ctx = {
|
|
327
|
+
language: getLanguage(),
|
|
328
|
+
force: options.force === true,
|
|
329
|
+
};
|
|
330
|
+
const hostResult = await installer.uninstall(ctx);
|
|
331
|
+
if (hostResult.success) {
|
|
332
|
+
if (hostResult.removedPaths.length > 0) {
|
|
333
|
+
logger.success(`Host "${installer.hostId}": cleaned ${hostResult.removedPaths.length} path(s)`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
logger.warn(`Host "${installer.hostId}" cleanup: ${hostResult.reason ?? 'partial failure'}. Next: ${hostResult.nextAction}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
catch (err) {
|
|
341
|
+
// rc-9: never silently swallow — surface the failure.
|
|
342
|
+
logger.warn(`Host "${installer.hostId}" cleanup threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// Shared runtime is removed only after the final host is uninstalled.
|
|
346
|
+
let sharedRuntimeRemovalFailed = false;
|
|
347
|
+
if (runtimePlan.removeSharedRuntime && existsSync(getPdRuntimeDir())) {
|
|
348
|
+
try {
|
|
349
|
+
await removeWithRetry(getPdRuntimeDir(), 'dir');
|
|
350
|
+
result.removedDirs.push(getPdRuntimeDir());
|
|
351
|
+
}
|
|
352
|
+
catch (err) {
|
|
353
|
+
sharedRuntimeRemovalFailed = true;
|
|
354
|
+
deleteErrors.push({ name: 'PD shared runtime', error: err instanceof Error ? err.message : String(err) });
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (runtimePlan.removeSharedRuntime && !sharedRuntimeRemovalFailed && existsSync(getInstallManifestPath())) {
|
|
358
|
+
try {
|
|
359
|
+
await removeWithRetry(getInstallManifestPath(), 'file');
|
|
360
|
+
result.removedFiles.push(getInstallManifestPath());
|
|
361
|
+
}
|
|
362
|
+
catch (err) {
|
|
363
|
+
deleteErrors.push({ name: 'PD install manifest', error: err instanceof Error ? err.message : String(err) });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
else if (!runtimePlan.removeSharedRuntime && runtimePlan.remainingHosts.length > 0) {
|
|
367
|
+
writeFileSync(getInstallManifestPath(), JSON.stringify({
|
|
368
|
+
layoutVersion: 1,
|
|
369
|
+
mode: 'canonical',
|
|
370
|
+
hosts: runtimePlan.remainingHosts,
|
|
371
|
+
}, null, 2) + '\n', 'utf8');
|
|
372
|
+
}
|
|
373
|
+
// 7. Record preserved paths
|
|
374
|
+
if (workspaceDir) {
|
|
375
|
+
result.preservedPaths.push(workspaceDir);
|
|
376
|
+
if (existsSync(path.join(workspaceDir, '.principles'))) {
|
|
377
|
+
result.preservedPaths.push(path.join(workspaceDir, '.principles'));
|
|
378
|
+
}
|
|
379
|
+
if (existsSync(path.join(workspaceDir, '.state'))) {
|
|
380
|
+
result.preservedPaths.push(path.join(workspaceDir, '.state'));
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
result.success = deleteErrors.length === 0;
|
|
384
|
+
if (deleteErrors.length > 0) {
|
|
385
|
+
result.error = deleteErrors.map(e => `${e.name}: ${e.error}`).join('; ');
|
|
386
|
+
}
|
|
387
|
+
console.log('\n');
|
|
388
|
+
if (deleteErrors.length > 0) {
|
|
389
|
+
logger.warn(t('uninstall_partial') || '卸载部分完成 — 部分文件未能删除,请按上方提示处理。');
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
logger.success(t('uninstall_complete'));
|
|
393
|
+
}
|
|
394
|
+
console.log('\n');
|
|
395
|
+
if (result.preservedPaths.length > 0) {
|
|
396
|
+
logger.info(t('personal_data_preserved'));
|
|
397
|
+
result.preservedPaths.forEach(p => console.log(` ${p}`));
|
|
398
|
+
console.log('\n');
|
|
399
|
+
logger.info(t('manual_cleanup_hint'));
|
|
400
|
+
}
|
|
401
|
+
return result;
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
result.error = error instanceof Error ? error.message : String(error);
|
|
405
|
+
logger.error(`${t('uninstall_failed')}: ${result.error}`);
|
|
406
|
+
return result;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
//# sourceMappingURL=uninstaller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstaller.js","sourceRoot":"","sources":["../src/uninstaller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomic record adapter (SPEC §8).
|
|
3
|
+
*
|
|
4
|
+
* Atomicity: readers see either the old record or the new record — never a
|
|
5
|
+
* partial write. Durability: the selected record survives a crash. The two
|
|
6
|
+
* are tested separately; rename alone does NOT guarantee durability, so the
|
|
7
|
+
* writer fsyncs the record file before renaming and flushes the containing
|
|
8
|
+
* directory where the platform allows it.
|
|
9
|
+
*
|
|
10
|
+
* Platform notes (deliberate, documented):
|
|
11
|
+
* - POSIX: file fsync + directory fd fsync after rename.
|
|
12
|
+
* - Windows: file fsync + rename; Node cannot fsync a directory handle on
|
|
13
|
+
* Windows, so durability of the RENAME itself relies on the filesystem
|
|
14
|
+
* journal. As a compensating control the record is REREAD and verified
|
|
15
|
+
* after the rename — a torn state fails loud instead of silently lying.
|
|
16
|
+
* - Windows rename after large writes can transiently fail EPERM/EACCES
|
|
17
|
+
* while antivirus holds handles; the rename is retried within a bounded
|
|
18
|
+
* window and fails loud afterwards (never falls back to non-atomic copy).
|
|
19
|
+
*/
|
|
20
|
+
export declare class AtomicRecordError extends Error {
|
|
21
|
+
readonly code: string;
|
|
22
|
+
constructor(code: string, message: string);
|
|
23
|
+
}
|
|
24
|
+
export interface AtomicWriteResult {
|
|
25
|
+
readonly path: string;
|
|
26
|
+
/** True when the post-rename reread reproduced the intended record bytes. */
|
|
27
|
+
readonly verified: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Replaces `recordPath` with `recordText` atomically:
|
|
31
|
+
* temp file (same directory) → fsync → rename → directory flush → reread.
|
|
32
|
+
* `recordText` must be the exact intended bytes; the reread must match.
|
|
33
|
+
*/
|
|
34
|
+
export declare function writeRecordAtomically(recordPath: string, recordText: string): AtomicWriteResult;
|
|
35
|
+
//# sourceMappingURL=atomic-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic-file.d.ts","sourceRoot":"","sources":["../../src/update/atomic-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,YAAY,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAIxC;CACF;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AA0DD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,iBAAiB,CAiC/F"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomic record adapter (SPEC §8).
|
|
3
|
+
*
|
|
4
|
+
* Atomicity: readers see either the old record or the new record — never a
|
|
5
|
+
* partial write. Durability: the selected record survives a crash. The two
|
|
6
|
+
* are tested separately; rename alone does NOT guarantee durability, so the
|
|
7
|
+
* writer fsyncs the record file before renaming and flushes the containing
|
|
8
|
+
* directory where the platform allows it.
|
|
9
|
+
*
|
|
10
|
+
* Platform notes (deliberate, documented):
|
|
11
|
+
* - POSIX: file fsync + directory fd fsync after rename.
|
|
12
|
+
* - Windows: file fsync + rename; Node cannot fsync a directory handle on
|
|
13
|
+
* Windows, so durability of the RENAME itself relies on the filesystem
|
|
14
|
+
* journal. As a compensating control the record is REREAD and verified
|
|
15
|
+
* after the rename — a torn state fails loud instead of silently lying.
|
|
16
|
+
* - Windows rename after large writes can transiently fail EPERM/EACCES
|
|
17
|
+
* while antivirus holds handles; the rename is retried within a bounded
|
|
18
|
+
* window and fails loud afterwards (never falls back to non-atomic copy).
|
|
19
|
+
*/
|
|
20
|
+
import { closeSync, existsSync, fsyncSync, openSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
21
|
+
import { randomUUID } from 'node:crypto';
|
|
22
|
+
import { dirname, join } from 'node:path';
|
|
23
|
+
export class AtomicRecordError extends Error {
|
|
24
|
+
code;
|
|
25
|
+
constructor(code, message) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = 'AtomicRecordError';
|
|
28
|
+
this.code = code;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function isDirectory(directoryPath) {
|
|
32
|
+
return existsSync(directoryPath) && statSync(directoryPath).isDirectory();
|
|
33
|
+
}
|
|
34
|
+
function fsyncDirectory(directoryPath) {
|
|
35
|
+
// Directory fd fsync is POSIX-only; on Windows this is a documented no-op
|
|
36
|
+
// compensated by the post-rename reread verification below.
|
|
37
|
+
if (process.platform === 'win32')
|
|
38
|
+
return;
|
|
39
|
+
let descriptor;
|
|
40
|
+
try {
|
|
41
|
+
descriptor = openSync(directoryPath, 'r');
|
|
42
|
+
fsyncSync(descriptor);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Some filesystems refuse directory fsync entirely; the file-level fsync
|
|
46
|
+
// already ran. Failing the whole write here would trade a durable-enough
|
|
47
|
+
// record for no record.
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
if (descriptor !== undefined)
|
|
51
|
+
closeSync(descriptor);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const RENAME_RETRY_DELAYS_MS = [50, 100, 200, 400, 800, 800, 800, 800];
|
|
55
|
+
/**
|
|
56
|
+
* Synchronous bounded sleep for the rename retry loop. Atomics.wait is
|
|
57
|
+
* intentional here: this module is a synchronous CLI code path where
|
|
58
|
+
* blocking the thread IS the requested behaviour (there is no concurrent
|
|
59
|
+
* work to yield to), and Node.js — unlike browsers — permits Atomics.wait on
|
|
60
|
+
* the main thread. It returns 'timed-out' after the delay, which is exactly
|
|
61
|
+
* the sleep semantics we need.
|
|
62
|
+
*/
|
|
63
|
+
function sleepSync(delayMs) {
|
|
64
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delayMs);
|
|
65
|
+
}
|
|
66
|
+
function renameWithWindowsRetry(source, destination) {
|
|
67
|
+
for (let attempt = 0;; attempt += 1) {
|
|
68
|
+
try {
|
|
69
|
+
renameSync(source, destination);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
const code = typeof error === 'object' && error !== null && Object.hasOwn(error, 'code')
|
|
74
|
+
? String(Reflect.get(error, 'code'))
|
|
75
|
+
: 'unknown';
|
|
76
|
+
const delayMs = RENAME_RETRY_DELAYS_MS[attempt];
|
|
77
|
+
if (!['EPERM', 'EACCES', 'EAGAIN'].includes(code) || delayMs === undefined) {
|
|
78
|
+
throw new AtomicRecordError('atomic_rename_failed', `Atomic record replacement failed (${code}): ${source} -> ${destination}. The previous record is untouched.`);
|
|
79
|
+
}
|
|
80
|
+
sleepSync(delayMs);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Replaces `recordPath` with `recordText` atomically:
|
|
86
|
+
* temp file (same directory) → fsync → rename → directory flush → reread.
|
|
87
|
+
* `recordText` must be the exact intended bytes; the reread must match.
|
|
88
|
+
*/
|
|
89
|
+
export function writeRecordAtomically(recordPath, recordText) {
|
|
90
|
+
const directory = dirname(recordPath);
|
|
91
|
+
if (!isDirectory(directory)) {
|
|
92
|
+
throw new AtomicRecordError('atomic_directory_missing', `Record directory does not exist: ${directory}`);
|
|
93
|
+
}
|
|
94
|
+
// UUID-named temp file: pid+timestamp naming collides when two atomic
|
|
95
|
+
// writes land in the same millisecond, and the 'wx' exclusive flag then
|
|
96
|
+
// fails the second write spuriously.
|
|
97
|
+
const tempPath = join(directory, `.record-${randomUUID()}.tmp`);
|
|
98
|
+
let descriptor;
|
|
99
|
+
try {
|
|
100
|
+
descriptor = openSync(tempPath, 'wx');
|
|
101
|
+
writeFileSync(descriptor, recordText);
|
|
102
|
+
fsyncSync(descriptor);
|
|
103
|
+
closeSync(descriptor);
|
|
104
|
+
descriptor = undefined;
|
|
105
|
+
renameWithWindowsRetry(tempPath, recordPath);
|
|
106
|
+
fsyncDirectory(directory);
|
|
107
|
+
const reread = existsSync(recordPath) ? readFileSync(recordPath, 'utf8') : null;
|
|
108
|
+
if (reread !== recordText) {
|
|
109
|
+
throw new AtomicRecordError('atomic_verify_failed', `Post-rename verification failed for ${recordPath}: the record on disk does not match the intended bytes. The previous record may or may not be in place — recovery must consult the journal.`);
|
|
110
|
+
}
|
|
111
|
+
return { path: recordPath, verified: true };
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (descriptor !== undefined)
|
|
115
|
+
closeSync(descriptor);
|
|
116
|
+
rmSync(tempPath, { force: true });
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=atomic-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic-file.js","sourceRoot":"","sources":["../../src/update/atomic-file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootstrap process protocol (SPEC §6.1) — strict single-JSON-object in,
|
|
3
|
+
* strict single-JSON-object out.
|
|
4
|
+
*
|
|
5
|
+
* Companion, Console, and the installer speak to the bootstrap through this
|
|
6
|
+
* one protocol. The transport layer (stdin/stdout around this pure handler)
|
|
7
|
+
* stays thin so the contract itself is testable: exactly one parseable JSON
|
|
8
|
+
* object per direction, stable reason codes, and a next action on every
|
|
9
|
+
* failure (rc-9, cli-1 semantics applied to the protocol boundary).
|
|
10
|
+
*/
|
|
11
|
+
import type { ReleaseChannelName } from './product-identity.js';
|
|
12
|
+
import type { ReleaseManager } from './release-manager.js';
|
|
13
|
+
export type BootstrapRequestOp = 'inspect' | 'check' | 'apply' | 'rollback';
|
|
14
|
+
export type BootstrapRequest = {
|
|
15
|
+
readonly op: 'inspect';
|
|
16
|
+
} | {
|
|
17
|
+
readonly op: 'check';
|
|
18
|
+
readonly channel: ReleaseChannelName;
|
|
19
|
+
} | {
|
|
20
|
+
readonly op: 'apply';
|
|
21
|
+
readonly releaseId: string;
|
|
22
|
+
} | {
|
|
23
|
+
readonly op: 'rollback';
|
|
24
|
+
};
|
|
25
|
+
export interface BootstrapOkResponse {
|
|
26
|
+
readonly ok: true;
|
|
27
|
+
readonly result: unknown;
|
|
28
|
+
}
|
|
29
|
+
export interface BootstrapFailureResponse {
|
|
30
|
+
readonly ok: false;
|
|
31
|
+
readonly reason: string;
|
|
32
|
+
readonly message: string;
|
|
33
|
+
readonly nextAction: string;
|
|
34
|
+
}
|
|
35
|
+
export type BootstrapResponse = BootstrapOkResponse | BootstrapFailureResponse;
|
|
36
|
+
export declare class BootstrapProtocolError extends Error {
|
|
37
|
+
readonly reason: string;
|
|
38
|
+
constructor(reason: string, message: string);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Parses exactly ONE JSON object from the raw request text. Trailing data,
|
|
42
|
+
* non-objects, arrays, and unknown/missing fields are hard errors — a
|
|
43
|
+
* protocol peer that emits garbage gets a structured refusal, never a
|
|
44
|
+
* best-effort guess.
|
|
45
|
+
*/
|
|
46
|
+
export declare function parseBootstrapRequest(raw: string): BootstrapRequest;
|
|
47
|
+
/** Serializes exactly one JSON object for stdout (no banners, no extra text). */
|
|
48
|
+
export declare function serializeBootstrapResponse(response: BootstrapResponse): string;
|
|
49
|
+
/**
|
|
50
|
+
* Dispatches a parsed request against a ReleaseManager and produces the
|
|
51
|
+
* protocol response. Manager failures become structured refusals — the
|
|
52
|
+
* protocol never leaks stack traces into the response object.
|
|
53
|
+
*/
|
|
54
|
+
export declare function handleBootstrapRequest(request: BootstrapRequest, manager: ReleaseManager): Promise<BootstrapResponse>;
|
|
55
|
+
//# sourceMappingURL=bootstrap-protocol.d.ts.map
|