@alfe.ai/gateway 0.0.9 → 0.0.10
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 +68 -48
- package/dist/logger.js +46 -0
- package/dist/src/index.js +2 -1
- package/dist/upgrade.js +62 -0
- package/package.json +2 -2
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,22 @@ 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
|
+
* Prefers ALFE_CLI_VERSION env var (set by the CLI entry point),
|
|
20107
|
+
* falls back to resolving @alfe.ai/cli/package.json directly.
|
|
20108
|
+
*/
|
|
20109
|
+
async function getCliVersion() {
|
|
20110
|
+
if (process.env.ALFE_CLI_VERSION) return process.env.ALFE_CLI_VERSION;
|
|
20111
|
+
try {
|
|
20112
|
+
const raw = await readFile(createRequire(import.meta.url).resolve("@alfe.ai/cli/package.json"), "utf-8");
|
|
20113
|
+
return JSON.parse(raw).version;
|
|
20114
|
+
} catch {
|
|
20115
|
+
logger$1.debug("Could not resolve @alfe.ai/cli version — daemon may not be running from CLI");
|
|
20116
|
+
return;
|
|
20117
|
+
}
|
|
20118
|
+
}
|
|
20140
20119
|
/**
|
|
20141
20120
|
* Flush pino's async transport and exit.
|
|
20142
20121
|
* process.exit() can drop buffered log lines — this ensures they're written first.
|
|
@@ -20229,6 +20208,8 @@ async function startDaemon() {
|
|
|
20229
20208
|
}, "Failed to start IPC server");
|
|
20230
20209
|
await flushAndExit(1);
|
|
20231
20210
|
}
|
|
20211
|
+
resolvedCliVersion = await getCliVersion();
|
|
20212
|
+
logger$1.info({ cliVersion: resolvedCliVersion }, "Resolved CLI version");
|
|
20232
20213
|
logger$1.debug({
|
|
20233
20214
|
wsUrl: config.gatewayWsUrl,
|
|
20234
20215
|
agentId: config.agentId
|
|
@@ -20236,7 +20217,8 @@ async function startDaemon() {
|
|
|
20236
20217
|
cloudClient = new CloudClient({
|
|
20237
20218
|
wsUrl: config.gatewayWsUrl,
|
|
20238
20219
|
apiKey: config.apiKey,
|
|
20239
|
-
agentId: config.agentId
|
|
20220
|
+
agentId: config.agentId,
|
|
20221
|
+
cliVersion: resolvedCliVersion
|
|
20240
20222
|
});
|
|
20241
20223
|
cloudClient.setCommandHandler(handleCloudCommand);
|
|
20242
20224
|
cloudClient.setConnectionChangeHandler((connected) => {
|
|
@@ -20411,6 +20393,44 @@ async function handleCloudCommand(command) {
|
|
|
20411
20393
|
};
|
|
20412
20394
|
}
|
|
20413
20395
|
}
|
|
20396
|
+
if (command.command === "daemon.update") {
|
|
20397
|
+
const version = command.payload?.version ?? "latest";
|
|
20398
|
+
if (!isManagedMode()) return {
|
|
20399
|
+
type: "COMMAND_ACK",
|
|
20400
|
+
commandId: command.commandId,
|
|
20401
|
+
status: "error",
|
|
20402
|
+
result: {
|
|
20403
|
+
code: "NOT_MANAGED",
|
|
20404
|
+
message: "daemon.update is only supported in managed mode"
|
|
20405
|
+
}
|
|
20406
|
+
};
|
|
20407
|
+
try {
|
|
20408
|
+
const { spawnUpgradeScript } = await import("./upgrade.js");
|
|
20409
|
+
await spawnUpgradeScript(version);
|
|
20410
|
+
logger$1.info({ version }, "Upgrade script spawned — daemon will restart shortly");
|
|
20411
|
+
return {
|
|
20412
|
+
type: "COMMAND_ACK",
|
|
20413
|
+
commandId: command.commandId,
|
|
20414
|
+
status: "ok",
|
|
20415
|
+
result: {
|
|
20416
|
+
upgrading: true,
|
|
20417
|
+
version
|
|
20418
|
+
}
|
|
20419
|
+
};
|
|
20420
|
+
} catch (err) {
|
|
20421
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
20422
|
+
logger$1.error({ err: message }, "Failed to spawn upgrade script");
|
|
20423
|
+
return {
|
|
20424
|
+
type: "COMMAND_ACK",
|
|
20425
|
+
commandId: command.commandId,
|
|
20426
|
+
status: "error",
|
|
20427
|
+
result: {
|
|
20428
|
+
code: "UPGRADE_FAILED",
|
|
20429
|
+
message
|
|
20430
|
+
}
|
|
20431
|
+
};
|
|
20432
|
+
}
|
|
20433
|
+
}
|
|
20414
20434
|
if (command.command === "integration.status") {
|
|
20415
20435
|
const payload = command.payload;
|
|
20416
20436
|
try {
|
|
@@ -20665,4 +20685,4 @@ function formatDuration(ms) {
|
|
|
20665
20685
|
return `${String(Math.round(seconds / 3600))}h`;
|
|
20666
20686
|
}
|
|
20667
20687
|
//#endregion
|
|
20668
|
-
export {
|
|
20688
|
+
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.10",
|
|
4
4
|
"description": "Alfe local gateway daemon — persistent control plane for agent integrations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -30,7 +30,7 @@
|
|
|
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",
|