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,715 @@
1
+ import { access, mkdir, writeFile } from "node:fs/promises";
2
+ import { basename, extname, join } from "node:path";
3
+ import process from "node:process";
4
+ import { CommandExecutionError, runCommand } from "./command-launch.js";
5
+ import { buildManagedRuntimeEnvironment, getManagedRuntimePathEntries } from "./runtime-executor.js";
6
+ import { getRuntimeExecutablePaths } from "./node-verify.js";
7
+ import { loadRuntimeManifest } from "./runtime-manifest.js";
8
+ import { getComponentConfigDirectory, getComponentManagedRoot, getComponentPm2Home, getComponentRuntimeDataHome, getServerSharedDataRoot, resolveManagedPath, resolveReleasedServicePath, resolveRuntimePaths } from "./runtime-paths.js";
9
+ import { getManagedServerVersionStatePath, readManagedServerVersionState } from "./server-version-state.js";
10
+ export const supportedPm2Services = ["server", "omniroute", "code-server"];
11
+ export class ManagedPm2Error extends Error {
12
+ constructor(message, options) {
13
+ super(message, options);
14
+ this.name = "ManagedPm2Error";
15
+ }
16
+ }
17
+ const DEFAULT_PM2_STATUS_RETRY_DELAY_MS = 500;
18
+ const DEFAULT_PM2_STATUS_MAX_RETRIES = 3;
19
+ const PM2_NAME_IDENTIFIER_PATTERN = /^[a-z0-9_]+$/u;
20
+ const PM2_NAME_IDENTIFIER_BOOTSTRAP_DEFAULT = "hagicode";
21
+ const DEFAULT_PM2_NAME_IDENTIFIER_ENV = "hagicode_instance";
22
+ export async function runManagedPm2Command(options) {
23
+ const manifest = await loadRuntimeManifest({ manifestPath: options.manifestPath });
24
+ const paths = resolveRuntimePaths(manifest, { runtimeRoot: options.runtimeRoot });
25
+ const definition = await resolveManagedPm2ServiceDefinition(manifest, paths, options.service, options.nameIdentifierValue, options.environmentOverrides, options.componentRootOverride);
26
+ const runner = options.runner ?? runCommand;
27
+ switch (options.action) {
28
+ case "start":
29
+ return recreateManagedPm2App(definition, "start", runner);
30
+ case "restart":
31
+ return recreateManagedPm2App(definition, "restart", runner);
32
+ case "stop":
33
+ await cleanupManagedPm2App(definition, runner);
34
+ return readManagedPm2Status(definition, "stop", runner);
35
+ case "delete":
36
+ await executePm2(definition, buildPm2ActionArgs(definition, "delete"), runner, {
37
+ allowMissingProcess: true
38
+ });
39
+ return readManagedPm2Status(definition, "delete", runner);
40
+ case "status":
41
+ return readManagedPm2Status(definition, "status", runner);
42
+ }
43
+ }
44
+ async function recreateManagedPm2App(definition, action, runner) {
45
+ await cleanupManagedPm2App(definition, runner);
46
+ if (definition.launchStrategy === "released-service") {
47
+ await prepareReleasedServicePm2Files(definition);
48
+ }
49
+ await executePm2(definition, buildPm2ActionArgs(definition, "start"), runner);
50
+ return readManagedPm2Status(definition, action, runner);
51
+ }
52
+ async function cleanupManagedPm2App(definition, runner) {
53
+ await executePm2(definition, buildPm2ActionArgs(definition, "stop"), runner, {
54
+ allowMissingProcess: true
55
+ });
56
+ await executePm2(definition, buildPm2ActionArgs(definition, "delete"), runner, {
57
+ allowMissingProcess: true
58
+ });
59
+ }
60
+ export async function resolveManagedPm2Environment(options) {
61
+ const manifest = await loadRuntimeManifest({ manifestPath: options.manifestPath });
62
+ const paths = resolveRuntimePaths(manifest, { runtimeRoot: options.runtimeRoot });
63
+ const definition = await resolveManagedPm2ServiceDefinition(manifest, paths, options.service, options.nameIdentifierValue, options.environmentOverrides, options.componentRootOverride);
64
+ const env = buildManagedPm2Environment(definition);
65
+ return {
66
+ service: definition.service,
67
+ baseAppName: definition.baseAppName,
68
+ appName: definition.appName,
69
+ nameIdentifierEnv: definition.nameIdentifierEnv,
70
+ nameIdentifier: definition.nameIdentifier,
71
+ bootstrapNameIdentifierValue: definition.defaultNameIdentifier,
72
+ cwd: definition.cwd,
73
+ script: definition.script,
74
+ args: [...definition.args],
75
+ env,
76
+ pathKey: process.platform === "win32" ? "Path" : "PATH",
77
+ pathEntries: getManagedRuntimePathEntries(definition.paths, {
78
+ includeRuntimeBin: definition.component.type !== "released-service"
79
+ }),
80
+ runtimeHome: definition.runtimeHome,
81
+ runtimeDataHome: definition.runtimeDataHome,
82
+ componentRoot: definition.componentRoot,
83
+ componentConfigDir: definition.componentConfigDir,
84
+ pm2Home: definition.pm2Home,
85
+ pm2Binary: definition.pm2Binary,
86
+ nodePath: definition.nodePath,
87
+ launchStrategy: definition.launchStrategy,
88
+ dotnetPath: definition.dotnetPath,
89
+ runtimeFilesDir: definition.runtimeFilesDir,
90
+ ecosystemPath: definition.ecosystemPath,
91
+ envFilePath: definition.envFilePath
92
+ };
93
+ }
94
+ export async function resolveManagedPm2ServiceDefinition(manifest, paths, service, nameIdentifierValue, environmentOverrides, componentRootOverride) {
95
+ assertSupportedPm2Service(service);
96
+ const component = manifest.componentMap.get(service);
97
+ if (!component) {
98
+ throw new ManagedPm2Error(`Runtime manifest does not define the ${service} service.`);
99
+ }
100
+ const defaultComponentRoot = componentRootOverride
101
+ ? resolveManagedPath(componentRootOverride, paths.root)
102
+ : getComponentManagedRoot(paths, component.name);
103
+ const defaultRuntimeDataHome = getComponentRuntimeDataHome(paths, component.name, component.runtimeDataDir);
104
+ const defaultComponentConfigDir = getComponentConfigDirectory(paths, component.name, component.runtimeDataDir);
105
+ const nodePath = getRuntimeExecutablePaths(paths.nodeRuntime).nodePath;
106
+ const pm2Entrypoint = getManagedPm2Entrypoint(paths.npmPrefix);
107
+ const { baseAppName, nameIdentifierEnv, defaultNameIdentifier, nameIdentifier } = resolvePm2NameIdentifier(manifest, component, service, nameIdentifierValue);
108
+ const defaultPm2Home = getComponentPm2Home(paths, component.name, component.runtimeDataDir, component.pm2?.pm2Home, baseAppName);
109
+ await Promise.all([
110
+ validateManagedPath(pm2Entrypoint, "Managed PM2 binary is missing from the configured runtime npm prefix. Install pm2 into that prefix before using `hagiscript pm2 ...`."),
111
+ validateManagedPath(nodePath, "Managed Node runtime is missing. Install the runtime node component first.")
112
+ ]);
113
+ if (component.type === "released-service") {
114
+ const releasedService = component.releasedService;
115
+ if (!releasedService) {
116
+ throw new ManagedPm2Error(`Runtime manifest component ${component.name} is missing releasedService metadata.`);
117
+ }
118
+ const resolvedServerPaths = service === "server"
119
+ ? await resolveManagedServerActivePaths(paths, baseAppName, component.pm2?.pm2Home)
120
+ : {
121
+ componentRoot: defaultComponentRoot,
122
+ runtimeDataHome: defaultRuntimeDataHome,
123
+ componentConfigDir: defaultComponentConfigDir,
124
+ pm2Home: defaultPm2Home
125
+ };
126
+ const cwd = resolveReleasedServicePath(releasedService.workingDirectory, resolvedServerPaths.componentRoot);
127
+ const script = resolveReleasedServicePath(releasedService.dllPath, resolvedServerPaths.componentRoot);
128
+ const runtimeFilesDir = join(resolvedServerPaths.runtimeDataHome, releasedService.runtimeFilesDir ?? "pm2-runtime");
129
+ const ecosystemPath = join(runtimeFilesDir, "ecosystem.config.cjs");
130
+ const envFilePath = join(runtimeFilesDir, ".env");
131
+ const dotnetPath = join(paths.dotnetRuntime, "current", process.platform === "win32" ? "dotnet.exe" : "dotnet");
132
+ await Promise.all([
133
+ validateManagedPath(script, `Managed released-service payload for ${service} is missing.`),
134
+ validateManagedPath(cwd, `Managed working directory for ${service} is missing.`),
135
+ validateManagedPath(dotnetPath, "Managed .NET runtime is missing. Install the runtime dotnet component first.")
136
+ ]);
137
+ return {
138
+ service,
139
+ component,
140
+ manifestDir: manifest.manifestDir,
141
+ paths,
142
+ baseAppName,
143
+ appName: `${baseAppName}-${nameIdentifier}`,
144
+ nameIdentifierEnv,
145
+ defaultNameIdentifier,
146
+ nameIdentifier,
147
+ cwd,
148
+ script,
149
+ args: component.pm2?.args ?? [],
150
+ env: mergeManagedEnvironment(component.pm2?.env, environmentOverrides),
151
+ runtimeHome: paths.runtimeHome,
152
+ runtimeDataHome: resolvedServerPaths.runtimeDataHome,
153
+ componentRoot: resolvedServerPaths.componentRoot,
154
+ componentConfigDir: resolvedServerPaths.componentConfigDir,
155
+ pm2Home: resolvedServerPaths.pm2Home,
156
+ pm2Binary: pm2Entrypoint,
157
+ nodePath,
158
+ launchStrategy: "released-service",
159
+ dotnetPath,
160
+ runtimeFilesDir,
161
+ ecosystemPath,
162
+ envFilePath
163
+ };
164
+ }
165
+ const script = resolveManagedPath(component.pm2?.script ?? defaultPm2Script(component.name), defaultComponentRoot);
166
+ const cwd = resolveManagedPath(component.pm2?.cwd ?? ".", defaultComponentRoot);
167
+ await Promise.all([
168
+ validateManagedPath(script, `Managed runtime entrypoint for ${service} is missing.`),
169
+ validateManagedPath(cwd, `Managed working directory for ${service} is missing.`)
170
+ ]);
171
+ return {
172
+ service,
173
+ component,
174
+ manifestDir: manifest.manifestDir,
175
+ paths,
176
+ baseAppName,
177
+ appName: `${baseAppName}-${nameIdentifier}`,
178
+ nameIdentifierEnv,
179
+ defaultNameIdentifier,
180
+ nameIdentifier,
181
+ cwd,
182
+ script,
183
+ args: component.pm2?.args ?? defaultBundledRuntimePm2Args(component.name, defaultComponentConfigDir),
184
+ env: mergeManagedEnvironment(component.pm2?.env, environmentOverrides),
185
+ runtimeHome: paths.runtimeHome,
186
+ runtimeDataHome: defaultRuntimeDataHome,
187
+ componentRoot: defaultComponentRoot,
188
+ componentConfigDir: defaultComponentConfigDir,
189
+ pm2Home: defaultPm2Home,
190
+ pm2Binary: pm2Entrypoint,
191
+ nodePath,
192
+ launchStrategy: resolveBundledRuntimeLaunchStrategy(script)
193
+ };
194
+ }
195
+ async function resolveManagedServerActivePaths(paths, serviceHomeName, pm2HomeOverride) {
196
+ const runtimeDataHome = getServerSharedDataRoot(paths);
197
+ const state = await readManagedServerVersionState(getManagedServerVersionStatePath(paths));
198
+ const activeVersion = state.activeVersion;
199
+ if (!activeVersion) {
200
+ throw new ManagedPm2Error("Managed server does not have an active version. Run `hagiscript server install` or `hagiscript server use <version>` first.");
201
+ }
202
+ const installedVersion = state.versions[activeVersion];
203
+ if (!installedVersion) {
204
+ throw new ManagedPm2Error(`Managed server active version ${activeVersion} is missing from the installed version inventory.`);
205
+ }
206
+ return {
207
+ componentRoot: installedVersion.installPath,
208
+ runtimeDataHome,
209
+ componentConfigDir: join(runtimeDataHome, "config"),
210
+ pm2Home: getComponentPm2Home(paths, "server", undefined, pm2HomeOverride, serviceHomeName)
211
+ };
212
+ }
213
+ export function renderManagedPm2StatusText(result) {
214
+ return [
215
+ `Service: ${result.service}`,
216
+ `Base app: ${result.baseAppName}`,
217
+ `Action: ${result.action}`,
218
+ `App: ${result.appName}`,
219
+ `Name identifier env: ${result.nameIdentifierEnv}`,
220
+ `Name identifier: ${result.nameIdentifier}`,
221
+ `Status: ${result.status}`,
222
+ `Launch strategy: ${result.launchStrategy}`,
223
+ `Runtime home: ${result.runtimeHome}`,
224
+ `Runtime data home: ${result.runtimeDataHome}`,
225
+ `PM2 home: ${result.pm2Home}`,
226
+ `Script: ${result.script}`,
227
+ `Working directory: ${result.cwd}`,
228
+ `PM2 binary: ${result.pm2Binary}`,
229
+ ...(result.dotnetPath ? [`Dotnet: ${result.dotnetPath}`] : []),
230
+ ...(result.runtimeFilesDir ? [`Runtime files: ${result.runtimeFilesDir}`] : []),
231
+ `PID: ${result.pid ?? "n/a"}`
232
+ ].join("\n");
233
+ }
234
+ export function renderManagedPm2EnvironmentText(result) {
235
+ const argsText = result.args.length > 0 ? result.args.map((entry) => JSON.stringify(entry)).join(" ") : "(none)";
236
+ const envLines = Object.entries(result.env)
237
+ .filter(([key, value]) => key.trim().length > 0 && value !== undefined)
238
+ .sort(([left], [right]) => left.localeCompare(right))
239
+ .map(([key, value]) => ` ${key}=${String(value).replace(/\r?\n/g, "\\n")}`);
240
+ return [
241
+ `Service: ${result.service}`,
242
+ `Base app: ${result.baseAppName}`,
243
+ `App: ${result.appName}`,
244
+ `Name identifier env: ${result.nameIdentifierEnv}`,
245
+ `Name identifier: ${result.nameIdentifier}`,
246
+ `Default instance: ${result.nameIdentifierEnv}=${result.bootstrapNameIdentifierValue}`,
247
+ "Use --instance to override the manifest-defined runtime instance when needed.",
248
+ `Launch strategy: ${result.launchStrategy}`,
249
+ `Working directory: ${result.cwd}`,
250
+ `Script: ${result.script}`,
251
+ `Arguments: ${argsText}`,
252
+ `Runtime home: ${result.runtimeHome}`,
253
+ `Runtime data home: ${result.runtimeDataHome}`,
254
+ `Component root: ${result.componentRoot}`,
255
+ `Component config dir: ${result.componentConfigDir}`,
256
+ `PM2 home: ${result.pm2Home}`,
257
+ `PM2 binary: ${result.pm2Binary}`,
258
+ `Node: ${result.nodePath}`,
259
+ ...(result.dotnetPath ? [`Dotnet: ${result.dotnetPath}`] : []),
260
+ ...(result.runtimeFilesDir ? [`Runtime files: ${result.runtimeFilesDir}`] : []),
261
+ ...(result.ecosystemPath ? [`PM2 ecosystem: ${result.ecosystemPath}`] : []),
262
+ ...(result.envFilePath ? [`PM2 env file: ${result.envFilePath}`] : []),
263
+ `Path key: ${result.pathKey}`,
264
+ "Managed PATH entries:",
265
+ ...result.pathEntries.map((entry) => ` - ${entry}`),
266
+ "Environment:",
267
+ ...envLines
268
+ ].join("\n");
269
+ }
270
+ function assertSupportedPm2Service(service) {
271
+ if (supportedPm2Services.includes(service)) {
272
+ return;
273
+ }
274
+ throw new ManagedPm2Error(`Unsupported managed PM2 service "${service}". Supported services: ${supportedPm2Services.join(", ")}.`);
275
+ }
276
+ async function readManagedPm2Status(definition, action, runner) {
277
+ let result;
278
+ let parsed;
279
+ for (let attempt = 0; attempt <= DEFAULT_PM2_STATUS_MAX_RETRIES; attempt += 1) {
280
+ result = await executePm2(definition, ["jlist"], runner);
281
+ parsed = parseManagedPm2Status(result, definition.appName);
282
+ if (parsed.kind === "status") {
283
+ return {
284
+ service: definition.service,
285
+ action,
286
+ baseAppName: definition.baseAppName,
287
+ appName: definition.appName,
288
+ nameIdentifierEnv: definition.nameIdentifierEnv,
289
+ nameIdentifier: definition.nameIdentifier,
290
+ cwd: definition.cwd,
291
+ script: definition.script,
292
+ runtimeHome: definition.runtimeHome,
293
+ runtimeDataHome: definition.runtimeDataHome,
294
+ pm2Home: definition.pm2Home,
295
+ pm2Binary: definition.pm2Binary,
296
+ exists: parsed.statusEntry !== null,
297
+ status: parsed.statusEntry?.status ?? "missing",
298
+ pid: parsed.statusEntry?.pid ?? null,
299
+ stdout: result.stdout,
300
+ stderr: result.stderr,
301
+ launchStrategy: definition.launchStrategy,
302
+ dotnetPath: definition.dotnetPath,
303
+ runtimeFilesDir: definition.runtimeFilesDir
304
+ };
305
+ }
306
+ if (parsed.kind === "failure") {
307
+ throw new ManagedPm2Error(parsed.message);
308
+ }
309
+ const retriesRemaining = DEFAULT_PM2_STATUS_MAX_RETRIES - attempt;
310
+ if (retriesRemaining <= 0) {
311
+ throw new ManagedPm2Error(`Managed PM2 status output could not be normalized after ${attempt + 1} attempt${attempt === 0 ? "" : "s"} during PM2 bootstrap. Last PM2 output: ${parsed.summary}`);
312
+ }
313
+ await sleep(DEFAULT_PM2_STATUS_RETRY_DELAY_MS);
314
+ }
315
+ throw new ManagedPm2Error(`Managed PM2 status output could not be normalized for ${definition.appName}.`);
316
+ }
317
+ async function executePm2(definition, args, runner, options = {}) {
318
+ const env = buildManagedPm2Environment(definition);
319
+ try {
320
+ return await runner(definition.nodePath, [definition.pm2Binary, ...args], {
321
+ cwd: definition.cwd,
322
+ env,
323
+ maxBuffer: 10 * 1024 * 1024
324
+ });
325
+ }
326
+ catch (error) {
327
+ if (options.allowMissingProcess &&
328
+ error instanceof CommandExecutionError &&
329
+ /not found|doesn't exist|process or namespace/i.test(`${error.context.stderr}\n${error.context.stdout}`)) {
330
+ return {
331
+ command: error.context.command,
332
+ args: error.context.args,
333
+ stdout: error.context.stdout,
334
+ stderr: error.context.stderr,
335
+ cwd: error.context.cwd,
336
+ exitCode: error.context.exitCode,
337
+ signal: error.context.signal,
338
+ timedOut: error.context.timedOut
339
+ };
340
+ }
341
+ throw new ManagedPm2Error(error instanceof Error ? error.message : String(error), error instanceof Error ? { cause: error } : undefined);
342
+ }
343
+ }
344
+ async function prepareReleasedServicePm2Files(definition) {
345
+ if (definition.launchStrategy !== "released-service" ||
346
+ !definition.runtimeFilesDir ||
347
+ !definition.ecosystemPath ||
348
+ !definition.envFilePath ||
349
+ !definition.dotnetPath) {
350
+ return;
351
+ }
352
+ const env = buildManagedPm2Environment(definition);
353
+ await mkdir(definition.runtimeFilesDir, { recursive: true });
354
+ await writeFile(definition.envFilePath, buildPm2EnvFile(definition, env), "utf8");
355
+ await writeFile(definition.ecosystemPath, buildReleasedServiceEcosystemConfig(definition, env), "utf8");
356
+ }
357
+ function buildReleasedServiceEcosystemConfig(definition, env) {
358
+ if (!definition.dotnetPath || !definition.envFilePath) {
359
+ throw new ManagedPm2Error(`Released-service PM2 launch files are unavailable for ${definition.service}.`);
360
+ }
361
+ const args = [definition.script, ...definition.args];
362
+ return [
363
+ "module.exports = {",
364
+ " apps: [",
365
+ " {",
366
+ ` name: ${JSON.stringify(definition.appName)},`,
367
+ ` script: ${JSON.stringify(definition.dotnetPath)},`,
368
+ ` args: ${JSON.stringify(args)},`,
369
+ ` cwd: ${JSON.stringify(definition.cwd)},`,
370
+ ' interpreter: "none",',
371
+ ' exec_mode: "fork",',
372
+ " autorestart: true,",
373
+ " watch: false,",
374
+ ` env: ${serializePm2Environment(env)},`,
375
+ ` env_file: ${JSON.stringify(definition.envFilePath)}`,
376
+ " }",
377
+ " ]",
378
+ "};",
379
+ ""
380
+ ].join("\n");
381
+ }
382
+ function buildPm2EnvFile(definition, env) {
383
+ return ([
384
+ `# Default instance: ${definition.nameIdentifierEnv}=${definition.defaultNameIdentifier}`,
385
+ `# Use --instance to override the manifest-defined runtime instance when needed.`,
386
+ ...Object.entries(env)
387
+ .filter(([key, value]) => key.trim().length > 0 && value !== undefined)
388
+ .sort(([left], [right]) => left.localeCompare(right))
389
+ .map(([key, value]) => `${key}=${String(value).replace(/\r?\n/g, "\\n")}`)
390
+ ].join("\n") + "\n");
391
+ }
392
+ function serializePm2Environment(env) {
393
+ const normalizedEntries = Object.entries(env)
394
+ .filter(([key, value]) => key.trim().length > 0 && value !== undefined)
395
+ .sort(([left], [right]) => left.localeCompare(right));
396
+ return JSON.stringify(Object.fromEntries(normalizedEntries), null, 8);
397
+ }
398
+ function buildManagedPm2Environment(definition, baseEnv = process.env) {
399
+ const resolvedBaseEnv = {
400
+ ...baseEnv,
401
+ ...definition.env,
402
+ [definition.nameIdentifierEnv]: definition.nameIdentifier
403
+ };
404
+ if (definition.service === "server" &&
405
+ !normalizeManagedEnvironmentValue(resolvedBaseEnv.ASPNETCORE_ENVIRONMENT)) {
406
+ resolvedBaseEnv.ASPNETCORE_ENVIRONMENT = "Production";
407
+ }
408
+ return buildManagedRuntimeEnvironment({
409
+ component: definition.component,
410
+ manifest: { manifestDir: definition.manifestDir },
411
+ paths: definition.paths,
412
+ componentRoot: definition.componentRoot,
413
+ componentConfigDir: definition.componentConfigDir,
414
+ componentDataHome: definition.runtimeDataHome,
415
+ pm2Home: definition.pm2Home,
416
+ scriptBasename: basename(definition.script),
417
+ includeNpmConfigPrefix: definition.service !== "server"
418
+ }, resolvedBaseEnv);
419
+ }
420
+ function mergeManagedEnvironment(baseEnvironment, overrides) {
421
+ const merged = {
422
+ ...(baseEnvironment ?? {})
423
+ };
424
+ if (!overrides) {
425
+ return merged;
426
+ }
427
+ for (const [key, value] of Object.entries(overrides)) {
428
+ if (!key.trim()) {
429
+ continue;
430
+ }
431
+ if (value === undefined) {
432
+ delete merged[key];
433
+ continue;
434
+ }
435
+ merged[key] = value;
436
+ }
437
+ return merged;
438
+ }
439
+ function normalizeManagedEnvironmentValue(value) {
440
+ const normalized = value?.trim();
441
+ return normalized ? normalized : undefined;
442
+ }
443
+ function resolvePm2NameIdentifier(manifest, component, service, nameIdentifierValue) {
444
+ const nameIdentifierEnv = DEFAULT_PM2_NAME_IDENTIFIER_ENV;
445
+ const defaultNameIdentifier = manifest.runtime.hagicodeInstance?.trim() || PM2_NAME_IDENTIFIER_BOOTSTRAP_DEFAULT;
446
+ const nameIdentifier = nameIdentifierValue?.trim() ||
447
+ manifest.runtime.hagicodeInstance?.trim() ||
448
+ process.env[nameIdentifierEnv]?.trim();
449
+ if (!nameIdentifier) {
450
+ throw new ManagedPm2Error(`Managed PM2 service ${service} requires a runtime instance name. Set runtime.hagicodeInstance in the manifest, pass --instance, or set ${nameIdentifierEnv}.`);
451
+ }
452
+ if (!PM2_NAME_IDENTIFIER_PATTERN.test(nameIdentifier)) {
453
+ throw new ManagedPm2Error(`Managed PM2 service ${service} requires ${nameIdentifierEnv} to use only lowercase letters, digits, and underscores. Received "${nameIdentifier}".`);
454
+ }
455
+ return {
456
+ baseAppName: component.pm2?.appName ?? `hagicode-${component.name}`,
457
+ nameIdentifierEnv,
458
+ defaultNameIdentifier,
459
+ nameIdentifier
460
+ };
461
+ }
462
+ function parseManagedPm2Status(result, appName) {
463
+ const trimmedStdout = result.stdout.trim();
464
+ const trimmedStderr = result.stderr.trim();
465
+ if (!trimmedStdout) {
466
+ if (!trimmedStderr) {
467
+ return {
468
+ kind: "status",
469
+ statusEntry: null
470
+ };
471
+ }
472
+ if (isRetryablePm2BootstrapOutput(result.stdout, result.stderr)) {
473
+ return {
474
+ kind: "bootstrap",
475
+ summary: summarizePm2Output(result)
476
+ };
477
+ }
478
+ return {
479
+ kind: "failure",
480
+ message: "Managed PM2 status output was empty on stdout and could not be normalized from stderr."
481
+ };
482
+ }
483
+ try {
484
+ const parsed = parsePm2ProcessList(result);
485
+ const entry = parsed.find((value) => isPm2ProcessRecord(value) && value.name === appName);
486
+ if (!entry || !isPm2ProcessRecord(entry)) {
487
+ return {
488
+ kind: "status",
489
+ statusEntry: null
490
+ };
491
+ }
492
+ return {
493
+ kind: "status",
494
+ statusEntry: {
495
+ status: normalizePm2Status(entry.pm2_env?.status),
496
+ pid: typeof entry.pid === "number" ? entry.pid : null
497
+ }
498
+ };
499
+ }
500
+ catch (error) {
501
+ if (isRetryablePm2BootstrapOutput(result.stdout, result.stderr)) {
502
+ return {
503
+ kind: "bootstrap",
504
+ summary: summarizePm2Output(result)
505
+ };
506
+ }
507
+ return {
508
+ kind: "failure",
509
+ message: `Managed PM2 status returned invalid JSON: ${error instanceof Error ? error.message : String(error)} (${summarizePm2Output(result)})`
510
+ };
511
+ }
512
+ }
513
+ function parsePm2ProcessList(result) {
514
+ const directCandidates = [result.stdout.trim(), `${result.stdout}\n${result.stderr}`.trim()].filter(Boolean);
515
+ for (const candidate of directCandidates) {
516
+ const direct = tryParseJson(candidate);
517
+ if (Array.isArray(direct)) {
518
+ return direct;
519
+ }
520
+ const extracted = extractPm2JsonArray(candidate);
521
+ if (Array.isArray(extracted)) {
522
+ return extracted;
523
+ }
524
+ }
525
+ throw new Error("No JSON array payload was found in PM2 output.");
526
+ }
527
+ function extractPm2JsonArray(output) {
528
+ for (let index = 0; index < output.length; index += 1) {
529
+ if (output[index] !== "[") {
530
+ continue;
531
+ }
532
+ const nextNonWhitespace = output.slice(index + 1).match(/\S/u)?.[0];
533
+ if (nextNonWhitespace !== "{" && nextNonWhitespace !== "]") {
534
+ continue;
535
+ }
536
+ const parsed = tryParseJson(output.slice(index));
537
+ if (Array.isArray(parsed)) {
538
+ return parsed;
539
+ }
540
+ }
541
+ return null;
542
+ }
543
+ function tryParseJson(value) {
544
+ try {
545
+ return JSON.parse(value);
546
+ }
547
+ catch {
548
+ return null;
549
+ }
550
+ }
551
+ function summarizePm2Output(result) {
552
+ return [result.stdout.trim(), result.stderr.trim()]
553
+ .filter(Boolean)
554
+ .join(" | ")
555
+ .slice(0, 400);
556
+ }
557
+ function isRetryablePm2BootstrapOutput(stdout, stderr) {
558
+ const combined = `${stdout}\n${stderr}`.toLowerCase();
559
+ return [
560
+ "[pm2] spawning",
561
+ "[pm2] launching",
562
+ "[pm2] starting",
563
+ "[pm2] pm2 successfully daemonized",
564
+ "spawning pm2 daemon",
565
+ "pm2 home",
566
+ "rpc socket",
567
+ "pub socket",
568
+ "daemon launched"
569
+ ].some((marker) => combined.includes(marker));
570
+ }
571
+ function normalizePm2Status(value) {
572
+ switch (value) {
573
+ case "online":
574
+ return "online";
575
+ case "stopped":
576
+ return "stopped";
577
+ case "errored":
578
+ return "errored";
579
+ default:
580
+ return "unknown";
581
+ }
582
+ }
583
+ function isPm2ProcessRecord(value) {
584
+ return typeof value === "object" && value !== null;
585
+ }
586
+ function defaultPm2Script(componentName) {
587
+ switch (componentName) {
588
+ case "omniroute":
589
+ return "current/bin/omniroute.mjs";
590
+ case "code-server":
591
+ return "current/out/node/entry.js";
592
+ default:
593
+ throw new ManagedPm2Error(`Unsupported PM2 component ${componentName}.`);
594
+ }
595
+ }
596
+ function defaultBundledRuntimePm2Args(componentName, componentConfigDir) {
597
+ const configPath = join(componentConfigDir, "config.yaml");
598
+ switch (componentName) {
599
+ case "omniroute":
600
+ return ["--config", configPath, "--no-open"];
601
+ case "code-server":
602
+ return ["--config", configPath];
603
+ default:
604
+ return [];
605
+ }
606
+ }
607
+ function toPm2Args(args) {
608
+ return args.length > 0 ? ["--", ...args] : [];
609
+ }
610
+ function buildPm2ActionArgs(definition, action) {
611
+ if (definition.launchStrategy === "released-service") {
612
+ if (!definition.ecosystemPath) {
613
+ throw new ManagedPm2Error(`Released-service PM2 ecosystem file is unavailable for ${definition.service}.`);
614
+ }
615
+ switch (action) {
616
+ case "start":
617
+ return ["start", definition.ecosystemPath, "--only", definition.appName, "--update-env"];
618
+ case "stop":
619
+ return ["stop", definition.appName];
620
+ case "delete":
621
+ return ["delete", definition.appName];
622
+ }
623
+ }
624
+ switch (action) {
625
+ case "start":
626
+ if (isNodeLauncherScript(definition.script)) {
627
+ return [
628
+ "start",
629
+ definition.script,
630
+ "--name",
631
+ definition.appName,
632
+ "--cwd",
633
+ definition.cwd,
634
+ "--interpreter",
635
+ definition.nodePath,
636
+ "--update-env",
637
+ ...toPm2Args(definition.args)
638
+ ];
639
+ }
640
+ if (definition.launchStrategy === "native-wrapper") {
641
+ return buildNativeWrapperPm2StartArgs(definition);
642
+ }
643
+ return [
644
+ "start",
645
+ definition.script,
646
+ "--name",
647
+ definition.appName,
648
+ "--cwd",
649
+ definition.cwd,
650
+ "--update-env",
651
+ ...toPm2Args(definition.args)
652
+ ];
653
+ case "stop":
654
+ return ["stop", definition.appName];
655
+ case "delete":
656
+ return ["delete", definition.appName];
657
+ }
658
+ }
659
+ function buildNativeWrapperPm2StartArgs(definition) {
660
+ if (process.platform === "win32" && isWindowsBatchScript(definition.script)) {
661
+ const cmdExe = process.env.ComSpec || "C:\\Windows\\System32\\cmd.exe";
662
+ return [
663
+ "start",
664
+ cmdExe,
665
+ "--name",
666
+ definition.appName,
667
+ "--cwd",
668
+ definition.cwd,
669
+ "--interpreter",
670
+ "none",
671
+ "--update-env",
672
+ ...toPm2Args(["/d", "/s", "/c", definition.script, ...definition.args])
673
+ ];
674
+ }
675
+ return [
676
+ "start",
677
+ definition.script,
678
+ "--name",
679
+ definition.appName,
680
+ "--cwd",
681
+ definition.cwd,
682
+ "--interpreter",
683
+ "none",
684
+ "--update-env",
685
+ ...toPm2Args(definition.args)
686
+ ];
687
+ }
688
+ function isNodeLauncherScript(scriptPath) {
689
+ const extension = extname(scriptPath).toLowerCase();
690
+ return extension === ".js" || extension === ".mjs" || extension === ".cjs";
691
+ }
692
+ function isWindowsBatchScript(scriptPath) {
693
+ const extension = extname(scriptPath).toLowerCase();
694
+ return extension === ".cmd" || extension === ".bat";
695
+ }
696
+ function resolveBundledRuntimeLaunchStrategy(scriptPath) {
697
+ return isNodeLauncherScript(scriptPath) ? "node-script" : "native-wrapper";
698
+ }
699
+ function getManagedPm2Entrypoint(npmPrefix) {
700
+ return process.platform === "win32"
701
+ ? join(npmPrefix, "node_modules", "pm2", "bin", "pm2")
702
+ : join(npmPrefix, "lib", "node_modules", "pm2", "bin", "pm2");
703
+ }
704
+ async function validateManagedPath(pathValue, message) {
705
+ try {
706
+ await access(pathValue);
707
+ }
708
+ catch (error) {
709
+ throw new ManagedPm2Error(message, error instanceof Error ? { cause: error } : undefined);
710
+ }
711
+ }
712
+ async function sleep(ms) {
713
+ await new Promise((resolve) => setTimeout(resolve, ms));
714
+ }
715
+ //# sourceMappingURL=pm2-manager.js.map