@robbiesrobotics/alice-agents 1.5.1 → 1.5.3
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 +4 -2
- package/bin/alice-cloud.cjs +350 -0
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Adaptive Learning & Intelligent Coordination Engine**
|
|
4
4
|
|
|
5
|
-
One conversation. One orchestrator. Ten starter agents — with
|
|
5
|
+
One conversation. One orchestrator. Ten starter agents — with 21 more in Pro. A.L.I.C.E. turns your agent runtime into a full AI team — talk to A.L.I.C.E. (or just Alice), and she routes your request to the right expert.
|
|
6
6
|
|
|
7
7
|
**NemoClaw compatible** — A.L.I.C.E. v1.2.7+ runs natively on [NVIDIA NemoClaw](https://nvidia.com/nemoclaw), the secure open-source agent runtime. Agents execute inside the OpenShell sandbox for enterprise-grade security.
|
|
8
8
|
|
|
@@ -16,7 +16,7 @@ That's it. The installer detects your runtime (NemoClaw or OpenClaw) and sets ev
|
|
|
16
16
|
|
|
17
17
|
## What You Get
|
|
18
18
|
|
|
19
|
-
**Starter** includes 10 agents. **Pro** unlocks
|
|
19
|
+
**Starter** includes 10 agents. **Pro** unlocks 21 more — [sign up at getalice.av3.ai](https://getalice.av3.ai/signup?plan=pro)
|
|
20
20
|
|
|
21
21
|
**Mission Control Cloud** is available as a Pro add-on. If enabled during install, the package now:
|
|
22
22
|
- installs the `mission-control-bridge` plugin into your OpenClaw home
|
|
@@ -56,6 +56,8 @@ An orchestrator (A.L.I.C.E., also addressable as Alice or Olivia) backed by spec
|
|
|
56
56
|
| **Logan** | Legal | ⚖️ | Pro |
|
|
57
57
|
| **Eva** | Executive Assistant | 📌 | Pro |
|
|
58
58
|
| **Parker** | Project Management | 📅 | Pro |
|
|
59
|
+
| **Aria** | Autonomous Research | 🔬 | Pro |
|
|
60
|
+
| **Nate** | n8n Automation | 🔧 | Pro |
|
|
59
61
|
|
|
60
62
|
## Model Flexibility
|
|
61
63
|
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* alice-cloud - Zero-touch client onboarding CLI for A.L.I.C.E. | Control
|
|
4
|
+
* Usage: alice-cloud <command>
|
|
5
|
+
* --non-interactive Skip interactive prompts (use env vars)
|
|
6
|
+
* ALICE_SUPABASE_TOKEN Supabase access token (non-interactive mode)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const { spawn } = require('child_process');
|
|
13
|
+
|
|
14
|
+
const CONFIG_DIR = path.join(os.homedir(), '.openclaw');
|
|
15
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'alice-cloud.json');
|
|
16
|
+
const API_BASE = 'https://alice.av3.ai/api/cloud';
|
|
17
|
+
|
|
18
|
+
const NON_INTERACTIVE = process.argv.includes('--non-interactive') || process.env.ALICE_NON_INTERACTIVE === '1';
|
|
19
|
+
const SUPABASE_TOKEN = process.env.ALICE_SUPABASE_TOKEN;
|
|
20
|
+
|
|
21
|
+
function loadConfig() {
|
|
22
|
+
try {
|
|
23
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
24
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
25
|
+
}
|
|
26
|
+
} catch (_) {}
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function saveConfig(config) {
|
|
31
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
32
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function sleep(ms) {
|
|
36
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function runCommand(cmd, args) {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
const child = spawn(cmd, args, { shell: true });
|
|
42
|
+
let stdout = '', stderr = '';
|
|
43
|
+
child.stdout.on('data', (d) => { stdout += d; });
|
|
44
|
+
child.stderr.on('data', (d) => { stderr += d; });
|
|
45
|
+
child.on('close', (code) => {
|
|
46
|
+
if (code === 0) resolve(stdout);
|
|
47
|
+
else reject(new Error(stderr || `exit ${code}`));
|
|
48
|
+
});
|
|
49
|
+
child.on('error', reject);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ── Login ──────────────────────────────────────────────────────────────────────
|
|
54
|
+
async function login(args) {
|
|
55
|
+
const open = (await import('open')).default;
|
|
56
|
+
const got = (await import('got')).default;
|
|
57
|
+
|
|
58
|
+
// Non-interactive mode: use token from env
|
|
59
|
+
if (NON_INTERACTIVE) {
|
|
60
|
+
const token = SUPABASE_TOKEN;
|
|
61
|
+
if (!token) {
|
|
62
|
+
console.error('❌ ALICE_SUPABASE_TOKEN env var required for non-interactive login.');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const res = await got(`${process.env.ALICE_SUPABASE_URL || 'https://xxxgvtwnlbtdgmlgccee.supabase.co'}/auth/v1/user`, {
|
|
67
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
68
|
+
throwHttpErrors: false,
|
|
69
|
+
}).json();
|
|
70
|
+
const config = loadConfig();
|
|
71
|
+
config.supabaseToken = token;
|
|
72
|
+
config.user = res;
|
|
73
|
+
saveConfig(config);
|
|
74
|
+
console.log('✅ Logged in as', res.email || res.user_metadata?.user_name || 'unknown');
|
|
75
|
+
return;
|
|
76
|
+
} catch (err) {
|
|
77
|
+
console.error('❌ Token validation failed:', err.message);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log('🔐 A.L.I.C.E. Cloud login');
|
|
83
|
+
console.log(' Opening browser for Supabase OAuth…');
|
|
84
|
+
|
|
85
|
+
const supabaseUrl = process.env.ALICE_SUPABASE_URL || 'https://xxxgvtwnlbtdgmlgccee.supabase.co';
|
|
86
|
+
const redirectUri = `${API_BASE}/auth/callback`;
|
|
87
|
+
const oauthUrl = `${supabaseUrl}/auth/v1/authorize?provider=github&redirect_to=${encodeURIComponent(redirectUri)}`;
|
|
88
|
+
|
|
89
|
+
await open(oauthUrl);
|
|
90
|
+
console.log(' Browser opened. Complete login in your browser.');
|
|
91
|
+
console.log(' After login, paste your Supabase access token here:');
|
|
92
|
+
process.stdout.write(' > ');
|
|
93
|
+
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
let token = '';
|
|
96
|
+
process.stdin.setEncoding('utf8');
|
|
97
|
+
process.stdin.on('data', (d) => { token += d; });
|
|
98
|
+
process.stdin.on('end', async () => {
|
|
99
|
+
token = token.trim();
|
|
100
|
+
if (!token) { reject(new Error('No token provided')); return; }
|
|
101
|
+
try {
|
|
102
|
+
const res = await got(`${supabaseUrl}/auth/v1/user`, {
|
|
103
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
104
|
+
throwHttpErrors: false,
|
|
105
|
+
}).json();
|
|
106
|
+
const config = loadConfig();
|
|
107
|
+
config.supabaseToken = token;
|
|
108
|
+
config.user = res;
|
|
109
|
+
saveConfig(config);
|
|
110
|
+
console.log(' ✅ Logged in as', res.email || res.user_metadata?.user_name || 'unknown');
|
|
111
|
+
resolve();
|
|
112
|
+
} catch (err) {
|
|
113
|
+
reject(new Error('Token validation failed: ' + err.message));
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── Register ───────────────────────────────────────────────────────────────────
|
|
120
|
+
async function detectGatewayUrl() {
|
|
121
|
+
const got = (await import('got')).default;
|
|
122
|
+
const candidates = ['https://localhost:18789', 'http://localhost:18789'];
|
|
123
|
+
for (const url of candidates) {
|
|
124
|
+
try {
|
|
125
|
+
await got.get(url, { throwHttpErrors: true, timeout: { request: 2000 }, retry: { limit: 0 } });
|
|
126
|
+
return url;
|
|
127
|
+
} catch (_) {}
|
|
128
|
+
}
|
|
129
|
+
return 'https://localhost:18789';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function readGatewayToken() {
|
|
133
|
+
const cfg = path.join(os.homedir(), '.openclaw', 'openclaw.json');
|
|
134
|
+
try {
|
|
135
|
+
const data = JSON.parse(fs.readFileSync(cfg, 'utf8'));
|
|
136
|
+
return data.gateway?.auth?.token || '';
|
|
137
|
+
} catch (_) {}
|
|
138
|
+
return '';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function getOpenClawVersion() {
|
|
142
|
+
try { return (await runCommand('openclaw', ['--version'])).trim(); } catch (_) {}
|
|
143
|
+
return 'unknown';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function register(args) {
|
|
147
|
+
const got = (await import('got')).default;
|
|
148
|
+
const config = loadConfig();
|
|
149
|
+
|
|
150
|
+
if (!config.supabaseToken) {
|
|
151
|
+
console.error('❌ Not logged in. Run `alice-cloud login` first.');
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const gatewayUrl = await detectGatewayUrl();
|
|
156
|
+
const gatewayToken = await readGatewayToken();
|
|
157
|
+
const hostname = os.hostname();
|
|
158
|
+
const version = await getOpenClawVersion();
|
|
159
|
+
|
|
160
|
+
console.log('🔗 Registering gateway with A.L.I.C.E. Cloud…');
|
|
161
|
+
console.log(` Gateway URL : ${gatewayUrl}`);
|
|
162
|
+
console.log(` Hostname : ${hostname}`);
|
|
163
|
+
console.log(` Version : ${version}`);
|
|
164
|
+
|
|
165
|
+
let lastError;
|
|
166
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
167
|
+
try {
|
|
168
|
+
const response = await got.post(`${API_BASE}/register`, {
|
|
169
|
+
json: { gatewayUrl, gatewayToken, hostname, version },
|
|
170
|
+
headers: { Authorization: `Bearer ${config.supabaseToken}` },
|
|
171
|
+
throwHttpErrors: true,
|
|
172
|
+
}).json();
|
|
173
|
+
config.registration = { gatewayUrl, hostname, version, registeredAt: new Date().toISOString() };
|
|
174
|
+
saveConfig(config);
|
|
175
|
+
console.log(' ✅ Gateway registered!');
|
|
176
|
+
return;
|
|
177
|
+
} catch (err) {
|
|
178
|
+
lastError = err;
|
|
179
|
+
const delay = attempt * 2000;
|
|
180
|
+
console.log(` ⚠️ Attempt ${attempt} failed: ${err.message}. Retrying in ${delay}ms…`);
|
|
181
|
+
await sleep(delay);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
console.error('❌ Registration failed after 3 attempts:', lastError.message);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ── Status ─────────────────────────────────────────────────────────────────────
|
|
189
|
+
async function status(args) {
|
|
190
|
+
const got = (await import('got')).default;
|
|
191
|
+
const config = loadConfig();
|
|
192
|
+
|
|
193
|
+
if (!config.supabaseToken) {
|
|
194
|
+
console.error('❌ Not logged in. Run `alice-cloud login` first.');
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
const data = await got.get(`${API_BASE}/status`, {
|
|
200
|
+
headers: { Authorization: `Bearer ${config.supabaseToken}` },
|
|
201
|
+
throwHttpErrors: false,
|
|
202
|
+
}).json();
|
|
203
|
+
|
|
204
|
+
if (!data.registered) {
|
|
205
|
+
console.log('⚪ No gateway registered.');
|
|
206
|
+
console.log(' Run `alice-cloud register` to connect your gateway.');
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
console.log('🟢 A.L.I.C.E. Cloud Status');
|
|
211
|
+
console.log(` Gateway URL : ${data.gatewayUrl}`);
|
|
212
|
+
console.log(` Hostname : ${data.hostname}`);
|
|
213
|
+
console.log(` Status : ${data.connected ? '🟢 Connected' : '🔴 Disconnected'}`);
|
|
214
|
+
console.log(` Last heartbeat: ${data.lastHeartbeat ? new Date(data.lastHeartbeat).toLocaleString() : 'Never'}`);
|
|
215
|
+
console.log(` Version : ${data.version}`);
|
|
216
|
+
} catch (err) {
|
|
217
|
+
if (err.response?.statusCode === 401) {
|
|
218
|
+
console.error('❌ Session expired. Run `alice-cloud login` again.');
|
|
219
|
+
} else {
|
|
220
|
+
console.error('❌ Status check failed:', err.message);
|
|
221
|
+
}
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ── Unregister ─────────────────────────────────────────────────────────────────
|
|
227
|
+
async function unregister(args) {
|
|
228
|
+
const got = (await import('got')).default;
|
|
229
|
+
const config = loadConfig();
|
|
230
|
+
|
|
231
|
+
if (!config.supabaseToken) {
|
|
232
|
+
console.error('❌ Not logged in. Run `alice-cloud login` first.');
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
console.log('🗑️ Unregistering gateway…');
|
|
237
|
+
try {
|
|
238
|
+
await got.delete(`${API_BASE}/status`, {
|
|
239
|
+
headers: { Authorization: `Bearer ${config.supabaseToken}` },
|
|
240
|
+
throwHttpErrors: false,
|
|
241
|
+
});
|
|
242
|
+
delete config.registration;
|
|
243
|
+
saveConfig(config);
|
|
244
|
+
console.log(' ✅ Gateway unregistered.');
|
|
245
|
+
} catch (err) {
|
|
246
|
+
if (err.response?.statusCode === 404) {
|
|
247
|
+
console.log(' ℹ️ No gateway was registered.');
|
|
248
|
+
} else {
|
|
249
|
+
console.error('❌ Unregister failed:', err.message);
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ── Watch (daemon) ─────────────────────────────────────────────────────────────
|
|
256
|
+
async function checkGatewayUp(gatewayUrl) {
|
|
257
|
+
const got = (await import('got')).default;
|
|
258
|
+
try {
|
|
259
|
+
await got.get(gatewayUrl, { throwHttpErrors: true, timeout: { request: 3000 }, retry: { limit: 0 } });
|
|
260
|
+
return true;
|
|
261
|
+
} catch (_) {}
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async function startHeartbeatLoop(config) {
|
|
266
|
+
const got = (await import('got')).default;
|
|
267
|
+
const gatewayUrl = config.registration?.gatewayUrl || 'https://localhost:18789';
|
|
268
|
+
|
|
269
|
+
async function heartbeat() {
|
|
270
|
+
try {
|
|
271
|
+
const isUp = await checkGatewayUp(gatewayUrl);
|
|
272
|
+
if (!isUp) {
|
|
273
|
+
console.log('[watch] Gateway down, attempting restart…');
|
|
274
|
+
try { await runCommand('openclaw', ['gateway', 'start']); await sleep(3000); } catch (_) {}
|
|
275
|
+
}
|
|
276
|
+
await got.post(`${API_BASE}/heartbeat`, {
|
|
277
|
+
json: { hostname: os.hostname(), connected: await checkGatewayUp(gatewayUrl), timestamp: new Date().toISOString() },
|
|
278
|
+
headers: { Authorization: `Bearer ${config.supabaseToken}` },
|
|
279
|
+
throwHttpErrors: false, timeout: { request: 10000 },
|
|
280
|
+
});
|
|
281
|
+
console.log(`[${new Date().toLocaleTimeString()}] heartbeat sent (gateway: ${isUp ? 'up' : 'down'})`);
|
|
282
|
+
} catch (err) {
|
|
283
|
+
console.error('[watch] heartbeat error:', err.message);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
await heartbeat();
|
|
288
|
+
const interval = setInterval(heartbeat, 30_000);
|
|
289
|
+
process.on('SIGINT', () => { clearInterval(interval); process.exit(0); });
|
|
290
|
+
process.on('SIGTERM', () => { clearInterval(interval); process.exit(0); });
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async function watch(args) {
|
|
294
|
+
const config = loadConfig();
|
|
295
|
+
if (!config.supabaseToken) {
|
|
296
|
+
console.error('❌ Not logged in. Run `alice-cloud login` first.');
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (args.daemon || process.argv.includes('--daemon')) {
|
|
301
|
+
const child = spawn(process.execPath, [__filename, 'watch', '--running'], {
|
|
302
|
+
detached: true, stdio: 'ignore', env: { ...process.env, ALICE_NON_INTERACTIVE: '1' },
|
|
303
|
+
});
|
|
304
|
+
child.unref();
|
|
305
|
+
console.log('🚀 alice-cloud watch started in background (PID:', child.pid, ')');
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
console.log('👁️ A.L.I.C.E. Cloud watch daemon started');
|
|
310
|
+
console.log(' Press Ctrl+C to stop.');
|
|
311
|
+
await startHeartbeatLoop(config);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// ── CLI dispatcher ─────────────────────────────────────────────────────────────
|
|
315
|
+
const commands = { login, register, status, unregister, watch };
|
|
316
|
+
const cmd = process.argv[2];
|
|
317
|
+
|
|
318
|
+
if (!cmd) {
|
|
319
|
+
console.log(`alice-cloud v1.0.1 — A.L.I.C.E. | Control Cloud CLI
|
|
320
|
+
|
|
321
|
+
Usage: alice-cloud <command> [options]
|
|
322
|
+
|
|
323
|
+
Commands:
|
|
324
|
+
login Authenticate with Supabase
|
|
325
|
+
register Register this gateway with A.L.I.C.E. Cloud
|
|
326
|
+
status Show gateway connection status
|
|
327
|
+
unregister Remove gateway registration
|
|
328
|
+
watch Start background heartbeat daemon
|
|
329
|
+
|
|
330
|
+
Options:
|
|
331
|
+
--non-interactive Use env vars (ALICE_SUPABASE_TOKEN) instead of prompts
|
|
332
|
+
|
|
333
|
+
Environment:
|
|
334
|
+
ALICE_SUPABASE_TOKEN Supabase access token (for non-interactive mode)
|
|
335
|
+
ALICE_SUPABASE_URL Supabase project URL (default: xxx project)
|
|
336
|
+
|
|
337
|
+
Run 'alice-cloud <command> --help' for more options.`);
|
|
338
|
+
process.exit(0);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const handler = commands[cmd];
|
|
342
|
+
if (!handler) {
|
|
343
|
+
console.error(`Unknown command: ${cmd}`);
|
|
344
|
+
process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
handler(process.argv.slice(3)).catch((err) => {
|
|
348
|
+
console.error('Error:', err.message);
|
|
349
|
+
process.exit(1);
|
|
350
|
+
});
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robbiesrobotics/alice-agents",
|
|
3
|
-
"version": "1.5.
|
|
4
|
-
"description": "A.L.I.C.E.
|
|
3
|
+
"version": "1.5.3",
|
|
4
|
+
"description": "A.L.I.C.E. \u2014 31 AI agents for OpenClaw. One conversation, one team.",
|
|
5
5
|
"bin": {
|
|
6
|
-
"alice-agents": "bin/alice-install.mjs"
|
|
6
|
+
"alice-agents": "bin/alice-install.mjs",
|
|
7
|
+
"alice-cloud": "bin/alice-cloud.cjs"
|
|
7
8
|
},
|
|
8
9
|
"type": "module",
|
|
9
10
|
"engines": {
|
|
@@ -38,4 +39,4 @@
|
|
|
38
39
|
"publishConfig": {
|
|
39
40
|
"access": "public"
|
|
40
41
|
}
|
|
41
|
-
}
|
|
42
|
+
}
|