linage-cli 1.0.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 +63 -0
- package/commands/ingest.js +60 -0
- package/commands/login.js +37 -0
- package/commands/logout.js +7 -0
- package/commands/open.js +32 -0
- package/commands/projects.js +32 -0
- package/commands/stats.js +33 -0
- package/commands/whoami.js +21 -0
- package/index.js +95 -0
- package/package.json +45 -0
- package/utils/api.js +65 -0
- package/utils/config.js +16 -0
- package/utils/scanner.js +26 -0
- package/utils/ui.js +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# linage-cli
|
|
2
|
+
|
|
3
|
+
The Official CLI for Li'nage Cloud - Automate Dependency Discovery & Lineage Mapping.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g linage-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Login
|
|
14
|
+
|
|
15
|
+
First, authenticate with your API Key from the Li'nage Cloud dashboard:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
linage-cli login
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Ingest Dependencies
|
|
22
|
+
|
|
23
|
+
Scan your current directory and upload your dependency graph:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
linage-cli ingest
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### View Projects
|
|
30
|
+
|
|
31
|
+
List all projects in your organization:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
linage-cli projects
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Show Stats
|
|
38
|
+
|
|
39
|
+
Show organization health metrics:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
linage-cli stats
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Whoami
|
|
46
|
+
|
|
47
|
+
Show current authenticated session info:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
linage-cli whoami
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Open Dashboard
|
|
54
|
+
|
|
55
|
+
Open your dashboard in the browser:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
linage-cli open
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
ISC
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const ora = require('ora');
|
|
4
|
+
const api = require('../utils/api');
|
|
5
|
+
const { log } = require('../utils/ui');
|
|
6
|
+
const { detectProject } = require('../utils/scanner');
|
|
7
|
+
|
|
8
|
+
module.exports = async (options) => {
|
|
9
|
+
const projectInfo = detectProject();
|
|
10
|
+
|
|
11
|
+
if (!projectInfo) {
|
|
12
|
+
log.error('No supported dependency file found (package.json, requirements.txt, Cargo.toml).');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const spinner = ora(`Reading ${projectInfo.file}...`).start();
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const filePath = path.resolve(process.cwd(), projectInfo.file);
|
|
20
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
21
|
+
|
|
22
|
+
let projectCode = options.project;
|
|
23
|
+
let version = 'unknown';
|
|
24
|
+
|
|
25
|
+
if (projectInfo.type === 'node') {
|
|
26
|
+
const parsed = JSON.parse(content);
|
|
27
|
+
projectCode = projectCode || parsed.name;
|
|
28
|
+
version = parsed.version;
|
|
29
|
+
} else {
|
|
30
|
+
// For python/rust, we'll use the directory name as default project code
|
|
31
|
+
projectCode = projectCode || path.basename(process.cwd());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!projectCode) {
|
|
35
|
+
spinner.fail('Could not determine project name.');
|
|
36
|
+
log.error('Please specify with --project <name>.');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
spinner.text = `Ingesting ${projectCode} (${projectInfo.type})...`;
|
|
41
|
+
|
|
42
|
+
const apiOpts = {};
|
|
43
|
+
if (options.key) apiOpts.apiKey = options.key;
|
|
44
|
+
|
|
45
|
+
const result = await api.post('/api/ingest/cli', {
|
|
46
|
+
projectCode,
|
|
47
|
+
fileName: projectInfo.file,
|
|
48
|
+
ecosystem: projectInfo.type,
|
|
49
|
+
content
|
|
50
|
+
}, apiOpts);
|
|
51
|
+
|
|
52
|
+
spinner.succeed(`Ingested ${projectCode} successfully!`);
|
|
53
|
+
if (result.version) log.info(`Version: ${result.version}`);
|
|
54
|
+
log.dim(`View at: ${require('../utils/config').getApiUrl()}/${result.orgSlug}/projects/${result.projectCode}`);
|
|
55
|
+
|
|
56
|
+
} catch (error) {
|
|
57
|
+
spinner.fail('Ingestion failed.');
|
|
58
|
+
log.error(error.message);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const inquirer = require('inquirer');
|
|
2
|
+
const ora = require('ora');
|
|
3
|
+
const config = require('../utils/config');
|
|
4
|
+
const api = require('../utils/api');
|
|
5
|
+
const { log, printBox } = require('../utils/ui');
|
|
6
|
+
|
|
7
|
+
module.exports = async () => {
|
|
8
|
+
log.info("To get started, you need an API Key from your Li'nage Cloud dashboard.");
|
|
9
|
+
|
|
10
|
+
const answers = await inquirer.prompt([
|
|
11
|
+
{
|
|
12
|
+
type: 'password',
|
|
13
|
+
name: 'apiKey',
|
|
14
|
+
message: 'Paste your API Key:',
|
|
15
|
+
validate: (input) => input.length > 5 || 'API Key seems too short.'
|
|
16
|
+
}
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
const spinner = ora('Verifying API Key...').start();
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const isValid = await api.validateKey(answers.apiKey);
|
|
23
|
+
|
|
24
|
+
if (isValid && isValid.success) {
|
|
25
|
+
config.setApiKey(answers.apiKey);
|
|
26
|
+
spinner.succeed('Authentication successful!');
|
|
27
|
+
printBox(`Logged in as:\nOrganization: ${isValid.organization.name} (${isValid.organization.slug})\nKey Name: ${isValid.keyName}`);
|
|
28
|
+
} else {
|
|
29
|
+
spinner.fail('Invalid API Key.');
|
|
30
|
+
log.error('Please check your key and try again.');
|
|
31
|
+
}
|
|
32
|
+
} catch (error) {
|
|
33
|
+
spinner.fail('Connection failed.');
|
|
34
|
+
log.error(error.message);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
package/commands/open.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const open = require('open');
|
|
2
|
+
const config = require('../utils/config');
|
|
3
|
+
const api = require('../utils/api');
|
|
4
|
+
const { log } = require('../utils/ui');
|
|
5
|
+
|
|
6
|
+
module.exports = async (projectName, options) => {
|
|
7
|
+
const baseUrl = config.getApiUrl();
|
|
8
|
+
const apiKey = options.key || config.getApiKey();
|
|
9
|
+
|
|
10
|
+
if (!apiKey) {
|
|
11
|
+
log.error('Not authenticated. Please run "linage-cli login" first.');
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let url = `${baseUrl}`;
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const session = await api.get('/api/cli/whoami', { apiKey });
|
|
19
|
+
const orgSlug = session.organization.slug;
|
|
20
|
+
|
|
21
|
+
if (projectName) {
|
|
22
|
+
url = `${baseUrl}/${orgSlug}/projects/${projectName}`;
|
|
23
|
+
} else {
|
|
24
|
+
url = `${baseUrl}/${orgSlug}/dashboard`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
log.info(`Opening ${url}...`);
|
|
28
|
+
await open(url);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
log.error(`Failed to open dashboard: ${error.message}`);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const { table } = require('table');
|
|
2
|
+
const ora = require('ora');
|
|
3
|
+
const api = require('../utils/api');
|
|
4
|
+
const { log } = require('../utils/ui');
|
|
5
|
+
|
|
6
|
+
module.exports = async (options) => {
|
|
7
|
+
const spinner = ora('Fetching projects...').start();
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const apiOpts = {};
|
|
11
|
+
if (options.key) apiOpts.apiKey = options.key;
|
|
12
|
+
|
|
13
|
+
const data = await api.get('/api/cli/projects', apiOpts);
|
|
14
|
+
spinner.stop();
|
|
15
|
+
|
|
16
|
+
if (data.projects.length === 0) {
|
|
17
|
+
log.info('No projects found in this organization.');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const outputData = [
|
|
22
|
+
['Name', 'Code', 'Status', 'Latest Version'],
|
|
23
|
+
...data.projects.map(p => [p.name, p.code, p.status, p.latestVersion])
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
console.log(table(outputData));
|
|
27
|
+
|
|
28
|
+
} catch (error) {
|
|
29
|
+
spinner.fail('Failed to fetch projects.');
|
|
30
|
+
log.error(error.message);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const ora = require('ora');
|
|
2
|
+
const api = require('../utils/api');
|
|
3
|
+
const { log, printBox } = require('../utils/ui');
|
|
4
|
+
const { table } = require('table');
|
|
5
|
+
|
|
6
|
+
module.exports = async (options) => {
|
|
7
|
+
const spinner = ora('Calculating metrics...').start();
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const apiOpts = {};
|
|
11
|
+
if (options.key) apiOpts.apiKey = options.key;
|
|
12
|
+
|
|
13
|
+
const data = await api.get('/api/cli/stats', apiOpts);
|
|
14
|
+
spinner.stop();
|
|
15
|
+
|
|
16
|
+
const { stats } = data;
|
|
17
|
+
|
|
18
|
+
printBox(`Organization Summary\n\nProjects: ${stats.projects}\nIngestions: ${stats.ingestions}\nTeam Members: ${stats.members}`, { borderColor: 'magenta' });
|
|
19
|
+
|
|
20
|
+
if (stats.ecosystems.length > 0) {
|
|
21
|
+
log.info('Ecosystem Distribution:');
|
|
22
|
+
const ecoTable = [
|
|
23
|
+
['Ecosystem', 'Count'],
|
|
24
|
+
...stats.ecosystems.map(e => [e.name || 'Unknown', e.count])
|
|
25
|
+
];
|
|
26
|
+
console.log(table(ecoTable));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
} catch (error) {
|
|
30
|
+
spinner.fail('Failed to fetch stats.');
|
|
31
|
+
log.error(error.message);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const ora = require('ora');
|
|
2
|
+
const api = require('../utils/api');
|
|
3
|
+
const { log, printBox } = require('../utils/ui');
|
|
4
|
+
|
|
5
|
+
module.exports = async (options) => {
|
|
6
|
+
const spinner = ora('Fetching session info...').start();
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const apiOpts = {};
|
|
10
|
+
if (options.key) apiOpts.apiKey = options.key;
|
|
11
|
+
|
|
12
|
+
const data = await api.get('/api/cli/whoami', apiOpts);
|
|
13
|
+
|
|
14
|
+
spinner.stop();
|
|
15
|
+
printBox(`Authenticated Session\n\nOrganization: ${data.organization.name}\nSlug: ${data.organization.slug}\nKey Name: ${data.keyName}`, { borderColor: 'green' });
|
|
16
|
+
|
|
17
|
+
} catch (error) {
|
|
18
|
+
spinner.fail('Failed to fetch info.');
|
|
19
|
+
log.error(error.message);
|
|
20
|
+
}
|
|
21
|
+
};
|
package/index.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { program } = require('commander');
|
|
4
|
+
const { printHeader } = require('./utils/ui');
|
|
5
|
+
|
|
6
|
+
// Import Commands
|
|
7
|
+
const loginCmd = require('./commands/login');
|
|
8
|
+
const ingestCmd = require('./commands/ingest');
|
|
9
|
+
const projectsCmd = require('./commands/projects');
|
|
10
|
+
const whoamiCmd = require('./commands/whoami');
|
|
11
|
+
const logoutCmd = require('./commands/logout');
|
|
12
|
+
const openCmd = require('./commands/open');
|
|
13
|
+
const statsCmd = require('./commands/stats');
|
|
14
|
+
|
|
15
|
+
// Initial Setup
|
|
16
|
+
printHeader();
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.name('linage-cli')
|
|
20
|
+
.description("The Official CLI for Li'nage Cloud - Automate Dependency Discovery")
|
|
21
|
+
.version('1.0.0');
|
|
22
|
+
|
|
23
|
+
program
|
|
24
|
+
.command('login')
|
|
25
|
+
.description('Interactive login to save your API Key')
|
|
26
|
+
.action(loginCmd);
|
|
27
|
+
|
|
28
|
+
program
|
|
29
|
+
.command('ingest')
|
|
30
|
+
.description('Scan current directory and upload dependency graph')
|
|
31
|
+
.option('-k, --key <key>', 'Override API Key')
|
|
32
|
+
.option('-p, --project <name>', 'Override Project Code Name')
|
|
33
|
+
.action(ingestCmd);
|
|
34
|
+
|
|
35
|
+
program
|
|
36
|
+
.command('projects')
|
|
37
|
+
.description('List all projects in your organization')
|
|
38
|
+
.option('-k, --key <key>', 'Override API Key')
|
|
39
|
+
.action(projectsCmd);
|
|
40
|
+
|
|
41
|
+
program
|
|
42
|
+
.command('stats')
|
|
43
|
+
.description('Show organization health metrics')
|
|
44
|
+
.option('-k, --key <key>', 'Override API Key')
|
|
45
|
+
.action(statsCmd);
|
|
46
|
+
|
|
47
|
+
program
|
|
48
|
+
.command('whoami')
|
|
49
|
+
.description('Show current authenticated session info')
|
|
50
|
+
.option('-k, --key <key>', 'Override API Key')
|
|
51
|
+
.action(whoamiCmd);
|
|
52
|
+
|
|
53
|
+
program
|
|
54
|
+
.command('open [project]')
|
|
55
|
+
.description('Open your dashboard in the browser')
|
|
56
|
+
.option('-k, --key <key>', 'Override API Key')
|
|
57
|
+
.action(openCmd);
|
|
58
|
+
|
|
59
|
+
program
|
|
60
|
+
.command('logout')
|
|
61
|
+
.description('Clear saved credentials')
|
|
62
|
+
.action(logoutCmd);
|
|
63
|
+
|
|
64
|
+
// Normalize arguments to handle case-insensitivity
|
|
65
|
+
const knownCommands = ['login', 'ingest', 'projects', 'whoami', 'logout', 'help', 'open', 'stats'];
|
|
66
|
+
const knownFlags = ['--key', '--project', '--version', '--help'];
|
|
67
|
+
|
|
68
|
+
const args = process.argv.map((arg, index) => {
|
|
69
|
+
if (index < 2) return arg; // Skip node and script path
|
|
70
|
+
|
|
71
|
+
const lowerArg = arg.toLowerCase();
|
|
72
|
+
|
|
73
|
+
// 1. Map -v to -V for version
|
|
74
|
+
if (arg === '-v') return '-V';
|
|
75
|
+
|
|
76
|
+
// 2. Lowercase known commands
|
|
77
|
+
if (knownCommands.includes(lowerArg)) return lowerArg;
|
|
78
|
+
|
|
79
|
+
// 3. Handle long flags (e.g., --KEY or --PROJECT)
|
|
80
|
+
if (arg.startsWith('--')) {
|
|
81
|
+
const parts = arg.split('=');
|
|
82
|
+
const flagName = parts[0].toLowerCase();
|
|
83
|
+
if (knownFlags.includes(flagName)) {
|
|
84
|
+
return parts.length > 1 ? `${flagName}=${parts.slice(1).join('=')}` : flagName;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return arg;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
program.parse(args);
|
|
92
|
+
|
|
93
|
+
if (!process.argv.slice(2).length) {
|
|
94
|
+
program.outputHelp();
|
|
95
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "linage-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "The Official CLI for Li'nage Cloud - Automate Dependency Discovery",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": "index.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"index.js",
|
|
9
|
+
"commands/",
|
|
10
|
+
"utils/",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/linage-cloud/linage-cloud.git"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"linage",
|
|
22
|
+
"cli",
|
|
23
|
+
"dependencies",
|
|
24
|
+
"lineage",
|
|
25
|
+
"mapping"
|
|
26
|
+
],
|
|
27
|
+
"author": "Li'nage",
|
|
28
|
+
"license": "ISC",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"boxen": "^5.1.2",
|
|
31
|
+
"chalk": "^4.1.2",
|
|
32
|
+
"commander": "^14.0.2",
|
|
33
|
+
"conf": "^10.2.0",
|
|
34
|
+
"figlet": "^1.9.4",
|
|
35
|
+
"gradient-string": "^3.0.0",
|
|
36
|
+
"inquirer": "^8.2.5",
|
|
37
|
+
"node-fetch": "^2.7.0",
|
|
38
|
+
"open": "^8.4.2",
|
|
39
|
+
"ora": "^5.4.1",
|
|
40
|
+
"table": "^6.8.0"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=14.0.0"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/utils/api.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const fetch = global.fetch || require('node-fetch');
|
|
2
|
+
const config = require('./config');
|
|
3
|
+
const { log } = require('./ui');
|
|
4
|
+
|
|
5
|
+
const request = async (endpoint, method = 'GET', opts = {}) => {
|
|
6
|
+
const apiKey = opts.apiKey || config.getApiKey();
|
|
7
|
+
const baseUrl = config.getApiUrl();
|
|
8
|
+
|
|
9
|
+
if (!apiKey) {
|
|
10
|
+
throw new Error('Not authenticated. Please run "linage-cli login" first or provide --key.');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const headers = {
|
|
14
|
+
'Content-Type': 'application/json',
|
|
15
|
+
'Authorization': `Bearer ${apiKey}`
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const options = {
|
|
19
|
+
method,
|
|
20
|
+
headers,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
if (opts.body) {
|
|
24
|
+
options.body = JSON.stringify(opts.body);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const res = await fetch(`${baseUrl}${endpoint}`, options);
|
|
29
|
+
|
|
30
|
+
// Handle 401 specifically
|
|
31
|
+
if (res.status === 401 || res.status === 403) {
|
|
32
|
+
throw new Error('Invalid or expired API Key. Please login again.');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const data = await res.json();
|
|
36
|
+
|
|
37
|
+
if (!res.ok) {
|
|
38
|
+
throw new Error(data.error || data.message || `API Error: ${res.statusText}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return data;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
throw error; // Re-throw to be handled by commands
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
get: (endpoint, opts = {}) => request(endpoint, 'GET', opts),
|
|
49
|
+
post: (endpoint, body, opts = {}) => request(endpoint, 'POST', { ...opts, body }),
|
|
50
|
+
delete: (endpoint, opts = {}) => request(endpoint, 'DELETE', opts),
|
|
51
|
+
// Helper to validate key without throwing immediately
|
|
52
|
+
validateKey: async (key) => {
|
|
53
|
+
const baseUrl = config.getApiUrl();
|
|
54
|
+
try {
|
|
55
|
+
// We'll create a simple /api/whoami endpoint to validate
|
|
56
|
+
const res = await fetch(`${baseUrl}/api/cli/whoami`, {
|
|
57
|
+
headers: { 'Authorization': `Bearer ${key}` }
|
|
58
|
+
});
|
|
59
|
+
if (!res.ok) return false;
|
|
60
|
+
return await res.json();
|
|
61
|
+
} catch (e) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
package/utils/config.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const Conf = require('conf');
|
|
2
|
+
|
|
3
|
+
const config = new Conf({
|
|
4
|
+
projectName: 'linage-cli',
|
|
5
|
+
projectVersion: '1.0.0'
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
getApiKey: () => config.get('apiKey'),
|
|
10
|
+
setApiKey: (key) => config.set('apiKey', key),
|
|
11
|
+
clearApiKey: () => config.delete('apiKey'),
|
|
12
|
+
|
|
13
|
+
// Base URL handling (useful for dev vs prod switching if needed later)
|
|
14
|
+
getApiUrl: () => process.env.LINAGE_API_URL || config.get('apiUrl') || 'http://localhost:3000',
|
|
15
|
+
setApiUrl: (url) => config.set('apiUrl', url)
|
|
16
|
+
};
|
package/utils/scanner.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const detectProject = () => {
|
|
5
|
+
const cwd = process.cwd();
|
|
6
|
+
|
|
7
|
+
if (fs.existsSync(path.join(cwd, 'package.json'))) {
|
|
8
|
+
return { type: 'node', file: 'package.json' };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (fs.existsSync(path.join(cwd, 'requirements.txt'))) {
|
|
12
|
+
return { type: 'python', file: 'requirements.txt' };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (fs.existsSync(path.join(cwd, 'Cargo.toml'))) {
|
|
16
|
+
return { type: 'rust', file: 'Cargo.toml' };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (fs.existsSync(path.join(cwd, 'go.mod'))) {
|
|
20
|
+
return { type: 'go', file: 'go.mod' };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return null;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
module.exports = { detectProject };
|
package/utils/ui.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const figlet = require('figlet');
|
|
3
|
+
const gradient = require('gradient-string');
|
|
4
|
+
const boxen = require('boxen');
|
|
5
|
+
|
|
6
|
+
const printHeader = () => {
|
|
7
|
+
console.log('');
|
|
8
|
+
console.log(gradient.pastel.multiline(figlet.textSync("LI'NAGE", { horizontalLayout: 'full' })));
|
|
9
|
+
console.log(chalk.dim(" Automated Dependency Discovery & Lineage Mapping by Li'nage"));
|
|
10
|
+
console.log('');
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const log = {
|
|
14
|
+
info: (msg) => console.log(chalk.blue('ℹ'), msg),
|
|
15
|
+
success: (msg) => console.log(chalk.green('✔'), msg),
|
|
16
|
+
warning: (msg) => console.log(chalk.yellow('⚠'), msg),
|
|
17
|
+
error: (msg) => console.log(chalk.red('✖'), msg),
|
|
18
|
+
dim: (msg) => console.log(chalk.dim(msg)),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const printBox = (text, options = {}) => {
|
|
22
|
+
console.log(boxen(text, {
|
|
23
|
+
padding: 1,
|
|
24
|
+
margin: 1,
|
|
25
|
+
borderStyle: 'round',
|
|
26
|
+
borderColor: 'cyan',
|
|
27
|
+
...options
|
|
28
|
+
}));
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
printHeader,
|
|
33
|
+
log,
|
|
34
|
+
printBox
|
|
35
|
+
};
|