@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.

Files changed (2) hide show
  1. package/dist/bin/setup.js +23 -373
  2. 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: Secrets provider
966
- const secretsChoice = await p.select({
967
- message: 'Where are your API and MCP credentials stored?',
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
- { value: 'vault', label: 'HashiCorp Vault' },
970
- { value: 'aws-sm', label: 'AWS Secrets Manager' },
971
- { value: 'azure-kv', label: 'Azure Key Vault' },
972
- { value: '1password', label: '1Password CLI' },
973
- { value: 'doppler', label: 'Doppler' },
974
- { value: 'env', label: 'Environment variables', hint: 'already exported in shell' },
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(secretsChoice)) {
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
- for (const groupKey of selectedGroups) {
1126
- const group = GROUPS[groupKey];
1127
- if (!group)
1128
- continue;
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 8: Save state and config
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: secretsChoice,
963
+ secretsProvider: 'manual',
1312
964
  aiClient: configuredClients.join(','),
1313
965
  localBackend: system.localBackend || undefined,
1314
966
  });
1315
967
  // Outro
1316
968
  p.note([
1317
- `Add adapters: ${pc.cyan('npx @epicai/legion add <name>')}`,
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.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",