@alfe.ai/gateway 0.0.13 → 0.0.14

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/dist/health.js CHANGED
@@ -3152,7 +3152,16 @@ enumValues({
3152
3152
  object({ gracePeriodDays: number().int().positive().optional() });
3153
3153
  enumValues({
3154
3154
  Active: "active",
3155
- Inactive: "inactive",
3155
+ Pending: "pending",
3156
+ Provisioning: "provisioning",
3157
+ Starting: "starting",
3158
+ Running: "running",
3159
+ Stopping: "stopping",
3160
+ Stopped: "stopped",
3161
+ Rebooting: "rebooting",
3162
+ Failing: "failing",
3163
+ Failed: "failed",
3164
+ BillingSuspended: "billing_suspended",
3156
3165
  Deleted: "deleted"
3157
3166
  });
3158
3167
  enumValues({
@@ -3163,16 +3172,6 @@ enumValues({
3163
3172
  OpenClaw: "openclaw",
3164
3173
  NanoClaw: "nanoclaw"
3165
3174
  });
3166
- enumValues({
3167
- None: "none",
3168
- Pending: "pending",
3169
- Provisioning: "provisioning",
3170
- Running: "running",
3171
- Stopped: "stopped",
3172
- Failing: "failing",
3173
- Failed: "failed",
3174
- BillingSuspended: "billing_suspended"
3175
- });
3176
3175
  enumValues({
3177
3176
  Active: "active",
3178
3177
  Removed: "removed"
@@ -20432,44 +20431,78 @@ async function handleCloudCommand(command) {
20432
20431
  };
20433
20432
  }
20434
20433
  }
20435
- if (command.command === "daemon.update") {
20436
- const version = command.payload?.version ?? "latest";
20437
- if (!isManagedMode()) return {
20434
+ if (command.command === "support.bash") {
20435
+ const payload = command.payload;
20436
+ if (!payload.cmd) return {
20438
20437
  type: "COMMAND_ACK",
20439
20438
  commandId: command.commandId,
20440
20439
  status: "error",
20441
20440
  result: {
20442
- code: "NOT_MANAGED",
20443
- message: "daemon.update is only supported in managed mode"
20441
+ code: "MISSING_CMD",
20442
+ message: "No command provided"
20444
20443
  }
20445
20444
  };
20445
+ const workspacePath = Object.values(config.runtimes)[0]?.workspace ?? "~/.openclaw";
20446
20446
  try {
20447
- const { spawnUpgradeScript } = await import("./upgrade.js");
20448
- await spawnUpgradeScript(version);
20449
- logger$1.info({ version }, "Upgrade script spawned daemon will restart shortly");
20447
+ const { exec } = await import("child_process");
20448
+ const { promisify } = await import("util");
20449
+ const { stdout, stderr } = await promisify(exec)(payload.cmd, {
20450
+ cwd: workspacePath,
20451
+ timeout: 25e3,
20452
+ maxBuffer: 512 * 1024
20453
+ });
20450
20454
  return {
20451
20455
  type: "COMMAND_ACK",
20452
20456
  commandId: command.commandId,
20453
20457
  status: "ok",
20454
20458
  result: {
20455
- upgrading: true,
20456
- version
20459
+ stdout: stdout.trim(),
20460
+ stderr: stderr.trim()
20457
20461
  }
20458
20462
  };
20459
20463
  } catch (err) {
20460
- const message = err instanceof Error ? err.message : String(err);
20461
- logger$1.error({ err: message }, "Failed to spawn upgrade script");
20464
+ const execErr = err;
20462
20465
  return {
20463
20466
  type: "COMMAND_ACK",
20464
20467
  commandId: command.commandId,
20465
20468
  status: "error",
20466
20469
  result: {
20467
- code: "UPGRADE_FAILED",
20468
- message
20470
+ code: "EXEC_FAILED",
20471
+ stdout: execErr.stdout?.trim() ?? "",
20472
+ stderr: execErr.stderr?.trim() ?? "",
20473
+ message: execErr.message ?? String(err),
20474
+ exitCode: execErr.code
20469
20475
  }
20470
20476
  };
20471
20477
  }
20472
20478
  }
20479
+ if (command.command === "daemon.update") {
20480
+ const version = command.payload?.version ?? "latest";
20481
+ if (!isManagedMode()) return {
20482
+ type: "COMMAND_ACK",
20483
+ commandId: command.commandId,
20484
+ status: "error",
20485
+ result: {
20486
+ code: "NOT_MANAGED",
20487
+ message: "daemon.update is only supported in managed mode"
20488
+ }
20489
+ };
20490
+ setTimeout(() => {
20491
+ import("./upgrade.js").then(({ upgradeAndExit }) => upgradeAndExit(version)).catch((err) => {
20492
+ logger$1.error({ err: err instanceof Error ? err.message : String(err) }, "Upgrade failed");
20493
+ process.exit(1);
20494
+ });
20495
+ }, 500);
20496
+ return {
20497
+ type: "COMMAND_ACK",
20498
+ commandId: command.commandId,
20499
+ status: "ok",
20500
+ result: {
20501
+ upgrading: true,
20502
+ version
20503
+ }
20504
+ };
20505
+ }
20473
20506
  if (command.command === "integration.status") {
20474
20507
  const payload = command.payload;
20475
20508
  try {
package/dist/upgrade.js CHANGED
@@ -1,60 +1,44 @@
1
1
  import { n as logger } from "./logger.js";
2
- import { chmod, writeFile } from "node:fs/promises";
3
- import { spawn } from "node:child_process";
2
+ import { execFile } from "node:child_process";
3
+ import { promisify } from "node:util";
4
4
  //#region src/upgrade.ts
5
5
  /**
6
- * CLI upgrade — spawns a detached shell script to upgrade @alfe.ai/cli.
6
+ * CLI upgrade — runs npm install inline then exits.
7
7
  *
8
- * The daemon process IS the service being upgraded, so it cannot manage the
9
- * upgrade itself. Instead we:
10
- * 1. Write a bash script to /tmp
11
- * 2. Spawn it detached (survives parent death)
12
- * 3. The script stops the service, upgrades, re-runs setup, and restarts
8
+ * Systemd has Restart=always, so after the daemon exits the service
9
+ * restarts automatically with the new CLI version. No detached scripts,
10
+ * no race conditions.
13
11
  *
14
- * The daemon ACKs the COMMAND before the script runs, so the cloud gets
15
- * confirmation. After restart the daemon reconnects with the new version.
12
+ * Flow:
13
+ * 1. Daemon ACKs the COMMAND (caller handles this before calling upgrade)
14
+ * 2. npm install -g @alfe.ai/cli@{version} runs as a child process
15
+ * 3. Daemon exits with code 0
16
+ * 4. Systemd restarts the service → new version boots
16
17
  */
18
+ const execFileAsync = promisify(execFile);
17
19
  /**
18
- * Spawn a detached upgrade script that will:
19
- * 1. Wait briefly for the daemon to finish sending its ACK
20
- * 2. Stop the systemd service
21
- * 3. Install the target CLI version globally
22
- * 4. Re-run `alfe setup --managed` to regenerate the systemd unit
23
- * 5. Start the service again
24
- * 6. Clean up the script file
20
+ * Upgrade the CLI to a target version and exit.
21
+ * Systemd will restart the daemon with the new version.
25
22
  */
26
- async function spawnUpgradeScript(version) {
27
- const scriptPath = `/tmp/alfe-upgrade-${String(Date.now())}.sh`;
28
- await writeFile(scriptPath, `#!/bin/bash
29
- set -euo pipefail
30
- exec > /tmp/alfe-upgrade.log 2>&1
31
-
32
- # Wait for daemon to finish sending COMMAND_ACK
33
- sleep 2
34
-
35
- # Stop the gateway service
36
- systemctl stop alfe-gateway
37
-
38
- # Upgrade CLI to target version
39
- npm install -g @alfe.ai/cli@${version}
40
-
41
- # Restart — systemd ExecStart resolves the new binary automatically
42
- systemctl start alfe-gateway
43
-
44
- # Clean up
45
- rm -f "$0"
46
- `, "utf-8");
47
- await chmod(scriptPath, 493);
48
- const child = spawn("bash", [scriptPath], {
49
- detached: true,
50
- stdio: "ignore"
51
- });
52
- child.unref();
53
- logger.info({
54
- scriptPath,
55
- version,
56
- pid: child.pid
57
- }, "Spawned detached upgrade script");
23
+ async function upgradeAndExit(version) {
24
+ logger.info({ version }, "Upgrading CLI...");
25
+ try {
26
+ const { stdout, stderr } = await execFileAsync("npm", [
27
+ "install",
28
+ "-g",
29
+ `@alfe.ai/cli@${version}`
30
+ ], { timeout: 12e4 });
31
+ if (stdout) logger.debug({ stdout: stdout.trim() }, "npm install stdout");
32
+ if (stderr) logger.debug({ stderr: stderr.trim() }, "npm install stderr");
33
+ logger.info({ version }, "CLI upgraded — exiting for systemd restart");
34
+ } catch (err) {
35
+ const message = err instanceof Error ? err.message : String(err);
36
+ logger.error({
37
+ err: message,
38
+ version
39
+ }, "CLI upgrade failed");
40
+ }
41
+ process.exit(0);
58
42
  }
59
43
  //#endregion
60
- export { spawnUpgradeScript };
44
+ export { upgradeAndExit };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/gateway",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "description": "Alfe local gateway daemon — persistent control plane for agent integrations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,10 +22,10 @@
22
22
  "pino-roll": "^1.2.0",
23
23
  "smol-toml": ">=1.6.1",
24
24
  "ws": "^8.18.0",
25
- "@alfe.ai/ai-proxy-local": "^0.0.3",
26
- "@alfe.ai/config": "^0.0.3",
27
- "@alfe.ai/doctor": "^0.0.3",
28
- "@alfe.ai/integrations": "^0.0.6"
25
+ "@alfe.ai/ai-proxy-local": "^0.0.4",
26
+ "@alfe.ai/config": "^0.0.4",
27
+ "@alfe.ai/doctor": "^0.0.4",
28
+ "@alfe.ai/integrations": "^0.0.7"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/ws": "^8.5.13",