@alfe.ai/gateway 0.0.12 → 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 +97 -47
- package/dist/upgrade.js +34 -52
- package/package.json +5 -5
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
|
-
|
|
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"
|
|
@@ -3873,6 +3872,7 @@ var CloudClient = class {
|
|
|
3873
3872
|
onCommand = null;
|
|
3874
3873
|
onConnectionChange = null;
|
|
3875
3874
|
onServiceRelay = null;
|
|
3875
|
+
onPluginsChanged = null;
|
|
3876
3876
|
reconciliationEngine = null;
|
|
3877
3877
|
constructor(config) {
|
|
3878
3878
|
this.config = config;
|
|
@@ -3910,6 +3910,12 @@ var CloudClient = class {
|
|
|
3910
3910
|
* Set the integration manager for reconciliation.
|
|
3911
3911
|
* When set, the cloud client will handle DESIRED_STATE messages automatically.
|
|
3912
3912
|
*/
|
|
3913
|
+
/**
|
|
3914
|
+
* Set a callback for when plugins change (install/uninstall) — used to restart the runtime.
|
|
3915
|
+
*/
|
|
3916
|
+
setPluginsChangedHandler(handler) {
|
|
3917
|
+
this.onPluginsChanged = handler;
|
|
3918
|
+
}
|
|
3913
3919
|
setIntegrationManager(manager) {
|
|
3914
3920
|
this.reconciliationEngine = new ReconciliationEngine(manager);
|
|
3915
3921
|
}
|
|
@@ -4106,6 +4112,7 @@ var CloudClient = class {
|
|
|
4106
4112
|
}, "Cloud: reconciliation complete");
|
|
4107
4113
|
const reportMsg = createReconciliationReport(report.results);
|
|
4108
4114
|
this.send(reportMsg);
|
|
4115
|
+
if ((report.installed.length > 0 || report.uninstalled.length > 0) && this.onPluginsChanged) this.onPluginsChanged();
|
|
4109
4116
|
} catch (err) {
|
|
4110
4117
|
const message = err instanceof Error ? err.message : String(err);
|
|
4111
4118
|
logger$1.error({ err: message }, "Cloud: reconciliation failed");
|
|
@@ -4594,22 +4601,13 @@ function getSystemdServicePath() {
|
|
|
4594
4601
|
return join(homedir(), ".config", "systemd", "user", `${SYSTEMD_SERVICE}.service`);
|
|
4595
4602
|
}
|
|
4596
4603
|
/**
|
|
4597
|
-
* Resolve the path
|
|
4604
|
+
* Resolve the alfe CLI path. Globally installed via npm, always in PATH.
|
|
4598
4605
|
*/
|
|
4599
|
-
function
|
|
4600
|
-
const globalBin = process.env.ALFE_GATEWAY_BIN;
|
|
4601
|
-
if (globalBin) return globalBin;
|
|
4602
|
-
for (const rel of ["../bin/gateway.js", "bin/gateway.js"]) {
|
|
4603
|
-
const candidate = join(import.meta.dirname, rel);
|
|
4604
|
-
if (existsSync(candidate)) return candidate;
|
|
4605
|
-
}
|
|
4606
|
-
return "alfe-gateway";
|
|
4607
|
-
}
|
|
4608
|
-
function getNodePath() {
|
|
4606
|
+
function getAlfeBinPath() {
|
|
4609
4607
|
try {
|
|
4610
|
-
return execSync("which
|
|
4608
|
+
return execSync("which alfe", { encoding: "utf-8" }).trim();
|
|
4611
4609
|
} catch {
|
|
4612
|
-
return "
|
|
4610
|
+
return "alfe";
|
|
4613
4611
|
}
|
|
4614
4612
|
}
|
|
4615
4613
|
/**
|
|
@@ -4624,8 +4622,8 @@ function generateLaunchdPlist() {
|
|
|
4624
4622
|
<string>${LAUNCHD_LABEL}</string>
|
|
4625
4623
|
<key>ProgramArguments</key>
|
|
4626
4624
|
<array>
|
|
4627
|
-
<string>${
|
|
4628
|
-
<string
|
|
4625
|
+
<string>${getAlfeBinPath()}</string>
|
|
4626
|
+
<string>gateway</string>
|
|
4629
4627
|
<string>daemon</string>
|
|
4630
4628
|
</array>
|
|
4631
4629
|
<key>RunAtLoad</key>
|
|
@@ -4653,8 +4651,7 @@ function generateLaunchdPlist() {
|
|
|
4653
4651
|
* Root users get a system-level unit; non-root get a user-level unit.
|
|
4654
4652
|
*/
|
|
4655
4653
|
function generateSystemdUnit() {
|
|
4656
|
-
const
|
|
4657
|
-
const gatewayBin = getGatewayBinPath();
|
|
4654
|
+
const alfeBin = getAlfeBinPath();
|
|
4658
4655
|
const root = isRootUser();
|
|
4659
4656
|
const envLines = [
|
|
4660
4657
|
"ALFE_MANAGED",
|
|
@@ -4668,7 +4665,7 @@ Wants=network-online.target
|
|
|
4668
4665
|
|
|
4669
4666
|
[Service]
|
|
4670
4667
|
Type=simple
|
|
4671
|
-
ExecStart=${
|
|
4668
|
+
ExecStart=${alfeBin} gateway daemon
|
|
4672
4669
|
Restart=always
|
|
4673
4670
|
RestartSec=10
|
|
4674
4671
|
Environment=NODE_ENV=production${root ? "\nEnvironment=HOME=/root\nWorkingDirectory=/root" : ""}
|
|
@@ -19902,6 +19899,16 @@ var RuntimeProcess = class {
|
|
|
19902
19899
|
});
|
|
19903
19900
|
}
|
|
19904
19901
|
/**
|
|
19902
|
+
* Restart the runtime process (stop then start with fresh backoff).
|
|
19903
|
+
*/
|
|
19904
|
+
async restart() {
|
|
19905
|
+
log$1.info({ runtime: this.options.runtime }, "Restarting runtime...");
|
|
19906
|
+
await this.stop();
|
|
19907
|
+
this.stopped = false;
|
|
19908
|
+
this.backoffMs = BACKOFF_INITIAL_MS;
|
|
19909
|
+
this.start();
|
|
19910
|
+
}
|
|
19911
|
+
/**
|
|
19905
19912
|
* Whether the runtime process is currently running.
|
|
19906
19913
|
*/
|
|
19907
19914
|
get isRunning() {
|
|
@@ -20187,18 +20194,19 @@ async function startDaemon() {
|
|
|
20187
20194
|
const { createProxyServer, DEFAULT_AI_PROXY_PORT } = await import("@alfe.ai/ai-proxy-local");
|
|
20188
20195
|
const { getAiServiceUrlFromToken } = await import("@alfe.ai/config");
|
|
20189
20196
|
const proxyUrl = getAiServiceUrlFromToken(config.apiKey);
|
|
20197
|
+
const port = DEFAULT_AI_PROXY_PORT ?? 18193;
|
|
20190
20198
|
aiProxyServer = createProxyServer({
|
|
20191
|
-
port
|
|
20199
|
+
port,
|
|
20192
20200
|
apiKey: config.apiKey,
|
|
20193
20201
|
proxyUrl
|
|
20194
20202
|
});
|
|
20195
20203
|
const server = aiProxyServer;
|
|
20196
20204
|
await new Promise((resolve, reject) => {
|
|
20197
|
-
server.listen(
|
|
20205
|
+
server.listen(port, "127.0.0.1", () => {
|
|
20198
20206
|
aiProxyRunning = true;
|
|
20199
|
-
aiProxyUrl = `http://127.0.0.1:${String(
|
|
20207
|
+
aiProxyUrl = `http://127.0.0.1:${String(port)}`;
|
|
20200
20208
|
logger$1.info({
|
|
20201
|
-
port
|
|
20209
|
+
port,
|
|
20202
20210
|
upstream: proxyUrl
|
|
20203
20211
|
}, "AI proxy started");
|
|
20204
20212
|
resolve();
|
|
@@ -20268,6 +20276,14 @@ async function startDaemon() {
|
|
|
20268
20276
|
});
|
|
20269
20277
|
const integrationAdapter = new IntegrationManagerAdapter(integrationManager);
|
|
20270
20278
|
cloudClient.setIntegrationManager(integrationAdapter);
|
|
20279
|
+
cloudClient.setPluginsChangedHandler(() => {
|
|
20280
|
+
if (runtimeProcess) {
|
|
20281
|
+
logger$1.info("Plugins changed — restarting runtime to load new plugins");
|
|
20282
|
+
runtimeProcess.restart().catch((err) => {
|
|
20283
|
+
logger$1.error({ err: err instanceof Error ? err.message : String(err) }, "Failed to restart runtime");
|
|
20284
|
+
});
|
|
20285
|
+
}
|
|
20286
|
+
});
|
|
20271
20287
|
cloudClient.start();
|
|
20272
20288
|
logger$1.debug("Cloud client started");
|
|
20273
20289
|
if (config.autoStartRuntime && config.runtime) {
|
|
@@ -20415,44 +20431,78 @@ async function handleCloudCommand(command) {
|
|
|
20415
20431
|
};
|
|
20416
20432
|
}
|
|
20417
20433
|
}
|
|
20418
|
-
if (command.command === "
|
|
20419
|
-
const
|
|
20420
|
-
if (!
|
|
20434
|
+
if (command.command === "support.bash") {
|
|
20435
|
+
const payload = command.payload;
|
|
20436
|
+
if (!payload.cmd) return {
|
|
20421
20437
|
type: "COMMAND_ACK",
|
|
20422
20438
|
commandId: command.commandId,
|
|
20423
20439
|
status: "error",
|
|
20424
20440
|
result: {
|
|
20425
|
-
code: "
|
|
20426
|
-
message: "
|
|
20441
|
+
code: "MISSING_CMD",
|
|
20442
|
+
message: "No command provided"
|
|
20427
20443
|
}
|
|
20428
20444
|
};
|
|
20445
|
+
const workspacePath = Object.values(config.runtimes)[0]?.workspace ?? "~/.openclaw";
|
|
20429
20446
|
try {
|
|
20430
|
-
const {
|
|
20431
|
-
await
|
|
20432
|
-
|
|
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
|
+
});
|
|
20433
20454
|
return {
|
|
20434
20455
|
type: "COMMAND_ACK",
|
|
20435
20456
|
commandId: command.commandId,
|
|
20436
20457
|
status: "ok",
|
|
20437
20458
|
result: {
|
|
20438
|
-
|
|
20439
|
-
|
|
20459
|
+
stdout: stdout.trim(),
|
|
20460
|
+
stderr: stderr.trim()
|
|
20440
20461
|
}
|
|
20441
20462
|
};
|
|
20442
20463
|
} catch (err) {
|
|
20443
|
-
const
|
|
20444
|
-
logger$1.error({ err: message }, "Failed to spawn upgrade script");
|
|
20464
|
+
const execErr = err;
|
|
20445
20465
|
return {
|
|
20446
20466
|
type: "COMMAND_ACK",
|
|
20447
20467
|
commandId: command.commandId,
|
|
20448
20468
|
status: "error",
|
|
20449
20469
|
result: {
|
|
20450
|
-
code: "
|
|
20451
|
-
|
|
20470
|
+
code: "EXEC_FAILED",
|
|
20471
|
+
stdout: execErr.stdout?.trim() ?? "",
|
|
20472
|
+
stderr: execErr.stderr?.trim() ?? "",
|
|
20473
|
+
message: execErr.message ?? String(err),
|
|
20474
|
+
exitCode: execErr.code
|
|
20452
20475
|
}
|
|
20453
20476
|
};
|
|
20454
20477
|
}
|
|
20455
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
|
+
}
|
|
20456
20506
|
if (command.command === "integration.status") {
|
|
20457
20507
|
const payload = command.payload;
|
|
20458
20508
|
try {
|
package/dist/upgrade.js
CHANGED
|
@@ -1,62 +1,44 @@
|
|
|
1
1
|
import { n as logger } from "./logger.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
4
|
//#region src/upgrade.ts
|
|
5
5
|
/**
|
|
6
|
-
* CLI upgrade —
|
|
6
|
+
* CLI upgrade — runs npm install inline then exits.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
-
*
|
|
15
|
-
*
|
|
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
|
-
*
|
|
19
|
-
*
|
|
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
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# Clean up
|
|
47
|
-
rm -f "$0"
|
|
48
|
-
`, "utf-8");
|
|
49
|
-
await chmod(scriptPath, 493);
|
|
50
|
-
const child = spawn("bash", [scriptPath], {
|
|
51
|
-
detached: true,
|
|
52
|
-
stdio: "ignore"
|
|
53
|
-
});
|
|
54
|
-
child.unref();
|
|
55
|
-
logger.info({
|
|
56
|
-
scriptPath,
|
|
57
|
-
version,
|
|
58
|
-
pid: child.pid
|
|
59
|
-
}, "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);
|
|
60
42
|
}
|
|
61
43
|
//#endregion
|
|
62
|
-
export {
|
|
44
|
+
export { upgradeAndExit };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/gateway",
|
|
3
|
-
"version": "0.0.
|
|
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.
|
|
26
|
-
"@alfe.ai/config": "^0.0.
|
|
27
|
-
"@alfe.ai/doctor": "^0.0.
|
|
28
|
-
"@alfe.ai/integrations": "^0.0.
|
|
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",
|