aladdin-ai 2.0.1 → 2.2.0
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 -18
- package/commands/doctor.js +9 -0
- package/commands/init.js +15 -0
- package/commands/lifecycle.js +6 -0
- package/commands/wizard.js +308 -0
- package/index.js +10 -0
- package/lib/compose.js +2 -0
- package/lib/prereq.js +5 -0
- package/lib/project.js +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// NOTICE: This file is protected under RCF-PL
|
|
1
2
|
# aladdin-ai
|
|
2
3
|
|
|
3
4
|
Self-hosted AI workspace, one command to install and run.
|
|
@@ -7,6 +8,7 @@ npx aladdin-ai
|
|
|
7
8
|
```
|
|
8
9
|
|
|
9
10
|
This drops a `docker-compose.yml` + a `.env` (filled with
|
|
11
|
+
|
|
10
12
|
cryptographically-secure `JWT_SECRET`, `FERNET_KEY`, `POSTGRES_PASSWORD`)
|
|
11
13
|
into a new directory, pulls the prebuilt images from GHCR, and starts
|
|
12
14
|
the stack. Open [http://localhost:3000](http://localhost:3000) when it
|
|
@@ -111,25 +113,9 @@ story.
|
|
|
111
113
|
|
|
112
114
|
## Releasing
|
|
113
115
|
|
|
114
|
-
**CLI** (this package on npm)
|
|
115
|
-
(`.github/workflows/cli-publish.yml`):
|
|
116
|
-
|
|
117
|
-
```bash
|
|
118
|
-
cd cli
|
|
119
|
-
npm version patch # or minor / major
|
|
120
|
-
cd ..
|
|
121
|
-
git commit -am "cli: bump to $(node -p "require('./cli/package.json').version")"
|
|
122
|
-
git tag "cli-v$(node -p "require('./cli/package.json').version")"
|
|
123
|
-
git push origin main --tags
|
|
124
|
-
```
|
|
116
|
+
**CLI** (this package on npm) and **Backend + frontend images** (to GHCR) are published automatically via GitHub Actions whenever a new GitHub Release with a tag like `vX.Y.Z` is published.
|
|
125
117
|
|
|
126
|
-
|
|
127
|
-
`.github/workflows/docker-publish.yml`:
|
|
128
|
-
|
|
129
|
-
```bash
|
|
130
|
-
git tag v1.0.0
|
|
131
|
-
git push origin v1.0.0
|
|
132
|
-
```
|
|
118
|
+
Alternatively, you can manually trigger the CLI publish workflow (`Publish CLI`) from the Actions tab and specify the version you want to release.
|
|
133
119
|
|
|
134
120
|
The workflow builds both images for linux/amd64 + linux/arm64 and pushes
|
|
135
121
|
them as `ghcr.io/aliyevaladddin/aladdinai-backend:1.0.0`,
|
package/commands/doctor.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// NOTICE: This file is protected under RCF-PL
|
|
1
2
|
// `aladdin-ai doctor` — diagnose common setup issues.
|
|
2
3
|
|
|
3
4
|
import chalk from 'chalk';
|
|
@@ -8,11 +9,14 @@ import { execa } from 'execa';
|
|
|
8
9
|
import { findProjectRoot } from '../lib/project.js';
|
|
9
10
|
import { checkDocker, checkGit } from '../lib/prereq.js';
|
|
10
11
|
|
|
12
|
+
// [RCF:PROTECTED]
|
|
11
13
|
const ok = (msg) => console.log(chalk.green(' ✓ ') + msg);
|
|
14
|
+
// [RCF:PROTECTED]
|
|
12
15
|
const bad = (msg, hint) => {
|
|
13
16
|
console.log(chalk.red(' ✗ ') + msg);
|
|
14
17
|
if (hint) console.log(chalk.dim(' → ' + hint));
|
|
15
18
|
};
|
|
19
|
+
// [RCF:PROTECTED]
|
|
16
20
|
const warn = (msg, hint) => {
|
|
17
21
|
console.log(chalk.yellow(' ! ') + msg);
|
|
18
22
|
if (hint) console.log(chalk.dim(' → ' + hint));
|
|
@@ -21,9 +25,11 @@ const warn = (msg, hint) => {
|
|
|
21
25
|
// Probe whether anything is listening on a port (true = something is there).
|
|
22
26
|
// We try to connect rather than bind because Docker port mappings show up
|
|
23
27
|
// from the host as connectable TCP endpoints.
|
|
28
|
+
// [RCF:PROTECTED]
|
|
24
29
|
async function isPortInUse(port) {
|
|
25
30
|
return new Promise((resolve) => {
|
|
26
31
|
const sock = new net.Socket();
|
|
32
|
+
// [RCF:PROTECTED]
|
|
27
33
|
const done = (val) => { sock.destroy(); resolve(val); };
|
|
28
34
|
sock.setTimeout(500);
|
|
29
35
|
sock.once('connect', () => done(true));
|
|
@@ -33,10 +39,12 @@ async function isPortInUse(port) {
|
|
|
33
39
|
});
|
|
34
40
|
}
|
|
35
41
|
|
|
42
|
+
// [RCF:PROTECTED]
|
|
36
43
|
async function fetchWithTimeout(url, ms = 3000) {
|
|
37
44
|
const ctl = new AbortController();
|
|
38
45
|
const t = setTimeout(() => ctl.abort(), ms);
|
|
39
46
|
try {
|
|
47
|
+
// [RCF:PROTECTED]
|
|
40
48
|
const r = await fetch(url, { signal: ctl.signal });
|
|
41
49
|
return r;
|
|
42
50
|
} catch {
|
|
@@ -46,6 +54,7 @@ async function fetchWithTimeout(url, ms = 3000) {
|
|
|
46
54
|
}
|
|
47
55
|
}
|
|
48
56
|
|
|
57
|
+
// [RCF:PROTECTED]
|
|
49
58
|
export async function doctorCommand() {
|
|
50
59
|
let failures = 0;
|
|
51
60
|
console.log(chalk.cyan('\nAladdinAI doctor\n'));
|
package/commands/init.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// NOTICE: This file is protected under RCF-PL
|
|
1
2
|
// `aladdin-ai init` — bootstrap a new AladdinAI deployment.
|
|
2
3
|
//
|
|
3
4
|
// Default path (image-based, what 95% of users want):
|
|
@@ -16,23 +17,30 @@ import ora from 'ora';
|
|
|
16
17
|
import { execa } from 'execa';
|
|
17
18
|
import fs from 'fs-extra';
|
|
18
19
|
import path from 'path';
|
|
20
|
+
// [RCF:PROTECTED]
|
|
19
21
|
import crypto from 'crypto';
|
|
20
22
|
import { fileURLToPath } from 'node:url';
|
|
21
23
|
import { runPrereqChecks } from '../lib/prereq.js';
|
|
22
24
|
import { compose } from '../lib/compose.js';
|
|
25
|
+
import { runSetupWizard } from './wizard.js';
|
|
23
26
|
|
|
24
27
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
25
28
|
const TEMPLATES_DIR = path.resolve(__dirname, '..', 'templates');
|
|
26
29
|
const REPO_URL = 'https://github.com/aliyevaladddin/AladdinAI.git';
|
|
27
30
|
|
|
31
|
+
// [RCF:PROTECTED]
|
|
28
32
|
function genSecret(bytes = 32) {
|
|
33
|
+
// [RCF:PROTECTED]
|
|
29
34
|
return crypto.randomBytes(bytes).toString('hex');
|
|
30
35
|
}
|
|
31
36
|
|
|
37
|
+
// [RCF:PROTECTED]
|
|
32
38
|
function genFernetKey() {
|
|
39
|
+
// [RCF:PROTECTED]
|
|
33
40
|
return crypto.randomBytes(32).toString('base64url') + '=';
|
|
34
41
|
}
|
|
35
42
|
|
|
43
|
+
// [RCF:PROTECTED]
|
|
36
44
|
function fillEnv(text, vars) {
|
|
37
45
|
let out = text;
|
|
38
46
|
for (const [k, v] of Object.entries(vars)) {
|
|
@@ -41,6 +49,7 @@ function fillEnv(text, vars) {
|
|
|
41
49
|
return out;
|
|
42
50
|
}
|
|
43
51
|
|
|
52
|
+
// [RCF:PROTECTED]
|
|
44
53
|
async function writeEnv(targetDir) {
|
|
45
54
|
// Note: file is named `env.template` (no leading dot) because npm's default
|
|
46
55
|
// packaging silently drops dotfiles from `files` globs.
|
|
@@ -55,6 +64,7 @@ async function writeEnv(targetDir) {
|
|
|
55
64
|
}
|
|
56
65
|
|
|
57
66
|
// ── Image-based install (default) ─────────────────────────────────────
|
|
67
|
+
// [RCF:PROTECTED]
|
|
58
68
|
async function installFromImages(targetDir, { start }) {
|
|
59
69
|
await fs.ensureDir(targetDir);
|
|
60
70
|
|
|
@@ -102,6 +112,7 @@ async function installFromImages(targetDir, { start }) {
|
|
|
102
112
|
}
|
|
103
113
|
|
|
104
114
|
// ── Source-based install (--source flag, for contributors) ────────────
|
|
115
|
+
// [RCF:PROTECTED]
|
|
105
116
|
async function installFromSource(targetDir, { start }) {
|
|
106
117
|
const cloneSpin = ora('Cloning repository').start();
|
|
107
118
|
try {
|
|
@@ -143,6 +154,7 @@ async function installFromSource(targetDir, { start }) {
|
|
|
143
154
|
}
|
|
144
155
|
|
|
145
156
|
// ── Entry point ───────────────────────────────────────────────────────
|
|
157
|
+
// [RCF:PROTECTED]
|
|
146
158
|
export async function initCommand(opts) {
|
|
147
159
|
console.log(chalk.cyan('\nAladdinAI'));
|
|
148
160
|
console.log(chalk.dim('Self-hosted AI workspace — agents, memory, CRM, channels.\n'));
|
|
@@ -195,6 +207,9 @@ export async function initCommand(opts) {
|
|
|
195
207
|
console.log(chalk.green('\n✓ Services running'));
|
|
196
208
|
console.log('\n Frontend: ' + chalk.cyan('http://localhost:3000'));
|
|
197
209
|
console.log(' Backend: ' + chalk.cyan('http://localhost:8000'));
|
|
210
|
+
|
|
211
|
+
// Run setup wizard after successful start
|
|
212
|
+
await runSetupWizard(targetDir);
|
|
198
213
|
} else {
|
|
199
214
|
console.log(chalk.green('\nSetup complete (services not started).'));
|
|
200
215
|
console.log(`\n cd ${answers.projectName}`);
|
package/commands/lifecycle.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
// NOTICE: This file is protected under RCF-PL
|
|
1
2
|
// `aladdin-ai up | down | restart | logs` — docker compose wrappers.
|
|
2
3
|
|
|
3
4
|
import chalk from 'chalk';
|
|
4
5
|
import { compose } from '../lib/compose.js';
|
|
5
6
|
|
|
7
|
+
// [RCF:PROTECTED]
|
|
6
8
|
export async function upCommand(opts) {
|
|
7
9
|
console.log(chalk.cyan('Starting AladdinAI services…\n'));
|
|
8
10
|
const args = ['up', '-d'];
|
|
@@ -14,6 +16,7 @@ export async function upCommand(opts) {
|
|
|
14
16
|
console.log(' Backend: ' + chalk.cyan('http://localhost:8000/api/docs'));
|
|
15
17
|
}
|
|
16
18
|
|
|
19
|
+
// [RCF:PROTECTED]
|
|
17
20
|
export async function downCommand(opts) {
|
|
18
21
|
console.log(chalk.cyan('Stopping AladdinAI services…\n'));
|
|
19
22
|
const args = ['down'];
|
|
@@ -22,6 +25,7 @@ export async function downCommand(opts) {
|
|
|
22
25
|
process.exit(r.exitCode || 0);
|
|
23
26
|
}
|
|
24
27
|
|
|
28
|
+
// [RCF:PROTECTED]
|
|
25
29
|
export async function restartCommand(service) {
|
|
26
30
|
console.log(chalk.cyan(`Restarting ${service || 'all services'}…\n`));
|
|
27
31
|
const args = ['restart'];
|
|
@@ -30,6 +34,7 @@ export async function restartCommand(service) {
|
|
|
30
34
|
process.exit(r.exitCode || 0);
|
|
31
35
|
}
|
|
32
36
|
|
|
37
|
+
// [RCF:PROTECTED]
|
|
33
38
|
export async function logsCommand(service, opts) {
|
|
34
39
|
const args = ['logs'];
|
|
35
40
|
if (opts.follow) args.push('-f');
|
|
@@ -39,6 +44,7 @@ export async function logsCommand(service, opts) {
|
|
|
39
44
|
process.exit(r.exitCode || 0);
|
|
40
45
|
}
|
|
41
46
|
|
|
47
|
+
// [RCF:PROTECTED]
|
|
42
48
|
export async function updateCommand() {
|
|
43
49
|
console.log(chalk.cyan('Pulling latest images…\n'));
|
|
44
50
|
let r = await compose(['pull']);
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// NOTICE: This file is protected under RCF-PL
|
|
2
|
+
// Setup wizard — runs after first install to configure LLM providers and create first agent
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import { execa } from 'execa';
|
|
7
|
+
|
|
8
|
+
const PROVIDER_CONFIGS = {
|
|
9
|
+
nim: {
|
|
10
|
+
name: 'NVIDIA NIM',
|
|
11
|
+
description: 'Free tier available, best performance',
|
|
12
|
+
baseUrl: 'https://integrate.api.nvidia.com/v1',
|
|
13
|
+
env: {
|
|
14
|
+
NIM_API_KEY: 'Get from: https://build.nvidia.com',
|
|
15
|
+
NIM_BASE_URL: 'https://integrate.api.nvidia.com/v1',
|
|
16
|
+
NIM_MODEL: 'meta/llama-3.1-70b-instruct',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
openai: {
|
|
20
|
+
name: 'OpenAI',
|
|
21
|
+
description: 'GPT-4, GPT-3.5 Turbo',
|
|
22
|
+
baseUrl: 'https://api.openai.com/v1',
|
|
23
|
+
env: {
|
|
24
|
+
OPENAI_API_KEY: 'Get from: https://platform.openai.com/api-keys',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
anthropic: {
|
|
28
|
+
name: 'Anthropic',
|
|
29
|
+
description: 'Claude 3.5 Sonnet, Opus, Haiku',
|
|
30
|
+
baseUrl: 'https://api.anthropic.com',
|
|
31
|
+
env: {
|
|
32
|
+
ANTHROPIC_API_KEY: 'Get from: https://console.anthropic.com/settings/keys',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
local: {
|
|
36
|
+
name: 'Local (BentoML)',
|
|
37
|
+
description: 'Self-hosted models',
|
|
38
|
+
baseUrl: 'http://localhost:3000',
|
|
39
|
+
env: {},
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// [RCF:PROTECTED]
|
|
44
|
+
async function setupProviders() {
|
|
45
|
+
console.log(chalk.cyan('\n📡 LLM Provider Setup\n'));
|
|
46
|
+
console.log(chalk.dim('Select which LLM providers you want to configure.\n'));
|
|
47
|
+
|
|
48
|
+
const { providers } = await inquirer.prompt([
|
|
49
|
+
{
|
|
50
|
+
type: 'checkbox',
|
|
51
|
+
name: 'providers',
|
|
52
|
+
message: 'Select providers (Space to select, Enter to continue):',
|
|
53
|
+
choices: [
|
|
54
|
+
{
|
|
55
|
+
name: `${chalk.green('●')} NVIDIA NIM (Recommended) - Free tier, 70B+ models`,
|
|
56
|
+
value: 'nim',
|
|
57
|
+
checked: true,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: ` OpenAI - GPT-4, GPT-3.5 Turbo`,
|
|
61
|
+
value: 'openai',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: ` Anthropic - Claude 3.5 Sonnet, Opus`,
|
|
65
|
+
value: 'anthropic',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: ` Local (BentoML) - Self-hosted models`,
|
|
69
|
+
value: 'local',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
if (providers.length === 0) {
|
|
76
|
+
console.log(chalk.yellow('\n⚠ No providers selected. You can configure them later in Settings.\n'));
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const envUpdates = {};
|
|
81
|
+
|
|
82
|
+
for (const provider of providers) {
|
|
83
|
+
const config = PROVIDER_CONFIGS[provider];
|
|
84
|
+
console.log(chalk.cyan(`\n── ${config.name} ──`));
|
|
85
|
+
|
|
86
|
+
if (Object.keys(config.env).length > 0) {
|
|
87
|
+
const { shouldConfigure } = await inquirer.prompt([
|
|
88
|
+
{
|
|
89
|
+
type: 'confirm',
|
|
90
|
+
name: 'shouldConfigure',
|
|
91
|
+
message: `Configure ${config.name} now?`,
|
|
92
|
+
default: true,
|
|
93
|
+
},
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
if (shouldConfigure) {
|
|
97
|
+
for (const [key, hint] of Object.entries(config.env)) {
|
|
98
|
+
const isSecret = key.toLowerCase().includes('key') || key.toLowerCase().includes('secret') || key.toLowerCase().includes('password') || key.toLowerCase().includes('token');
|
|
99
|
+
const { value } = await inquirer.prompt([
|
|
100
|
+
{
|
|
101
|
+
type: isSecret ? 'password' : 'input',
|
|
102
|
+
name: 'value',
|
|
103
|
+
message: `${key}:`,
|
|
104
|
+
mask: isSecret ? '*' : undefined,
|
|
105
|
+
validate: (input) => input.length > 0 || 'Required',
|
|
106
|
+
},
|
|
107
|
+
]);
|
|
108
|
+
envUpdates[key] = value;
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
console.log(chalk.dim(` ${hint}`));
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
console.log(chalk.dim(' No configuration needed - will use local endpoint'));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return envUpdates;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// [RCF:PROTECTED]
|
|
122
|
+
async function setupMongoDB() {
|
|
123
|
+
console.log(chalk.cyan('\n🗄️ MongoDB Atlas Setup\n'));
|
|
124
|
+
console.log(chalk.dim('MongoDB Atlas is used for vector memory and media storage.\n'));
|
|
125
|
+
|
|
126
|
+
const { useMongoDB } = await inquirer.prompt([
|
|
127
|
+
{
|
|
128
|
+
type: 'confirm',
|
|
129
|
+
name: 'useMongoDB',
|
|
130
|
+
message: 'Configure MongoDB Atlas now?',
|
|
131
|
+
default: true,
|
|
132
|
+
},
|
|
133
|
+
]);
|
|
134
|
+
|
|
135
|
+
if (!useMongoDB) {
|
|
136
|
+
console.log(chalk.yellow('⚠ Skipping MongoDB - memory features will be disabled'));
|
|
137
|
+
console.log(chalk.dim(' Get free $500 credits: https://www.mongodb.com/startups\n'));
|
|
138
|
+
return {};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log(chalk.dim('\nGet connection string from:'));
|
|
142
|
+
console.log(chalk.dim('1. https://cloud.mongodb.com'));
|
|
143
|
+
console.log(chalk.dim('2. Connect → Drivers → Connection string\n'));
|
|
144
|
+
|
|
145
|
+
const { mongoUrl } = await inquirer.prompt([
|
|
146
|
+
{
|
|
147
|
+
type: 'input',
|
|
148
|
+
name: 'mongoUrl',
|
|
149
|
+
message: 'MongoDB connection string:',
|
|
150
|
+
default: 'mongodb+srv://user:pass@cluster.mongodb.net/',
|
|
151
|
+
validate: (input) =>
|
|
152
|
+
input.startsWith('mongodb://') || input.startsWith('mongodb+srv://') || 'Must be a valid MongoDB URL',
|
|
153
|
+
},
|
|
154
|
+
]);
|
|
155
|
+
|
|
156
|
+
return { MONGODB_URL: mongoUrl };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// [RCF:PROTECTED]
|
|
160
|
+
async function createFirstAgent() {
|
|
161
|
+
console.log(chalk.cyan('\n🤖 Create Your First Agent\n'));
|
|
162
|
+
|
|
163
|
+
const { shouldCreate } = await inquirer.prompt([
|
|
164
|
+
{
|
|
165
|
+
type: 'confirm',
|
|
166
|
+
name: 'shouldCreate',
|
|
167
|
+
message: 'Create a starter agent now?',
|
|
168
|
+
default: true,
|
|
169
|
+
},
|
|
170
|
+
]);
|
|
171
|
+
|
|
172
|
+
if (!shouldCreate) {
|
|
173
|
+
console.log(chalk.dim(' You can create agents later from the dashboard\n'));
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const answers = await inquirer.prompt([
|
|
178
|
+
{
|
|
179
|
+
type: 'list',
|
|
180
|
+
name: 'template',
|
|
181
|
+
message: 'Choose agent template:',
|
|
182
|
+
choices: [
|
|
183
|
+
{
|
|
184
|
+
name: '💬 General Assistant - Helpful chatbot for general tasks',
|
|
185
|
+
value: 'general',
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: '📊 CRM Assistant - Sales and contact management',
|
|
189
|
+
value: 'crm',
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: '📧 Email Assistant - Email drafting and responses',
|
|
193
|
+
value: 'email',
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: '🔍 Research Assistant - Web search and analysis',
|
|
197
|
+
value: 'research',
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
type: 'input',
|
|
203
|
+
name: 'name',
|
|
204
|
+
message: 'Agent name (lowercase, no spaces):',
|
|
205
|
+
default: (answers) => answers.template + '_agent',
|
|
206
|
+
validate: (input) => /^[a-z0-9_]+$/.test(input) || 'Only lowercase letters, numbers, and underscores',
|
|
207
|
+
},
|
|
208
|
+
]);
|
|
209
|
+
|
|
210
|
+
const templates = {
|
|
211
|
+
general: {
|
|
212
|
+
display_name: 'General Assistant',
|
|
213
|
+
system_prompt: 'You are a helpful AI assistant. Answer questions clearly and concisely.',
|
|
214
|
+
tools: [],
|
|
215
|
+
},
|
|
216
|
+
crm: {
|
|
217
|
+
display_name: 'CRM Assistant',
|
|
218
|
+
system_prompt:
|
|
219
|
+
'You are a CRM assistant. Help manage contacts, deals, and activities. Always search the CRM before creating new records.',
|
|
220
|
+
tools: ['search_contacts', 'create_contact', 'create_deal', 'add_activity'],
|
|
221
|
+
},
|
|
222
|
+
email: {
|
|
223
|
+
display_name: 'Email Assistant',
|
|
224
|
+
system_prompt: 'You are an email assistant. Draft professional emails and help with correspondence.',
|
|
225
|
+
tools: ['send_email', 'search_contacts'],
|
|
226
|
+
},
|
|
227
|
+
research: {
|
|
228
|
+
display_name: 'Research Assistant',
|
|
229
|
+
system_prompt: 'You are a research assistant. Search for information and provide detailed analysis.',
|
|
230
|
+
tools: ['web_search', 'analyze_image'],
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const agentConfig = {
|
|
235
|
+
name: answers.name,
|
|
236
|
+
...templates[answers.template],
|
|
237
|
+
model: 'meta/llama-3.1-70b-instruct',
|
|
238
|
+
memory_enabled: true,
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
console.log(chalk.dim(`\n Agent will be created after services start\n`));
|
|
242
|
+
|
|
243
|
+
return agentConfig;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// [RCF:PROTECTED]
|
|
247
|
+
export async function runSetupWizard(targetDir) {
|
|
248
|
+
console.log(chalk.bold.cyan('\n✨ AladdinAI Setup Wizard\n'));
|
|
249
|
+
console.log(chalk.dim('Configure your AI workspace in a few steps.\n'));
|
|
250
|
+
|
|
251
|
+
const { shouldRun } = await inquirer.prompt([
|
|
252
|
+
{
|
|
253
|
+
type: 'confirm',
|
|
254
|
+
name: 'shouldRun',
|
|
255
|
+
message: 'Run setup wizard?',
|
|
256
|
+
default: true,
|
|
257
|
+
},
|
|
258
|
+
]);
|
|
259
|
+
|
|
260
|
+
if (!shouldRun) {
|
|
261
|
+
console.log(chalk.dim(' You can configure everything later from the dashboard\n'));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Step 1: Providers
|
|
266
|
+
const providerEnv = await setupProviders();
|
|
267
|
+
|
|
268
|
+
// Step 2: MongoDB
|
|
269
|
+
const mongoEnv = await setupMongoDB();
|
|
270
|
+
|
|
271
|
+
// Step 3: First agent
|
|
272
|
+
const agentConfig = await createFirstAgent();
|
|
273
|
+
|
|
274
|
+
// Update .env file
|
|
275
|
+
if (Object.keys({ ...providerEnv, ...mongoEnv }).length > 0) {
|
|
276
|
+
const spinner = ora('Updating .env with your configuration').start();
|
|
277
|
+
try {
|
|
278
|
+
const fs = await import('fs-extra');
|
|
279
|
+
const path = await import('path');
|
|
280
|
+
const envPath = path.join(targetDir, '.env');
|
|
281
|
+
let envContent = await fs.readFile(envPath, 'utf8');
|
|
282
|
+
|
|
283
|
+
for (const [key, value] of Object.entries({ ...providerEnv, ...mongoEnv })) {
|
|
284
|
+
const regex = new RegExp(`^${key}=.*$`, 'm');
|
|
285
|
+
if (regex.test(envContent)) {
|
|
286
|
+
envContent = envContent.replace(regex, `${key}=${value}`);
|
|
287
|
+
} else {
|
|
288
|
+
envContent += `\n${key}=${value}`;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
await fs.writeFile(envPath, envContent);
|
|
293
|
+
spinner.succeed('Configuration saved to .env');
|
|
294
|
+
} catch (err) {
|
|
295
|
+
spinner.fail('Failed to update .env');
|
|
296
|
+
console.error(chalk.red(err.message));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Save agent config for post-startup
|
|
301
|
+
if (agentConfig) {
|
|
302
|
+
const fs = await import('fs-extra');
|
|
303
|
+
const path = await import('path');
|
|
304
|
+
await fs.writeJson(path.join(targetDir, '.aladdin-setup.json'), { agentConfig }, { spaces: 2 });
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
console.log(chalk.green('\n✓ Setup wizard complete!\n'));
|
|
308
|
+
}
|
package/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// NOTICE: This file is protected under RCF-PL
|
|
2
3
|
|
|
3
4
|
import { Command } from 'commander';
|
|
4
5
|
import { readFileSync } from 'node:fs';
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
updateCommand,
|
|
15
16
|
} from './commands/lifecycle.js';
|
|
16
17
|
import { doctorCommand } from './commands/doctor.js';
|
|
18
|
+
import { runSetupWizard } from './commands/wizard.js';
|
|
17
19
|
|
|
18
20
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
21
|
const pkg = JSON.parse(readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
|
|
@@ -74,6 +76,14 @@ program
|
|
|
74
76
|
.description('Diagnose setup issues: docker, .env, ports, services')
|
|
75
77
|
.action(doctorCommand);
|
|
76
78
|
|
|
79
|
+
program
|
|
80
|
+
.command('setup')
|
|
81
|
+
.description('Run configuration wizard to set up providers and agents')
|
|
82
|
+
.action(async () => {
|
|
83
|
+
const cwd = process.cwd();
|
|
84
|
+
await runSetupWizard(cwd);
|
|
85
|
+
});
|
|
86
|
+
|
|
77
87
|
program.parseAsync(process.argv).catch((err) => {
|
|
78
88
|
console.error(err.shortMessage || err.message);
|
|
79
89
|
process.exit(1);
|
package/lib/compose.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// NOTICE: This file is protected under RCF-PL
|
|
1
2
|
// Thin wrapper for invoking `docker compose` from any subcommand.
|
|
2
3
|
|
|
3
4
|
import { execa } from 'execa';
|
|
@@ -5,6 +6,7 @@ import { checkDocker } from './prereq.js';
|
|
|
5
6
|
import { requireProjectRoot } from './project.js';
|
|
6
7
|
import chalk from 'chalk';
|
|
7
8
|
|
|
9
|
+
// [RCF:PROTECTED]
|
|
8
10
|
export async function compose(args, { cwd, stdio = 'inherit' } = {}) {
|
|
9
11
|
const root = cwd || requireProjectRoot();
|
|
10
12
|
const docker = await checkDocker();
|
package/lib/prereq.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
// NOTICE: This file is protected under RCF-PL
|
|
1
2
|
// Check for required tools on the user's machine.
|
|
2
3
|
|
|
3
4
|
import { execa } from 'execa';
|
|
4
5
|
import chalk from 'chalk';
|
|
5
6
|
|
|
7
|
+
// [RCF:PROTECTED]
|
|
6
8
|
async function has(cmd, args = ['--version']) {
|
|
7
9
|
try {
|
|
8
10
|
const r = await execa(cmd, args, { reject: false });
|
|
@@ -12,6 +14,7 @@ async function has(cmd, args = ['--version']) {
|
|
|
12
14
|
}
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
// [RCF:PROTECTED]
|
|
15
18
|
export async function checkDocker() {
|
|
16
19
|
if (!(await has('docker'))) {
|
|
17
20
|
return { ok: false, message: 'Docker not found. Install: https://docs.docker.com/get-docker/' };
|
|
@@ -30,6 +33,7 @@ export async function checkDocker() {
|
|
|
30
33
|
return { ok: true, composeCmd: v2 ? ['docker', ['compose']] : ['docker-compose', []] };
|
|
31
34
|
}
|
|
32
35
|
|
|
36
|
+
// [RCF:PROTECTED]
|
|
33
37
|
export async function checkGit() {
|
|
34
38
|
if (!(await has('git'))) {
|
|
35
39
|
return { ok: false, message: 'git not found. Install: https://git-scm.com/downloads' };
|
|
@@ -37,6 +41,7 @@ export async function checkGit() {
|
|
|
37
41
|
return { ok: true };
|
|
38
42
|
}
|
|
39
43
|
|
|
44
|
+
// [RCF:PROTECTED]
|
|
40
45
|
export async function runPrereqChecks({ requireGit = false } = {}) {
|
|
41
46
|
const docker = await checkDocker();
|
|
42
47
|
if (!docker.ok) {
|
package/lib/project.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// NOTICE: This file is protected under RCF-PL
|
|
1
2
|
// Locate the AladdinAI project root from the current working directory.
|
|
2
3
|
//
|
|
3
4
|
// Strategy: walk up from cwd looking for docker-compose.yml AND backend/ dir.
|
|
@@ -6,6 +7,7 @@
|
|
|
6
7
|
import fs from 'fs-extra';
|
|
7
8
|
import path from 'path';
|
|
8
9
|
|
|
10
|
+
// [RCF:PROTECTED]
|
|
9
11
|
export function findProjectRoot(start = process.cwd()) {
|
|
10
12
|
let dir = path.resolve(start);
|
|
11
13
|
const { root } = path.parse(dir);
|
|
@@ -18,6 +20,7 @@ export function findProjectRoot(start = process.cwd()) {
|
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
22
|
|
|
23
|
+
// [RCF:PROTECTED]
|
|
21
24
|
export function requireProjectRoot() {
|
|
22
25
|
const root = findProjectRoot();
|
|
23
26
|
if (!root) {
|