@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 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 18 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.
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 18 more — [sign up at getalice.av3.ai](https://getalice.av3.ai/signup?plan=pro)
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.1",
4
- "description": "A.L.I.C.E. 28 AI agents for OpenClaw. One conversation, one team.",
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
+ }