hagiscript-sdk 0.2.9-dev.1780142424.1.dedbfb7

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 (86) hide show
  1. package/README.md +25 -0
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/package.json +38 -0
  4. package/dist/src/index.d.ts +24 -0
  5. package/dist/src/index.js +27 -0
  6. package/dist/src/index.js.map +1 -0
  7. package/dist/src/runtime/command-launch.d.ts +41 -0
  8. package/dist/src/runtime/command-launch.js +100 -0
  9. package/dist/src/runtime/command-launch.js.map +1 -0
  10. package/dist/src/runtime/component-service-manager.d.ts +71 -0
  11. package/dist/src/runtime/component-service-manager.js +367 -0
  12. package/dist/src/runtime/component-service-manager.js.map +1 -0
  13. package/dist/src/runtime/dotnet-installer.d.ts +39 -0
  14. package/dist/src/runtime/dotnet-installer.js +282 -0
  15. package/dist/src/runtime/dotnet-installer.js.map +1 -0
  16. package/dist/src/runtime/download-cache.d.ts +7 -0
  17. package/dist/src/runtime/download-cache.js +110 -0
  18. package/dist/src/runtime/download-cache.js.map +1 -0
  19. package/dist/src/runtime/manifest-manager.d.ts +57 -0
  20. package/dist/src/runtime/manifest-manager.js +188 -0
  21. package/dist/src/runtime/manifest-manager.js.map +1 -0
  22. package/dist/src/runtime/node-download.d.ts +13 -0
  23. package/dist/src/runtime/node-download.js +78 -0
  24. package/dist/src/runtime/node-download.js.map +1 -0
  25. package/dist/src/runtime/node-extract.d.ts +18 -0
  26. package/dist/src/runtime/node-extract.js +170 -0
  27. package/dist/src/runtime/node-extract.js.map +1 -0
  28. package/dist/src/runtime/node-installer.d.ts +43 -0
  29. package/dist/src/runtime/node-installer.js +114 -0
  30. package/dist/src/runtime/node-installer.js.map +1 -0
  31. package/dist/src/runtime/node-platform.d.ts +15 -0
  32. package/dist/src/runtime/node-platform.js +51 -0
  33. package/dist/src/runtime/node-platform.js.map +1 -0
  34. package/dist/src/runtime/node-release.d.ts +28 -0
  35. package/dist/src/runtime/node-release.js +149 -0
  36. package/dist/src/runtime/node-release.js.map +1 -0
  37. package/dist/src/runtime/node-verify.d.ts +30 -0
  38. package/dist/src/runtime/node-verify.js +102 -0
  39. package/dist/src/runtime/node-verify.js.map +1 -0
  40. package/dist/src/runtime/npm-global.d.ts +28 -0
  41. package/dist/src/runtime/npm-global.js +77 -0
  42. package/dist/src/runtime/npm-global.js.map +1 -0
  43. package/dist/src/runtime/npm-sync.d.ts +164 -0
  44. package/dist/src/runtime/npm-sync.js +603 -0
  45. package/dist/src/runtime/npm-sync.js.map +1 -0
  46. package/dist/src/runtime/pm2-manager.d.ts +102 -0
  47. package/dist/src/runtime/pm2-manager.js +715 -0
  48. package/dist/src/runtime/pm2-manager.js.map +1 -0
  49. package/dist/src/runtime/runtime-executor.d.ts +58 -0
  50. package/dist/src/runtime/runtime-executor.js +291 -0
  51. package/dist/src/runtime/runtime-executor.js.map +1 -0
  52. package/dist/src/runtime/runtime-manager.d.ts +92 -0
  53. package/dist/src/runtime/runtime-manager.js +798 -0
  54. package/dist/src/runtime/runtime-manager.js.map +1 -0
  55. package/dist/src/runtime/runtime-manifest.d.ts +106 -0
  56. package/dist/src/runtime/runtime-manifest.js +450 -0
  57. package/dist/src/runtime/runtime-manifest.js.map +1 -0
  58. package/dist/src/runtime/runtime-paths.d.ts +45 -0
  59. package/dist/src/runtime/runtime-paths.js +138 -0
  60. package/dist/src/runtime/runtime-paths.js.map +1 -0
  61. package/dist/src/runtime/runtime-state.d.ts +45 -0
  62. package/dist/src/runtime/runtime-state.js +82 -0
  63. package/dist/src/runtime/runtime-state.js.map +1 -0
  64. package/dist/src/runtime/server-config.d.ts +21 -0
  65. package/dist/src/runtime/server-config.js +169 -0
  66. package/dist/src/runtime/server-config.js.map +1 -0
  67. package/dist/src/runtime/server-manager.d.ts +107 -0
  68. package/dist/src/runtime/server-manager.js +946 -0
  69. package/dist/src/runtime/server-manager.js.map +1 -0
  70. package/dist/src/runtime/server-version-state.d.ts +43 -0
  71. package/dist/src/runtime/server-version-state.js +156 -0
  72. package/dist/src/runtime/server-version-state.js.map +1 -0
  73. package/dist/src/runtime/seven-zip-extract.d.ts +15 -0
  74. package/dist/src/runtime/seven-zip-extract.js +104 -0
  75. package/dist/src/runtime/seven-zip-extract.js.map +1 -0
  76. package/dist/src/runtime/tool-sync-catalog.config.json +59 -0
  77. package/dist/src/runtime/tool-sync-catalog.d.ts +48 -0
  78. package/dist/src/runtime/tool-sync-catalog.js +189 -0
  79. package/dist/src/runtime/tool-sync-catalog.js.map +1 -0
  80. package/dist/src/runtime/zip-extract.d.ts +6 -0
  81. package/dist/src/runtime/zip-extract.js +90 -0
  82. package/dist/src/runtime/zip-extract.js.map +1 -0
  83. package/dist/src/version.d.ts +7 -0
  84. package/dist/src/version.js +23 -0
  85. package/dist/src/version.js.map +1 -0
  86. package/package.json +38 -0
