@lifeaitools/clauth 1.30.16 → 1.30.17

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.
@@ -494,20 +494,33 @@ export function runSurfaceAction(id, action, actor = "localhost") {
494
494
  if (!command || command.length === 0) {
495
495
  return operation(action, { surface_id: id }, surface, { ok: false, state: "command_missing" }, actor);
496
496
  }
497
- const [cmd, ...args] = command;
498
- const result = spawnSync(cmd, args, {
499
- cwd: surface.cwd || undefined,
500
- env: { ...process.env, CLAUTH_PM2_HOME: getClauthPm2Home(), PM2_HOME: getClauthPm2Home() },
501
- windowsHide: true,
502
- encoding: "utf8",
503
- timeout: Number(surface.timeoutMs || 30000),
504
- });
497
+ const execute = (selectedCommand) => {
498
+ const [cmd, ...args] = selectedCommand;
499
+ return spawnSync(cmd, args, {
500
+ cwd: surface.cwd || undefined,
501
+ env: { ...process.env, CLAUTH_PM2_HOME: getClauthPm2Home(), PM2_HOME: getClauthPm2Home() },
502
+ windowsHide: true,
503
+ encoding: "utf8",
504
+ timeout: Number(surface.timeoutMs || 30000),
505
+ });
506
+ };
507
+ let result = execute(command);
508
+ let fallbackUsed = false;
509
+ // PM2 restart returns non-zero when the process was deleted. Reconcile is
510
+ // allowed to fall back to the declared start command; explicit restart keeps
511
+ // its strict failure semantics for operator-requested actions.
512
+ if (action === "reconcile" && result.status !== 0 && Array.isArray(surface.start) && surface.start.length > 0 && command !== surface.start) {
513
+ const restartStatus = result.status;
514
+ result = execute(surface.start);
515
+ fallbackUsed = true;
516
+ result.stderr = `restart exited ${restartStatus}; start fallback attempted\n${result.stderr || ""}`;
517
+ }
505
518
  return operation(action, { surface_id: id }, surface, {
506
519
  ok: result.status === 0,
507
520
  state: result.status === 0 ? "operation_completed" : "operation_failed",
508
521
  status: result.status,
509
522
  stderr: result.stderr?.slice(0, 2000),
510
- evidence: [`CLAUTH_PM2_HOME=${getClauthPm2Home()}`],
523
+ evidence: [`CLAUTH_PM2_HOME=${getClauthPm2Home()}`, ...(fallbackUsed ? ["reconcile_start_fallback=true"] : [])],
511
524
  }, actor);
512
525
  }
513
526
 
@@ -174,6 +174,29 @@ test("surface actions use dedicated clauth PM2 home and keep CodeFlow observe-on
174
174
  assert.equal(supervisorHealth().surfaces, 2);
175
175
  }));
176
176
 
177
+ test("reconcile falls back to the declared start command when restart reports a missing process", () => withTempSupervisor((root) => {
178
+ const managed = path.join(root, "managed");
179
+ process.env.CLAUTH_MANAGED_PLUGIN_ROOTS = managed;
180
+ process.env.CLAUTH_USER_PLUGIN_ROOTS = path.join(root, "user");
181
+ writePlugin(root, "managed", "fallback-demo", baseManifest("fallback-demo", {
182
+ core: true,
183
+ enable_default: true,
184
+ surfaces: [{
185
+ id: "primary",
186
+ destination: "local/clauth/pm2",
187
+ lifecycle_owner: "clauth",
188
+ port: 39114,
189
+ health: "/health",
190
+ start: [process.execPath, "--version"],
191
+ restart: [process.execPath, "-e", "process.exit(1)"],
192
+ }],
193
+ }));
194
+ discoverPlugins();
195
+ const receipt = runSurfaceAction("fallback-demo:primary", "reconcile");
196
+ assert.equal(receipt.resulting_state.ok, true);
197
+ assert.equal(receipt.resulting_state.evidence.includes("reconcile_start_fallback=true"), true);
198
+ }));
199
+
177
200
  test("surface promote and rollback never fall through to restart commands", () => withTempSupervisor((root) => {
178
201
  const managed = path.join(root, "managed");
179
202
  process.env.CLAUTH_MANAGED_PLUGIN_ROOTS = managed;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/clauth",
3
- "version": "1.30.16",
3
+ "version": "1.30.17",
4
4
  "description": "Hardware-bound credential vault for the LIFEAI infrastructure stack",
5
5
  "type": "module",
6
6
  "bin": {