@dsh-enhanced/dsh-rsi-cli 0.1.48 → 0.1.53

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.
Files changed (53) hide show
  1. package/README.md +38 -13
  2. package/lib/diagnose.js +7 -7
  3. package/lib/diagnose.js.map +1 -1
  4. package/lib/home-lifecycle-lock.d.ts +4 -0
  5. package/lib/home-lifecycle-lock.d.ts.map +1 -0
  6. package/lib/home-lifecycle-lock.js +69 -0
  7. package/lib/home-lifecycle-lock.js.map +1 -0
  8. package/lib/host-runtime.d.ts +36 -0
  9. package/lib/host-runtime.d.ts.map +1 -0
  10. package/lib/host-runtime.js +400 -0
  11. package/lib/host-runtime.js.map +1 -0
  12. package/lib/index.d.ts +5 -1
  13. package/lib/index.d.ts.map +1 -1
  14. package/lib/index.js +179 -22
  15. package/lib/index.js.map +1 -1
  16. package/lib/install-host.d.ts +49 -0
  17. package/lib/install-host.d.ts.map +1 -0
  18. package/lib/install-host.js +326 -0
  19. package/lib/install-host.js.map +1 -0
  20. package/lib/install.d.ts +10 -2
  21. package/lib/install.d.ts.map +1 -1
  22. package/lib/install.js +187 -28
  23. package/lib/install.js.map +1 -1
  24. package/lib/launchd-lock.d.ts +3 -0
  25. package/lib/launchd-lock.d.ts.map +1 -0
  26. package/lib/launchd-lock.js +46 -0
  27. package/lib/launchd-lock.js.map +1 -0
  28. package/lib/launchd-service.d.ts +24 -0
  29. package/lib/launchd-service.d.ts.map +1 -0
  30. package/lib/launchd-service.js +146 -0
  31. package/lib/launchd-service.js.map +1 -0
  32. package/lib/process-guard.d.ts +4 -5
  33. package/lib/process-guard.d.ts.map +1 -1
  34. package/lib/process-guard.js +17 -8
  35. package/lib/process-guard.js.map +1 -1
  36. package/lib/purge.d.ts.map +1 -1
  37. package/lib/purge.js +30 -5
  38. package/lib/purge.js.map +1 -1
  39. package/lib/run.d.ts +2 -1
  40. package/lib/run.d.ts.map +1 -1
  41. package/lib/run.js +8 -10
  42. package/lib/run.js.map +1 -1
  43. package/lib/services.d.ts +10 -3
  44. package/lib/services.d.ts.map +1 -1
  45. package/lib/services.js +126 -42
  46. package/lib/services.js.map +1 -1
  47. package/lib/version.d.ts +1 -1
  48. package/lib/version.js +1 -1
  49. package/lib/weburl.d.ts +1 -1
  50. package/lib/weburl.d.ts.map +1 -1
  51. package/lib/weburl.js +17 -2
  52. package/lib/weburl.js.map +1 -1
  53. package/package.json +1 -1
