@epicai/legion 1.0.1 → 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 -278
  2. package/package.json +1 -1
package/dist/bin/setup.js CHANGED
@@ -918,288 +918,35 @@ async function runSetupWizard() {
918
918
  p.log.success(`Using ${system.localBackend} on port ${system.localPort}`);
919
919
  configuredClients.push('local');
920
920
  }
921
- // Step 3: Secrets provider
922
- const secretsChoice = await p.select({
923
- 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:',
924
930
  options: [
925
- { value: 'vault', label: 'HashiCorp Vault' },
926
- { value: 'aws-sm', label: 'AWS Secrets Manager' },
927
- { value: 'azure-kv', label: 'Azure Key Vault' },
928
- { value: '1password', label: '1Password CLI' },
929
- { value: 'doppler', label: 'Doppler' },
930
- { value: 'env', label: 'Environment variables', hint: 'already exported in shell' },
931
- { 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>' },
932
937
  ],
933
938
  });
934
- if (p.isCancel(secretsChoice)) {
939
+ if (p.isCancel(demoChoice)) {
935
940
  p.cancel('Setup cancelled.');
936
941
  process.exit(0);
937
942
  }
938
- let vaultAddr = '';
939
- let vaultToken = '';
940
- let vaultConnected = false;
941
- if (secretsChoice === 'vault') {
942
- const envAddr = process.env.VAULT_ADDR;
943
- const envToken = process.env.VAULT_TOKEN;
944
- if (envAddr && envToken) {
945
- p.log.success(`Detected VAULT_ADDR=${envAddr}`);
946
- vaultAddr = envAddr;
947
- vaultToken = envToken;
948
- }
949
- else {
950
- const addr = await p.text({
951
- message: 'Vault address',
952
- placeholder: 'https://vault.example.com:8200',
953
- validate: (v) => { if (!v)
954
- return 'Required'; if (!v.startsWith('http'))
955
- return 'Must start with http:// or https://'; },
956
- });
957
- if (p.isCancel(addr)) {
958
- p.cancel('Setup cancelled.');
959
- process.exit(0);
960
- }
961
- const token = await p.password({ message: 'Vault token', validate: (v) => { if (!v)
962
- return 'Required'; } });
963
- if (p.isCancel(token)) {
964
- p.cancel('Setup cancelled.');
965
- process.exit(0);
966
- }
967
- vaultAddr = addr;
968
- vaultToken = token;
969
- }
970
- s.start('Connecting to Vault');
971
- try {
972
- const controller = new AbortController();
973
- const t = setTimeout(() => controller.abort(), 5000);
974
- const resp = await fetch(`${vaultAddr}/v1/sys/health`, { headers: { 'X-Vault-Token': vaultToken }, signal: controller.signal });
975
- clearTimeout(t);
976
- if (resp.ok) {
977
- vaultConnected = true;
978
- s.stop(`Connected to Vault at ${vaultAddr}`);
979
- writeCredential('VAULT_ADDR', vaultAddr);
980
- writeCredential('VAULT_TOKEN', vaultToken);
981
- }
982
- else {
983
- s.stop(`Vault returned ${resp.status} — check your token`);
984
- }
985
- }
986
- catch {
987
- s.stop(`Cannot reach Vault at ${vaultAddr}`);
988
- }
989
- }
990
- if (secretsChoice === '1password') {
991
- try {
992
- execSync('op --version', { stdio: 'pipe' });
993
- p.log.success('1Password CLI detected');
994
- }
995
- catch {
996
- p.log.warning('1Password CLI not found — install from https://1password.com/downloads/command-line');
997
- }
998
- }
999
- if (secretsChoice === 'doppler') {
1000
- try {
1001
- execSync('doppler --version', { stdio: 'pipe' });
1002
- p.log.success('Doppler CLI detected');
1003
- }
1004
- catch {
1005
- p.log.warning('Doppler CLI not found — install from https://docs.doppler.com/docs/install-cli');
1006
- }
1007
- }
1008
- // Step 4: Auto-wire adapters from credentials
1009
943
  const selectedAdapterIds = [];
1010
- const existingCreds = loadCredentials();
1011
- // Match adapters whose required env keys are already present
1012
- const autoMatched = [];
1013
- for (const adapter of allAdapters) {
1014
- const restKey = adapter.rest?.envKey;
1015
- const mcpKeys = adapter.mcp?.envKeys || [];
1016
- const allKeys = [...(restKey ? [restKey] : []), ...mcpKeys];
1017
- if (allKeys.length > 0 && allKeys.every(k => !!existingCreds[k])) {
1018
- autoMatched.push(adapter);
1019
- }
1020
- }
1021
- if (autoMatched.length > 0) {
1022
- p.note(autoMatched.map(a => `${pc.green('✓')} ${a.name || a.id}`).join('\n'), `${autoMatched.length} adapter${autoMatched.length !== 1 ? 's' : ''} matched from your credentials`);
1023
- selectedAdapterIds.push(...autoMatched.map(a => a.id));
1024
- }
1025
- else {
1026
- p.log.info('No adapters matched from credentials — use ' + pc.cyan('npx @epicai/legion add <name>') + ' to connect adapters.');
1027
- }
1028
- // Optional: search for additional adapters
1029
- const addMore = await p.confirm({
1030
- message: 'Search for additional adapters to configure?',
1031
- initialValue: false,
1032
- });
1033
- if (!p.isCancel(addMore) && addMore) {
1034
- let searching = true;
1035
- while (searching) {
1036
- const searchTerm = await p.text({
1037
- message: 'Search adapters (name or keyword)',
1038
- placeholder: 'e.g. grafana, paypal, github',
1039
- });
1040
- if (p.isCancel(searchTerm) || !searchTerm)
1041
- break;
1042
- const term = searchTerm.toLowerCase();
1043
- const matches = allAdapters.filter(a => !selectedAdapterIds.includes(a.id) && (a.id.includes(term) ||
1044
- (a.name || '').toLowerCase().includes(term) ||
1045
- (a.description || '').toLowerCase().includes(term) ||
1046
- (a.category || '').includes(term)));
1047
- if (matches.length === 0) {
1048
- p.log.warning(`No adapters found for "${searchTerm}"`);
1049
- }
1050
- else {
1051
- const options = matches.slice(0, 50).map(a => ({
1052
- value: a.id,
1053
- label: a.name || a.id,
1054
- hint: a.description?.slice(0, 60),
1055
- }));
1056
- const picked = await p.multiselect({
1057
- message: `${matches.length} match${matches.length !== 1 ? 'es' : ''} — select to add`,
1058
- options,
1059
- required: false,
1060
- });
1061
- if (!p.isCancel(picked)) {
1062
- selectedAdapterIds.push(...picked);
1063
- }
1064
- }
1065
- const cont = await p.confirm({ message: 'Search for more?', initialValue: false });
1066
- if (p.isCancel(cont) || !cont)
1067
- searching = false;
1068
- }
1069
- }
1070
- // Step 5: Credentials
1071
- if (selectedAdapterIds.length > 0) {
1072
- if (secretsChoice === 'vault' && vaultConnected) {
1073
- s.start('Pulling credentials from Vault');
1074
- let pulled = 0;
1075
- for (const adapterId of selectedAdapterIds) {
1076
- try {
1077
- const controller = new AbortController();
1078
- const t = setTimeout(() => controller.abort(), 3000);
1079
- const resp = await fetch(`${vaultAddr}/v1/secret/data/${adapterId}`, {
1080
- headers: { 'X-Vault-Token': vaultToken },
1081
- signal: controller.signal,
1082
- });
1083
- clearTimeout(t);
1084
- if (resp.ok) {
1085
- const data = await resp.json();
1086
- const secrets = data?.data?.data;
1087
- if (secrets) {
1088
- for (const [k, v] of Object.entries(secrets)) {
1089
- writeCredential(k, v);
1090
- }
1091
- pulled++;
1092
- }
1093
- }
1094
- }
1095
- catch { /* skip */ }
1096
- }
1097
- s.stop(`Pulled credentials for ${pulled} of ${selectedAdapterIds.length} adapters`);
1098
- }
1099
- else if (secretsChoice === '1password') {
1100
- s.start('Pulling credentials from 1Password');
1101
- let pulled = 0;
1102
- for (const adapterId of selectedAdapterIds) {
1103
- try {
1104
- const result = execSync(`op item get "${adapterId}" --format json 2>/dev/null`, { encoding: 'utf-8', timeout: 5000 });
1105
- const item = JSON.parse(result);
1106
- const fields = item.fields || [];
1107
- for (const field of fields) {
1108
- if (field.value && field.label) {
1109
- const envKey = `${adapterId.toUpperCase().replace(/-/g, '_')}_${field.label.toUpperCase().replace(/\s+/g, '_')}`;
1110
- writeCredential(envKey, field.value);
1111
- }
1112
- }
1113
- pulled++;
1114
- }
1115
- catch { /* skip */ }
1116
- }
1117
- s.stop(`Pulled credentials for ${pulled} of ${selectedAdapterIds.length} adapters`);
1118
- }
1119
- else if (secretsChoice === 'doppler') {
1120
- s.start('Pulling credentials from Doppler');
1121
- try {
1122
- const result = execSync('doppler secrets download --no-file --format json 2>/dev/null', { encoding: 'utf-8', timeout: 10000 });
1123
- const secrets = JSON.parse(result);
1124
- for (const [k, v] of Object.entries(secrets)) {
1125
- if (typeof v === 'string')
1126
- writeCredential(k, v);
1127
- }
1128
- s.stop('Credentials pulled from Doppler');
1129
- }
1130
- catch {
1131
- s.stop('Doppler pull failed — configure with: doppler setup');
1132
- }
1133
- }
1134
- else if (secretsChoice === 'manual') {
1135
- for (const adapterId of selectedAdapterIds) {
1136
- const adapter = allAdapters.find(a => a.id === adapterId);
1137
- if (!adapter)
1138
- continue;
1139
- const envKey = adapter.rest?.envKey || `${adapterId.toUpperCase().replace(/-/g, '_')}_API_KEY`;
1140
- const key = await p.password({ message: `${envKey} for ${adapter.name || adapterId}` });
1141
- if (!p.isCancel(key) && key) {
1142
- writeCredential(envKey, key);
1143
- }
1144
- }
1145
- }
1146
- else if (secretsChoice === 'env') {
1147
- p.log.info('Using credentials from environment variables');
1148
- }
1149
- // Step 6: Install stdio dependencies
1150
- for (const adapterId of selectedAdapterIds) {
1151
- const adapter = allAdapters.find(a => a.id === adapterId);
1152
- if (!adapter?.mcp?.packageName || adapter.mcp.transport !== 'stdio')
1153
- continue;
1154
- s.start(`Installing ${adapter.name || adapterId}`);
1155
- try {
1156
- execSync(`npm install -g --ignore-scripts ${adapter.mcp.packageName}`, { stdio: 'pipe', timeout: 60000 });
1157
- // Verify artifact integrity
1158
- try {
1159
- const { ArtifactVerifier } = await import('../trust/ArtifactVerifier.js');
1160
- const verifier = new ArtifactVerifier({ verifyDigests: true });
1161
- const pkgPath = execSync(`npm root -g`, { encoding: 'utf-8', timeout: 5000 }).trim();
1162
- const mainFile = join(pkgPath, adapter.mcp.packageName, 'package.json');
1163
- const result = await verifier.verify(adapter.mcp.packageName, mainFile);
1164
- if (!result.valid) {
1165
- s.stop(`${pc.red('✗')} ${adapter.name || adapterId} — integrity check failed`);
1166
- }
1167
- else {
1168
- s.stop(`${pc.green('✓')} ${adapter.name || adapterId} installed — verified`);
1169
- }
1170
- }
1171
- catch {
1172
- s.stop(`${pc.green('✓')} ${adapter.name || adapterId} installed`);
1173
- }
1174
- }
1175
- catch {
1176
- s.stop(`${pc.yellow('!')} ${adapter.name || adapterId} — run manually: npm install -g ${adapter.mcp.packageName}`);
1177
- }
1178
- }
1179
- // Step 7: Verify
1180
- s.start('Verifying connections');
1181
- const creds = loadCredentials();
1182
- let verified = 0;
1183
- const results = [];
1184
- for (const adapterId of selectedAdapterIds) {
1185
- const adapter = allAdapters.find(a => a.id === adapterId);
1186
- if (!adapter)
1187
- continue;
1188
- const hasKey = adapter.rest?.envKey ? !!creds[adapter.rest.envKey] : false;
1189
- if (hasKey) {
1190
- verified++;
1191
- results.push(`${pc.green('✓')} ${adapter.name || adapterId} ${adapter.rest?.toolCount || 0} tools`);
1192
- }
1193
- else {
1194
- results.push(`${pc.yellow('○')} ${adapter.name || adapterId} credentials needed`);
1195
- }
1196
- }
1197
- s.stop('Verification complete');
1198
- if (results.length > 0) {
1199
- p.note(results.join('\n'), `${verified} of ${selectedAdapterIds.length} adapters ready`);
1200
- }
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`);
1201
948
  }
1202
- // Step 8: Save state and config
949
+ // Step 4: Save state and config
1203
950
  const state = loadState();
1204
951
  for (const id of selectedAdapterIds) {
1205
952
  const adapter = allAdapters.find(a => a.id === id);
@@ -1213,16 +960,14 @@ async function runSetupWizard() {
1213
960
  saveState(state);
1214
961
  saveConfig({
1215
962
  selectedAdapters: selectedAdapterIds,
1216
- secretsProvider: secretsChoice,
963
+ secretsProvider: 'manual',
1217
964
  aiClient: configuredClients.join(','),
1218
965
  localBackend: system.localBackend || undefined,
1219
966
  });
1220
967
  // Outro
1221
968
  p.note([
1222
- `Add adapters: ${pc.cyan('npx @epicai/legion add <name>')}`,
1223
- `Remove adapters: ${pc.cyan('npx @epicai/legion remove <name>')}`,
969
+ `Connect more: ${pc.cyan('npx @epicai/legion add <name>')}`,
1224
970
  `Health check: ${pc.cyan('npx @epicai/legion health')}`,
1225
- `Update adapters: ${pc.cyan('npx @epicai/legion update')}`,
1226
971
  `List adapters: ${pc.cyan('npx @epicai/legion list')}`,
1227
972
  ].join('\n'), 'Manage your adapters');
1228
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.1",
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",