@robbiesrobotics/alice-agents 1.5.3 → 1.5.4
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/bin/alice-cloud.cjs +34 -15
- package/lib/installer.mjs +140 -38
- package/lib/mission-control.mjs +61 -0
- package/lib/prompter.mjs +20 -14
- package/package.json +1 -1
package/bin/alice-cloud.cjs
CHANGED
|
@@ -311,12 +311,30 @@ async function watch(args) {
|
|
|
311
311
|
await startHeartbeatLoop(config);
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
+
// ── Programmatic API exports ───────────────────────────────────────────────────
|
|
315
|
+
// Allow ESM callers to import these via createRequire or spawn as child process.
|
|
316
|
+
module.exports = {
|
|
317
|
+
login,
|
|
318
|
+
register,
|
|
319
|
+
status,
|
|
320
|
+
unregister,
|
|
321
|
+
watch,
|
|
322
|
+
loadConfig,
|
|
323
|
+
saveConfig,
|
|
324
|
+
detectGatewayUrl,
|
|
325
|
+
readGatewayToken,
|
|
326
|
+
API_BASE,
|
|
327
|
+
CONFIG_FILE,
|
|
328
|
+
};
|
|
329
|
+
|
|
314
330
|
// ── CLI dispatcher ─────────────────────────────────────────────────────────────
|
|
315
|
-
|
|
316
|
-
|
|
331
|
+
// Only run as CLI when this file is the main module (not when required/imported)
|
|
332
|
+
if (require.main === module) {
|
|
333
|
+
const commands = { login, register, status, unregister, watch };
|
|
334
|
+
const cmd = process.argv[2];
|
|
317
335
|
|
|
318
|
-
if (!cmd) {
|
|
319
|
-
|
|
336
|
+
if (!cmd) {
|
|
337
|
+
console.log(`alice-cloud v1.0.1 — A.L.I.C.E. | Control Cloud CLI
|
|
320
338
|
|
|
321
339
|
Usage: alice-cloud <command> [options]
|
|
322
340
|
|
|
@@ -335,16 +353,17 @@ Environment:
|
|
|
335
353
|
ALICE_SUPABASE_URL Supabase project URL (default: xxx project)
|
|
336
354
|
|
|
337
355
|
Run 'alice-cloud <command> --help' for more options.`);
|
|
338
|
-
|
|
339
|
-
}
|
|
356
|
+
process.exit(0);
|
|
357
|
+
}
|
|
340
358
|
|
|
341
|
-
const handler = commands[cmd];
|
|
342
|
-
if (!handler) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
359
|
+
const handler = commands[cmd];
|
|
360
|
+
if (!handler) {
|
|
361
|
+
console.error(`Unknown command: ${cmd}`);
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
346
364
|
|
|
347
|
-
handler(process.argv.slice(3)).catch((err) => {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
});
|
|
365
|
+
handler(process.argv.slice(3)).catch((err) => {
|
|
366
|
+
console.error('Error:', err.message);
|
|
367
|
+
process.exit(1);
|
|
368
|
+
});
|
|
369
|
+
}
|
package/lib/installer.mjs
CHANGED
|
@@ -6,7 +6,14 @@ import { homedir } from 'node:os';
|
|
|
6
6
|
import { configExists, mergeConfig, removeAliceAgents, detectAvailableModels } from './config-merger.mjs';
|
|
7
7
|
import { scaffoldAll } from './workspace-scaffolder.mjs';
|
|
8
8
|
import { readManifest, writeManifest } from './manifest.mjs';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
configureMissionControlCloud,
|
|
11
|
+
getDefaultMissionControlSettings,
|
|
12
|
+
getCloudStatus,
|
|
13
|
+
isCloudAuthenticated,
|
|
14
|
+
isCloudRegistered,
|
|
15
|
+
configureCloudFromSupabase,
|
|
16
|
+
} from './mission-control.mjs';
|
|
10
17
|
import {
|
|
11
18
|
promptInstallMode,
|
|
12
19
|
promptUserInfo,
|
|
@@ -15,8 +22,10 @@ import {
|
|
|
15
22
|
promptTier,
|
|
16
23
|
promptLicenseKey,
|
|
17
24
|
promptCloudAddon,
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
promptCloudSetup,
|
|
26
|
+
promptCloudReauth,
|
|
27
|
+
promptCloudHeartbeat,
|
|
28
|
+
promptCloudToken,
|
|
20
29
|
confirm,
|
|
21
30
|
choose,
|
|
22
31
|
input,
|
|
@@ -470,11 +479,10 @@ function printSummaryWithOptions(mode, tier, agents, preset, userInfo, detectedM
|
|
|
470
479
|
`${dim('Model:')} ${green(modelLabel)}`,
|
|
471
480
|
`${dim('User:')} ${green(userInfo.name)}`,
|
|
472
481
|
`${dim('Timezone:')} ${green(userInfo.timezone)}`,
|
|
473
|
-
`${dim('
|
|
482
|
+
`${dim('Cloud:')} ${green(missionControl?.enabled ? 'enabled' : 'local only')}`,
|
|
474
483
|
...(missionControl?.enabled
|
|
475
484
|
? [
|
|
476
485
|
`${dim('Dashboard:')} ${green(missionControl.dashboardUrl)}`,
|
|
477
|
-
`${dim('Ingest:')} ${green(missionControl.ingestUrl)}`,
|
|
478
486
|
]
|
|
479
487
|
: []),
|
|
480
488
|
'',
|
|
@@ -486,6 +494,75 @@ function printSummaryWithOptions(mode, tier, agents, preset, userInfo, detectedM
|
|
|
486
494
|
console.log('');
|
|
487
495
|
}
|
|
488
496
|
|
|
497
|
+
/**
|
|
498
|
+
* Run inline cloud onboarding — opens browser, validates token, registers gateway.
|
|
499
|
+
* Uses alice-cloud CLI spawned as a child process (CJS ↔ ESM boundary).
|
|
500
|
+
*/
|
|
501
|
+
async function _runCloudOnboarding(auto, options, existingMissionControl) {
|
|
502
|
+
const { execSync: execSyncLocal, spawn: spawnLocal } = await import('node:child_process');
|
|
503
|
+
const aliceCloudBin = join(__dirname, '..', 'bin', 'alice-cloud.cjs');
|
|
504
|
+
const defaults = getDefaultMissionControlSettings();
|
|
505
|
+
|
|
506
|
+
try {
|
|
507
|
+
// Step 1: Login — open browser for Supabase OAuth
|
|
508
|
+
console.log('');
|
|
509
|
+
console.log(` ${icons.pkg} ${bold('Setting up A.L.I.C.E. Cloud...')}`);
|
|
510
|
+
console.log(` Opening your browser to sign in with GitHub...`);
|
|
511
|
+
console.log('');
|
|
512
|
+
|
|
513
|
+
// Spawn alice-cloud login as a child process
|
|
514
|
+
// In non-interactive mode, use env var for token
|
|
515
|
+
if (auto && options.cloudSupabaseToken) {
|
|
516
|
+
execSyncLocal(
|
|
517
|
+
`node ${JSON.stringify(aliceCloudBin)} login --non-interactive`,
|
|
518
|
+
{
|
|
519
|
+
stdio: 'inherit',
|
|
520
|
+
env: { ...process.env, ALICE_SUPABASE_TOKEN: options.cloudSupabaseToken, ALICE_NON_INTERACTIVE: '1' },
|
|
521
|
+
}
|
|
522
|
+
);
|
|
523
|
+
} else if (!auto) {
|
|
524
|
+
// Interactive: spawn login which opens browser and waits for token paste
|
|
525
|
+
execSyncLocal(`node ${JSON.stringify(aliceCloudBin)} login`, { stdio: 'inherit' });
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Step 2: Register gateway
|
|
529
|
+
if (isCloudAuthenticated()) {
|
|
530
|
+
console.log('');
|
|
531
|
+
console.log(` ${icons.pkg} ${bold('Registering gateway with A.L.I.C.E. Cloud...')}`);
|
|
532
|
+
execSyncLocal(`node ${JSON.stringify(aliceCloudBin)} register`, { stdio: 'inherit' });
|
|
533
|
+
printStepDone('Cloud login and registration complete');
|
|
534
|
+
|
|
535
|
+
// Step 3: Ask about heartbeat daemon
|
|
536
|
+
const startHeartbeat = auto ? false : await promptCloudHeartbeat();
|
|
537
|
+
if (startHeartbeat) {
|
|
538
|
+
console.log(` ${dim('Starting heartbeat daemon...')}`);
|
|
539
|
+
execSyncLocal(`node ${JSON.stringify(aliceCloudBin)} watch --daemon`, { stdio: 'inherit' });
|
|
540
|
+
printStepDone('Heartbeat daemon started');
|
|
541
|
+
}
|
|
542
|
+
} else {
|
|
543
|
+
printStepSkip('Cloud registration', 'login was skipped or failed — run `alice-cloud login` later');
|
|
544
|
+
}
|
|
545
|
+
} catch (err) {
|
|
546
|
+
console.log(` ${icons.warn} ${yellow('Cloud setup encountered an issue: ' + (err.message || 'unknown error'))}`);
|
|
547
|
+
console.log(` ${dim('You can complete setup later with:')} ${cyan('alice-cloud login && alice-cloud register')}`);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Build missionControl object from whatever state we achieved
|
|
551
|
+
const cloudStatus = getCloudStatus();
|
|
552
|
+
return {
|
|
553
|
+
enabled: true,
|
|
554
|
+
provider: 'cloud',
|
|
555
|
+
dashboardUrl: existingMissionControl?.dashboardUrl || defaults.dashboardUrl,
|
|
556
|
+
ingestUrl: existingMissionControl?.ingestUrl || `${defaults.dashboardUrl}/api/v1/ingest`,
|
|
557
|
+
sourceNode: existingMissionControl?.sourceNode || defaults.sourceNode,
|
|
558
|
+
teamId: String(options.cloudTeamId || existingMissionControl?.teamId || '').trim(),
|
|
559
|
+
teamSlug: String(options.cloudTeamSlug || existingMissionControl?.teamSlug || '').trim(),
|
|
560
|
+
teamName: String(options.cloudTeamName || existingMissionControl?.teamName || '').trim(),
|
|
561
|
+
teamPlan: String(options.cloudTeamPlan || existingMissionControl?.teamPlan || '').trim(),
|
|
562
|
+
...(cloudStatus.authenticated ? { hasIngestToken: true } : {}),
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
|
|
489
566
|
export async function runInstall(options = {}) {
|
|
490
567
|
const auto = options.yes || false;
|
|
491
568
|
const manifest = readManifest();
|
|
@@ -718,36 +795,61 @@ export async function runInstall(options = {}) {
|
|
|
718
795
|
: cloudFlag ?? await promptCloudAddon();
|
|
719
796
|
|
|
720
797
|
if (enableCloud) {
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
798
|
+
const cloudStatus = getCloudStatus();
|
|
799
|
+
|
|
800
|
+
// Upgrade mode: show existing cloud status
|
|
801
|
+
if (mode === 'upgrade' && (cloudStatus.authenticated || existingMissionControl?.enabled)) {
|
|
802
|
+
console.log('');
|
|
803
|
+
console.log(` ${icons.ok} ${green('A.L.I.C.E. Cloud is configured')}`);
|
|
804
|
+
if (cloudStatus.user) {
|
|
805
|
+
console.log(` ${dim('User:')} ${cloudStatus.user}`);
|
|
806
|
+
}
|
|
807
|
+
if (cloudStatus.gatewayUrl) {
|
|
808
|
+
console.log(` ${dim('Gateway:')} ${cloudStatus.gatewayUrl}`);
|
|
809
|
+
}
|
|
810
|
+
if (cloudStatus.registeredAt) {
|
|
811
|
+
console.log(` ${dim('Registered:')} ${cloudStatus.registeredAt}`);
|
|
812
|
+
}
|
|
813
|
+
console.log('');
|
|
814
|
+
|
|
815
|
+
const reauth = auto ? false : await promptCloudReauth();
|
|
816
|
+
if (!reauth) {
|
|
817
|
+
// Keep existing config — use Supabase config if available, fall back to legacy
|
|
818
|
+
if (cloudStatus.authenticated) {
|
|
819
|
+
missionControl = {
|
|
820
|
+
enabled: true,
|
|
821
|
+
provider: 'cloud',
|
|
822
|
+
dashboardUrl: existingMissionControl?.dashboardUrl || 'https://alice.av3.ai',
|
|
823
|
+
ingestUrl: existingMissionControl?.ingestUrl || 'https://alice.av3.ai/api/v1/ingest',
|
|
824
|
+
sourceNode: existingMissionControl?.sourceNode || getDefaultMissionControlSettings().sourceNode,
|
|
825
|
+
};
|
|
826
|
+
} else {
|
|
827
|
+
missionControl = { ...existingMissionControl, enabled: true };
|
|
828
|
+
}
|
|
829
|
+
} else {
|
|
830
|
+
// Re-authenticate — run alice-cloud login inline
|
|
831
|
+
missionControl = await _runCloudOnboarding(auto, options, existingMissionControl);
|
|
832
|
+
}
|
|
833
|
+
} else {
|
|
834
|
+
// Fresh install — run cloud onboarding
|
|
835
|
+
const wantsSetup = auto ? true : await promptCloudSetup();
|
|
836
|
+
|
|
837
|
+
if (wantsSetup) {
|
|
838
|
+
missionControl = await _runCloudOnboarding(auto, options, existingMissionControl);
|
|
839
|
+
} else {
|
|
840
|
+
console.log('');
|
|
841
|
+
console.log(` ${dim('You can set up cloud later with:')} ${cyan('alice-cloud login && alice-cloud register')}`);
|
|
842
|
+
console.log('');
|
|
843
|
+
// Cloud addon enabled but setup skipped — bridge installed, no auth yet
|
|
844
|
+
missionControl = {
|
|
845
|
+
enabled: true,
|
|
846
|
+
provider: 'cloud',
|
|
847
|
+
dashboardUrl: 'https://alice.av3.ai',
|
|
848
|
+
ingestUrl: 'https://alice.av3.ai/api/v1/ingest',
|
|
849
|
+
sourceNode: getDefaultMissionControlSettings().sourceNode,
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
}
|
|
751
853
|
}
|
|
752
854
|
}
|
|
753
855
|
|
|
@@ -790,12 +892,12 @@ export async function runInstall(options = {}) {
|
|
|
790
892
|
|
|
791
893
|
if (missionControl?.enabled) {
|
|
792
894
|
const missionControlResult = configureMissionControlCloud(missionControl);
|
|
793
|
-
printStepDone('
|
|
895
|
+
printStepDone('A.L.I.C.E. Cloud', missionControlResult.summary.dashboardUrl);
|
|
794
896
|
if (!missionControlResult.summary.hasIngestToken) {
|
|
795
|
-
printStepSkip('Cloud
|
|
897
|
+
printStepSkip('Cloud authentication pending', 'run `alice-cloud login && alice-cloud register` to complete setup');
|
|
796
898
|
}
|
|
797
899
|
} else {
|
|
798
|
-
printStepSkip('
|
|
900
|
+
printStepSkip('A.L.I.C.E. Cloud', 'not enabled for this install');
|
|
799
901
|
}
|
|
800
902
|
|
|
801
903
|
// Scaffold workspaces
|
package/lib/mission-control.mjs
CHANGED
|
@@ -179,3 +179,64 @@ export function getDefaultMissionControlSettings() {
|
|
|
179
179
|
export function hasMissionControlBridgeInstalled() {
|
|
180
180
|
return existsSync(join(OPENCLAW_HOME, 'extensions', BRIDGE_ID));
|
|
181
181
|
}
|
|
182
|
+
|
|
183
|
+
// ── Supabase-based cloud onboarding helpers ────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
const CLOUD_CONFIG_PATH = join(OPENCLAW_HOME, 'alice-cloud.json');
|
|
186
|
+
|
|
187
|
+
export function readCloudConfig() {
|
|
188
|
+
return readJsonFile(CLOUD_CONFIG_PATH);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function isCloudAuthenticated() {
|
|
192
|
+
const config = readCloudConfig();
|
|
193
|
+
return !!(config && config.supabaseToken);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function isCloudRegistered() {
|
|
197
|
+
const config = readCloudConfig();
|
|
198
|
+
return !!(config && config.registration);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function getCloudStatus() {
|
|
202
|
+
const config = readCloudConfig();
|
|
203
|
+
if (!config) return { authenticated: false, registered: false };
|
|
204
|
+
return {
|
|
205
|
+
authenticated: !!config.supabaseToken,
|
|
206
|
+
registered: !!config.registration,
|
|
207
|
+
user: config.user?.email || config.user?.user_metadata?.user_name || null,
|
|
208
|
+
gatewayUrl: config.registration?.gatewayUrl || null,
|
|
209
|
+
hostname: config.registration?.hostname || null,
|
|
210
|
+
registeredAt: config.registration?.registeredAt || null,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Configure cloud using Supabase auth (new flow).
|
|
216
|
+
* This preserves the existing bridge installation and MC config writing,
|
|
217
|
+
* and reads auth state from alice-cloud.json.
|
|
218
|
+
*/
|
|
219
|
+
export function configureCloudFromSupabase(input = {}) {
|
|
220
|
+
// Read alice-cloud.json for auth info
|
|
221
|
+
const cloudConfig = readCloudConfig() || {};
|
|
222
|
+
|
|
223
|
+
// Build settings — we still use the same bridge + MC config infrastructure,
|
|
224
|
+
// but tokens now come from Supabase auth rather than manual ingest tokens
|
|
225
|
+
const settingsInput = {
|
|
226
|
+
dashboardUrl: input.dashboardUrl || DEFAULT_DASHBOARD_URL,
|
|
227
|
+
sourceNode: input.sourceNode || hostname() || 'openclaw-local',
|
|
228
|
+
teamId: input.teamId || '',
|
|
229
|
+
teamSlug: input.teamSlug || '',
|
|
230
|
+
teamName: input.teamName || '',
|
|
231
|
+
teamPlan: input.teamPlan || '',
|
|
232
|
+
enabled: true,
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// If we have a supabase token, use it as the ingest/worker token for bridge compatibility
|
|
236
|
+
if (cloudConfig.supabaseToken) {
|
|
237
|
+
settingsInput.ingestToken = cloudConfig.supabaseToken;
|
|
238
|
+
settingsInput.workerToken = cloudConfig.supabaseToken;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return configureMissionControlCloud(settingsInput);
|
|
242
|
+
}
|
package/lib/prompter.mjs
CHANGED
|
@@ -128,27 +128,33 @@ export async function promptLicenseKey() {
|
|
|
128
128
|
|
|
129
129
|
export async function promptCloudAddon() {
|
|
130
130
|
console.log('');
|
|
131
|
-
console.log('
|
|
132
|
-
console.log('
|
|
131
|
+
console.log(' A.L.I.C.E. Cloud add-on');
|
|
132
|
+
console.log(' Remote monitoring, fleet management, and cloud sync for Pro users.');
|
|
133
133
|
console.log('');
|
|
134
|
-
return confirm(' Enable
|
|
134
|
+
return confirm(' Enable A.L.I.C.E. Cloud on this machine?', true);
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
export async function
|
|
137
|
+
export async function promptCloudSetup() {
|
|
138
138
|
console.log('');
|
|
139
|
-
console.log('
|
|
140
|
-
console.log('
|
|
141
|
-
console.log(' You can find it in the "A.L.I.C.E. Pro License Delivered" email.');
|
|
142
|
-
console.log(' Press Enter to skip — you can add it later.');
|
|
139
|
+
console.log(' A.L.I.C.E. Cloud connects your local gateway to alice.av3.ai');
|
|
140
|
+
console.log(' for remote monitoring, fleet management, and cloud sync.');
|
|
143
141
|
console.log('');
|
|
144
|
-
return
|
|
142
|
+
return confirm(' Would you like to set up A.L.I.C.E. Cloud now?', true);
|
|
145
143
|
}
|
|
146
144
|
|
|
147
|
-
export async function
|
|
148
|
-
if (!ingestToken) return '';
|
|
145
|
+
export async function promptCloudReauth() {
|
|
149
146
|
console.log('');
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
return confirm(' Re-authenticate with A.L.I.C.E. Cloud?', false);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export async function promptCloudHeartbeat() {
|
|
151
|
+
return confirm(' Start the heartbeat daemon (keeps cloud connection alive)?', true);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export async function promptCloudToken() {
|
|
155
|
+
console.log('');
|
|
156
|
+
console.log(' After logging in via your browser, paste the access token here.');
|
|
157
|
+
console.log(' Press Enter to skip — you can run `alice-cloud login` later.');
|
|
152
158
|
console.log('');
|
|
153
|
-
return input('
|
|
159
|
+
return input(' Access token', '');
|
|
154
160
|
}
|