@hagicode/hagiscript 0.1.10-dev.61.1.008a9f9 → 0.1.11-dev.63.1.e96bae4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -47,6 +47,7 @@ The packaged manifest currently manages:
47
47
  - `node` - managed Node.js runtime
48
48
  - `dotnet` - managed .NET and ASP.NET Core runtime
49
49
  - `npm-packages` - managed npm prefix, including `pm2`
50
+ - `server` - released backend package metadata and PM2 launch assets for `lib/PCode.Web.dll`
50
51
  - `omniroute` - vendored bundled runtime
51
52
  - `code-server` - vendored bundled runtime
52
53
 
@@ -124,18 +125,23 @@ Lifecycle commands print the resolved manifest, managed root, changed component
124
125
 
125
126
  Hagiscript manages runtime-scoped PM2 services for:
126
127
 
128
+ - `server`
127
129
  - `omniroute`
128
130
  - `code-server`
129
131
 
130
132
  Supported actions:
131
133
 
132
134
  - `start`
135
+ - `restart`
133
136
  - `stop`
134
137
  - `status`
135
138
 
136
139
  Examples:
137
140
 
138
141
  ```bash
142
+ hagiscript pm2 server start
143
+ hagiscript pm2 server restart
144
+ hagiscript pm2 server status
139
145
  hagiscript pm2 omniroute status
140
146
  hagiscript pm2 omniroute start
141
147
  hagiscript pm2 code-server stop
@@ -150,6 +156,28 @@ The PM2 flow is runtime-scoped:
150
156
 
151
157
  This means maintenance scripts should call `hagiscript pm2 ...` instead of a system `pm2` binary.
152
158
 
159
+ ### Released backend `server` contract
160
+
161
+ The packaged runtime manifest treats `server` as a `released-service` component. The managed runtime expects a published backend package staged under:
162
+
163
+ ```text
164
+ <runtime-root>/program/components/server/current/
165
+ lib/PCode.Web.dll
166
+ lib/PCode.Web.deps.json
167
+ lib/PCode.Web.runtimeconfig.json
168
+ start.sh (or the platform equivalent from the release package)
169
+ ```
170
+
171
+ Hagiscript keeps mutable launch state for that service under:
172
+
173
+ ```text
174
+ <runtime-root>/runtime-data/components/services/server/
175
+ .pm2/
176
+ pm2-runtime/
177
+ ```
178
+
179
+ `hagiscript runtime install --components server` prepares the runtime-owned launch assets and reports whether the published payload is already staged. `hagiscript pm2 server start` then generates the final PM2 ecosystem/env files under `pm2-runtime/` and launches the released backend through the managed `dotnet` runtime.
180
+
153
181
  ## Runtime Environment Contract
154
182
 
155
183
  Runtime lifecycle scripts and managed services receive a stable environment contract:
@@ -226,6 +254,14 @@ npm run integration:runtime-management
226
254
  npm run integration:installed-runtime
227
255
  ```
228
256
 
257
+ The runtime-management integration path also supports a release-oriented validation mode for CI:
258
+
259
+ ```bash
260
+ HAGISCRIPT_ENABLE_RELEASED_SERVER_TEST=1 npm run integration:runtime-management
261
+ ```
262
+
263
+ That mode downloads the latest public backend package from `https://github.com/HagiCode-org/releases/releases`, stages it into the managed runtime, and verifies `server` start/restart/stop/remove through HagiScript-managed PM2.
264
+
229
265
  ## License
230
266
 
231
267
  MIT. See [LICENSE](./LICENSE).
@@ -30,10 +30,10 @@ function parseManagedPm2Service(value) {
30
30
  throw new InvalidArgumentError(`Unsupported managed PM2 service "${value}". Supported services: ${supportedPm2Services.join(", ")}.`);
31
31
  }