@@ -0,0 +1,798 @@
1
+ import { existsSync } from "node:fs";
2
+ import { chmod, mkdir, rm, writeFile } from "node:fs/promises";
3
+ import { dirname, join, relative } from "node:path";
4
+ import process from "node:process";
5
+ import semver from "semver";
6
+ import { installGlobalPackage, listGlobalPackages } from "./npm-global.js";
7
+ import { validateNpmSyncManifest } from "./npm-sync.js";
8
+ import { installNodeRuntime, resolveManagedNodeRuntime } from "./node-installer.js";
9
+ import { getRuntimeExecutablePaths, verifyNodeRuntime } from "./node-verify.js";
10
+ import { executeRuntimeScript, writeRuntimeLog } from "./runtime-executor.js";
11
+ import { ManagedPm2Error, runManagedPm2Command, supportedPm2Services } from "./pm2-manager.js";
12
+ import { loadRuntimeManifest } from "./runtime-manifest.js";
13
+ import { getComponentConfigDirectory, getComponentLogsDirectory, getComponentManagedRoot, getComponentPm2Home, getComponentRuntimeDataHome, isPathInsideRuntimeRoot, resolveReleasedServicePath, resolveRuntimePaths } from "./runtime-paths.js";
14
+ import { mergeRuntimeState, readRuntimeState, writeRuntimeState } from "./runtime-state.js";
15
+ export class RuntimeLifecycleError extends Error {
16
+ constructor(message, options) {
17
+ super(message, options);
18
+ this.name = "RuntimeLifecycleError";
19
+ }
20
+ }
21
+ const DEFAULT_MANAGED_PM2_VERSION = "7.0.1";
22
+ export async function installRuntime(options = {}) {
23
+ return runRuntimeLifecycle("install", options);
24
+ }
25
+ export async function removeRuntime(options = {}) {
26
+ return runRuntimeLifecycle("remove", options);
27
+ }
28
+ export async function updateRuntime(options = {}) {
29
+ return runRuntimeLifecycle("update", options);
30
+ }
31
+ export async function runRuntimeLifecycle(phase, options = {}) {
32
+ const manifest = await loadRuntimeManifest({ manifestPath: options.manifestPath });
33
+ const paths = resolveRuntimePaths(manifest, { runtimeRoot: options.runtimeRoot });
34
+ const existingState = await readRuntimeState(paths.stateFile);
35
+ const state = mergeRuntimeState(manifest, paths, existingState);
36
+ const { plan, skipped } = planRuntimeLifecycle(phase, manifest, state, options);
37
+ const now = options.now ?? (() => new Date());
38
+ const logger = options.logger ?? (() => undefined);
39
+ if (options.dryRun || (phase === "update" && options.checkOnly)) {
40
+ for (const action of plan) {
41
+ logger(`Plan: ${phase} ${action.componentName} (${action.strategy}${action.reason ? `: ${action.reason}` : ""})`);
42
+ }
43
+ for (const item of skipped) {
44
+ logger(`Skip: ${item.componentName} (${item.reason})`);
45
+ }
46
+ return {
47
+ manifest,
48
+ paths,
49
+ state,
50
+ plan,
51
+ skipped,
52
+ changedComponents: []
53
+ };
54
+ }
55
+ await ensureManagedDirectories(paths);
56
+ const logFilePath = join(paths.logs, `${phase}-${now().toISOString().replaceAll(":", "-")}.log`);
57
+ const operationState = {
58
+ phase,
59
+ status: "success",
60
+ selectedComponents: plan.map((item) => item.componentName),
61
+ completedComponents: [],
62
+ startedAt: now().toISOString(),
63
+ finishedAt: now().toISOString(),
64
+ logFile: logFilePath
65
+ };
66
+ const changedComponents = [];
67
+ await writeRuntimeLog(logFilePath, `Runtime ${phase} starting with managed root ${paths.root}`);
68
+ try {
69
+ for (const action of plan) {
70
+ const component = manifest.componentMap.get(action.componentName);
71
+ if (!component) {
72
+ throw new RuntimeLifecycleError(`Unknown runtime component ${action.componentName}`);
73
+ }
74
+ logger(`Running ${phase}: ${component.name}`);
75
+ try {
76
+ const componentState = await executeRuntimeAction(action, component, manifest, paths, options, logFilePath);
77
+ state.components[component.name] = componentState;
78
+ changedComponents.push(component.name);
79
+ operationState.completedComponents.push(component.name);
80
+ }
81
+ catch (error) {
82
+ state.components[component.name] = {
83
+ name: component.name,
84
+ type: component.type,
85
+ status: "failed",
86
+ version: state.components[component.name]?.version ?? null,
87
+ managedProgramPaths: [getComponentManagedRoot(paths, component.name)],
88
+ managedDataPaths: [
89
+ getComponentRuntimeDataHome(paths, component.name, component.runtimeDataDir),
90
+ getComponentConfigDirectory(paths, component.name, component.runtimeDataDir),
91
+ getComponentLogsDirectory(paths, component.name, component.runtimeDataDir),
92
+ ...(component.pm2
93
+ ? [
94
+ getComponentPm2Home(paths, component.name, component.runtimeDataDir, component.pm2.pm2Home)
95
+ ]
96
+ : [])
97
+ ],
98
+ managedPaths: [
99
+ getComponentManagedRoot(paths, component.name),
100
+ getComponentRuntimeDataHome(paths, component.name, component.runtimeDataDir),
101
+ getComponentConfigDirectory(paths, component.name, component.runtimeDataDir),
102
+ getComponentLogsDirectory(paths, component.name, component.runtimeDataDir),
103
+ ...(component.pm2
104
+ ? [
105
+ getComponentPm2Home(paths, component.name, component.runtimeDataDir, component.pm2.pm2Home)
106
+ ]
107
+ : [])
108
+ ],
109
+ lastAction: phase,
110
+ lastUpdatedAt: now().toISOString(),
111
+ logFile: logFilePath,
112
+ details: {
113
+ error: error instanceof Error ? error.message : String(error)
114
+ }
115
+ };
116
+ throw new RuntimeLifecycleError(`${phase} failed for component ${component.name}: ${error instanceof Error ? error.message : String(error)}\nLog: ${logFilePath}`, error instanceof Error ? { cause: error } : undefined);
117
+ }
118
+ }
119
+ if (phase !== "remove" && shouldEnsureManagedPm2(plan, manifest)) {
120
+ const pm2Result = await ensureManagedPm2Package(manifest, paths, options);
121
+ await writeRuntimeLog(logFilePath, pm2Result.changed
122
+ ? `Managed pm2 installed into ${pm2Result.prefix} using ${pm2Result.selector}`
123
+ : `Managed pm2 already satisfied in ${pm2Result.prefix} (${pm2Result.installedVersion ?? "unknown version"})`);
124
+ }
125
+ operationState.finishedAt = now().toISOString();
126
+ state.lastOperation = operationState;
127
+ await writeRuntimeState(paths.stateFile, state);
128
+ return {
129
+ manifest,
130
+ paths,
131
+ state,
132
+ plan,
133
+ skipped,
134
+ changedComponents,
135
+ logFilePath
136
+ };
137
+ }
138
+ catch (error) {
139
+ operationState.status = "failed";
140
+ operationState.finishedAt = now().toISOString();
141
+ operationState.message = error instanceof Error ? error.message : String(error);
142
+ state.lastOperation = operationState;
143
+ await writeRuntimeLog(logFilePath, `Failure: ${operationState.message}`);
144
+ await writeRuntimeState(paths.stateFile, state);
145
+ throw error;
146
+ }
147
+ }
148
+ export async function queryRuntimeState(options) {
149
+ const manifest = await loadRuntimeManifest({ manifestPath: options.manifestPath });
150
+ const paths = resolveRuntimePaths(manifest, { runtimeRoot: options.runtimeRoot });
151
+ const state = mergeRuntimeState(manifest, paths, await readRuntimeState(paths.stateFile));
152
+ const components = manifest.components.map((component) => {
153
+ const entry = state.components[component.name];
154
+ const fallbackProgramPaths = [getComponentManagedRoot(paths, component.name)];
155
+ const runtimeDataHome = getComponentRuntimeDataHome(paths, component.name, component.runtimeDataDir);
156
+ const pm2Home = component.pm2
157
+ ? getComponentPm2Home(paths, component.name, component.runtimeDataDir, component.pm2.pm2Home)
158
+ : null;
159
+ const fallbackDataPaths = [
160
+ runtimeDataHome,
161
+ getComponentConfigDirectory(paths, component.name, component.runtimeDataDir),
162
+ getComponentLogsDirectory(paths, component.name, component.runtimeDataDir),
163
+ ...(pm2Home ? [pm2Home] : [])
164
+ ];
165
+ const programPaths = entry?.managedProgramPaths ?? fallbackProgramPaths;
166
+ const externalDataPaths = entry?.managedDataPaths ?? fallbackDataPaths;
167
+ return {
168
+ name: component.name,
169
+ type: component.type,
170
+ required: component.required,
171
+ status: entry?.status ?? "not-installed",
172
+ version: entry?.version ?? null,
173
+ runtimeDataHome,
174
+ pm2Home,
175
+ programPaths,
176
+ externalDataPaths,
177
+ managedPaths: entry?.managedPaths ?? [...programPaths, ...externalDataPaths],
178
+ details: entry?.details
179
+ };
180
+ });
181
+ const programRoots = [
182
+ paths.runtimeHome,
183
+ paths.bin,
184
+ paths.componentsRoot
185
+ ];
186
+ const externalDataRoots = [
187
+ paths.runtimeDataRoot,
188
+ paths.config,
189
+ paths.logs,
190
+ paths.data,
191
+ paths.componentDataRoot,
192
+ paths.npmPrefix
193
+ ];
194
+ return {
195
+ runtime: state.runtime,
196
+ managedRoot: state.managedRoot,
197
+ managedPaths: state.managedPaths,
198
+ layout: {
199
+ separated: paths.runtimeHome !== paths.runtimeDataRoot,
200
+ runtimeHome: paths.runtimeHome,
201
+ runtimeDataRoot: paths.runtimeDataRoot,
202
+ programRoots,
203
+ externalDataRoots
204
+ },
205
+ ready: components
206
+ .filter((component) => component.required)
207
+ .every((component) => component.status === "installed" &&
208
+ component.details?.releasedServiceReady !== false),
209
+ components,
210
+ lastOperation: state.lastOperation
211
+ };
212
+ }
213
+ export function renderRuntimeStateText(report) {
214
+ const lines = [
215
+ `Runtime: ${report.runtime.name} ${report.runtime.version}`,
216
+ `Manifest: ${report.runtime.manifestPath}`,
217
+ `Managed root: ${report.managedRoot}`,
218
+ `Runtime home: ${report.layout.runtimeHome}`,
219
+ `Runtime data root: ${report.layout.runtimeDataRoot}`,
220
+ `Bin: ${report.managedPaths.bin}`,
221
+ `State file: ${report.managedPaths.stateFile}`,
222
+ `Program roots: ${report.layout.programRoots.join(", ")}`,
223
+ `External data roots: ${report.layout.externalDataRoots.join(", ")}`,
224
+ `Separated layout: ${report.layout.separated ? "yes" : "no"}`,
225
+ `Ready: ${report.ready ? "yes" : "no"}`
226
+ ];
227
+ for (const component of report.components) {
228
+ lines.push(`- ${component.name}: ${component.status} version=${component.version ?? "n/a"} program=${component.programPaths.join("|") || "n/a"} data=${component.externalDataPaths.join("|") || "n/a"}`);
229
+ if (component.runtimeDataHome) {
230
+ lines.push(` runtime-data-home=${component.runtimeDataHome}`);
231
+ }
232
+ if (component.pm2Home) {
233
+ lines.push(` pm2-home=${component.pm2Home}`);
234
+ }
235
+ const details = stateDetailsFromComponent(component);
236
+ if (details) {
237
+ lines.push(` details=${details}`);
238
+ }
239
+ }
240
+ if (report.lastOperation) {
241
+ lines.push(`Last operation: ${report.lastOperation.phase} ${report.lastOperation.status} (${report.lastOperation.completedComponents.length}/${report.lastOperation.selectedComponents.length})`);
242
+ }
243
+ return lines.join("\n");
244
+ }
245
+ function stateDetailsFromComponent(component) {
246
+ const details = component.details;
247
+ if (!details) {
248
+ return null;
249
+ }
250
+ const summary = typeof details.readinessSummary === "string"
251
+ ? details.readinessSummary
252
+ : typeof details.cleanupSummary === "string"
253
+ ? details.cleanupSummary
254
+ : null;
255
+ return summary;
256
+ }
257
+ export function planRuntimeLifecycle(phase, manifest, state, options = {}) {
258
+ const requestedSet = selectRequestedComponents(manifest, options.components, phase);
259
+ const orderedComponentNames = orderedPhaseComponents(manifest, phase).filter((name) => requestedSet.has(name));
260
+ const plan = [];
261
+ const skipped = [];
262
+ for (const componentName of orderedComponentNames) {
263
+ const component = manifest.componentMap.get(componentName);
264
+ if (!component) {
265
+ throw new RuntimeLifecycleError(`Unknown runtime component ${componentName}`);
266
+ }
267
+ if (phase === "update" && !options.force) {
268
+ const currentState = state.components[component.name];
269
+ if (currentState?.status === "installed" && !componentNeedsUpdate(component, currentState)) {
270
+ skipped.push({
271
+ componentName: component.name,
272
+ reason: "already up to date"
273
+ });
274
+ continue;
275
+ }
276
+ }
277
+ plan.push(resolveRuntimeAction(component, phase));
278
+ }
279
+ return { plan, skipped };
280
+ }
281
+ async function executeRuntimeAction(action, component, manifest, paths, options, logFilePath) {
282
+ const componentRoot = getComponentManagedRoot(paths, component.name);
283
+ const componentConfigDir = getComponentConfigDirectory(paths, component.name, component.runtimeDataDir);
284
+ switch (component.name) {
285
+ case "node":
286
+ return executeNodeComponent(action.phase, component, paths, options, logFilePath);
287
+ default:
288
+ return executeScriptComponent(action, component, manifest, paths, componentRoot, componentConfigDir, options, logFilePath);
289
+ }
290
+ }
291
+ async function executeNodeComponent(phase, component, paths, options, logFilePath) {
292
+ await mkdir(dirname(paths.nodeRuntime), { recursive: true });
293
+ if (phase === "remove") {
294
+ await rm(paths.nodeRuntime, { recursive: true, force: true });
295
+ await removeWrapper(paths.bin, "node");
296
+ await removeWrapper(paths.bin, "npm");
297
+ await writeRuntimeLog(logFilePath, "Removed managed node runtime directories");
298
+ return {
299
+ name: component.name,
300
+ type: component.type,
301
+ status: "removed",
302
+ version: null,
303
+ managedProgramPaths: [paths.nodeRuntime],
304
+ managedDataPaths: [],
305
+ managedPaths: [paths.nodeRuntime],
306
+ lastAction: phase,
307
+ lastUpdatedAt: new Date().toISOString(),
308
+ logFile: logFilePath
309
+ };
310
+ }
311
+ const desiredVersion = component.version ?? component.channelVersion;
312
+ const currentVerification = await verifyNodeRuntime(paths.nodeRuntime);
313
+ const normalizedCurrentVersion = normalizeVersion(currentVerification.nodeVersion);
314
+ const normalizedDesiredVersion = normalizeVersion(desiredVersion);
315
+ if (!currentVerification.valid) {
316
+ await rm(paths.nodeRuntime, { recursive: true, force: true });
317
+ }
318
+ if (phase === "update" &&
319
+ currentVerification.valid &&
320
+ normalizedDesiredVersion &&
321
+ normalizedCurrentVersion &&
322
+ normalizedDesiredVersion !== normalizedCurrentVersion) {
323
+ await rm(paths.nodeRuntime, { recursive: true, force: true });
324
+ }
325
+ const resolvedRuntime = await resolveNodeRuntimeForPhase(phase, paths.nodeRuntime, desiredVersion, currentVerification, options.downloadCache, options.downloadCacheDir);
326
+ const wrappers = await materializeNodeWrappers(paths.bin, {
327
+ nodePath: resolvedRuntime.nodePath,
328
+ npmPath: resolvedRuntime.npmPath
329
+ });
330
+ await writeRuntimeLog(logFilePath, `Node runtime ready: ${resolvedRuntime.targetDirectory} (${resolvedRuntime.nodeVersion})`);
331
+ return {
332
+ name: component.name,
333
+ type: component.type,
334
+ status: "installed",
335
+ version: normalizeVersion(resolvedRuntime.nodeVersion),
336
+ managedProgramPaths: [paths.nodeRuntime, ...wrappers],
337
+ managedDataPaths: [],
338
+ managedPaths: [paths.nodeRuntime, ...wrappers],
339
+ lastAction: phase,
340
+ lastUpdatedAt: new Date().toISOString(),
341
+ logFile: logFilePath
342
+ };
343
+ }
344
+ async function executeScriptComponent(action, component, manifest, paths, componentRoot, componentConfigDir, options, logFilePath) {
345
+ await mkdir(componentRoot, { recursive: true });
346
+ await mkdir(componentConfigDir, { recursive: true });
347
+ const scriptPath = resolveScriptForAction(action, component);
348
+ if (scriptPath) {
349
+ if (action.phase === "remove") {
350
+ await cleanupManagedPm2Service(component, manifest, paths, options, logFilePath);
351
+ }
352
+ await executeRuntimeScript(scriptPath, {
353
+ component,
354
+ phase: action.phase,
355
+ manifest,
356
+ paths,
357
+ componentRoot,
358
+ componentConfigDir,
359
+ logFilePath,
360
+ purge: options.purge,
361
+ verbose: options.verbose,
362
+ downloadCache: options.downloadCache,
363
+ downloadCacheDir: options.downloadCacheDir,
364
+ npmRegistryMirror: options.npmRegistryMirror,
365
+ pm2VersionOverride: options.pm2VersionOverride
366
+ });
367
+ if (action.phase !== "remove" && component.scripts.configure) {
368
+ await executeRuntimeScript(component.scripts.configure, {
369
+ component,
370
+ phase: action.phase,
371
+ manifest,
372
+ paths,
373
+ componentRoot,
374
+ componentConfigDir,
375
+ logFilePath,
376
+ purge: options.purge,
377
+ verbose: options.verbose,
378
+ downloadCache: options.downloadCache,
379
+ downloadCacheDir: options.downloadCacheDir,
380
+ npmRegistryMirror: options.npmRegistryMirror,
381
+ pm2VersionOverride: options.pm2VersionOverride
382
+ });
383
+ }
384
+ if (action.phase !== "remove" && component.scripts.verify) {
385
+ await executeRuntimeScript(component.scripts.verify, {
386
+ component,
387
+ phase: action.phase,
388
+ manifest,
389
+ paths,
390
+ componentRoot,
391
+ componentConfigDir,
392
+ logFilePath,
393
+ purge: options.purge,
394
+ verbose: options.verbose,
395
+ downloadCache: options.downloadCache,
396
+ downloadCacheDir: options.downloadCacheDir,
397
+ npmRegistryMirror: options.npmRegistryMirror,
398
+ pm2VersionOverride: options.pm2VersionOverride
399
+ });
400
+ }
401
+ }
402
+ else {
403
+ const componentDataHome = getComponentRuntimeDataHome(paths, component.name, component.runtimeDataDir);
404
+ await cleanupManagedComponent([paths.root, paths.runtimeHome, paths.runtimeDataRoot], componentRoot, ...(options.purge ? [componentDataHome] : []));
405
+ }
406
+ const isRemoval = action.phase === "remove";
407
+ const managedProgramPaths = [componentRoot];
408
+ const componentDataHome = getComponentRuntimeDataHome(paths, component.name, component.runtimeDataDir);
409
+ const componentLogsDir = getComponentLogsDirectory(paths, component.name, component.runtimeDataDir);
410
+ const componentPm2Home = component.pm2
411
+ ? getComponentPm2Home(paths, component.name, component.runtimeDataDir, component.pm2.pm2Home)
412
+ : null;
413
+ const managedDataPaths = options.purge || !isRemoval
414
+ ? [
415
+ componentDataHome,
416
+ componentConfigDir,
417
+ componentLogsDir,
418
+ ...(componentPm2Home ? [componentPm2Home] : [])
419
+ ]
420
+ : [];
421
+ const details = component.type === "released-service"
422
+ ? buildReleasedServiceDetails(component, componentRoot, componentDataHome, isRemoval)
423
+ : undefined;
424
+ return {
425
+ name: component.name,
426
+ type: component.type,
427
+ status: isRemoval ? "removed" : "installed",
428
+ version: isRemoval ? null : component.version ?? component.channelVersion ?? null,
429
+ managedProgramPaths,
430
+ managedDataPaths,
431
+ managedPaths: [...managedProgramPaths, ...managedDataPaths],
432
+ lastAction: action.phase,
433
+ lastUpdatedAt: new Date().toISOString(),
434
+ logFile: logFilePath,
435
+ details
436
+ };
437
+ }
438
+ function selectRequestedComponents(manifest, requestedComponents, phase) {
439
+ const requestedSet = !requestedComponents || requestedComponents.length === 0
440
+ ? new Set(manifest.components
441
+ .filter((component) => phase === "remove" || component.required)
442
+ .map((component) => component.name))
443
+ : new Set();
444
+ if (requestedComponents && requestedComponents.length > 0) {
445
+ for (const value of requestedComponents) {
446
+ if (!manifest.componentMap.has(value)) {
447
+ throw new RuntimeLifecycleError(`Unknown runtime component: ${value}`);
448
+ }
449
+ requestedSet.add(value);
450
+ }
451
+ }
452
+ if (phase === "remove") {
453
+ return requestedSet;
454
+ }
455
+ const queue = [...requestedSet];
456
+ while (queue.length > 0) {
457
+ const componentName = queue.shift();
458
+ if (!componentName) {
459
+ continue;
460
+ }
461
+ const component = manifest.componentMap.get(componentName);
462
+ if (!component) {
463
+ throw new RuntimeLifecycleError(`Unknown runtime component: ${componentName}`);
464
+ }
465
+ for (const dependencyName of component.lifecycleDependencies) {
466
+ if (!manifest.componentMap.has(dependencyName)) {
467
+ throw new RuntimeLifecycleError(`Runtime component ${component.name} depends on unknown component ${dependencyName}`);
468
+ }
469
+ if (!requestedSet.has(dependencyName)) {
470
+ requestedSet.add(dependencyName);
471
+ queue.push(dependencyName);
472
+ }
473
+ }
474
+ }
475
+ return requestedSet;
476
+ }
477
+ function orderedPhaseComponents(manifest, phase) {
478
+ const definition = manifest.phases[phase];
479
+ const ordered = [...definition.order];
480
+ return definition.reverse ? ordered.reverse() : ordered;
481
+ }
482
+ function resolveRuntimeAction(component, phase) {
483
+ if (component.name === "node") {
484
+ return {
485
+ componentName: component.name,
486
+ phase,
487
+ strategy: "builtin"
488
+ };
489
+ }
490
+ if (phase === "install") {
491
+ return {
492
+ componentName: component.name,
493
+ phase,
494
+ strategy: "script",
495
+ scriptPath: component.scripts.install
496
+ };
497
+ }
498
+ if (phase === "update") {
499
+ if (component.scripts.update) {
500
+ return {
501
+ componentName: component.name,
502
+ phase,
503
+ strategy: "script",
504
+ scriptPath: component.scripts.update
505
+ };
506
+ }
507
+ return {
508
+ componentName: component.name,
509
+ phase,
510
+ strategy: "fallback-install",
511
+ scriptPath: component.scripts.install,
512
+ reason: "update hook missing; reusing install hook"
513
+ };
514
+ }
515
+ if (component.scripts.remove) {
516
+ return {
517
+ componentName: component.name,
518
+ phase,
519
+ strategy: "script",
520
+ scriptPath: component.scripts.remove
521
+ };
522
+ }
523
+ return {
524
+ componentName: component.name,
525
+ phase,
526
+ strategy: "fallback-cleanup",
527
+ reason: "remove hook missing; cleaning managed paths only"
528
+ };
529
+ }
530
+ function componentNeedsUpdate(component, state) {
531
+ if (state.status !== "installed") {
532
+ return true;
533
+ }
534
+ const desiredVersion = component.version ?? component.channelVersion ?? null;
535
+ return desiredVersion !== state.version;
536
+ }
537
+ function resolveScriptForAction(action, component) {
538
+ switch (action.strategy) {
539
+ case "script":
540
+ case "fallback-install":
541
+ return action.scriptPath ?? component.scripts.install;
542
+ case "builtin":
543
+ case "fallback-cleanup":
544
+ return null;
545
+ }
546
+ }
547
+ async function ensureManagedDirectories(paths) {
548
+ await Promise.all([
549
+ mkdir(paths.root, { recursive: true }),
550
+ mkdir(paths.runtimeHome, { recursive: true }),
551
+ mkdir(paths.runtimeDataRoot, { recursive: true }),
552
+ mkdir(paths.bin, { recursive: true }),
553
+ mkdir(paths.config, { recursive: true }),
554
+ mkdir(paths.logs, { recursive: true }),
555
+ mkdir(paths.data, { recursive: true }),
556
+ mkdir(paths.componentsRoot, { recursive: true }),
557
+ mkdir(paths.componentDataRoot, { recursive: true }),
558
+ mkdir(paths.vendoredRoot, { recursive: true })
559
+ ]);
560
+ }
561
+ function shouldEnsureManagedPm2(plan, manifest) {
562
+ return plan.some((action) => manifest.componentMap.get(action.componentName)?.pm2);
563
+ }
564
+ export async function ensureManagedPm2Package(manifest, paths, options = {}) {
565
+ const verification = await verifyNodeRuntime(paths.nodeRuntime);
566
+ if (!verification.valid || !verification.npmPath) {
567
+ throw new RuntimeLifecycleError("Managed Node runtime is missing. Install the runtime node component before ensuring pm2.");
568
+ }
569
+ const requirement = resolveManagedPm2Requirement(manifest, options.pm2VersionOverride);
570
+ await ensureManagedNpmPrefix(paths.npmPrefix);
571
+ const npmOptions = {
572
+ nodePath: verification.nodePath,
573
+ prefix: paths.npmPrefix,
574
+ registryMirror: options.npmRegistryMirror,
575
+ env: createManagedNpmInstallEnvironment(paths.nodeRuntime)
576
+ };
577
+ const inventoryResult = await listGlobalPackages(verification.npmPath, npmOptions);
578
+ const installedVersion = parseInstalledGlobalPackageVersion(inventoryResult.stdout, "pm2");
579
+ if (installedVersion && isManagedPm2RequirementSatisfied(installedVersion, requirement.range)) {
580
+ return {
581
+ changed: false,
582
+ installedVersion,
583
+ selector: requirement.selector,
584
+ prefix: paths.npmPrefix
585
+ };
586
+ }
587
+ await installGlobalPackage(verification.npmPath, requirement.selector, npmOptions);
588
+ return {
589
+ changed: true,
590
+ installedVersion,
591
+ selector: requirement.selector,
592
+ prefix: paths.npmPrefix
593
+ };
594
+ }
595
+ function resolveManagedPm2Requirement(manifest, override) {
596
+ const normalizedOverride = override?.trim();
597
+ if (normalizedOverride) {
598
+ return {
599
+ range: normalizedOverride.startsWith("pm2@")
600
+ ? normalizedOverride.slice("pm2@".length)
601
+ : normalizedOverride,
602
+ selector: normalizedOverride.startsWith("pm2@")
603
+ ? normalizedOverride
604
+ : `pm2@${normalizedOverride}`
605
+ };
606
+ }
607
+ if (manifest.npmSync) {
608
+ try {
609
+ const npmManifest = validateNpmSyncManifest(manifest.npmSync);
610
+ const entry = npmManifest.packages.pm2;
611
+ if (entry) {
612
+ const target = entry.target?.trim();
613
+ if (target) {
614
+ return {
615
+ range: target.startsWith("pm2@") ? target.slice("pm2@".length) : target,
616
+ selector: target.startsWith("pm2@") ? target : `pm2@${target}`
617
+ };
618
+ }
619
+ return {
620
+ range: entry.version,
621
+ selector: `pm2@${entry.version}`
622
+ };
623
+ }
624
+ }
625
+ catch (error) {
626
+ throw new RuntimeLifecycleError("Runtime manifest npmSync configuration is invalid for managed pm2 resolution.", error instanceof Error ? { cause: error } : undefined);
627
+ }
628
+ }
629
+ return {
630
+ range: DEFAULT_MANAGED_PM2_VERSION,
631
+ selector: `pm2@${DEFAULT_MANAGED_PM2_VERSION}`
632
+ };
633
+ }
634
+ async function ensureManagedNpmPrefix(prefix) {
635
+ const requiredDirectories = process.platform === "win32"
636
+ ? [join(prefix, "node_modules")]
637
+ : [join(prefix, "lib", "node_modules"), join(prefix, "bin")];
638
+ await Promise.all(requiredDirectories.map((directory) => mkdir(directory, { recursive: true })));
639
+ }
640
+ function createManagedNpmInstallEnvironment(runtimePath, baseEnv = process.env) {
641
+ const runtimeBinDirectory = dirname(getRuntimeExecutablePaths(runtimePath).nodePath);
642
+ const pathKey = process.platform === "win32" ? "Path" : "PATH";
643
+ const existingPath = process.platform === "win32" ? (baseEnv.Path ?? baseEnv.PATH ?? "") : (baseEnv.PATH ?? "");
644
+ return {
645
+ ...baseEnv,
646
+ [pathKey]: [runtimeBinDirectory, existingPath].filter(Boolean).join(process.platform === "win32" ? ";" : ":")
647
+ };
648
+ }
649
+ function parseInstalledGlobalPackageVersion(inventoryOutput, packageName) {
650
+ try {
651
+ const parsed = JSON.parse(inventoryOutput);
652
+ return parsed.dependencies?.[packageName]?.version?.trim() || null;
653
+ }
654
+ catch {
655
+ return null;
656
+ }
657
+ }
658
+ function isManagedPm2RequirementSatisfied(installedVersion, range) {
659
+ if (range === "*") {
660
+ return true;
661
+ }
662
+ return semver.validRange(range, { includePrerelease: true })
663
+ ? semver.satisfies(installedVersion, range, { includePrerelease: true })
664
+ : installedVersion === range;
665
+ }
666
+ async function cleanupManagedComponent(allowedRoots, ...paths) {
667
+ for (const pathValue of paths) {
668
+ if (!allowedRoots.some((rootPath) => isPathInsideRuntimeRoot(rootPath, pathValue))) {
669
+ throw new RuntimeLifecycleError(`Refusing to clean path outside managed runtime root: ${pathValue}`);
670
+ }
671
+ await rm(pathValue, { recursive: true, force: true });
672
+ }
673
+ }
674
+ async function materializeNodeWrappers(binDirectory, executables) {
675
+ await mkdir(binDirectory, { recursive: true });
676
+ return Promise.all([
677
+ writeExecutableWrapper(binDirectory, "node", executables.nodePath),
678
+ writeExecutableWrapper(binDirectory, "npm", executables.npmPath)
679
+ ]);
680
+ }
681
+ async function writeExecutableWrapper(binDirectory, commandName, targetPath) {
682
+ const wrapperPath = process.platform === "win32"
683
+ ? join(binDirectory, `${commandName}.cmd`)
684
+ : join(binDirectory, commandName);
685
+ const relativeTarget = relative(binDirectory, targetPath);
686
+ await writeFile(wrapperPath, process.platform === "win32"
687
+ ? `@echo off\r\n"%~dp0\\${relativeTarget.replaceAll("/", "\\")}" %*\r\n`
688
+ : `#!/usr/bin/env sh\nexec "$(dirname "$0")/${relativeTarget.replaceAll("\\", "/")}" "$@"\n`, "utf8");
689
+ if (process.platform !== "win32") {
690
+ await chmod(wrapperPath, 0o755);
691
+ }
692
+ return wrapperPath;
693
+ }
694
+ async function removeWrapper(binDirectory, commandName) {
695
+ const wrapperPath = process.platform === "win32"
696
+ ? join(binDirectory, `${commandName}.cmd`)
697
+ : join(binDirectory, commandName);
698
+ await rm(wrapperPath, { force: true });
699
+ }
700
+ function normalizeVersion(value) {
701
+ if (!value) {
702
+ return null;
703
+ }
704
+ return value.replace(/^v/u, "");
705
+ }
706
+ async function cleanupManagedPm2Service(component, manifest, paths, options, logFilePath) {
707
+ const service = asManagedPm2ServiceName(component.name);
708
+ if (!service || !component.pm2) {
709
+ return;
710
+ }
711
+ try {
712
+ const stopResult = await runManagedPm2Command({
713
+ manifestPath: manifest.manifestPath,
714
+ runtimeRoot: paths.root,
715
+ service,
716
+ action: "stop"
717
+ });
718
+ await writeRuntimeLog(logFilePath, `${component.name} PM2 stop result: ${stopResult.status} (${stopResult.appName})`);
719
+ const deleteResult = await runManagedPm2Command({
720
+ manifestPath: manifest.manifestPath,
721
+ runtimeRoot: paths.root,
722
+ service,
723
+ action: "delete"
724
+ });
725
+ await writeRuntimeLog(logFilePath, `${component.name} PM2 delete result: ${deleteResult.status} (${deleteResult.appName})`);
726
+ }
727
+ catch (error) {
728
+ if (error instanceof ManagedPm2Error && options.verbose) {
729
+ await writeRuntimeLog(logFilePath, `${component.name} PM2 cleanup warning: ${error.message}`);
730
+ return;
731
+ }
732
+ throw error;
733
+ }
734
+ }
735
+ function asManagedPm2ServiceName(value) {
736
+ return supportedPm2Services.includes(value)
737
+ ? value
738
+ : null;
739
+ }
740
+ function buildReleasedServiceDetails(component, componentRoot, componentDataHome, isRemoval) {
741
+ if (!component.releasedService) {
742
+ return undefined;
743
+ }
744
+ const details = {
745
+ releasedPayloadPath: resolveReleasedServicePath(component.releasedService.dllPath, componentRoot),
746
+ releasedWorkingDirectory: resolveReleasedServicePath(component.releasedService.workingDirectory, componentRoot),
747
+ launchAssetsDirectory: join(componentDataHome, component.releasedService.runtimeFilesDir ?? "pm2-runtime")
748
+ };
749
+ details.releasedServiceReady =
750
+ !isRemoval &&
751
+ existsSync(String(details.releasedPayloadPath)) &&
752
+ existsSync(String(details.releasedWorkingDirectory));
753
+ if (isRemoval) {
754
+ details.cleanupSummary =
755
+ "Released-service PM2 app cleanup completed before launch assets were removed.";
756
+ }
757
+ else {
758
+ details.readinessSummary = details.releasedServiceReady
759
+ ? "Released-service payload validated and launch assets prepared for `hagiscript pm2 server start`."
760
+ : "Released-service launch assets are prepared, but the published backend payload is not staged yet.";
761
+ }
762
+ return details;
763
+ }
764
+ async function resolveNodeRuntimeForPhase(phase, targetDirectory, desiredVersion, currentVerification, downloadCache, downloadCacheDir) {
765
+ if (phase === "update" &&
766
+ currentVerification.valid &&
767
+ normalizeVersion(currentVerification.nodeVersion) === normalizeVersion(desiredVersion)) {
768
+ return {
769
+ targetDirectory: currentVerification.targetDirectory,
770
+ nodePath: currentVerification.nodePath ?? "",
771
+ npmPath: currentVerification.npmPath ?? "",
772
+ nodeVersion: currentVerification.nodeVersion ?? "",
773
+ npmVersion: currentVerification.npmVersion ?? ""
774
+ };
775
+ }
776
+ if (phase === "update") {
777
+ const installed = await installNodeRuntime({
778
+ targetDirectory,
779
+ versionSelector: desiredVersion,
780
+ downloadCacheEnabled: downloadCache,
781
+ downloadCacheDirectory: downloadCacheDir
782
+ });
783
+ return {
784
+ targetDirectory: installed.targetDirectory,
785
+ nodePath: installed.nodePath,
786
+ npmPath: installed.npmPath,
787
+ nodeVersion: installed.version,
788
+ npmVersion: installed.npmVersion
789
+ };
790
+ }
791
+ return resolveManagedNodeRuntime({
792
+ targetDirectory,
793
+ versionSelector: desiredVersion,
794
+ downloadCacheEnabled: downloadCache,
795
+ downloadCacheDirectory: downloadCacheDir
796
+ });
797
+ }
798
+ //# sourceMappingURL=runtime-manager.js.map