@alfe.ai/gateway 0.0.9 → 0.0.11
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/bin/gateway.js +2 -1
- package/dist/health.js +82 -48
- package/dist/logger.js +46 -0
- package/dist/src/index.js +2 -1
- package/dist/upgrade.js +62 -0
- package/package.json +3 -3
package/dist/bin/gateway.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as installService, c as uninstallService, f as SOCKET_PATH,
|
|
2
|
+
import { a as installService, c as uninstallService, f as SOCKET_PATH, i as checkExistingDaemon, n as queryDaemonHealth, r as startDaemon, s as stopExistingDaemon, t as formatHealthReport } from "../health.js";
|
|
3
|
+
import { t as LOG_FILE } from "../logger.js";
|
|
3
4
|
import { spawn } from "node:child_process";
|
|
4
5
|
//#region bin/gateway.ts
|
|
5
6
|
/**
|
package/dist/health.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { n as logger$1 } from "./logger.js";
|
|
1
2
|
import { createRequire } from "node:module";
|
|
2
3
|
import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
import { homedir } from "node:os";
|
|
5
6
|
import pino from "pino";
|
|
6
|
-
import { chmodSync, existsSync,
|
|
7
|
+
import { chmodSync, existsSync, unlinkSync } from "node:fs";
|
|
7
8
|
import { getEndpointFromToken, readConfig } from "@alfe.ai/config";
|
|
8
9
|
import crypto from "crypto";
|
|
9
10
|
import { parse } from "smol-toml";
|
|
@@ -53,47 +54,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
53
54
|
}) : target, mod));
|
|
54
55
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
55
56
|
//#endregion
|
|
56
|
-
//#region src/logger.ts
|
|
57
|
-
/**
|
|
58
|
-
* Daemon logger — pino with rolling file output.
|
|
59
|
-
*
|
|
60
|
-
* Writes to ~/.alfe/logs/gateway.log with 10MB rotation, keep 5 files.
|
|
61
|
-
* In managed mode (ECS Fargate): always stdout for CloudWatch capture.
|
|
62
|
-
* In development: stdout.
|
|
63
|
-
*/
|
|
64
|
-
const LOG_DIR = join(homedir(), ".alfe", "logs");
|
|
65
|
-
const LOG_FILE = join(LOG_DIR, "gateway.log");
|
|
66
|
-
const isDev = process.env.NODE_ENV !== "production";
|
|
67
|
-
const isManaged = process.env.ALFE_MANAGED === "true";
|
|
68
|
-
if (!isManaged) try {
|
|
69
|
-
mkdirSync(LOG_DIR, { recursive: true });
|
|
70
|
-
} catch {}
|
|
71
|
-
/**
|
|
72
|
-
* Create the daemon logger.
|
|
73
|
-
* Managed mode (ECS): JSON to stdout (CloudWatch captures via awslogs driver).
|
|
74
|
-
* Development: JSON to stdout.
|
|
75
|
-
* Production (local): JSON to rolling file.
|
|
76
|
-
*/
|
|
77
|
-
function createLogger$1() {
|
|
78
|
-
if (isDev || isManaged) return pino({
|
|
79
|
-
level: process.env.LOG_LEVEL ?? (isDev ? "debug" : "info"),
|
|
80
|
-
transport: {
|
|
81
|
-
target: "pino/file",
|
|
82
|
-
options: { destination: 1 }
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
const transport = pino.transport({
|
|
86
|
-
target: "pino-roll",
|
|
87
|
-
options: {
|
|
88
|
-
file: LOG_FILE,
|
|
89
|
-
size: "10m",
|
|
90
|
-
limit: { count: 5 }
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
return pino({ level: process.env.LOG_LEVEL ?? "info" }, transport);
|
|
94
|
-
}
|
|
95
|
-
const logger$1 = createLogger$1();
|
|
96
|
-
//#endregion
|
|
97
57
|
//#region ../../packages-internal/ids/dist/prefixes.js
|
|
98
58
|
const ID_PREFIXES = {
|
|
99
59
|
agent: "agt",
|
|
@@ -3639,13 +3599,15 @@ function createServiceRegister(agentId, capabilities = [
|
|
|
3639
3599
|
"integrations",
|
|
3640
3600
|
"lifecycle",
|
|
3641
3601
|
"health"
|
|
3642
|
-
]) {
|
|
3643
|
-
|
|
3602
|
+
], cliVersion) {
|
|
3603
|
+
const msg = {
|
|
3644
3604
|
type: "SERVICE_REGISTER",
|
|
3645
3605
|
serviceId: `gateway-daemon-${agentId}`,
|
|
3646
3606
|
agentIds: [agentId],
|
|
3647
3607
|
capabilities
|
|
3648
3608
|
};
|
|
3609
|
+
if (cliVersion) msg.cliVersion = cliVersion;
|
|
3610
|
+
return msg;
|
|
3649
3611
|
}
|
|
3650
3612
|
/**
|
|
3651
3613
|
* Create an IPC success response.
|
|
@@ -4032,10 +3994,11 @@ var CloudClient = class {
|
|
|
4032
3994
|
});
|
|
4033
3995
|
}
|
|
4034
3996
|
sendRegister() {
|
|
4035
|
-
const msg = createServiceRegister(this.config.agentId);
|
|
3997
|
+
const msg = createServiceRegister(this.config.agentId, void 0, this.config.cliVersion);
|
|
4036
3998
|
logger$1.debug({
|
|
4037
3999
|
serviceId: msg.serviceId,
|
|
4038
|
-
agentIds: msg.agentIds
|
|
4000
|
+
agentIds: msg.agentIds,
|
|
4001
|
+
cliVersion: msg.cliVersion
|
|
4039
4002
|
}, "Cloud: sending SERVICE_REGISTER");
|
|
4040
4003
|
this.send(msg);
|
|
4041
4004
|
logger$1.info({ serviceId: msg.serviceId }, "Sent SERVICE_REGISTER");
|
|
@@ -20137,6 +20100,36 @@ let aiProxyUrl = null;
|
|
|
20137
20100
|
let aiProxyRunning = false;
|
|
20138
20101
|
let cloudConnected = false;
|
|
20139
20102
|
let shuttingDown = false;
|
|
20103
|
+
let resolvedCliVersion;
|
|
20104
|
+
/**
|
|
20105
|
+
* Resolve the installed @alfe.ai/cli version.
|
|
20106
|
+
*
|
|
20107
|
+
* Strategy (in order):
|
|
20108
|
+
* 1. ALFE_CLI_VERSION env var (set by CLI entry point when run directly)
|
|
20109
|
+
* 2. Walk up from this file to find @alfe.ai/cli/package.json
|
|
20110
|
+
* (works when systemd runs the gateway binary directly, since
|
|
20111
|
+
* the path is .../cli/node_modules/@alfe.ai/gateway/dist/...)
|
|
20112
|
+
*/
|
|
20113
|
+
async function getCliVersion() {
|
|
20114
|
+
if (process.env.ALFE_CLI_VERSION) return process.env.ALFE_CLI_VERSION;
|
|
20115
|
+
try {
|
|
20116
|
+
const { fileURLToPath } = await import("node:url");
|
|
20117
|
+
const { dirname } = await import("node:path");
|
|
20118
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
20119
|
+
for (let i = 0; i < 10; i++) {
|
|
20120
|
+
const candidate = join(dir, "package.json");
|
|
20121
|
+
try {
|
|
20122
|
+
const raw = await readFile(candidate, "utf-8");
|
|
20123
|
+
const pkg = JSON.parse(raw);
|
|
20124
|
+
if (pkg.name === "@alfe.ai/cli") return pkg.version;
|
|
20125
|
+
} catch {}
|
|
20126
|
+
const parent = dirname(dir);
|
|
20127
|
+
if (parent === dir) break;
|
|
20128
|
+
dir = parent;
|
|
20129
|
+
}
|
|
20130
|
+
} catch {}
|
|
20131
|
+
logger$1.debug("Could not resolve @alfe.ai/cli version");
|
|
20132
|
+
}
|
|
20140
20133
|
/**
|
|
20141
20134
|
* Flush pino's async transport and exit.
|
|
20142
20135
|
* process.exit() can drop buffered log lines — this ensures they're written first.
|
|
@@ -20229,6 +20222,8 @@ async function startDaemon() {
|
|
|
20229
20222
|
}, "Failed to start IPC server");
|
|
20230
20223
|
await flushAndExit(1);
|
|
20231
20224
|
}
|
|
20225
|
+
resolvedCliVersion = await getCliVersion();
|
|
20226
|
+
logger$1.info({ cliVersion: resolvedCliVersion }, "Resolved CLI version");
|
|
20232
20227
|
logger$1.debug({
|
|
20233
20228
|
wsUrl: config.gatewayWsUrl,
|
|
20234
20229
|
agentId: config.agentId
|
|
@@ -20236,7 +20231,8 @@ async function startDaemon() {
|
|
|
20236
20231
|
cloudClient = new CloudClient({
|
|
20237
20232
|
wsUrl: config.gatewayWsUrl,
|
|
20238
20233
|
apiKey: config.apiKey,
|
|
20239
|
-
agentId: config.agentId
|
|
20234
|
+
agentId: config.agentId,
|
|
20235
|
+
cliVersion: resolvedCliVersion
|
|
20240
20236
|
});
|
|
20241
20237
|
cloudClient.setCommandHandler(handleCloudCommand);
|
|
20242
20238
|
cloudClient.setConnectionChangeHandler((connected) => {
|
|
@@ -20411,6 +20407,44 @@ async function handleCloudCommand(command) {
|
|
|
20411
20407
|
};
|
|
20412
20408
|
}
|
|
20413
20409
|
}
|
|
20410
|
+
if (command.command === "daemon.update") {
|
|
20411
|
+
const version = command.payload?.version ?? "latest";
|
|
20412
|
+
if (!isManagedMode()) return {
|
|
20413
|
+
type: "COMMAND_ACK",
|
|
20414
|
+
commandId: command.commandId,
|
|
20415
|
+
status: "error",
|
|
20416
|
+
result: {
|
|
20417
|
+
code: "NOT_MANAGED",
|
|
20418
|
+
message: "daemon.update is only supported in managed mode"
|
|
20419
|
+
}
|
|
20420
|
+
};
|
|
20421
|
+
try {
|
|
20422
|
+
const { spawnUpgradeScript } = await import("./upgrade.js");
|
|
20423
|
+
await spawnUpgradeScript(version);
|
|
20424
|
+
logger$1.info({ version }, "Upgrade script spawned — daemon will restart shortly");
|
|
20425
|
+
return {
|
|
20426
|
+
type: "COMMAND_ACK",
|
|
20427
|
+
commandId: command.commandId,
|
|
20428
|
+
status: "ok",
|
|
20429
|
+
result: {
|
|
20430
|
+
upgrading: true,
|
|
20431
|
+
version
|
|
20432
|
+
}
|
|
20433
|
+
};
|
|
20434
|
+
} catch (err) {
|
|
20435
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
20436
|
+
logger$1.error({ err: message }, "Failed to spawn upgrade script");
|
|
20437
|
+
return {
|
|
20438
|
+
type: "COMMAND_ACK",
|
|
20439
|
+
commandId: command.commandId,
|
|
20440
|
+
status: "error",
|
|
20441
|
+
result: {
|
|
20442
|
+
code: "UPGRADE_FAILED",
|
|
20443
|
+
message
|
|
20444
|
+
}
|
|
20445
|
+
};
|
|
20446
|
+
}
|
|
20447
|
+
}
|
|
20414
20448
|
if (command.command === "integration.status") {
|
|
20415
20449
|
const payload = command.payload;
|
|
20416
20450
|
try {
|
|
@@ -20665,4 +20699,4 @@ function formatDuration(ms) {
|
|
|
20665
20699
|
return `${String(Math.round(seconds / 3600))}h`;
|
|
20666
20700
|
}
|
|
20667
20701
|
//#endregion
|
|
20668
|
-
export {
|
|
20702
|
+
export { installService as a, uninstallService as c, PID_PATH as d, SOCKET_PATH as f, resolveAgentIdentity as h, checkExistingDaemon as i, PROTOCOL_VERSION as l, loadDaemonConfig as m, queryDaemonHealth as n, startService as o, fetchAgentConfig as p, startDaemon as r, stopExistingDaemon as s, formatHealthReport as t, ALFE_DIR as u };
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import pino from "pino";
|
|
4
|
+
import { mkdirSync } from "node:fs";
|
|
5
|
+
//#region src/logger.ts
|
|
6
|
+
/**
|
|
7
|
+
* Daemon logger — pino with rolling file output.
|
|
8
|
+
*
|
|
9
|
+
* Writes to ~/.alfe/logs/gateway.log with 10MB rotation, keep 5 files.
|
|
10
|
+
* In managed mode (ECS Fargate): always stdout for CloudWatch capture.
|
|
11
|
+
* In development: stdout.
|
|
12
|
+
*/
|
|
13
|
+
const LOG_DIR = join(homedir(), ".alfe", "logs");
|
|
14
|
+
const LOG_FILE = join(LOG_DIR, "gateway.log");
|
|
15
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
16
|
+
const isManaged = process.env.ALFE_MANAGED === "true";
|
|
17
|
+
if (!isManaged) try {
|
|
18
|
+
mkdirSync(LOG_DIR, { recursive: true });
|
|
19
|
+
} catch {}
|
|
20
|
+
/**
|
|
21
|
+
* Create the daemon logger.
|
|
22
|
+
* Managed mode (ECS): JSON to stdout (CloudWatch captures via awslogs driver).
|
|
23
|
+
* Development: JSON to stdout.
|
|
24
|
+
* Production (local): JSON to rolling file.
|
|
25
|
+
*/
|
|
26
|
+
function createLogger() {
|
|
27
|
+
if (isDev || isManaged) return pino({
|
|
28
|
+
level: process.env.LOG_LEVEL ?? (isDev ? "debug" : "info"),
|
|
29
|
+
transport: {
|
|
30
|
+
target: "pino/file",
|
|
31
|
+
options: { destination: 1 }
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const transport = pino.transport({
|
|
35
|
+
target: "pino-roll",
|
|
36
|
+
options: {
|
|
37
|
+
file: LOG_FILE,
|
|
38
|
+
size: "10m",
|
|
39
|
+
limit: { count: 5 }
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
return pino({ level: process.env.LOG_LEVEL ?? "info" }, transport);
|
|
43
|
+
}
|
|
44
|
+
const logger = createLogger();
|
|
45
|
+
//#endregion
|
|
46
|
+
export { logger as n, LOG_FILE as t };
|
package/dist/src/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as installService, c as uninstallService, d as PID_PATH, f as SOCKET_PATH, h as resolveAgentIdentity, i as checkExistingDaemon, l as PROTOCOL_VERSION, m as loadDaemonConfig, n as queryDaemonHealth, o as startService, p as fetchAgentConfig, r as startDaemon, s as stopExistingDaemon, t as formatHealthReport, u as ALFE_DIR } from "../health.js";
|
|
2
|
+
import { n as logger } from "../logger.js";
|
|
2
3
|
export { ALFE_DIR, PID_PATH, PROTOCOL_VERSION, SOCKET_PATH, checkExistingDaemon, fetchAgentConfig, formatHealthReport, installService, loadDaemonConfig, logger, queryDaemonHealth, resolveAgentIdentity, startDaemon, startService, stopExistingDaemon, uninstallService };
|
package/dist/upgrade.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { n as logger } from "./logger.js";
|
|
2
|
+
import { chmod, writeFile } from "node:fs/promises";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
//#region src/upgrade.ts
|
|
5
|
+
/**
|
|
6
|
+
* CLI upgrade — spawns a detached shell script to upgrade @alfe.ai/cli.
|
|
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
|
|
13
|
+
*
|
|
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.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
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
|
|
25
|
+
*/
|
|
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
|
+
|
|
31
|
+
# Wait for daemon to finish sending COMMAND_ACK
|
|
32
|
+
sleep 2
|
|
33
|
+
|
|
34
|
+
# Stop the gateway service
|
|
35
|
+
systemctl stop alfe-gateway
|
|
36
|
+
|
|
37
|
+
# Upgrade CLI to target version
|
|
38
|
+
npm install -g @alfe.ai/cli@${version}
|
|
39
|
+
|
|
40
|
+
# Re-run setup to regenerate systemd unit with new binary path
|
|
41
|
+
alfe setup --managed
|
|
42
|
+
|
|
43
|
+
# Start the service — daemon will reconnect with new version
|
|
44
|
+
systemctl start alfe-gateway
|
|
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");
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
export { spawnUpgradeScript };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/gateway",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "Alfe local gateway daemon — persistent control plane for agent integrations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
"@alfe.ai/ai-proxy-local": "^0.0.2",
|
|
26
26
|
"@alfe.ai/config": "^0.0.2",
|
|
27
27
|
"@alfe.ai/doctor": "^0.0.2",
|
|
28
|
-
"@alfe.ai/integrations": "^0.0.
|
|
28
|
+
"@alfe.ai/integrations": "^0.0.4"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/ws": "^8.5.13",
|
|
32
32
|
"tsx": "^4.19.0",
|
|
33
|
-
"@alfe/api-client": "0.1.
|
|
33
|
+
"@alfe/api-client": "0.1.1",
|
|
34
34
|
"@alfe/ids": "0.1.0"
|
|
35
35
|
},
|
|
36
36
|
"license": "UNLICENSED",
|