@epicai/legion 1.0.0 → 1.0.2
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.
Potentially problematic release.
This version of @epicai/legion might be problematic. Click here for more details.
- package/dist/bin/setup.js +23 -373
- package/package.json +1 -1
package/dist/bin/setup.js
CHANGED
|
@@ -104,50 +104,6 @@ function loadCredentials() {
|
|
|
104
104
|
}
|
|
105
105
|
return creds;
|
|
106
106
|
}
|
|
107
|
-
// ─── Category display names ─────────────────────────────────
|
|
108
|
-
const CATEGORY_LABELS = {
|
|
109
|
-
'ai': 'AI / ML',
|
|
110
|
-
'cloud': 'Cloud Infrastructure',
|
|
111
|
-
'collaboration': 'Collaboration',
|
|
112
|
-
'commerce': 'Commerce & eCommerce',
|
|
113
|
-
'communication': 'Communication',
|
|
114
|
-
'compliance': 'Compliance & GRC',
|
|
115
|
-
'construction': 'Construction',
|
|
116
|
-
'crm': 'CRM & Sales',
|
|
117
|
-
'customer-support': 'Customer Support',
|
|
118
|
-
'cybersecurity': 'Cybersecurity',
|
|
119
|
-
'data': 'Data & Analytics',
|
|
120
|
-
'design': 'Design & Creative',
|
|
121
|
-
'devops': 'DevOps & CI/CD',
|
|
122
|
-
'education': 'Education',
|
|
123
|
-
'energy': 'Energy',
|
|
124
|
-
'engineering': 'Engineering & Aerospace',
|
|
125
|
-
'erp': 'ERP',
|
|
126
|
-
'finance': 'Finance & Payments',
|
|
127
|
-
'government': 'Government',
|
|
128
|
-
'healthcare': 'Healthcare',
|
|
129
|
-
'hospitality': 'Hospitality & Food',
|
|
130
|
-
'hr': 'HR & People',
|
|
131
|
-
'identity': 'Identity & Access',
|
|
132
|
-
'insurance': 'Insurance',
|
|
133
|
-
'iot': 'IoT & Devices',
|
|
134
|
-
'legal': 'Legal',
|
|
135
|
-
'logistics': 'Logistics & Shipping',
|
|
136
|
-
'manufacturing': 'Manufacturing',
|
|
137
|
-
'marketing': 'Marketing',
|
|
138
|
-
'media': 'Media & Entertainment',
|
|
139
|
-
'misc': 'Other',
|
|
140
|
-
'observability': 'Observability & Monitoring',
|
|
141
|
-
'productivity': 'Productivity',
|
|
142
|
-
'real-estate': 'Real Estate & Property',
|
|
143
|
-
'science': 'Science & Research',
|
|
144
|
-
'social': 'Social Media',
|
|
145
|
-
'telecom': 'Telecom',
|
|
146
|
-
'travel': 'Travel & Transportation',
|
|
147
|
-
};
|
|
148
|
-
function formatCategory(cat) {
|
|
149
|
-
return CATEGORY_LABELS[cat] || cat.charAt(0).toUpperCase() + cat.slice(1).replace(/-/g, ' ');
|
|
150
|
-
}
|
|
151
107
|
function expandHome(p) {
|
|
152
108
|
return p.startsWith('~') ? join(homedir(), p.slice(1)) : p;
|
|
153
109
|
}
|
|
@@ -962,339 +918,35 @@ async function runSetupWizard() {
|
|
|
962
918
|
p.log.success(`Using ${system.localBackend} on port ${system.localPort}`);
|
|
963
919
|
configuredClients.push('local');
|
|
964
920
|
}
|
|
965
|
-
// Step 3:
|
|
966
|
-
const
|
|
967
|
-
|
|
921
|
+
// Step 3: Zero-credential live demo
|
|
922
|
+
const ZERO_CRED = [
|
|
923
|
+
{ id: 'searchcode', name: 'Searchcode', desc: 'Search 75 billion lines of open source code', tools: 6 },
|
|
924
|
+
{ id: 'com-claude-mcp-pubmed-pubmed', name: 'PubMed', desc: 'Search 36 million biomedical research papers', tools: 7 },
|
|
925
|
+
{ id: 'robtex', name: 'Robtex', desc: 'Network intelligence — DNS, IP, ASN lookups', tools: 45 },
|
|
926
|
+
{ id: 'govbase-mcp', name: 'Govbase', desc: 'Government data — legislators, bills, committees', tools: 10 },
|
|
927
|
+
];
|
|
928
|
+
const demoChoice = await p.select({
|
|
929
|
+
message: 'Try a live data connection? These require no credentials:',
|
|
968
930
|
options: [
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
{ value: '
|
|
975
|
-
{ value: 'manual', label: 'I\'ll enter them manually' },
|
|
931
|
+
...ZERO_CRED.map(a => ({
|
|
932
|
+
value: a.id,
|
|
933
|
+
label: `${a.name}`,
|
|
934
|
+
hint: `${a.desc} — ${a.tools} tools`,
|
|
935
|
+
})),
|
|
936
|
+
{ value: 'skip', label: 'Skip for now', hint: 'add adapters later with: npx @epicai/legion add <name>' },
|
|
976
937
|
],
|
|
977
938
|
});
|
|
978
|
-
if (p.isCancel(
|
|
979
|
-
p.cancel('Setup cancelled.');
|
|
980
|
-
process.exit(0);
|
|
981
|
-
}
|
|
982
|
-
let vaultAddr = '';
|
|
983
|
-
let vaultToken = '';
|
|
984
|
-
let vaultConnected = false;
|
|
985
|
-
if (secretsChoice === 'vault') {
|
|
986
|
-
const envAddr = process.env.VAULT_ADDR;
|
|
987
|
-
const envToken = process.env.VAULT_TOKEN;
|
|
988
|
-
if (envAddr && envToken) {
|
|
989
|
-
p.log.success(`Detected VAULT_ADDR=${envAddr}`);
|
|
990
|
-
vaultAddr = envAddr;
|
|
991
|
-
vaultToken = envToken;
|
|
992
|
-
}
|
|
993
|
-
else {
|
|
994
|
-
const addr = await p.text({
|
|
995
|
-
message: 'Vault address',
|
|
996
|
-
placeholder: 'https://vault.example.com:8200',
|
|
997
|
-
validate: (v) => { if (!v)
|
|
998
|
-
return 'Required'; if (!v.startsWith('http'))
|
|
999
|
-
return 'Must start with http:// or https://'; },
|
|
1000
|
-
});
|
|
1001
|
-
if (p.isCancel(addr)) {
|
|
1002
|
-
p.cancel('Setup cancelled.');
|
|
1003
|
-
process.exit(0);
|
|
1004
|
-
}
|
|
1005
|
-
const token = await p.password({ message: 'Vault token', validate: (v) => { if (!v)
|
|
1006
|
-
return 'Required'; } });
|
|
1007
|
-
if (p.isCancel(token)) {
|
|
1008
|
-
p.cancel('Setup cancelled.');
|
|
1009
|
-
process.exit(0);
|
|
1010
|
-
}
|
|
1011
|
-
vaultAddr = addr;
|
|
1012
|
-
vaultToken = token;
|
|
1013
|
-
}
|
|
1014
|
-
s.start('Connecting to Vault');
|
|
1015
|
-
try {
|
|
1016
|
-
const controller = new AbortController();
|
|
1017
|
-
const t = setTimeout(() => controller.abort(), 5000);
|
|
1018
|
-
const resp = await fetch(`${vaultAddr}/v1/sys/health`, { headers: { 'X-Vault-Token': vaultToken }, signal: controller.signal });
|
|
1019
|
-
clearTimeout(t);
|
|
1020
|
-
if (resp.ok) {
|
|
1021
|
-
vaultConnected = true;
|
|
1022
|
-
s.stop(`Connected to Vault at ${vaultAddr}`);
|
|
1023
|
-
writeCredential('VAULT_ADDR', vaultAddr);
|
|
1024
|
-
writeCredential('VAULT_TOKEN', vaultToken);
|
|
1025
|
-
}
|
|
1026
|
-
else {
|
|
1027
|
-
s.stop(`Vault returned ${resp.status} — check your token`);
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
catch {
|
|
1031
|
-
s.stop(`Cannot reach Vault at ${vaultAddr}`);
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
if (secretsChoice === '1password') {
|
|
1035
|
-
try {
|
|
1036
|
-
execSync('op --version', { stdio: 'pipe' });
|
|
1037
|
-
p.log.success('1Password CLI detected');
|
|
1038
|
-
}
|
|
1039
|
-
catch {
|
|
1040
|
-
p.log.warning('1Password CLI not found — install from https://1password.com/downloads/command-line');
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
if (secretsChoice === 'doppler') {
|
|
1044
|
-
try {
|
|
1045
|
-
execSync('doppler --version', { stdio: 'pipe' });
|
|
1046
|
-
p.log.success('Doppler CLI detected');
|
|
1047
|
-
}
|
|
1048
|
-
catch {
|
|
1049
|
-
p.log.warning('Doppler CLI not found — install from https://docs.doppler.com/docs/install-cli');
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
// Step 4: Adapter selection — tree structure (group → category → adapters)
|
|
1053
|
-
// Build category map
|
|
1054
|
-
const categories = new Map();
|
|
1055
|
-
for (const adapter of allAdapters) {
|
|
1056
|
-
const cat = adapter.category || 'other';
|
|
1057
|
-
if (!categories.has(cat))
|
|
1058
|
-
categories.set(cat, []);
|
|
1059
|
-
categories.get(cat).push(adapter);
|
|
1060
|
-
}
|
|
1061
|
-
// Top-level groups that map to subcategories
|
|
1062
|
-
const GROUPS = {
|
|
1063
|
-
'security': {
|
|
1064
|
-
label: 'Security & Identity',
|
|
1065
|
-
cats: ['cybersecurity', 'identity', 'compliance'],
|
|
1066
|
-
},
|
|
1067
|
-
'devops': {
|
|
1068
|
-
label: 'DevOps & Infrastructure',
|
|
1069
|
-
cats: ['devops', 'cloud', 'observability', 'engineering'],
|
|
1070
|
-
},
|
|
1071
|
-
'business': {
|
|
1072
|
-
label: 'Business & Finance',
|
|
1073
|
-
cats: ['finance', 'crm', 'commerce', 'marketing', 'customer-support', 'hr', 'productivity', 'erp'],
|
|
1074
|
-
},
|
|
1075
|
-
'comms': {
|
|
1076
|
-
label: 'Communication & Collaboration',
|
|
1077
|
-
cats: ['communication', 'collaboration'],
|
|
1078
|
-
},
|
|
1079
|
-
'data-ai': {
|
|
1080
|
-
label: 'Data & AI',
|
|
1081
|
-
cats: ['data', 'ai', 'science'],
|
|
1082
|
-
},
|
|
1083
|
-
'industry': {
|
|
1084
|
-
label: 'Industry',
|
|
1085
|
-
cats: ['healthcare', 'hospitality', 'construction', 'manufacturing', 'real-estate', 'travel', 'education', 'insurance', 'legal', 'energy', 'automotive', 'telecom', 'logistics', 'government', 'iot'],
|
|
1086
|
-
},
|
|
1087
|
-
'media': {
|
|
1088
|
-
label: 'Media & Social',
|
|
1089
|
-
cats: ['media', 'social', 'design'],
|
|
1090
|
-
},
|
|
1091
|
-
'other': {
|
|
1092
|
-
label: 'Other',
|
|
1093
|
-
cats: ['misc', 'other'],
|
|
1094
|
-
},
|
|
1095
|
-
};
|
|
1096
|
-
// Count adapters per group
|
|
1097
|
-
function countGroup(cats) {
|
|
1098
|
-
let total = 0;
|
|
1099
|
-
for (const cat of cats)
|
|
1100
|
-
total += (categories.get(cat) || []).length;
|
|
1101
|
-
return total;
|
|
1102
|
-
}
|
|
1103
|
-
// Collect any categories not in a group
|
|
1104
|
-
const allGroupedCats = new Set(Object.values(GROUPS).flatMap(g => g.cats));
|
|
1105
|
-
const ungrouped = Array.from(categories.keys()).filter(c => !allGroupedCats.has(c));
|
|
1106
|
-
if (ungrouped.length > 0) {
|
|
1107
|
-
GROUPS['other'].cats.push(...ungrouped);
|
|
1108
|
-
}
|
|
1109
|
-
const groupOptions = Object.entries(GROUPS)
|
|
1110
|
-
.filter(([_, g]) => countGroup(g.cats) > 0)
|
|
1111
|
-
.map(([key, g]) => ({
|
|
1112
|
-
value: key,
|
|
1113
|
-
label: `${g.label} (${countGroup(g.cats)})`,
|
|
1114
|
-
}));
|
|
1115
|
-
const selectedGroups = await p.multiselect({
|
|
1116
|
-
message: 'What do you work with? (Space to select, Enter to confirm)',
|
|
1117
|
-
options: groupOptions,
|
|
1118
|
-
required: false,
|
|
1119
|
-
});
|
|
1120
|
-
if (p.isCancel(selectedGroups)) {
|
|
939
|
+
if (p.isCancel(demoChoice)) {
|
|
1121
940
|
p.cancel('Setup cancelled.');
|
|
1122
941
|
process.exit(0);
|
|
1123
942
|
}
|
|
1124
943
|
const selectedAdapterIds = [];
|
|
1125
|
-
|
|
1126
|
-
const
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
// Show subcategories within this group
|
|
1130
|
-
const subCatOptions = group.cats
|
|
1131
|
-
.filter(cat => (categories.get(cat) || []).length > 0)
|
|
1132
|
-
.map(cat => ({
|
|
1133
|
-
value: cat,
|
|
1134
|
-
label: `${formatCategory(cat)} (${(categories.get(cat) || []).length})`,
|
|
1135
|
-
}));
|
|
1136
|
-
if (subCatOptions.length === 0)
|
|
1137
|
-
continue;
|
|
1138
|
-
const selectedSubCats = await p.multiselect({
|
|
1139
|
-
message: `${group.label} — select categories (Space to select, Enter to confirm)`,
|
|
1140
|
-
options: subCatOptions,
|
|
1141
|
-
required: false,
|
|
1142
|
-
});
|
|
1143
|
-
if (p.isCancel(selectedSubCats))
|
|
1144
|
-
continue;
|
|
1145
|
-
if (selectedSubCats.length > 0) {
|
|
1146
|
-
// Show adapters from selected subcategories
|
|
1147
|
-
const available = [];
|
|
1148
|
-
for (const cat of selectedSubCats) {
|
|
1149
|
-
for (const a of categories.get(cat) || []) {
|
|
1150
|
-
available.push({ value: a.id, label: a.name || a.id, hint: a.description?.slice(0, 60) });
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
if (available.length > 0) {
|
|
1154
|
-
const selected = await p.multiselect({
|
|
1155
|
-
message: `Select adapters (${available.length} available)`,
|
|
1156
|
-
options: available.slice(0, 50),
|
|
1157
|
-
required: false,
|
|
1158
|
-
});
|
|
1159
|
-
if (!p.isCancel(selected)) {
|
|
1160
|
-
selectedAdapterIds.push(...selected);
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
// Step 5: Credentials
|
|
1166
|
-
if (selectedAdapterIds.length > 0) {
|
|
1167
|
-
if (secretsChoice === 'vault' && vaultConnected) {
|
|
1168
|
-
s.start('Pulling credentials from Vault');
|
|
1169
|
-
let pulled = 0;
|
|
1170
|
-
for (const adapterId of selectedAdapterIds) {
|
|
1171
|
-
try {
|
|
1172
|
-
const controller = new AbortController();
|
|
1173
|
-
const t = setTimeout(() => controller.abort(), 3000);
|
|
1174
|
-
const resp = await fetch(`${vaultAddr}/v1/secret/data/${adapterId}`, {
|
|
1175
|
-
headers: { 'X-Vault-Token': vaultToken },
|
|
1176
|
-
signal: controller.signal,
|
|
1177
|
-
});
|
|
1178
|
-
clearTimeout(t);
|
|
1179
|
-
if (resp.ok) {
|
|
1180
|
-
const data = await resp.json();
|
|
1181
|
-
const secrets = data?.data?.data;
|
|
1182
|
-
if (secrets) {
|
|
1183
|
-
for (const [k, v] of Object.entries(secrets)) {
|
|
1184
|
-
writeCredential(k, v);
|
|
1185
|
-
}
|
|
1186
|
-
pulled++;
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
catch { /* skip */ }
|
|
1191
|
-
}
|
|
1192
|
-
s.stop(`Pulled credentials for ${pulled} of ${selectedAdapterIds.length} adapters`);
|
|
1193
|
-
}
|
|
1194
|
-
else if (secretsChoice === '1password') {
|
|
1195
|
-
s.start('Pulling credentials from 1Password');
|
|
1196
|
-
let pulled = 0;
|
|
1197
|
-
for (const adapterId of selectedAdapterIds) {
|
|
1198
|
-
try {
|
|
1199
|
-
const result = execSync(`op item get "${adapterId}" --format json 2>/dev/null`, { encoding: 'utf-8', timeout: 5000 });
|
|
1200
|
-
const item = JSON.parse(result);
|
|
1201
|
-
const fields = item.fields || [];
|
|
1202
|
-
for (const field of fields) {
|
|
1203
|
-
if (field.value && field.label) {
|
|
1204
|
-
const envKey = `${adapterId.toUpperCase().replace(/-/g, '_')}_${field.label.toUpperCase().replace(/\s+/g, '_')}`;
|
|
1205
|
-
writeCredential(envKey, field.value);
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
pulled++;
|
|
1209
|
-
}
|
|
1210
|
-
catch { /* skip */ }
|
|
1211
|
-
}
|
|
1212
|
-
s.stop(`Pulled credentials for ${pulled} of ${selectedAdapterIds.length} adapters`);
|
|
1213
|
-
}
|
|
1214
|
-
else if (secretsChoice === 'doppler') {
|
|
1215
|
-
s.start('Pulling credentials from Doppler');
|
|
1216
|
-
try {
|
|
1217
|
-
const result = execSync('doppler secrets download --no-file --format json 2>/dev/null', { encoding: 'utf-8', timeout: 10000 });
|
|
1218
|
-
const secrets = JSON.parse(result);
|
|
1219
|
-
for (const [k, v] of Object.entries(secrets)) {
|
|
1220
|
-
if (typeof v === 'string')
|
|
1221
|
-
writeCredential(k, v);
|
|
1222
|
-
}
|
|
1223
|
-
s.stop('Credentials pulled from Doppler');
|
|
1224
|
-
}
|
|
1225
|
-
catch {
|
|
1226
|
-
s.stop('Doppler pull failed — configure with: doppler setup');
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
else if (secretsChoice === 'manual') {
|
|
1230
|
-
for (const adapterId of selectedAdapterIds) {
|
|
1231
|
-
const adapter = allAdapters.find(a => a.id === adapterId);
|
|
1232
|
-
if (!adapter)
|
|
1233
|
-
continue;
|
|
1234
|
-
const envKey = adapter.rest?.envKey || `${adapterId.toUpperCase().replace(/-/g, '_')}_API_KEY`;
|
|
1235
|
-
const key = await p.password({ message: `${envKey} for ${adapter.name || adapterId}` });
|
|
1236
|
-
if (!p.isCancel(key) && key) {
|
|
1237
|
-
writeCredential(envKey, key);
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
else if (secretsChoice === 'env') {
|
|
1242
|
-
p.log.info('Using credentials from environment variables');
|
|
1243
|
-
}
|
|
1244
|
-
// Step 6: Install stdio dependencies
|
|
1245
|
-
for (const adapterId of selectedAdapterIds) {
|
|
1246
|
-
const adapter = allAdapters.find(a => a.id === adapterId);
|
|
1247
|
-
if (!adapter?.mcp?.packageName || adapter.mcp.transport !== 'stdio')
|
|
1248
|
-
continue;
|
|
1249
|
-
s.start(`Installing ${adapter.name || adapterId}`);
|
|
1250
|
-
try {
|
|
1251
|
-
execSync(`npm install -g --ignore-scripts ${adapter.mcp.packageName}`, { stdio: 'pipe', timeout: 60000 });
|
|
1252
|
-
// Verify artifact integrity
|
|
1253
|
-
try {
|
|
1254
|
-
const { ArtifactVerifier } = await import('../trust/ArtifactVerifier.js');
|
|
1255
|
-
const verifier = new ArtifactVerifier({ verifyDigests: true });
|
|
1256
|
-
const pkgPath = execSync(`npm root -g`, { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
1257
|
-
const mainFile = join(pkgPath, adapter.mcp.packageName, 'package.json');
|
|
1258
|
-
const result = await verifier.verify(adapter.mcp.packageName, mainFile);
|
|
1259
|
-
if (!result.valid) {
|
|
1260
|
-
s.stop(`${pc.red('✗')} ${adapter.name || adapterId} — integrity check failed`);
|
|
1261
|
-
}
|
|
1262
|
-
else {
|
|
1263
|
-
s.stop(`${pc.green('✓')} ${adapter.name || adapterId} installed — verified`);
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
catch {
|
|
1267
|
-
s.stop(`${pc.green('✓')} ${adapter.name || adapterId} installed`);
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
catch {
|
|
1271
|
-
s.stop(`${pc.yellow('!')} ${adapter.name || adapterId} — run manually: npm install -g ${adapter.mcp.packageName}`);
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
// Step 7: Verify
|
|
1275
|
-
s.start('Verifying connections');
|
|
1276
|
-
const creds = loadCredentials();
|
|
1277
|
-
let verified = 0;
|
|
1278
|
-
const results = [];
|
|
1279
|
-
for (const adapterId of selectedAdapterIds) {
|
|
1280
|
-
const adapter = allAdapters.find(a => a.id === adapterId);
|
|
1281
|
-
if (!adapter)
|
|
1282
|
-
continue;
|
|
1283
|
-
const hasKey = adapter.rest?.envKey ? !!creds[adapter.rest.envKey] : false;
|
|
1284
|
-
if (hasKey) {
|
|
1285
|
-
verified++;
|
|
1286
|
-
results.push(`${pc.green('✓')} ${adapter.name || adapterId} ${adapter.rest?.toolCount || 0} tools`);
|
|
1287
|
-
}
|
|
1288
|
-
else {
|
|
1289
|
-
results.push(`${pc.yellow('○')} ${adapter.name || adapterId} credentials needed`);
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
s.stop('Verification complete');
|
|
1293
|
-
if (results.length > 0) {
|
|
1294
|
-
p.note(results.join('\n'), `${verified} of ${selectedAdapterIds.length} adapters ready`);
|
|
1295
|
-
}
|
|
944
|
+
if (demoChoice !== 'skip') {
|
|
945
|
+
const picked = ZERO_CRED.find(a => a.id === demoChoice);
|
|
946
|
+
selectedAdapterIds.push(demoChoice);
|
|
947
|
+
p.log.success(`${picked.name} connected — ${picked.tools} tools ready`);
|
|
1296
948
|
}
|
|
1297
|
-
// Step
|
|
949
|
+
// Step 4: Save state and config
|
|
1298
950
|
const state = loadState();
|
|
1299
951
|
for (const id of selectedAdapterIds) {
|
|
1300
952
|
const adapter = allAdapters.find(a => a.id === id);
|
|
@@ -1308,16 +960,14 @@ async function runSetupWizard() {
|
|
|
1308
960
|
saveState(state);
|
|
1309
961
|
saveConfig({
|
|
1310
962
|
selectedAdapters: selectedAdapterIds,
|
|
1311
|
-
secretsProvider:
|
|
963
|
+
secretsProvider: 'manual',
|
|
1312
964
|
aiClient: configuredClients.join(','),
|
|
1313
965
|
localBackend: system.localBackend || undefined,
|
|
1314
966
|
});
|
|
1315
967
|
// Outro
|
|
1316
968
|
p.note([
|
|
1317
|
-
`
|
|
1318
|
-
`Remove adapters: ${pc.cyan('npx @epicai/legion remove <name>')}`,
|
|
969
|
+
`Connect more: ${pc.cyan('npx @epicai/legion add <name>')}`,
|
|
1319
970
|
`Health check: ${pc.cyan('npx @epicai/legion health')}`,
|
|
1320
|
-
`Update adapters: ${pc.cyan('npx @epicai/legion update')}`,
|
|
1321
971
|
`List adapters: ${pc.cyan('npx @epicai/legion list')}`,
|
|
1322
972
|
].join('\n'), 'Manage your adapters');
|
|
1323
973
|
p.outro(`${pc.green('Legion is ready.')} Your credentials never leave this machine.`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epicai/legion",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Epic AI® Legion — 35,020 tools. One self-hosted MCP server. Intelligent Virtual Assistant (IVA) integration layer for AI agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|