@alfe.ai/gateway 0.0.1
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/README.md +163 -0
- package/dist/bin/gateway.d.ts +1 -0
- package/dist/bin/gateway.js +125 -0
- package/dist/health.js +1846 -0
- package/dist/src/index.d.ts +164 -0
- package/dist/src/index.js +2 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# @alfe.ai/gateway
|
|
2
|
+
|
|
3
|
+
Local gateway daemon for Alfe — the always-on control plane for agent integrations.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The gateway daemon is a standalone Node.js process that:
|
|
8
|
+
|
|
9
|
+
- Maintains a persistent WebSocket connection to Alfe cloud (`@alfe/gateway-service` on Fly.io)
|
|
10
|
+
- Exposes a Unix socket at `~/.alfe/gateway.sock` for local plugin IPC
|
|
11
|
+
- Translates between the cloud protocol and local IPC protocol
|
|
12
|
+
- Survives OpenClaw restarts — it's the always-on bridge between cloud and agent
|
|
13
|
+
- Queues commands when plugins are offline (5min TTL)
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Alfe Cloud ←—— control WS ——→ @alfe.ai/gateway (daemon, always running)
|
|
19
|
+
↕ IPC (~/.alfe/gateway.sock)
|
|
20
|
+
@alfe.ai/openclaw (OpenClaw plugin, Phase 2)
|
|
21
|
+
↕
|
|
22
|
+
OpenClaw process
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Prerequisites
|
|
26
|
+
|
|
27
|
+
1. Run `alfe login` to configure your API key in `~/.alfe/config.toml`
|
|
28
|
+
2. Node.js 22+
|
|
29
|
+
3. pnpm
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
### Via `alfe` CLI (recommended)
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Start daemon (foreground)
|
|
37
|
+
alfe gateway start
|
|
38
|
+
|
|
39
|
+
# Check status
|
|
40
|
+
alfe gateway status
|
|
41
|
+
|
|
42
|
+
# Stop daemon
|
|
43
|
+
alfe gateway stop
|
|
44
|
+
|
|
45
|
+
# Install as system service (auto-start on boot)
|
|
46
|
+
alfe gateway install
|
|
47
|
+
|
|
48
|
+
# Remove system service
|
|
49
|
+
alfe gateway uninstall
|
|
50
|
+
|
|
51
|
+
# View logs
|
|
52
|
+
alfe gateway logs
|
|
53
|
+
|
|
54
|
+
# Full health check
|
|
55
|
+
alfe doctor
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Via standalone binary
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Start daemon
|
|
62
|
+
alfe-gateway daemon
|
|
63
|
+
|
|
64
|
+
# Status
|
|
65
|
+
alfe-gateway status
|
|
66
|
+
|
|
67
|
+
# Stop
|
|
68
|
+
alfe-gateway stop
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## IPC Protocol
|
|
72
|
+
|
|
73
|
+
Plugins connect to `~/.alfe/gateway.sock` and speak newline-delimited JSON:
|
|
74
|
+
|
|
75
|
+
### Request (plugin → daemon)
|
|
76
|
+
```json
|
|
77
|
+
{ "type": "req", "id": "uuid", "method": "register", "params": { "name": "my-plugin", "version": "1.0.0", "protocolVersion": 1, "capabilities": ["integrations"] } }
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Response (daemon → plugin)
|
|
81
|
+
```json
|
|
82
|
+
{ "id": "uuid", "ok": true, "payload": { "status": "registered", "daemonVersion": "0.1.0", "protocolVersion": 1 } }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Event (daemon → plugin)
|
|
86
|
+
```json
|
|
87
|
+
{ "type": "event", "event": "cloud.status", "payload": { "connected": true } }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Phase 1 Methods
|
|
91
|
+
|
|
92
|
+
| Direction | Method | Description |
|
|
93
|
+
|-----------|--------|-------------|
|
|
94
|
+
| Daemon→Plugin | `integration.install` | Install an integration |
|
|
95
|
+
| Daemon→Plugin | `integration.remove` | Remove an integration |
|
|
96
|
+
| Daemon→Plugin | `integration.configure` | Update integration config |
|
|
97
|
+
| Daemon→Plugin | `integration.health` | Check integration health |
|
|
98
|
+
| Daemon→Plugin | `integration.activate` | Activate an installed integration |
|
|
99
|
+
| Daemon→Plugin | `integration.deactivate` | Deactivate a running integration |
|
|
100
|
+
| Daemon→Plugin | `integration.list` | List installed integrations |
|
|
101
|
+
| Plugin→Daemon | `register` | Register plugin with daemon |
|
|
102
|
+
| Plugin→Daemon | `status` | Get daemon health status |
|
|
103
|
+
| Plugin→Daemon | `integration.list` | List known integrations |
|
|
104
|
+
| Plugin→Daemon | `integration.report` | Report integration status |
|
|
105
|
+
|
|
106
|
+
## File Locations
|
|
107
|
+
|
|
108
|
+
| File | Path |
|
|
109
|
+
|------|------|
|
|
110
|
+
| Config | `~/.alfe/config.toml` |
|
|
111
|
+
| Socket | `~/.alfe/gateway.sock` |
|
|
112
|
+
| PID file | `~/.alfe/gateway.pid` |
|
|
113
|
+
| Logs | `~/.alfe/logs/gateway.log` |
|
|
114
|
+
| launchd plist | `~/Library/LaunchAgents/ai.alfe.gateway.plist` |
|
|
115
|
+
| systemd unit | `~/.config/systemd/user/alfe-gateway.service` |
|
|
116
|
+
|
|
117
|
+
## Security
|
|
118
|
+
|
|
119
|
+
- Socket file is `chmod 0600` (owner-only access)
|
|
120
|
+
- PID file prevents multiple daemon instances
|
|
121
|
+
- Secrets pass through in-memory only — never persisted to disk
|
|
122
|
+
- Auth via user's `api_key` from `~/.alfe/config.toml`
|
|
123
|
+
|
|
124
|
+
## Development
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# From monorepo root
|
|
128
|
+
pnpm install
|
|
129
|
+
pnpm --filter @alfe.ai/gateway build
|
|
130
|
+
pnpm --filter @alfe.ai/gateway test
|
|
131
|
+
|
|
132
|
+
# Dev mode (auto-restart on changes)
|
|
133
|
+
pnpm --filter @alfe.ai/gateway dev
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Cloud Protocol
|
|
137
|
+
|
|
138
|
+
The daemon speaks the following message types with the cloud gateway service:
|
|
139
|
+
|
|
140
|
+
| Direction | Message | Description |
|
|
141
|
+
|-----------|---------|-------------|
|
|
142
|
+
| Outbound | `SERVICE_REGISTER` | Register daemon with cloud |
|
|
143
|
+
| Inbound | `SERVICE_ACK` | Cloud acknowledges registration |
|
|
144
|
+
| Inbound | `COMMAND` | Cloud sends a command to execute |
|
|
145
|
+
| Outbound | `COMMAND_ACK` | Daemon reports command result |
|
|
146
|
+
| Inbound | `DESIRED_STATE` | Cloud pushes desired integration state |
|
|
147
|
+
| Outbound | `RECONCILIATION_REPORT` | Daemon reports reconciliation results |
|
|
148
|
+
| Inbound | `PING` | Cloud heartbeat |
|
|
149
|
+
| Outbound | `PONG` | Daemon heartbeat response |
|
|
150
|
+
|
|
151
|
+
See `src/protocol.ts` for full type definitions.
|
|
152
|
+
|
|
153
|
+
## Completed
|
|
154
|
+
|
|
155
|
+
- `@alfe.ai/openclaw` plugin connecting via IPC (`packages/openclaw/`)
|
|
156
|
+
- Integration manifest spec (`packages/integration-manifest/`)
|
|
157
|
+
- Integration registry — now DynamoDB-backed via `services/integrations/`
|
|
158
|
+
- Reconciliation engine (`src/reconciliation.ts`) — converges local state to cloud desired state
|
|
159
|
+
|
|
160
|
+
## Roadmap
|
|
161
|
+
|
|
162
|
+
- Dashboard integration management (backend wired, UI in progress)
|
|
163
|
+
- Voice migration to integration model (`packages/openclaw-voice/` exists, not yet full integration)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { a as installService, d as SOCKET_PATH, h as LOG_FILE, i as checkExistingDaemon, n as queryDaemonHealth, o as stopExistingDaemon, r as startDaemon, s as uninstallService, t as formatHealthReport } from "../health.js";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
//#region bin/gateway.ts
|
|
5
|
+
/**
|
|
6
|
+
* Alfe Gateway CLI — manages the local daemon.
|
|
7
|
+
*
|
|
8
|
+
* Commands:
|
|
9
|
+
* daemon - Run the daemon in foreground (used by service managers)
|
|
10
|
+
* start - Start the daemon (background via service manager, or foreground fallback)
|
|
11
|
+
* stop - Stop a running daemon
|
|
12
|
+
* restart - Restart the daemon
|
|
13
|
+
* status - Show daemon status
|
|
14
|
+
* install - Install as a system service (launchd/systemd)
|
|
15
|
+
* uninstall - Remove the system service
|
|
16
|
+
* logs - Tail daemon logs
|
|
17
|
+
*/
|
|
18
|
+
const command = process.argv[2];
|
|
19
|
+
async function main() {
|
|
20
|
+
switch (command) {
|
|
21
|
+
case "daemon":
|
|
22
|
+
await startDaemon();
|
|
23
|
+
break;
|
|
24
|
+
case "start": {
|
|
25
|
+
const existingPid = await checkExistingDaemon();
|
|
26
|
+
if (existingPid) {
|
|
27
|
+
console.log(`Daemon already running (PID ${String(existingPid)})`);
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
console.log("Starting Alfe Gateway Daemon...");
|
|
31
|
+
await startDaemon();
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
case "stop":
|
|
35
|
+
if (await stopExistingDaemon()) console.log("Daemon stopped");
|
|
36
|
+
else console.log("Daemon is not running");
|
|
37
|
+
break;
|
|
38
|
+
case "restart":
|
|
39
|
+
if (await stopExistingDaemon()) {
|
|
40
|
+
console.log("Stopped existing daemon");
|
|
41
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
42
|
+
}
|
|
43
|
+
console.log("Starting Alfe Gateway Daemon...");
|
|
44
|
+
await startDaemon();
|
|
45
|
+
break;
|
|
46
|
+
case "status":
|
|
47
|
+
try {
|
|
48
|
+
const health = await queryDaemonHealth(SOCKET_PATH);
|
|
49
|
+
console.log("\nAlfe Gateway Status\n");
|
|
50
|
+
console.log(formatHealthReport(health));
|
|
51
|
+
console.log();
|
|
52
|
+
} catch (err) {
|
|
53
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
54
|
+
console.log(`\nAlfe Gateway Status\n`);
|
|
55
|
+
console.log(` Gateway daemon ✗ ${message}`);
|
|
56
|
+
console.log();
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
case "install":
|
|
61
|
+
try {
|
|
62
|
+
const result = await installService();
|
|
63
|
+
console.log(result);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
66
|
+
console.error(`Install failed: ${message}`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
case "uninstall":
|
|
71
|
+
try {
|
|
72
|
+
const result = await uninstallService();
|
|
73
|
+
console.log(result);
|
|
74
|
+
} catch (err) {
|
|
75
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
76
|
+
console.error(`Uninstall failed: ${message}`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
break;
|
|
80
|
+
case "logs":
|
|
81
|
+
console.log(`Tailing ${LOG_FILE}\n`);
|
|
82
|
+
try {
|
|
83
|
+
spawn("tail", ["-f", LOG_FILE], { stdio: "inherit" }).on("error", () => {
|
|
84
|
+
console.error(`Cannot tail log file: ${LOG_FILE}`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
});
|
|
87
|
+
} catch {
|
|
88
|
+
console.error(`Log file not found: ${LOG_FILE}`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
default:
|
|
93
|
+
console.log(`
|
|
94
|
+
Alfe Gateway Daemon
|
|
95
|
+
|
|
96
|
+
Usage: alfe-gateway <command>
|
|
97
|
+
|
|
98
|
+
Commands:
|
|
99
|
+
daemon Run daemon in foreground (for service managers)
|
|
100
|
+
start Start the daemon
|
|
101
|
+
stop Stop the daemon
|
|
102
|
+
restart Restart the daemon
|
|
103
|
+
status Show daemon and connection status
|
|
104
|
+
install Install as system service (launchd/systemd)
|
|
105
|
+
uninstall Remove system service
|
|
106
|
+
logs Tail daemon logs
|
|
107
|
+
|
|
108
|
+
Examples:
|
|
109
|
+
alfe-gateway start # Start daemon
|
|
110
|
+
alfe-gateway status # Check status
|
|
111
|
+
alfe-gateway install # Auto-start on boot
|
|
112
|
+
`);
|
|
113
|
+
if (command) {
|
|
114
|
+
console.error(`Unknown command: ${command}`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
main().catch((err) => {
|
|
120
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
121
|
+
console.error("Fatal error:", message);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
});
|
|
124
|
+
//#endregion
|
|
125
|
+
export {};
|