32
32
  function parseManagedPm2Action(value) {
33
- if (value === "start" || value === "stop" || value === "status") {
33
+ if (value === "start" || value === "stop" || value === "restart" || value === "status") {
34
34
  return value;
35
35
  }
36
- throw new InvalidArgumentError(`Unsupported PM2 action "${value}". Supported actions: start, stop, status.`);
36
+ throw new InvalidArgumentError(`Unsupported PM2 action "${value}". Supported actions: start, stop, restart, status.`);
37
37
  }
38
38
  function validatePathOption(value, optionName) {
39
39
  if (value === undefined) {
@@ -1 +1 @@
1
- {"version":3,"file":"pm2-commands.js","sourceRoot":"","sources":["../../src/commands/pm2-commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,oBAAoB,EAAE,MAAM,WAAW,CAAA;AACzD,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,oBAAoB,EAGrB,MAAM,2BAA2B,CAAA;AAOlC,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO;SACJ,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,6CAA6C,CAAC;SAC1D,QAAQ,CAAC,WAAW,EAAE,sBAAsB,EAAE,sBAAsB,CAAC;SACrE,QAAQ,CAAC,UAAU,EAAE,YAAY,EAAE,qBAAqB,CAAC;SACzD,MAAM,CAAC,wBAAwB,EAAE,uCAAuC,CAAC;SACzE,MAAM,CAAC,uBAAuB,EAAE,+BAA+B,CAAC;SAChE,MAAM,CACL,KAAK,EACH,OAA8B,EAC9B,MAAwB,EACxB,OAA0B,EAC1B,OAAgB,EAChB,EAAE;QACF,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC;gBACxC,YAAY,EAAE,kBAAkB,CAAC,OAAO,CAAC,YAAY,EAAE,iBAAiB,CAAC;gBACzE,WAAW,EAAE,kBAAkB,CAAC,OAAO,CAAC,WAAW,EAAE,gBAAgB,CAAC;gBACtE,OAAO;gBACP,MAAM;aACP,CAAC,CAAA;YAEF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,0BAA0B,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAA;QACvD,CAAC;IACH,CAAC,CACF,CAAA;AACL,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAa;IAC3C,IAAK,oBAA0C,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,OAAO,KAA8B,CAAA;IACvC,CAAC;IAED,MAAM,IAAI,oBAAoB,CAC5B,oCAAoC,KAAK,0BAA0B,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACtG,CAAA;AACH,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAa;IAC1C,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChE,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,IAAI,oBAAoB,CAC5B,2BAA2B,KAAK,4CAA4C,CAC7E,CAAA;AACH,CAAC;AAED,SAAS,kBAAkB,CACzB,KAAyB,EACzB,UAAkB;IAElB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC/B,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,oBAAoB,CAAC,GAAG,UAAU,4BAA4B,CAAC,CAAA;IAC3E,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,oBAAoB,CAAC,GAAG,UAAU,iCAAiC,CAAC,CAAA;IAChF,CAAC;IAED,OAAO,UAAU,CAAA;AACnB,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC/D,CAAC"}
1
+ {"version":3,"file":"pm2-commands.js","sourceRoot":"","sources":["../../src/commands/pm2-commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,oBAAoB,EAAE,MAAM,WAAW,CAAA;AACzD,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,oBAAoB,EAGrB,MAAM,2BAA2B,CAAA;AAOlC,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO;SACJ,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,6CAA6C,CAAC;SAC1D,QAAQ,CAAC,WAAW,EAAE,sBAAsB,EAAE,sBAAsB,CAAC;SACrE,QAAQ,CAAC,UAAU,EAAE,YAAY,EAAE,qBAAqB,CAAC;SACzD,MAAM,CAAC,wBAAwB,EAAE,uCAAuC,CAAC;SACzE,MAAM,CAAC,uBAAuB,EAAE,+BAA+B,CAAC;SAChE,MAAM,CACL,KAAK,EACH,OAA8B,EAC9B,MAAwB,EACxB,OAA0B,EAC1B,OAAgB,EAChB,EAAE;QACF,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC;gBACxC,YAAY,EAAE,kBAAkB,CAAC,OAAO,CAAC,YAAY,EAAE,iBAAiB,CAAC;gBACzE,WAAW,EAAE,kBAAkB,CAAC,OAAO,CAAC,WAAW,EAAE,gBAAgB,CAAC;gBACtE,OAAO;gBACP,MAAM;aACP,CAAC,CAAA;YAEF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,0BAA0B,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAA;QACvD,CAAC;IACH,CAAC,CACF,CAAA;AACL,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAa;IAC3C,IAAK,oBAA0C,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,OAAO,KAA8B,CAAA;IACvC,CAAC;IAED,MAAM,IAAI,oBAAoB,CAC5B,oCAAoC,KAAK,0BAA0B,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACtG,CAAA;AACH,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAa;IAC1C,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvF,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,IAAI,oBAAoB,CAC5B,2BAA2B,KAAK,qDAAqD,CACtF,CAAA;AACH,CAAC;AAED,SAAS,kBAAkB,CACzB,KAAyB,EACzB,UAAkB;IAElB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC/B,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,oBAAoB,CAAC,GAAG,UAAU,4BAA4B,CAAC,CAAA;IAC3E,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,oBAAoB,CAAC,GAAG,UAAU,iCAAiC,CAAC,CAAA;IAChF,CAAC;IAED,OAAO,UAAU,CAAA;AACnB,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC/D,CAAC"}
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@ export { getDefaultManagedNodeRuntimeDirectory, installNodeRuntime, resolveManag
9
9
  export { verifyNodeRuntime, type NodeRuntimeVerificationResult } from "./runtime/node-verify.js";
10
10
  export { createNpmSyncPlan, loadNpmSyncManifest, normalizeGlobalInventory, syncNpmGlobals, validateNpmSyncManifest, type InstalledGlobalPackages, type NpmSyncActionKind, type NpmSyncActionResult, type NpmSyncCommandKind, type NpmSyncFallbackEvent, type NpmSyncFallbackPolicy, type NpmSyncManifest, type NpmSyncManifestEntry, type NpmSyncPlannedAction, type NpmSyncRuntimeMetadata, type NpmSyncSummary } from "./runtime/npm-sync.js";
11
11
  export { buildToolSyncPackageSet, builtInToolSyncCatalog, normalizeToolSyncEntry, validateToolSyncCatalog, type CustomAgentCliToolInput, type ToolSyncCatalogEntry, type ToolSyncGroupId, type ToolSyncPackageConstraint, type ToolSyncRequirement, type ToolSyncSelection } from "./runtime/tool-sync-catalog.js";
12
- export { getDefaultRuntimeManifestPath, loadRuntimeManifest, type LoadedRuntimeManifest, type RuntimeComponentDefinition, type RuntimeLifecyclePhase, type RuntimeManifestPaths } from "./runtime/runtime-manifest.js";
12
+ export { getDefaultRuntimeManifestPath, loadRuntimeManifest, type LoadedRuntimeManifest, type RuntimeComponentDefinition, type RuntimeLifecyclePhase, type RuntimeManifestPaths, type RuntimeReleasedServiceDefinition } from "./runtime/runtime-manifest.js";
13
13
  export { defaultRuntimeRoot, getComponentConfigDirectory, getComponentLogsDirectory, getComponentManagedRoot, getComponentPm2Home, getComponentRuntimeDataHome, resolveRuntimePaths, type ResolvedRuntimePaths } from "./runtime/runtime-paths.js";
14
14
  export { createInitialRuntimeState, mergeRuntimeState, readRuntimeState, writeRuntimeState, type RuntimeComponentState, type RuntimeOperationState, type RuntimeState } from "./runtime/runtime-state.js";
15
15
  export { renderManagedPm2StatusText, resolveManagedPm2ServiceDefinition, runManagedPm2Command, supportedPm2Services, type ManagedPm2Action, type ManagedPm2CommandResult, type ManagedPm2CommandOptions, type ManagedPm2ServiceName, type ManagedPm2Status, type ResolvedManagedPm2ServiceDefinition } from "./runtime/pm2-manager.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE3D,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,cAAc,EAEf,MAAM,cAAc,CAAC;AAQtB,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,WAAW;QACX,OAAO,EAAE,cAAc;QACvB,MAAM,EAAE,YAAY;KACrB,CAAC;AACJ,CAAC;AAED,OAAO,EACL,qCAAqC,EACrC,kBAAkB,EAClB,yBAAyB,EAG1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,iBAAiB,EAElB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,cAAc,EACd,uBAAuB,EAYxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,EAOxB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,6BAA6B,EAC7B,mBAAmB,EAKpB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,kBAAkB,EAClB,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,mBAAmB,EACnB,2BAA2B,EAC3B,mBAAmB,EAEpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EAIlB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,0BAA0B,EAC1B,kCAAkC,EAClC,oBAAoB,EACpB,oBAAoB,EAOrB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,8BAA8B,EAC9B,yBAAyB,EACzB,4BAA4B,EAC5B,kBAAkB,EACnB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACtB,mBAAmB,EACnB,aAAa,EAKd,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE3D,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,cAAc,EAEf,MAAM,cAAc,CAAC;AAQtB,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,WAAW;QACX,OAAO,EAAE,cAAc;QACvB,MAAM,EAAE,YAAY;KACrB,CAAC;AACJ,CAAC;AAED,OAAO,EACL,qCAAqC,EACrC,kBAAkB,EAClB,yBAAyB,EAG1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,iBAAiB,EAElB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,cAAc,EACd,uBAAuB,EAYxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,EAOxB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,6BAA6B,EAC7B,mBAAmB,EAMpB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,kBAAkB,EAClB,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,mBAAmB,EACnB,2BAA2B,EAC3B,mBAAmB,EAEpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EAIlB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,0BAA0B,EAC1B,kCAAkC,EAClC,oBAAoB,EACpB,oBAAoB,EAOrB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,8BAA8B,EAC9B,yBAAyB,EACzB,4BAA4B,EAC5B,kBAAkB,EACnB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACtB,mBAAmB,EACnB,aAAa,EAKd,MAAM,8BAA8B,CAAC"}
@@ -1,9 +1,9 @@
1
1
  import type { CommandRunner } from "./command-launch.js";
2
2
  import { type LoadedRuntimeManifest, type RuntimeComponentDefinition } from "./runtime-manifest.js";
3
3
  import { type ResolvedRuntimePaths } from "./runtime-paths.js";
4
- export declare const supportedPm2Services: readonly ["omniroute", "code-server"];
4
+ export declare const supportedPm2Services: readonly ["server", "omniroute", "code-server"];
5
5
  export type ManagedPm2ServiceName = (typeof supportedPm2Services)[number];
6
- export type ManagedPm2Action = "start" | "stop" | "status";
6
+ export type ManagedPm2Action = "start" | "stop" | "restart" | "status" | "delete";
7
7
  export type ManagedPm2Status = "online" | "stopped" | "errored" | "missing" | "unknown";
8
8
  export interface ManagedPm2CommandOptions {
9
9
  manifestPath?: string;
@@ -12,6 +12,7 @@ export interface ManagedPm2CommandOptions {
12
12
  action: ManagedPm2Action;
13
13
  runner?: CommandRunner;
14
14
  }
15
+ type ManagedPm2LaunchStrategy = "node-script" | "released-service";
15
16
  export interface ResolvedManagedPm2ServiceDefinition {
16
17
  service: ManagedPm2ServiceName;
17
18
  component: RuntimeComponentDefinition;
@@ -29,6 +30,11 @@ export interface ResolvedManagedPm2ServiceDefinition {
29
30
  pm2Home: string;
30
31
  pm2Binary: string;
31
32
  nodePath: string;
33
+ launchStrategy: ManagedPm2LaunchStrategy;
34
+ dotnetPath?: string;
35
+ runtimeFilesDir?: string;
36
+ ecosystemPath?: string;
37
+ envFilePath?: string;
32
38
  }
33
39
  export interface ManagedPm2CommandResult {
34
40
  service: ManagedPm2ServiceName;
@@ -45,6 +51,9 @@ export interface ManagedPm2CommandResult {
45
51
  pid: number | null;
46
52
  stdout: string;
47
53
  stderr: string;
54
+ launchStrategy: ManagedPm2LaunchStrategy;
55
+ dotnetPath?: string;
56
+ runtimeFilesDir?: string;
48
57
  }
49
58
  export declare class ManagedPm2Error extends Error {
50
59
  constructor(message: string, options?: ErrorOptions);
@@ -52,3 +61,4 @@ export declare class ManagedPm2Error extends Error {
52
61
  export declare function runManagedPm2Command(options: ManagedPm2CommandOptions): Promise<ManagedPm2CommandResult>;
53
62
  export declare function resolveManagedPm2ServiceDefinition(manifest: LoadedRuntimeManifest, paths: ResolvedRuntimePaths, service: ManagedPm2ServiceName): Promise<ResolvedManagedPm2ServiceDefinition>;
54
63
  export declare function renderManagedPm2StatusText(result: ManagedPm2CommandResult): string;
64
+ export {};
@@ -1,18 +1,20 @@
1
- import { access } from "node:fs/promises";
1
+ import { access, mkdir, writeFile } from "node:fs/promises";
2
2
  import { basename, extname, join } from "node:path";
3
3
  import process from "node:process";
4
- import { runCommand } from "./command-launch.js";
5
- import { buildManagedRuntimeEnvironment, } from "./runtime-executor.js";
4
+ import { CommandExecutionError, runCommand } from "./command-launch.js";
5
+ import { buildManagedRuntimeEnvironment } from "./runtime-executor.js";
6
6
  import { getRuntimeExecutablePaths } from "./node-verify.js";
7
7
  import { loadRuntimeManifest } from "./runtime-manifest.js";
8
8
  import { getComponentConfigDirectory, getComponentManagedRoot, getComponentPm2Home, getComponentRuntimeDataHome, resolveManagedPath, resolveRuntimePaths } from "./runtime-paths.js";
9
- export const supportedPm2Services = ["omniroute", "code-server"];
9
+ export const supportedPm2Services = ["server", "omniroute", "code-server"];
10
10
  export class ManagedPm2Error extends Error {
11
11
  constructor(message, options) {
12
12
  super(message, options);
13
13
  this.name = "ManagedPm2Error";
14
14
  }
15
15
  }
16
+ const DEFAULT_PM2_STATUS_RETRY_DELAY_MS = 500;
17
+ const DEFAULT_PM2_STATUS_MAX_RETRIES = 3;
16
18
  export async function runManagedPm2Command(options) {
17
19
  const manifest = await loadRuntimeManifest({ manifestPath: options.manifestPath });
18
20
  const paths = resolveRuntimePaths(manifest, { runtimeRoot: options.runtimeRoot });
@@ -20,11 +22,27 @@ export async function runManagedPm2Command(options) {
20
22
  const runner = options.runner ?? runCommand;
21
23
  switch (options.action) {
22
24
  case "start":
23
- await runPm2(definition, buildPm2StartArgs(definition), runner);
25
+ if (definition.launchStrategy === "released-service") {
26
+ await prepareReleasedServicePm2Files(definition);
27
+ }
28
+ await executePm2(definition, buildPm2ActionArgs(definition, "start"), runner);
24
29
  return readManagedPm2Status(definition, "start", runner);
30
+ case "restart":
31
+ if (definition.launchStrategy === "released-service") {
32
+ await prepareReleasedServicePm2Files(definition);
33
+ }
34
+ await executePm2(definition, buildPm2ActionArgs(definition, "restart"), runner);
35
+ return readManagedPm2Status(definition, "restart", runner);
25
36
  case "stop":
26
- await runPm2(definition, ["stop", definition.appName], runner);
37
+ await executePm2(definition, buildPm2ActionArgs(definition, "stop"), runner, {
38
+ allowMissingProcess: true
39
+ });
27
40
  return readManagedPm2Status(definition, "stop", runner);
41
+ case "delete":
42
+ await executePm2(definition, buildPm2ActionArgs(definition, "delete"), runner, {
43
+ allowMissingProcess: true
44
+ });
45
+ return readManagedPm2Status(definition, "delete", runner);
28
46
  case "status":
29
47
  return readManagedPm2Status(definition, "status", runner);
30
48
  }
@@ -39,16 +57,58 @@ export async function resolveManagedPm2ServiceDefinition(manifest, paths, servic
39
57
  const runtimeDataHome = getComponentRuntimeDataHome(paths, component.name, component.runtimeDataDir);
40
58
  const componentConfigDir = getComponentConfigDirectory(paths, component.name, component.runtimeDataDir);
41
59
  const pm2Home = getComponentPm2Home(paths, component.name, component.runtimeDataDir, component.pm2?.pm2Home);
42
- const script = resolveManagedPath(component.pm2?.script ?? defaultPm2Script(component.name), componentRoot);
43
- const cwd = resolveManagedPath(component.pm2?.cwd ?? ".", componentRoot);
44
60
  const nodePath = getRuntimeExecutablePaths(paths.nodeRuntime).nodePath;
45
61
  const pm2Entrypoint = getManagedPm2Entrypoint(paths.npmPrefix);
46
62
  await Promise.all([
47
- validateManagedPath(script, `Managed launcher for ${service} is missing`),
48
- validateManagedPath(cwd, `Managed working directory for ${service} is missing`),
49
63
  validateManagedPath(pm2Entrypoint, "Managed PM2 binary is missing. Install the runtime npm-packages component first."),
50
64
  validateManagedPath(nodePath, "Managed Node runtime is missing. Install the runtime node component first.")
51
65
  ]);
66
+ if (component.type === "released-service") {
67
+ const releasedService = component.releasedService;
68
+ if (!releasedService) {
69
+ throw new ManagedPm2Error(`Runtime manifest component ${component.name} is missing releasedService metadata.`);
70
+ }
71
+ const cwd = resolveManagedPath(releasedService.workingDirectory, componentRoot);
72
+ const script = resolveManagedPath(releasedService.dllPath, componentRoot);
73
+ const runtimeFilesDir = join(runtimeDataHome, releasedService.runtimeFilesDir ?? "pm2-runtime");
74
+ const ecosystemPath = join(runtimeFilesDir, "ecosystem.config.cjs");
75
+ const envFilePath = join(runtimeFilesDir, ".env");
76
+ const dotnetPath = join(paths.dotnetRuntime, "current", process.platform === "win32" ? "dotnet.exe" : "dotnet");
77
+ await Promise.all([
78
+ validateManagedPath(script, `Managed released-service payload for ${service} is missing.`),
79
+ validateManagedPath(cwd, `Managed working directory for ${service} is missing.`),
80
+ validateManagedPath(dotnetPath, "Managed .NET runtime is missing. Install the runtime dotnet component first.")
81
+ ]);
82
+ return {
83
+ service,
84
+ component,
85
+ manifestDir: manifest.manifestDir,
86
+ paths,
87
+ appName: component.pm2?.appName ?? `hagicode-${component.name}`,
88
+ cwd,
89
+ script,
90
+ args: component.pm2?.args ?? [],
91
+ env: component.pm2?.env ?? {},
92
+ runtimeHome: paths.runtimeHome,
93
+ runtimeDataHome,
94
+ componentRoot,
95
+ componentConfigDir,
96
+ pm2Home,
97
+ pm2Binary: pm2Entrypoint,
98
+ nodePath,
99
+ launchStrategy: "released-service",
100
+ dotnetPath,
101
+ runtimeFilesDir,
102
+ ecosystemPath,
103
+ envFilePath
104
+ };
105
+ }
106
+ const script = resolveManagedPath(component.pm2?.script ?? defaultPm2Script(component.name), componentRoot);
107
+ const cwd = resolveManagedPath(component.pm2?.cwd ?? ".", componentRoot);
108
+ await Promise.all([
109
+ validateManagedPath(script, `Managed launcher for ${service} is missing.`),
110
+ validateManagedPath(cwd, `Managed working directory for ${service} is missing.`)
111
+ ]);
52
112
  return {
53
113
  service,
54
114
  component,
@@ -65,7 +125,8 @@ export async function resolveManagedPm2ServiceDefinition(manifest, paths, servic
65
125
  componentConfigDir,
66
126
  pm2Home,
67
127
  pm2Binary: pm2Entrypoint,
68
- nodePath
128
+ nodePath,
129
+ launchStrategy: "node-script"
69
130
  };
70
131
  }
71
132
  export function renderManagedPm2StatusText(result) {
@@ -74,12 +135,15 @@ export function renderManagedPm2StatusText(result) {
74
135
  `Action: ${result.action}`,
75
136
  `App: ${result.appName}`,
76
137
  `Status: ${result.status}`,
138
+ `Launch strategy: ${result.launchStrategy}`,
77
139
  `Runtime home: ${result.runtimeHome}`,
78
140
  `Runtime data home: ${result.runtimeDataHome}`,
79
141
  `PM2 home: ${result.pm2Home}`,
80
142
  `Script: ${result.script}`,
81
143
  `Working directory: ${result.cwd}`,
82
144
  `PM2 binary: ${result.pm2Binary}`,
145
+ ...(result.dotnetPath ? [`Dotnet: ${result.dotnetPath}`] : []),
146
+ ...(result.runtimeFilesDir ? [`Runtime files: ${result.runtimeFilesDir}`] : []),
83
147
  `PID: ${result.pid ?? "n/a"}`
84
148
  ].join("\n");
85
149
  }
@@ -90,26 +154,87 @@ function assertSupportedPm2Service(service) {
90
154
  throw new ManagedPm2Error(`Unsupported managed PM2 service "${service}". Supported services: ${supportedPm2Services.join(", ")}.`);
91
155
  }
92
156
  async function readManagedPm2Status(definition, action, runner) {
93
- const result = await runPm2(definition, ["jlist"], runner);
94
- const statusEntry = parsePm2Status(result, definition.appName);
95
- return {
96
- service: definition.service,
97
- action,
98
- appName: definition.appName,
99
- cwd: definition.cwd,
100
- script: definition.script,
101
- runtimeHome: definition.runtimeHome,
102
- runtimeDataHome: definition.runtimeDataHome,
157
+ let result;
158
+ let parsed;
159
+ for (let attempt = 0; attempt <= DEFAULT_PM2_STATUS_MAX_RETRIES; attempt += 1) {
160
+ result = await executePm2(definition, ["jlist"], runner);
161
+ parsed = parseManagedPm2Status(result, definition.appName);
162
+ if (parsed.kind === "status") {
163
+ return {
164
+ service: definition.service,
165
+ action,
166
+ appName: definition.appName,
167
+ cwd: definition.cwd,
168
+ script: definition.script,
169
+ runtimeHome: definition.runtimeHome,
170
+ runtimeDataHome: definition.runtimeDataHome,
171
+ pm2Home: definition.pm2Home,
172
+ pm2Binary: definition.pm2Binary,
173
+ exists: parsed.statusEntry !== null,
174
+ status: parsed.statusEntry?.status ?? "missing",
175
+ pid: parsed.statusEntry?.pid ?? null,
176
+ stdout: result.stdout,
177
+ stderr: result.stderr,
178
+ launchStrategy: definition.launchStrategy,
179
+ dotnetPath: definition.dotnetPath,
180
+ runtimeFilesDir: definition.runtimeFilesDir
181
+ };
182
+ }
183
+ if (parsed.kind === "failure") {
184
+ throw new ManagedPm2Error(parsed.message);
185
+ }
186
+ const retriesRemaining = DEFAULT_PM2_STATUS_MAX_RETRIES - attempt;
187
+ if (retriesRemaining <= 0) {
188
+ 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}`);
189
+ }
190
+ await sleep(DEFAULT_PM2_STATUS_RETRY_DELAY_MS);
191
+ }
192
+ throw new ManagedPm2Error(`Managed PM2 status output could not be normalized for ${definition.appName}.`);
193
+ }
194
+ async function executePm2(definition, args, runner, options = {}) {
195
+ const env = buildManagedRuntimeEnvironment({
196
+ component: definition.component,
197
+ manifest: { manifestDir: definition.manifestDir },
198
+ paths: definition.paths,
199
+ componentRoot: definition.componentRoot,
200
+ componentConfigDir: definition.componentConfigDir,
201
+ componentDataHome: definition.runtimeDataHome,
103
202
  pm2Home: definition.pm2Home,
104
- pm2Binary: definition.pm2Binary,
105
- exists: statusEntry !== null,
106
- status: statusEntry?.status ?? "missing",
107
- pid: statusEntry?.pid ?? null,
108
- stdout: result.stdout,
109
- stderr: result.stderr
110
- };
203
+ scriptBasename: basename(definition.script)
204
+ }, { ...process.env, ...definition.env });
205
+ try {
206
+ return await runner(definition.nodePath, [definition.pm2Binary, ...args], {
207
+ cwd: definition.cwd,
208
+ env,
209
+ maxBuffer: 10 * 1024 * 1024
210
+ });
211
+ }
212
+ catch (error) {
213
+ if (options.allowMissingProcess &&
214
+ error instanceof CommandExecutionError &&
215
+ /not found|doesn't exist|process or namespace/i.test(`${error.context.stderr}\n${error.context.stdout}`)) {
216
+ return {
217
+ command: error.context.command,
218
+ args: error.context.args,
219
+ stdout: error.context.stdout,
220
+ stderr: error.context.stderr,
221
+ cwd: error.context.cwd,
222
+ exitCode: error.context.exitCode,
223
+ signal: error.context.signal,
224
+ timedOut: error.context.timedOut
225
+ };
226
+ }
227
+ throw new ManagedPm2Error(error instanceof Error ? error.message : String(error), error instanceof Error ? { cause: error } : undefined);
228
+ }
111
229
  }
112
- async function runPm2(definition, args, runner) {
230
+ async function prepareReleasedServicePm2Files(definition) {
231
+ if (definition.launchStrategy !== "released-service" ||
232
+ !definition.runtimeFilesDir ||
233
+ !definition.ecosystemPath ||
234
+ !definition.envFilePath ||
235
+ !definition.dotnetPath) {
236
+ return;
237
+ }
113
238
  const env = buildManagedRuntimeEnvironment({
114
239
  component: definition.component,
115
240
  manifest: { manifestDir: definition.manifestDir },
@@ -120,25 +245,91 @@ async function runPm2(definition, args, runner) {
120
245
  pm2Home: definition.pm2Home,
121
246
  scriptBasename: basename(definition.script)
122
247
  }, { ...process.env, ...definition.env });
123
- return runner(definition.nodePath, [definition.pm2Binary, ...args], {
124
- cwd: definition.cwd,
125
- env,
126
- maxBuffer: 10 * 1024 * 1024
127
- });
248
+ await mkdir(definition.runtimeFilesDir, { recursive: true });
249
+ await writeFile(definition.envFilePath, buildPm2EnvFile(env), "utf8");
250
+ await writeFile(definition.ecosystemPath, buildReleasedServiceEcosystemConfig(definition), "utf8");
128
251
  }
129
- function parsePm2Status(result, appName) {
130
- const parsed = parsePm2ProcessList(result);
131
- if (!Array.isArray(parsed)) {
132
- throw new ManagedPm2Error(`Managed PM2 status returned an unexpected payload for ${appName}.`);
252
+ function buildReleasedServiceEcosystemConfig(definition) {
253
+ if (!definition.dotnetPath || !definition.envFilePath) {
254
+ throw new ManagedPm2Error(`Released-service PM2 launch files are unavailable for ${definition.service}.`);
133
255
  }
134
- const entry = parsed.find((value) => isPm2ProcessRecord(value) && value.name === appName);
135
- if (!entry || !isPm2ProcessRecord(entry)) {
136
- return null;
256
+ const args = [definition.script, ...definition.args];
257
+ return [
258
+ "module.exports = {",
259
+ " apps: [",
260
+ " {",
261
+ ` name: ${JSON.stringify(definition.appName)},`,
262
+ ` script: ${JSON.stringify(definition.dotnetPath)},`,
263
+ ` args: ${JSON.stringify(args)},`,
264
+ ` cwd: ${JSON.stringify(definition.cwd)},`,
265
+ ' interpreter: "none",',
266
+ ' exec_mode: "fork",',
267
+ " autorestart: true,",
268
+ " watch: false,",
269
+ ` env_file: ${JSON.stringify(definition.envFilePath)}`,
270
+ " }",
271
+ " ]",
272
+ "};",
273
+ ""
274
+ ].join("\n");
275
+ }
276
+ function buildPm2EnvFile(env) {
277
+ return (Object.entries(env)
278
+ .filter(([key, value]) => key.trim().length > 0 && value !== undefined)
279
+ .sort(([left], [right]) => left.localeCompare(right))
280
+ .map(([key, value]) => `${key}=${String(value).replace(/\r?\n/g, "\\n")}`)
281
+ .join("\n") + "\n");
282
+ }
283
+ function parseManagedPm2Status(result, appName) {
284
+ const trimmedStdout = result.stdout.trim();
285
+ const trimmedStderr = result.stderr.trim();
286
+ if (!trimmedStdout) {
287
+ if (!trimmedStderr) {
288
+ return {
289
+ kind: "status",
290
+ statusEntry: null
291
+ };
292
+ }
293
+ if (isRetryablePm2BootstrapOutput(result.stdout, result.stderr)) {
294
+ return {
295
+ kind: "bootstrap",
296
+ summary: summarizePm2Output(result)
297
+ };
298
+ }
299
+ return {
300
+ kind: "failure",
301
+ message: "Managed PM2 status output was empty on stdout and could not be normalized from stderr."
302
+ };
303
+ }
304
+ try {
305
+ const parsed = parsePm2ProcessList(result);
306
+ const entry = parsed.find((value) => isPm2ProcessRecord(value) && value.name === appName);
307
+ if (!entry || !isPm2ProcessRecord(entry)) {
308
+ return {
309
+ kind: "status",
310
+ statusEntry: null
311
+ };
312
+ }
313
+ return {
314
+ kind: "status",
315
+ statusEntry: {
316
+ status: normalizePm2Status(entry.pm2_env?.status),
317
+ pid: typeof entry.pid === "number" ? entry.pid : null
318
+ }
319
+ };
320
+ }
321
+ catch (error) {
322
+ if (isRetryablePm2BootstrapOutput(result.stdout, result.stderr)) {
323
+ return {
324
+ kind: "bootstrap",
325
+ summary: summarizePm2Output(result)
326
+ };
327
+ }
328
+ return {
329
+ kind: "failure",
330
+ message: `Managed PM2 status returned invalid JSON: ${error instanceof Error ? error.message : String(error)} (${summarizePm2Output(result)})`
331
+ };
137
332
  }
138
- return {
139
- status: normalizePm2Status(entry.pm2_env?.status),
140
- pid: typeof entry.pid === "number" ? entry.pid : null
141
- };
142
333
  }
143
334
  function parsePm2ProcessList(result) {
144
335
  const directCandidates = [result.stdout.trim(), `${result.stdout}\n${result.stderr}`.trim()].filter(Boolean);
@@ -152,7 +343,7 @@ function parsePm2ProcessList(result) {
152
343
  return extracted;
153
344
  }
154
345
  }
155
- throw new ManagedPm2Error(`Managed PM2 status returned invalid JSON: ${summarizePm2Output(result)}`);
346
+ throw new Error("No JSON array payload was found in PM2 output.");
156
347
  }
157
348
  function extractPm2JsonArray(output) {
158
349
  for (let index = 0; index < output.length; index += 1) {
@@ -184,6 +375,20 @@ function summarizePm2Output(result) {
184
375
  .join(" | ")
185
376
  .slice(0, 400);
186
377
  }
378
+ function isRetryablePm2BootstrapOutput(stdout, stderr) {
379
+ const combined = `${stdout}\n${stderr}`.toLowerCase();
380
+ return [
381
+ "[pm2] spawning",
382
+ "[pm2] launching",
383
+ "[pm2] starting",
384
+ "[pm2] pm2 successfully daemonized",
385
+ "spawning pm2 daemon",
386
+ "pm2 home",
387
+ "rpc socket",
388
+ "pub socket",
389
+ "daemon launched"
390
+ ].some((marker) => combined.includes(marker));
391
+ }
187
392
  function normalizePm2Status(value) {
188
393
  switch (value) {
189
394
  case "online":
@@ -212,31 +417,55 @@ function defaultPm2Script(componentName) {
212
417
  function toPm2Args(args) {
213
418
  return args.length > 0 ? ["--", ...args] : [];
214
419
  }
215
- function buildPm2StartArgs(definition) {
216
- if (isNodeLauncherScript(definition.script)) {
217
- return [
218
- "start",
219
- definition.script,
220
- "--name",
221
- definition.appName,
222
- "--cwd",
223
- definition.cwd,
224
- "--interpreter",
225
- definition.nodePath,
226
- "--update-env",
227
- ...toPm2Args(definition.args)
228
- ];
420
+ function buildPm2ActionArgs(definition, action) {
421
+ if (definition.launchStrategy === "released-service") {
422
+ if (!definition.ecosystemPath) {
423
+ throw new ManagedPm2Error(`Released-service PM2 ecosystem file is unavailable for ${definition.service}.`);
424
+ }
425
+ switch (action) {
426
+ case "start":
427
+ return ["start", definition.ecosystemPath, "--only", definition.appName, "--update-env"];
428
+ case "restart":
429
+ return ["reload", definition.ecosystemPath, "--update-env"];
430
+ case "stop":
431
+ return ["stop", definition.appName];
432
+ case "delete":
433
+ return ["delete", definition.appName];
434
+ }
435
+ }
436
+ switch (action) {
437
+ case "start":
438
+ if (isNodeLauncherScript(definition.script)) {
439
+ return [
440
+ "start",
441
+ definition.script,
442
+ "--name",
443
+ definition.appName,
444
+ "--cwd",
445
+ definition.cwd,
446
+ "--interpreter",
447
+ definition.nodePath,
448
+ "--update-env",
449
+ ...toPm2Args(definition.args)
450
+ ];
451
+ }
452
+ return [
453
+ "start",
454
+ definition.script,
455
+ "--name",
456
+ definition.appName,
457
+ "--cwd",
458
+ definition.cwd,
459
+ "--update-env",
460
+ ...toPm2Args(definition.args)
461
+ ];
462
+ case "restart":
463
+ return ["restart", definition.appName, "--update-env"];
464
+ case "stop":
465
+ return ["stop", definition.appName];
466
+ case "delete":
467
+ return ["delete", definition.appName];
229
468
  }
230
- return [
231
- "start",
232
- definition.script,
233
- "--name",
234
- definition.appName,
235
- "--cwd",
236
- definition.cwd,
237
- "--update-env",
238
- ...toPm2Args(definition.args)
239
- ];
240
469
  }
241
470
  function isNodeLauncherScript(scriptPath) {
242
471
  const extension = extname(scriptPath).toLowerCase();
@@ -255,4 +484,7 @@ async function validateManagedPath(pathValue, message) {
255
484
  throw new ManagedPm2Error(message, error instanceof Error ? { cause: error } : undefined);
256
485
  }
257
486
  }
487
+ async function sleep(ms) {
488
+ await new Promise((resolve) => setTimeout(resolve, ms));
489
+ }
258
490
  //# sourceMappingURL=pm2-manager.js.map