@@ -0,0 +1,326 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { constants } from 'node:fs';
3
+ import { link, lstat, mkdir, open, readdir, realpath, unlink } from 'node:fs/promises';
4
+ import { homedir } from 'node:os';
5
+ import { delimiter, isAbsolute, join, resolve, sep } from 'node:path';
6
+ import { isSupportedCandidateVersion, isSupportedReceiptVersion, prepareManagedHostRuntime, readManagedHostRuntime } from "./host-runtime.js";
7
+ import { PurgeError } from "./purge.js";
8
+ const BINDING = '.dsh-rsi-host.json';
9
+ const ports = { prepare: prepareManagedHostRuntime, read: readManagedHostRuntime };
10
+ function versionOrder(version) {
11
+ const match = /^(\d+)\.(\d+)\.(\d+)(?:-rc\.(\d+))?$/u.exec(version);
12
+ return [Number(match[1]), Number(match[2]), Number(match[3]), match[4] === undefined ? Number.MAX_SAFE_INTEGER : Number(match[4])];
13
+ }
14
+ function earlierVersion(left, right) {
15
+ const leftOrder = versionOrder(left), rightOrder = versionOrder(right);
16
+ for (let index = 0; index < leftOrder.length; index++) {
17
+ if (leftOrder[index] !== rightOrder[index])
18
+ return leftOrder[index] < rightOrder[index];
19
+ }
20
+ return false;
21
+ }
22
+ function sameRuntime(left, right) {
23
+ return left.version === right.version && left.root === right.root && left.dshPath === right.dshPath
24
+ && left.binDirectory === right.binDirectory && left.integrity === right.integrity
25
+ && left.receiptDigest === right.receiptDigest;
26
+ }
27
+ async function readUpdateBinding(home) {
28
+ const path = join(home, BINDING);
29
+ let file;
30
+ try {
31
+ file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
32
+ }
33
+ catch (error) {
34
+ if (error.code === 'ENOENT')
35
+ throw new PurgeError('DSH_HOME 没有私有 Host 绑定,不能准备受管更新。');
36
+ throw error;
37
+ }
38
+ try {
39
+ const metadata = await file.stat();
40
+ if (!metadata.isFile() || metadata.nlink !== 1 || metadata.size < 1 || metadata.size > 16_384
41
+ || (metadata.mode & 0o077) || process.getuid && metadata.uid !== process.getuid()) {
42
+ throw new PurgeError('私有 Host 绑定文件不安全。');
43
+ }
44
+ const bytes = await file.readFile();
45
+ let source;
46
+ try {
47
+ source = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
48
+ }
49
+ catch {
50
+ throw new PurgeError('私有 Host 绑定文件无效。');
51
+ }
52
+ if (!Buffer.from(source, 'utf8').equals(bytes))
53
+ throw new PurgeError('私有 Host 绑定文件无效。');
54
+ let value;
55
+ try {
56
+ value = JSON.parse(source);
57
+ }
58
+ catch {
59
+ throw new PurgeError('私有 Host 绑定文件无效。');
60
+ }
61
+ if (typeof value !== 'object' || value === null || Array.isArray(value)
62
+ || Object.keys(value).sort().join(',') !== 'cacheRoot,receiptDigest,schemaVersion,version') {
63
+ throw new PurgeError('私有 Host 绑定文件无效。');
64
+ }
65
+ const binding = value;
66
+ if (binding.schemaVersion !== 1 || typeof binding.cacheRoot !== 'string' || !isAbsolute(binding.cacheRoot)
67
+ || resolve(binding.cacheRoot) !== binding.cacheRoot || typeof binding.version !== 'string'
68
+ || !isSupportedReceiptVersion(binding.version) || typeof binding.receiptDigest !== 'string'
69
+ || !/^[a-f0-9]{64}$/u.test(binding.receiptDigest))
70
+ throw new PurgeError('私有 Host 绑定文件无效。');
71
+ return { binding, bytes, source, dev: metadata.dev, ino: metadata.ino };
72
+ }
73
+ finally {
74
+ await file.close();
75
+ }
76
+ }
77
+ /** Prepare and verify a candidate without changing the bound Home or its service. */
78
+ export async function prepareHostUpdatePlan(input, dependencies = ports) {
79
+ input.signal?.throwIfAborted();
80
+ const requested = resolve(input.dshHome);
81
+ const entry = await lstat(requested).catch(error => {
82
+ if (error.code === 'ENOENT')
83
+ throw new PurgeError('DSH_HOME 不存在,不能准备受管 Host 更新。');
84
+ throw error;
85
+ });
86
+ if (!entry.isDirectory() && !entry.isSymbolicLink())
87
+ throw new PurgeError('DSH_HOME 必须是目录。');
88
+ const canonicalHome = await realpath(requested);
89
+ await assertOwnedHome(canonicalHome);
90
+ const original = await readUpdateBinding(canonicalHome);
91
+ const originalRuntime = await dependencies.read({ root: original.binding.cacheRoot, version: original.binding.version,
92
+ ...(input.signal === undefined ? {} : { signal: input.signal }) });
93
+ if (originalRuntime.version !== original.binding.version || originalRuntime.root !== join(original.binding.cacheRoot, original.binding.version)
94
+ || originalRuntime.receiptDigest !== original.binding.receiptDigest) {
95
+ throw new PurgeError('已绑定的私有 Host 制品发生变化。');
96
+ }
97
+ input.signal?.throwIfAborted();
98
+ const selector = input.selector ?? 'latest';
99
+ if (selector !== 'latest' && !isSupportedReceiptVersion(selector))
100
+ throw new PurgeError('请求的 Host 版本不兼容。');
101
+ if (selector !== 'latest' && earlierVersion(selector, original.binding.version))
102
+ throw new PurgeError('受管 Host 更新不能降级。');
103
+ if (selector !== 'latest' && !isSupportedCandidateVersion(selector))
104
+ throw new PurgeError('请求的 Host 版本不兼容。');
105
+ const preparedRuntime = await dependencies.prepare({ root: original.binding.cacheRoot, selector,
106
+ ...(input.signal === undefined ? {} : { signal: input.signal }) });
107
+ if (!isSupportedReceiptVersion(preparedRuntime.version) || selector !== 'latest' && preparedRuntime.version !== selector
108
+ || !/^[a-f0-9]{64}$/u.test(preparedRuntime.receiptDigest))
109
+ throw new PurgeError('候选 Host 版本或收据无效。');
110
+ if (earlierVersion(preparedRuntime.version, original.binding.version))
111
+ throw new PurgeError('受管 Host 更新不能降级。');
112
+ if (!isSupportedCandidateVersion(preparedRuntime.version))
113
+ throw new PurgeError('候选 Host 版本或收据无效。');
114
+ const candidateRuntime = await dependencies.read({ root: original.binding.cacheRoot, version: preparedRuntime.version,
115
+ ...(input.signal === undefined ? {} : { signal: input.signal }) });
116
+ if (!sameRuntime(candidateRuntime, preparedRuntime)
117
+ || candidateRuntime.root !== join(original.binding.cacheRoot, candidateRuntime.version)) {
118
+ throw new PurgeError('候选 Host 制品未通过原缓存根的独立收据验证。');
119
+ }
120
+ input.signal?.throwIfAborted();
121
+ await assertOwnedHome(canonicalHome);
122
+ const current = await readUpdateBinding(canonicalHome);
123
+ if (current.dev !== original.dev || current.ino !== original.ino || !current.bytes.equals(original.bytes)) {
124
+ throw new PurgeError('准备 Host 期间私有绑定发生变化;未切换 Host。');
125
+ }
126
+ const status = candidateRuntime.version === original.binding.version ? 'current' : 'update';
127
+ if (status === 'current' && !sameRuntime(candidateRuntime, originalRuntime)) {
128
+ throw new PurgeError('已绑定的私有 Host 制品发生变化。');
129
+ }
130
+ const candidateBindingSource = status === 'current' ? original.source : JSON.stringify({
131
+ schemaVersion: 1, cacheRoot: original.binding.cacheRoot, version: candidateRuntime.version,
132
+ receiptDigest: candidateRuntime.receiptDigest,
133
+ }) + '\n';
134
+ return { schemaVersion: 1, status, canonicalHome, bindingPath: join(canonicalHome, BINDING),
135
+ originalBindingSource: original.source, originalBindingDigest: createHash('sha256').update(original.bytes).digest('hex'),
136
+ originalRuntime, candidateRuntime, candidateBindingSource };
137
+ }
138
+ /** Plan an explicit legacy private Host migration without creating a Home binding. */
139
+ export async function prepareUnboundHostUpdatePlan(input, dependencies = ports) {
140
+ input.signal?.throwIfAborted();
141
+ const requested = resolve(input.dshHome);
142
+ const entry = await lstat(requested).catch(error => {
143
+ if (error.code === 'ENOENT')
144
+ throw new PurgeError('DSH_HOME 不存在,不能准备受管 Host 更新。');
145
+ throw error;
146
+ });
147
+ if (!entry.isDirectory() && !entry.isSymbolicLink())
148
+ throw new PurgeError('DSH_HOME 必须是目录。');
149
+ const canonicalHome = await realpath(requested);
150
+ await assertOwnedHome(canonicalHome);
151
+ const initialHome = await lstat(canonicalHome);
152
+ const bindingPath = join(canonicalHome, BINDING);
153
+ const assertUnbound = async () => {
154
+ try {
155
+ await lstat(bindingPath);
156
+ }
157
+ catch (error) {
158
+ if (error.code === 'ENOENT')
159
+ return;
160
+ throw error;
161
+ }
162
+ throw new PurgeError('DSH_HOME 已有私有 Host 绑定,不能准备无绑定迁移。');
163
+ };
164
+ await assertUnbound();
165
+ const { cacheRoot, version, dshPath } = input.unboundOriginal;
166
+ if (!isAbsolute(cacheRoot) || resolve(cacheRoot) !== cacheRoot || !isAbsolute(dshPath)
167
+ || resolve(dshPath) !== dshPath || !isSupportedReceiptVersion(version) || version === '0.2.0-rc.2') {
168
+ throw new PurgeError('原 Host 必须是显式指定的受支持旧版私有制品。');
169
+ }
170
+ const originalRuntime = await dependencies.read({ root: cacheRoot, version,
171
+ ...(input.signal === undefined ? {} : { signal: input.signal }) });
172
+ if (originalRuntime.version !== version || originalRuntime.root !== join(cacheRoot, version)
173
+ || originalRuntime.dshPath !== dshPath || !/^[a-f0-9]{64}$/u.test(originalRuntime.receiptDigest)) {
174
+ throw new PurgeError('原 Host 可执行文件与私有收据不一致。');
175
+ }
176
+ const originalExecutable = await realpath(dshPath).catch(() => { throw new PurgeError('原 Host 可执行文件与私有收据不一致。'); });
177
+ if (!originalExecutable.startsWith(`${originalRuntime.root}${sep}`)) {
178
+ throw new PurgeError('原 Host 可执行文件与私有收据不一致。');
179
+ }
180
+ input.signal?.throwIfAborted();
181
+ const selector = input.selector ?? 'latest';
182
+ if (selector !== 'latest' && selector !== '0.2.0-rc.2')
183
+ throw new PurgeError('请求的 Host 版本不兼容。');
184
+ const preparedRuntime = await dependencies.prepare({ root: cacheRoot, selector,
185
+ ...(input.signal === undefined ? {} : { signal: input.signal }) });
186
+ if (!isSupportedCandidateVersion(preparedRuntime.version) || selector !== 'latest' && preparedRuntime.version !== selector
187
+ || !/^[a-f0-9]{64}$/u.test(preparedRuntime.receiptDigest))
188
+ throw new PurgeError('候选 Host 版本或收据无效。');
189
+ const candidateRuntime = await dependencies.read({ root: cacheRoot, version: preparedRuntime.version,
190
+ ...(input.signal === undefined ? {} : { signal: input.signal }) });
191
+ if (!sameRuntime(candidateRuntime, preparedRuntime)
192
+ || candidateRuntime.root !== join(cacheRoot, candidateRuntime.version)) {
193
+ throw new PurgeError('候选 Host 制品未通过原缓存根的独立收据验证。');
194
+ }
195
+ input.signal?.throwIfAborted();
196
+ await assertOwnedHome(canonicalHome);
197
+ const currentHome = await lstat(canonicalHome);
198
+ if (currentHome.dev !== initialHome.dev || currentHome.ino !== initialHome.ino) {
199
+ throw new PurgeError('准备 Host 期间 DSH_HOME 身份发生变化;未切换 Host。');
200
+ }
201
+ await assertUnbound();
202
+ const candidateBindingSource = JSON.stringify({ schemaVersion: 1, cacheRoot, version: candidateRuntime.version,
203
+ receiptDigest: candidateRuntime.receiptDigest }) + '\n';
204
+ return { schemaVersion: 2, status: 'update', canonicalHome, bindingPath,
205
+ originalBindingSource: null, originalBindingDigest: null, originalRuntime, candidateRuntime, candidateBindingSource };
206
+ }
207
+ async function assertOwnedHome(home) {
208
+ const metadata = await lstat(home);
209
+ if (!metadata.isDirectory() || (metadata.mode & 0o022)
210
+ || process.getuid && metadata.uid !== process.getuid()) {
211
+ throw new PurgeError('Host 绑定目录必须由当前用户所有,且不可由其他用户写入。');
212
+ }
213
+ }
214
+ async function readBinding(home) {
215
+ let file;
216
+ try {
217
+ file = await open(join(home, BINDING), constants.O_RDONLY | constants.O_NOFOLLOW);
218
+ }
219
+ catch (error) {
220
+ if (error.code === 'ENOENT')
221
+ return undefined;
222
+ throw error;
223
+ }
224
+ try {
225
+ const metadata = await file.stat();
226
+ if (!metadata.isFile() || metadata.size > 16_384 || (metadata.mode & 0o077)
227
+ || process.getuid && metadata.uid !== process.getuid())
228
+ throw new PurgeError('私有 Host 绑定文件不安全。');
229
+ const value = JSON.parse(await file.readFile('utf8'));
230
+ if (value.schemaVersion !== 1 || typeof value.cacheRoot !== 'string' || !isAbsolute(value.cacheRoot)
231
+ || typeof value.version !== 'string' || !isSupportedReceiptVersion(value.version)
232
+ || !/^[a-f0-9]{64}$/u.test(value.receiptDigest))
233
+ throw new PurgeError('私有 Host 绑定文件无效。');
234
+ return value;
235
+ }
236
+ finally {
237
+ await file.close();
238
+ }
239
+ }
240
+ /** Bind a fresh Home once. Existing unbound installations remain on their
241
+ * current Host until the service-aware Host migration can update the whole Home. */
242
+ export async function prepareInstallHostEnvironment(input, dependencies = ports) {
243
+ const environment = { ...(input.environment ?? process.env) };
244
+ // This marker belongs to the selected Home, never to an ambient parent shell.
245
+ delete environment.DSH_ENHANCED_HOST_BIN;
246
+ const requested = resolve(input.dshHome);
247
+ const entry = await lstat(requested).catch(error => {
248
+ if (error.code === 'ENOENT')
249
+ return undefined;
250
+ throw error;
251
+ });
252
+ if (entry && !entry.isDirectory() && !entry.isSymbolicLink())
253
+ throw new PurgeError('DSH_HOME 必须是目录。');
254
+ if (!entry && input.prepareFresh === false)
255
+ return environment;
256
+ if (!entry)
257
+ await mkdir(requested, { recursive: true, mode: 0o700 });
258
+ const home = await realpath(requested);
259
+ const selector = input.selector ?? 'latest';
260
+ const use = async (binding) => {
261
+ await assertOwnedHome(home);
262
+ if (selector !== 'latest' && selector !== binding.version)
263
+ throw new PurgeError('已有 Home 的 Host 版本已固定;更换版本必须经过受管 Host 更新事务。');
264
+ const runtime = await dependencies.read({ root: binding.cacheRoot, version: binding.version });
265
+ if (runtime.receiptDigest !== binding.receiptDigest)
266
+ throw new PurgeError('已绑定的私有 Host 制品发生变化。');
267
+ return { ...environment, DSH_HOME: home, DSH_ENHANCED_HOST_BIN: runtime.binDirectory, PATH: [runtime.binDirectory, environment.PATH ?? ''].filter(Boolean).join(delimiter) };
268
+ };
269
+ const existing = await readBinding(home);
270
+ if (existing)
271
+ return use(existing);
272
+ if (input.prepareFresh === false)
273
+ return environment;
274
+ if ((await readdir(home)).length)
275
+ return environment;
276
+ await assertOwnedHome(home);
277
+ if (selector !== 'latest' && !isSupportedCandidateVersion(selector))
278
+ throw new PurgeError('请求的 Host 版本不兼容。');
279
+ const cache = resolve(input.cacheRoot ?? join(homedir(), '.local', 'share', 'dsh-enhanced', 'hosts'));
280
+ await mkdir(cache, { recursive: true, mode: 0o700 });
281
+ const cacheRoot = await realpath(cache);
282
+ const runtime = await dependencies.prepare({ root: cacheRoot, selector });
283
+ if (!isSupportedCandidateVersion(runtime.version) || selector !== 'latest' && runtime.version !== selector
284
+ || !/^[a-f0-9]{64}$/u.test(runtime.receiptDigest))
285
+ throw new PurgeError('候选 Host 版本或收据无效。');
286
+ const raced = await readBinding(home);
287
+ if (raced)
288
+ return use(raced);
289
+ if ((await readdir(home)).length)
290
+ throw new PurgeError('准备 Host 期间 DSH_HOME 已被其它安装修改;未切换 Host。');
291
+ await assertOwnedHome(home);
292
+ const binding = { schemaVersion: 1, cacheRoot, version: runtime.version, receiptDigest: runtime.receiptDigest };
293
+ const temporary = join(home, `.dsh-rsi-host-${randomUUID()}.tmp`);
294
+ const file = await open(temporary, 'wx', 0o600);
295
+ try {
296
+ try {
297
+ await file.writeFile(JSON.stringify(binding) + '\n');
298
+ await file.sync();
299
+ }
300
+ finally {
301
+ await file.close();
302
+ }
303
+ try {
304
+ await link(temporary, join(home, BINDING));
305
+ }
306
+ catch (error) {
307
+ if (error.code !== 'EEXIST')
308
+ throw error;
309
+ }
310
+ }
311
+ finally {
312
+ await unlink(temporary);
313
+ }
314
+ const directory = await open(home, constants.O_RDONLY);
315
+ try {
316
+ await directory.sync();
317
+ }
318
+ finally {
319
+ await directory.close();
320
+ }
321
+ const committed = await readBinding(home);
322
+ if (!committed)
323
+ throw new PurgeError('私有 Host 绑定未完成。');
324
+ return use(committed);
325
+ }
326
+ //# sourceMappingURL=install-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-host.js","sourceRoot":"","sources":["../src/install-host.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACtF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AACrE,OAAO,EAAE,2BAA2B,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,sBAAsB,EAA2B,MAAM,mBAAmB,CAAA;AACtK,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvC,MAAM,OAAO,GAAG,oBAAoB,CAAA;AAOpC,MAAM,KAAK,GAAqB,EAAE,OAAO,EAAE,yBAAyB,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAA;AAqBpG,SAAS,YAAY,CAAC,OAAe;IACnC,MAAM,KAAK,GAAG,uCAAuC,CAAC,IAAI,CAAC,OAAO,CAAE,CAAA;IACpE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACpI,CAAC;AAED,SAAS,cAAc,CAAC,IAAY,EAAE,KAAa;IACjD,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;IACtE,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACtD,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,UAAU,CAAC,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC,KAAK,CAAE,GAAG,UAAU,CAAC,KAAK,CAAE,CAAA;IAC3F,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,WAAW,CAAC,IAAwB,EAAE,KAAyB;IACtE,OAAO,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO;WAC9F,IAAI,CAAC,YAAY,KAAK,KAAK,CAAC,YAAY,IAAI,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS;WAC9E,IAAI,CAAC,aAAa,KAAK,KAAK,CAAC,aAAa,CAAA;AACjD,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,IAAY;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAChC,IAAI,IAAI,CAAA;IACR,IAAI,CAAC;QAAC,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,UAAU,CAAC,CAAA;IAAC,CAAC;IAC1E,OAAO,KAAK,EAAE,CAAC;QACb,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,IAAI,UAAU,CAAC,iCAAiC,CAAC,CAAA;QAC/G,MAAM,KAAK,CAAA;IACb,CAAC;IACD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAA;QAClC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,QAAQ,CAAC,KAAK,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC,IAAI,QAAQ,CAAC,IAAI,GAAG,MAAM;eACxF,CAAC,QAAQ,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,IAAI,QAAQ,CAAC,GAAG,KAAK,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YACpF,MAAM,IAAI,UAAU,CAAC,kBAAkB,CAAC,CAAA;QAC1C,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAA;QACnC,IAAI,MAAc,CAAA;QAClB,IAAI,CAAC;YAAC,MAAM,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAAC,CAAC;QACxE,MAAM,CAAC;YAAC,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAA;QAAC,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAA;QACvF,IAAI,KAAc,CAAA;QAClB,IAAI,CAAC;YAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QAAC,CAAC;QAClC,MAAM,CAAC;YAAC,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAA;QAAC,CAAC;QACjD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;eAClE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,+CAA+C,EAAE,CAAC;YAC7F,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAA;QACzC,CAAC;QACD,MAAM,OAAO,GAAG,KAAoB,CAAA;QACpC,IAAI,OAAO,CAAC,aAAa,KAAK,CAAC,IAAI,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC;eACrG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,OAAO,CAAC,SAAS,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ;eACvF,CAAC,yBAAyB,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,aAAa,KAAK,QAAQ;eACxF,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAA;QAC5F,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,CAAA;IACzE,CAAC;YAAS,CAAC;QAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;IAAC,CAAC;AAClC,CAAC;AAED,qFAAqF;AACrF,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,KAAmE,EAC7G,eAAiC,KAAK;IACtC,KAAK,CAAC,MAAM,EAAE,cAAc,EAAE,CAAA;IAC9B,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACxC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;QACjD,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,IAAI,UAAU,CAAC,8BAA8B,CAAC,CAAA;QAC5G,MAAM,KAAK,CAAA;IACb,CAAC,CAAC,CAAA;IACF,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE;QAAE,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAA;IAC5F,MAAM,aAAa,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAA;IAC/C,MAAM,eAAe,CAAC,aAAa,CAAC,CAAA;IACpC,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,aAAa,CAAC,CAAA;IACvD,MAAM,eAAe,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO;QACnH,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;IACpE,IAAI,eAAe,CAAC,OAAO,KAAK,QAAQ,CAAC,OAAO,CAAC,OAAO,IAAI,eAAe,CAAC,IAAI,KAAK,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC;WAC1I,eAAe,CAAC,aAAa,KAAK,QAAQ,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QACtE,MAAM,IAAI,UAAU,CAAC,qBAAqB,CAAC,CAAA;IAC7C,CAAC;IACD,KAAK,CAAC,MAAM,EAAE,cAAc,EAAE,CAAA;IAC9B,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,QAAQ,CAAA;IAC3C,IAAI,QAAQ,KAAK,QAAQ,IAAI,CAAC,yBAAyB,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAA;IAC1G,IAAI,QAAQ,KAAK,QAAQ,IAAI,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAA;IACxH,IAAI,QAAQ,KAAK,QAAQ,IAAI,CAAC,2BAA2B,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAA;IAC5G,MAAM,eAAe,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,QAAQ;QAC7F,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;IACpE,IAAI,CAAC,yBAAyB,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,QAAQ,KAAK,QAAQ,IAAI,eAAe,CAAC,OAAO,KAAK,QAAQ;WACnH,CAAC,iBAAiB,CAAC,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,kBAAkB,CAAC,CAAA;IACrG,IAAI,cAAc,CAAC,eAAe,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAA;IAC9G,IAAI,CAAC,2BAA2B,CAAC,eAAe,CAAC,OAAO,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,kBAAkB,CAAC,CAAA;IACnG,MAAM,gBAAgB,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,eAAe,CAAC,OAAO;QACnH,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;IACpE,IAAI,CAAC,WAAW,CAAC,gBAAgB,EAAE,eAAe,CAAC;WAC9C,gBAAgB,CAAC,IAAI,KAAK,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1F,MAAM,IAAI,UAAU,CAAC,2BAA2B,CAAC,CAAA;IACnD,CAAC;IACD,KAAK,CAAC,MAAM,EAAE,cAAc,EAAE,CAAA;IAC9B,MAAM,eAAe,CAAC,aAAa,CAAC,CAAA;IACpC,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,aAAa,CAAC,CAAA;IACtD,IAAI,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1G,MAAM,IAAI,UAAU,CAAC,8BAA8B,CAAC,CAAA;IACtD,CAAC;IACD,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,KAAK,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAA;IAC3F,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,gBAAgB,EAAE,eAAe,CAAC,EAAE,CAAC;QAC5E,MAAM,IAAI,UAAU,CAAC,qBAAqB,CAAC,CAAA;IAC7C,CAAC;IACD,MAAM,sBAAsB,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;QACrF,aAAa,EAAE,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,gBAAgB,CAAC,OAAO;QAC1F,aAAa,EAAE,gBAAgB,CAAC,aAAa;KAC9C,CAAC,GAAG,IAAI,CAAA;IACT,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC;QACzF,qBAAqB,EAAE,QAAQ,CAAC,MAAM,EAAE,qBAAqB,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QACxH,eAAe,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,CAAA;AAC/D,CAAC;AAED,sFAAsF;AACtF,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,KAKlD,EAAE,eAAiC,KAAK;IACvC,KAAK,CAAC,MAAM,EAAE,cAAc,EAAE,CAAA;IAC9B,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACxC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;QACjD,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,IAAI,UAAU,CAAC,8BAA8B,CAAC,CAAA;QAC5G,MAAM,KAAK,CAAA;IACb,CAAC,CAAC,CAAA;IACF,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE;QAAE,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAA;IAC5F,MAAM,aAAa,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAA;IAC/C,MAAM,eAAe,CAAC,aAAa,CAAC,CAAA;IACpC,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC,CAAA;IAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAA;IAChD,MAAM,aAAa,GAAG,KAAK,IAAmB,EAAE;QAC9C,IAAI,CAAC;YAAC,MAAM,KAAK,CAAC,WAAW,CAAC,CAAA;QAAC,CAAC;QAChC,OAAO,KAAK,EAAE,CAAC;YACb,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAM;YAC9D,MAAM,KAAK,CAAA;QACb,CAAC;QACD,MAAM,IAAI,UAAU,CAAC,kCAAkC,CAAC,CAAA;IAC1D,CAAC,CAAA;IACD,MAAM,aAAa,EAAE,CAAA;IACrB,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,eAAe,CAAA;IAC7D,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,SAAS,CAAC,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;WACjF,OAAO,CAAC,OAAO,CAAC,KAAK,OAAO,IAAI,CAAC,yBAAyB,CAAC,OAAO,CAAC,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;QACrG,MAAM,IAAI,UAAU,CAAC,2BAA2B,CAAC,CAAA;IACnD,CAAC;IACD,MAAM,eAAe,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO;QACxE,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;IACpE,IAAI,eAAe,CAAC,OAAO,KAAK,OAAO,IAAI,eAAe,CAAC,IAAI,KAAK,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC;WACvF,eAAe,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC,EAAE,CAAC;QACnG,MAAM,IAAI,UAAU,CAAC,uBAAuB,CAAC,CAAA;IAC/C,CAAC;IACD,MAAM,kBAAkB,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,MAAM,IAAI,UAAU,CAAC,uBAAuB,CAAC,CAAA,CAAC,CAAC,CAAC,CAAA;IACjH,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,GAAG,eAAe,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,UAAU,CAAC,uBAAuB,CAAC,CAAA;IAC/C,CAAC;IACD,KAAK,CAAC,MAAM,EAAE,cAAc,EAAE,CAAA;IAC9B,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,QAAQ,CAAA;IAC3C,IAAI,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,YAAY;QAAE,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAA;IAC/F,MAAM,eAAe,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ;QAC5E,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;IACpE,IAAI,CAAC,2BAA2B,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,QAAQ,KAAK,QAAQ,IAAI,eAAe,CAAC,OAAO,KAAK,QAAQ;WACrH,CAAC,iBAAiB,CAAC,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,kBAAkB,CAAC,CAAA;IACrG,MAAM,gBAAgB,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,eAAe,CAAC,OAAO;QAClG,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;IACpE,IAAI,CAAC,WAAW,CAAC,gBAAgB,EAAE,eAAe,CAAC;WAC9C,gBAAgB,CAAC,IAAI,KAAK,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;QACzE,MAAM,IAAI,UAAU,CAAC,2BAA2B,CAAC,CAAA;IACnD,CAAC;IACD,KAAK,CAAC,MAAM,EAAE,cAAc,EAAE,CAAA;IAC9B,MAAM,eAAe,CAAC,aAAa,CAAC,CAAA;IACpC,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC,CAAA;IAC9C,IAAI,WAAW,CAAC,GAAG,KAAK,WAAW,CAAC,GAAG,IAAI,WAAW,CAAC,GAAG,KAAK,WAAW,CAAC,GAAG,EAAE,CAAC;QAC/E,MAAM,IAAI,UAAU,CAAC,sCAAsC,CAAC,CAAA;IAC9D,CAAC;IACD,MAAM,aAAa,EAAE,CAAA;IACrB,MAAM,sBAAsB,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,gBAAgB,CAAC,OAAO;QAC5G,aAAa,EAAE,gBAAgB,CAAC,aAAa,EAAE,CAAC,GAAG,IAAI,CAAA;IACzD,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,WAAW;QACrE,qBAAqB,EAAE,IAAI,EAAE,qBAAqB,EAAE,IAAI,EAAE,eAAe,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,CAAA;AACzH,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,IAAY;IACzC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAA;IAClC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,GAAG,KAAK,CAAC;WACjD,OAAO,CAAC,MAAM,IAAI,QAAQ,CAAC,GAAG,KAAK,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QACzD,MAAM,IAAI,UAAU,CAAC,gCAAgC,CAAC,CAAA;IACxD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,IAAY;IACrC,IAAI,IAAI,CAAA;IACR,IAAI,CAAC;QAAC,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,EAAC,OAAO,CAAC,EAAC,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,UAAU,CAAC,CAAA;IAAC,CAAC;IACvF,OAAO,KAAK,EAAE,CAAC;QAAC,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QAAC,MAAM,KAAK,CAAA;IAAC,CAAC;IACvG,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAA;QAClC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,QAAQ,CAAC,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,GAAG,KAAK,CAAC;eACtE,OAAO,CAAC,MAAM,IAAI,QAAQ,CAAC,GAAG,KAAK,OAAO,CAAC,MAAM,EAAE;YAAE,MAAM,IAAI,UAAU,CAAC,kBAAkB,CAAC,CAAA;QAClG,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAgB,CAAA;QACpE,IAAI,KAAK,CAAC,aAAa,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC;eAC/F,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,yBAAyB,CAAC,KAAK,CAAC,OAAO,CAAC;eAC9E,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAA;QAC1F,OAAO,KAAK,CAAA;IACd,CAAC;YAAS,CAAC;QAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;IAAC,CAAC;AAClC,CAAC;AAED;oFACoF;AACpF,MAAM,CAAC,KAAK,UAAU,6BAA6B,CAAC,KAEnD,EAAE,eAAiC,KAAK;IACvC,MAAM,WAAW,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAA;IAC7D,8EAA8E;IAC9E,OAAO,WAAW,CAAC,qBAAqB,CAAA;IACxC,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACxC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;QACjD,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAA;QACxE,MAAM,KAAK,CAAA;IACb,CAAC,CAAC,CAAA;IACF,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE;QAAE,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAA;IACrG,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,YAAY,KAAK,KAAK;QAAE,OAAO,WAAW,CAAA;IAC9D,IAAI,CAAC,KAAK;QAAE,MAAM,KAAK,CAAC,SAAS,EAAC,EAAC,SAAS,EAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC,CAAC,CAAA;IAC9D,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAA;IACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,QAAQ,CAAA;IAC3C,MAAM,GAAG,GAAG,KAAK,EAAE,OAAoB,EAA8B,EAAE;QACrE,MAAM,eAAe,CAAC,IAAI,CAAC,CAAA;QAC3B,IAAI,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,OAAO,CAAC,OAAO;YAAE,MAAM,IAAI,UAAU,CAAC,4CAA4C,CAAC,CAAA;QAC7H,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,EAAC,IAAI,EAAC,OAAO,CAAC,SAAS,EAAC,OAAO,EAAC,OAAO,CAAC,OAAO,EAAC,CAAC,CAAA;QACzF,IAAI,OAAO,CAAC,aAAa,KAAK,OAAO,CAAC,aAAa;YAAE,MAAM,IAAI,UAAU,CAAC,qBAAqB,CAAC,CAAA;QAChG,OAAO,EAAE,GAAG,WAAW,EAAC,QAAQ,EAAC,IAAI,EAAC,qBAAqB,EAAC,OAAO,CAAC,YAAY,EAAC,IAAI,EAAC,CAAC,OAAO,CAAC,YAAY,EAAC,WAAW,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAA;IACvK,CAAC,CAAA;IACD,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAA;IACxC,IAAI,QAAQ;QAAE,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAA;IAClC,IAAI,KAAK,CAAC,YAAY,KAAK,KAAK;QAAE,OAAO,WAAW,CAAA;IACpD,IAAI,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;QAAE,OAAO,WAAW,CAAA;IACpD,MAAM,eAAe,CAAC,IAAI,CAAC,CAAA;IAC3B,IAAI,QAAQ,KAAK,QAAQ,IAAI,CAAC,2BAA2B,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAA;IAE5G,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO,EAAE,EAAC,QAAQ,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,CAAC,CAAC,CAAA;IACjG,MAAM,KAAK,CAAC,KAAK,EAAC,EAAC,SAAS,EAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC,CAAC,CAAA;IAC9C,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAA;IACvC,MAAM,OAAO,GAAY,MAAM,YAAY,CAAC,OAAO,CAAC,EAAC,IAAI,EAAC,SAAS,EAAC,QAAQ,EAAC,CAAC,CAAA;IAC9E,IAAI,CAAC,2BAA2B,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,QAAQ,KAAK,QAAQ,IAAI,OAAO,CAAC,OAAO,KAAK,QAAQ;WACrG,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,kBAAkB,CAAC,CAAA;IAC7F,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAA;IACrC,IAAI,KAAK;QAAE,OAAO,GAAG,CAAC,KAAK,CAAC,CAAA;IAC5B,IAAI,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;QAAE,MAAM,IAAI,UAAU,CAAC,wCAAwC,CAAC,CAAA;IAChG,MAAM,eAAe,CAAC,IAAI,CAAC,CAAA;IAC3B,MAAM,OAAO,GAAgB,EAAC,aAAa,EAAC,CAAC,EAAC,SAAS,EAAC,OAAO,EAAC,OAAO,CAAC,OAAO,EAAC,aAAa,EAAC,OAAO,CAAC,aAAa,EAAC,CAAA;IACpH,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAC,iBAAiB,UAAU,EAAE,MAAM,CAAC,CAAA;IAChE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,EAAC,IAAI,EAAC,KAAK,CAAC,CAAA;IAC7C,IAAI,CAAC;QACH,IAAI,CAAC;YAAC,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAC,IAAI,CAAC,CAAC;YAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAA;QAAC,CAAC;gBACrE,CAAC;YAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;QAAC,CAAC;QAC9B,IAAI,CAAC;YAAC,MAAM,IAAI,CAAC,SAAS,EAAC,IAAI,CAAC,IAAI,EAAC,OAAO,CAAC,CAAC,CAAA;QAAC,CAAC;QAChD,OAAO,KAAK,EAAE,CAAC;YAAC,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAA;QAAC,CAAC;IACvF,CAAC;YAAS,CAAC;QAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAAA;IAAC,CAAC;IACrC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,EAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;IACrD,IAAI,CAAC;QAAC,MAAM,SAAS,CAAC,IAAI,EAAE,CAAA;IAAC,CAAC;YAAS,CAAC;QAAC,MAAM,SAAS,CAAC,KAAK,EAAE,CAAA;IAAC,CAAC;IAClE,MAAM,SAAS,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAA;IACzC,IAAI,CAAC,SAAS;QAAE,MAAM,IAAI,UAAU,CAAC,gBAAgB,CAAC,CAAA;IACtD,OAAO,GAAG,CAAC,SAAS,CAAC,CAAA;AACvB,CAAC"}
package/lib/install.d.ts CHANGED
@@ -1,8 +1,13 @@
1
+ import { prepareHostUpdatePlan, prepareInstallHostEnvironment } from './install-host.ts';
1
2
  /**
2
3
  * install 委托的外部副作用面:下载引导脚本与 stdio inherit 执行。
3
4
  * 测试注入假实现,做到不联网、不真实执行安装器。
4
5
  */
5
6
  export interface InstallExecutor {
7
+ /** Custom executors may supply their own Host; the production executor always
8
+ * prepares or verifies the Home-bound runtime before invoking the installer. */
9
+ prepareHost?: typeof prepareInstallHostEnvironment;
10
+ prepareHostUpdate?: typeof prepareHostUpdatePlan;
6
11
  download: (url: string) => Promise<string>;
7
12
  runInherited: (command: string, args: readonly string[], options: {
8
13
  env: NodeJS.ProcessEnv;
@@ -16,14 +21,17 @@ export interface RunInstallOptions {
16
21
  localRepositoryRoot?: string;
17
22
  /** npm 形态拉取引导脚本的 release ref;rsi-cli 默认传与自身版本一致的 vX.Y.Z。 */
18
23
  releaseRef: string;
19
- /** 原样透传给安装器的参数。 */
24
+ /** 用户传入的安装器参数;默认 install 在未选择有限安装时补 supervised 场景。 */
20
25
  passthrough: readonly string[];
21
26
  /** 安装器不识别 --dsh-home,统一经 DSH_HOME 环境变量传入。 */
22
27
  dshHome?: string;
23
28
  executor?: InstallExecutor;
29
+ /** Recover a pending v4 Host transaction, then return for caller preflight. */
30
+ recoveryOnly?: boolean;
24
31
  }
32
+ export declare function hasPendingHostRecovery(dshHome: string): Promise<boolean>;
25
33
  /**
26
- * 薄委托:dsh-rsi 不复制任何安装逻辑。
34
+ * 新 Home 先选择私有 Host,再委托现有安装器。
27
35
  * npm 形态只负责把与自身版本同 tag 的 install-npm.sh 拉到临时目录执行,
28
36
  * common.sh 的 SHA-256 自校验由脚本自身完成;local 形态直接执行 checkout 内脚本。
29
37
  * 安装器 stdio 与当前终端直连,交互提示/输出不经缓冲。
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC1C,YAAY,EAAE,CACZ,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;KAAE,KAChC,OAAO,CAAC,MAAM,CAAC,CAAA;CACrB;AAED,eAAO,MAAM,eAAe,EAAE,eAa7B,CAAA;AAED,MAAM,WAAW,iBAAiB;IAChC,8FAA8F;IAC9F,IAAI,EAAE,KAAK,GAAG,OAAO,CAAA;IACrB,mEAAmE;IACnE,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAA;IAClB,mBAAmB;IACnB,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9B,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,eAAe,CAAA;CAC3B;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoC5E"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,qBAAqB,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAA;AAKxF;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B;oFACgF;IAChF,WAAW,CAAC,EAAE,OAAO,6BAA6B,CAAA;IAClD,iBAAiB,CAAC,EAAE,OAAO,qBAAqB,CAAA;IAChD,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC1C,YAAY,EAAE,CACZ,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;KAAE,KAChC,OAAO,CAAC,MAAM,CAAC,CAAA;CACrB;AAED,eAAO,MAAM,eAAe,EAAE,eAe7B,CAAA;AAED,MAAM,WAAW,iBAAiB;IAChC,8FAA8F;IAC9F,IAAI,EAAE,KAAK,GAAG,OAAO,CAAA;IACrB,mEAAmE;IACnE,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAA;IAClB,sDAAsD;IACtD,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9B,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,eAAe,CAAA;IAC1B,+EAA+E;IAC/E,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAkDD,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE9E;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8G5E"}
package/lib/install.js CHANGED
@@ -1,10 +1,14 @@
1
1
  import { spawn } from 'node:child_process';
2
- import { chmod, mkdtemp, rm, writeFile } from 'node:fs/promises';
2
+ import { chmod, lstat, mkdtemp, readFile, readlink, realpath, rm, writeFile } from 'node:fs/promises';
3
3
  import { tmpdir } from 'node:os';
4
- import { join } from 'node:path';
4
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
5
5
  import { PurgeError } from "./purge.js";
6
+ import { prepareHostUpdatePlan, prepareInstallHostEnvironment } from "./install-host.js";
7
+ import { resolveDshHome, TRANSACTION_SUFFIX } from "./paths.js";
6
8
  const INSTALL_BASE_URL = 'https://raw.githubusercontent.com/22-ai-00/dsh-enhanced';
7
9
  export const defaultExecutor = {
10
+ prepareHost: prepareInstallHostEnvironment,
11
+ prepareHostUpdate: prepareHostUpdatePlan,
8
12
  async download(url) {
9
13
  const response = await fetch(url, { redirect: 'follow' });
10
14
  if (!response.ok)
@@ -19,8 +23,67 @@ export const defaultExecutor = {
19
23
  });
20
24
  },
21
25
  };
26
+ async function canonicalMissingAllowed(path) {
27
+ let cursor = resolve(path);
28
+ const suffix = [];
29
+ const visited = new Set();
30
+ for (;;) {
31
+ if (visited.has(cursor))
32
+ throw new PurgeError('DSH_HOME 符号链接成环。');
33
+ visited.add(cursor);
34
+ try {
35
+ const entry = await lstat(cursor);
36
+ if (entry.isSymbolicLink()) {
37
+ const target = await readlink(cursor);
38
+ cursor = isAbsolute(target) ? resolve(target) : resolve(dirname(cursor), target);
39
+ continue;
40
+ }
41
+ return resolve(await realpath(cursor), ...suffix);
42
+ }
43
+ catch (error) {
44
+ if (error.code !== 'ENOENT')
45
+ throw error;
46
+ const parent = dirname(cursor);
47
+ if (parent === cursor)
48
+ throw error;
49
+ suffix.unshift(cursor.slice(parent.length + (parent === '/' ? 0 : 1)));
50
+ cursor = parent;
51
+ }
52
+ }
53
+ }
54
+ async function pendingHostRecovery(homePath) {
55
+ const path = `${homePath}${TRANSACTION_SUFFIX}/manifest.json`;
56
+ const info = await lstat(path).catch(error => {
57
+ if (error.code === 'ENOENT')
58
+ return undefined;
59
+ throw error;
60
+ });
61
+ if (info === undefined)
62
+ return undefined;
63
+ if (!info.isFile() || info.isSymbolicLink() || info.nlink !== 1 || info.uid !== process.getuid?.()
64
+ || (info.mode & 0o077) !== 0 || info.size > 2 * 1024 * 1024) {
65
+ throw new PurgeError('已有 Host 事务 manifest 文件不安全,拒绝继续升级。');
66
+ }
67
+ let manifest;
68
+ try {
69
+ manifest = JSON.parse(await readFile(path, 'utf8'));
70
+ }
71
+ catch {
72
+ throw new PurgeError('已有 Host 事务 manifest 无法解析,拒绝继续升级。');
73
+ }
74
+ if (typeof manifest !== 'object' || manifest === null || !('version' in manifest) || manifest.version !== 4)
75
+ return undefined;
76
+ if (!('profile' in manifest) || typeof manifest.profile !== 'string'
77
+ || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u.test(manifest.profile)) {
78
+ throw new PurgeError('已有 Host 事务 profile 无效,拒绝继续升级。');
79
+ }
80
+ return manifest.profile;
81
+ }
82
+ export async function hasPendingHostRecovery(dshHome) {
83
+ return await pendingHostRecovery(await canonicalMissingAllowed(dshHome)) !== undefined;
84
+ }
22
85
  /**
23
- * 薄委托:dsh-rsi 不复制任何安装逻辑。
86
+ * 新 Home 先选择私有 Host,再委托现有安装器。
24
87
  * npm 形态只负责把与自身版本同 tag 的 install-npm.sh 拉到临时目录执行,
25
88
  * common.sh 的 SHA-256 自校验由脚本自身完成;local 形态直接执行 checkout 内脚本。
26
89
  * 安装器 stdio 与当前终端直连,交互提示/输出不经缓冲。
@@ -28,37 +91,133 @@ export const defaultExecutor = {
28
91
  */
29
92
  export async function runInstall(options) {
30
93
  const executor = options.executor ?? defaultExecutor;
31
- const env = { ...process.env };
94
+ let env = { ...process.env };
95
+ for (const name of ['DSH_ENHANCED_HOST_UPDATE_PLAN', 'DSH_ENHANCED_HOST_UPDATE_BIN',
96
+ 'DSH_ENHANCED_HOST_SELECTOR_VALIDATED', 'DSH_ENHANCED_HOST_RECOVERY_ONLY',
97
+ 'DSH_ENHANCED_HOST_RECOVERY_PROFILE'])
98
+ delete env[name];
32
99
  if (options.dshHome !== undefined)
33
100
  env.DSH_HOME = options.dshHome;
34
- if (options.mode === 'local') {
35
- const root = options.localRepositoryRoot;
36
- if (root === undefined || root.length === 0) {
37
- throw new PurgeError('local 安装形态需要 --local <checkout 目录>');
38
- }
39
- const script = join(root, 'scripts', 'install', 'install-local.sh');
40
- return await executor.runInherited('bash', [script, ...options.passthrough], { env });
41
- }
42
- if (!/^v\d+\.\d+\.\d+$/u.test(options.releaseRef)) {
101
+ // Validate the invocation before preparing a runtime or touching the Home.
102
+ if (options.mode === 'local' && !options.localRepositoryRoot)
103
+ throw new PurgeError('local 安装形态需要 --local <checkout 目录>');
104
+ if (options.mode === 'npm' && !/^v\d+\.\d+\.\d+$/u.test(options.releaseRef))
43
105
  throw new PurgeError(`非法的 release ref:${options.releaseRef}(应为 vX.Y.Z)`);
44
- }
45
- const url = `${INSTALL_BASE_URL}/${options.releaseRef}/scripts/install/install-npm.sh`;
46
- let scriptBody;
47
- try {
48
- scriptBody = await executor.download(url);
49
- }
50
- catch (error) {
51
- throw new PurgeError(`下载 install-npm.sh 失败(${url}):${error.message}。请检查网络,或改用 local 形态。`);
52
- }
53
- const staging = await mkdtemp(join(tmpdir(), 'dsh-rsi-install-'));
54
- const scriptPath = join(staging, 'install-npm.sh');
106
+ const value = (name) => {
107
+ const index = options.passthrough.indexOf(name);
108
+ if (index < 0)
109
+ return undefined;
110
+ const result = options.passthrough[index + 1];
111
+ if (!result || result.startsWith('-') || options.passthrough.lastIndexOf(name) !== index) {
112
+ throw new PurgeError(`${name} 必须提供一个且仅一个值。`);
113
+ }
114
+ return result;
115
+ };
116
+ const operation = value('--operation') ?? 'install';
117
+ const selector = value('--dsh-version') ?? 'latest';
118
+ const help = options.passthrough.includes('--help') || options.passthrough.includes('-h');
119
+ const explicitlySet = (name) => {
120
+ let present = false;
121
+ for (let index = 0; index < options.passthrough.length; index++) {
122
+ if (options.passthrough[index] !== name)
123
+ continue;
124
+ const selected = options.passthrough[index + 1];
125
+ if (!selected || selected.startsWith('-'))
126
+ throw new PurgeError(`${name} 必须提供一个值。`);
127
+ present = true;
128
+ }
129
+ return present;
130
+ };
131
+ const scenarioExplicit = !help && explicitlySet('--scenario');
132
+ const modeExplicit = !help && explicitlySet('--mode');
133
+ const larkOptionIndex = options.passthrough.lastIndexOf('--lark');
134
+ const limitedOptOut = options.passthrough.includes('--no-service')
135
+ || (larkOptionIndex >= 0 && options.passthrough[larkOptionIndex + 1] === 'skip');
136
+ const passthrough = operation === 'install' && !help && !scenarioExplicit && !modeExplicit && !limitedOptOut
137
+ ? [...options.passthrough, '--scenario', 'supervised']
138
+ : options.passthrough;
139
+ const inactive = options.passthrough.some(arg => ['--help', '-h', '--dry-run'].includes(arg));
140
+ let installerStaging;
141
+ let planStaging;
55
142
  try {
56
- await writeFile(scriptPath, scriptBody, { mode: 0o700 });
57
- await chmod(scriptPath, 0o700);
58
- return await executor.runInherited('bash', [scriptPath, ...options.passthrough], { env });
143
+ let script;
144
+ if (options.mode === 'local') {
145
+ script = join(options.localRepositoryRoot, 'scripts', 'install', 'install-local.sh');
146
+ }
147
+ else {
148
+ const url = `${INSTALL_BASE_URL}/${options.releaseRef}/scripts/install/install-npm.sh`;
149
+ let source;
150
+ try {
151
+ source = await executor.download(url);
152
+ }
153
+ catch (error) {
154
+ throw new PurgeError(`下载 install-npm.sh 失败(${url}):${error.message}。请检查网络,或改用 local 形态。`);
155
+ }
156
+ installerStaging = await mkdtemp(join(tmpdir(), 'dsh-rsi-install-'));
157
+ script = join(installerStaging, 'install-npm.sh');
158
+ await writeFile(script, source, { mode: 0o700 });
159
+ await chmod(script, 0o700);
160
+ }
161
+ if (operation === 'upgrade' && !inactive) {
162
+ const canonicalHome = await canonicalMissingAllowed(resolveDshHome(env));
163
+ const recoveryProfile = await pendingHostRecovery(canonicalHome);
164
+ if (recoveryProfile !== undefined) {
165
+ const recovered = await executor.runInherited('bash', [script, ...passthrough], {
166
+ env: { ...env, DSH_HOME: canonicalHome, DSH_ENHANCED_HOST_RECOVERY_ONLY: '1',
167
+ DSH_ENHANCED_HOST_RECOVERY_PROFILE: recoveryProfile },
168
+ });
169
+ if (recovered !== 0)
170
+ return recovered;
171
+ if (options.recoveryOnly)
172
+ return 0;
173
+ }
174
+ }
175
+ if (options.recoveryOnly)
176
+ throw new PurgeError('没有待恢复的 v4 Host 事务。');
177
+ if (executor.prepareHost && !inactive) {
178
+ env = await executor.prepareHost({ dshHome: resolveDshHome(env),
179
+ selector: operation === 'upgrade' ? 'latest' : selector, environment: env,
180
+ prepareFresh: operation === 'install' });
181
+ }
182
+ if (operation === 'upgrade' && !inactive && executor.prepareHostUpdate) {
183
+ const home = await canonicalMissingAllowed(resolveDshHome(env));
184
+ const binding = await lstat(join(home, '.dsh-rsi-host.json')).catch(error => {
185
+ if (error.code === 'ENOENT')
186
+ return undefined;
187
+ throw error;
188
+ });
189
+ if (binding === undefined) {
190
+ if (selector !== 'latest')
191
+ throw new PurgeError('现有 Home 没有私有 Host 绑定;不能用 --dsh-version 修改全局 Host。');
192
+ process.stdout.write('现有 Home 未绑定私有 Host;保留当前 Host,仅升级插件集合。\n');
193
+ }
194
+ else {
195
+ const plan = await executor.prepareHostUpdate({ dshHome: home, selector });
196
+ if (plan.originalRuntime.version.startsWith('0.1.') && plan.candidateRuntime.version.startsWith('0.2.')) {
197
+ throw new PurgeError('DSH 0.1 → 0.2 跨 ABI 自动升级尚未支持;未修改原 Home 或插件。请保留原安装,在新的 DSH_HOME 中安装并验证新版,勿先替换旧 profile 的插件。');
198
+ }
199
+ if (process.platform === 'darwin' && plan.status === 'update') {
200
+ throw new PurgeError('macOS 尚不支持受管 Host 跨版本更新;未修改原 Home 或插件。请保留当前 Host,或在新的 DSH_HOME 中安装并验证目标版本。');
201
+ }
202
+ env = { ...env, DSH_ENHANCED_HOST_SELECTOR_VALIDATED: '1' };
203
+ if (plan.status === 'update') {
204
+ planStaging = await mkdtemp(join(tmpdir(), 'dsh-rsi-host-plan-'));
205
+ const planPath = join(planStaging, 'plan.json');
206
+ await writeFile(planPath, `${JSON.stringify(plan)}\n`, { mode: 0o600 });
207
+ env = { ...env, DSH_ENHANCED_HOST_UPDATE_PLAN: planPath,
208
+ DSH_ENHANCED_HOST_UPDATE_BIN: plan.candidateRuntime.binDirectory };
209
+ }
210
+ else
211
+ process.stdout.write(`私有 Host 已是当前版本:${plan.originalRuntime.version}\n`);
212
+ }
213
+ }
214
+ return await executor.runInherited('bash', [script, ...passthrough], { env });
59
215
  }
60
216
  finally {
61
- await rm(staging, { recursive: true, force: true });
217
+ if (planStaging !== undefined)
218
+ await rm(planStaging, { recursive: true, force: true });
219
+ if (installerStaging !== undefined)
220
+ await rm(installerStaging, { recursive: true, force: true });
62
221
  }
63
222
  }
64
223
  //# sourceMappingURL=install.js.map