@underlingscloud/cli 0.1.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/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.js +71 -0
- package/dist/commands/project.d.ts +2 -0
- package/dist/commands/project.js +112 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +138 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +22 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +101 -0
- package/dist/utils/auth.d.ts +12 -0
- package/dist/utils/auth.js +54 -0
- package/dist/utils/config.d.ts +20 -0
- package/dist/utils/config.js +77 -0
- package/package.json +40 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.logout = exports.whoami = exports.login = void 0;
|
|
7
|
+
const open_1 = __importDefault(require("open"));
|
|
8
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const axios_1 = __importDefault(require("axios"));
|
|
11
|
+
const constants_1 = require("../constants");
|
|
12
|
+
const auth_1 = require("../utils/auth");
|
|
13
|
+
const login = async () => {
|
|
14
|
+
console.log(chalk_1.default.blue('Initiating login...'));
|
|
15
|
+
console.log('Opening browser to authenticate...');
|
|
16
|
+
await (0, open_1.default)((0, constants_1.getAuthUrl)());
|
|
17
|
+
const response = await (0, prompts_1.default)({
|
|
18
|
+
type: 'password',
|
|
19
|
+
name: 'token',
|
|
20
|
+
message: 'Please paste the authentication token from the browser:',
|
|
21
|
+
validate: (value) => value ? true : 'Token is required'
|
|
22
|
+
});
|
|
23
|
+
if (!response.token) {
|
|
24
|
+
console.log(chalk_1.default.red('Login cancelled.'));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const token = response.token.trim();
|
|
28
|
+
try {
|
|
29
|
+
const userQuery = `
|
|
30
|
+
query Me {
|
|
31
|
+
me {
|
|
32
|
+
id
|
|
33
|
+
email
|
|
34
|
+
name
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
const result = await axios_1.default.post((0, constants_1.getApiUrl)(), { query: userQuery }, {
|
|
39
|
+
headers: {
|
|
40
|
+
'Content-Type': 'application/json',
|
|
41
|
+
'Authorization': `Bearer ${token}`
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
if (result.data.errors) {
|
|
45
|
+
throw new Error(result.data.errors[0].message);
|
|
46
|
+
}
|
|
47
|
+
const user = result.data.data.me;
|
|
48
|
+
(0, auth_1.setAuthToken)(token);
|
|
49
|
+
(0, auth_1.setUserInfo)(user);
|
|
50
|
+
console.log(chalk_1.default.green(`Successfully logged in as ${user.email || user.name}!`));
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
console.error(chalk_1.default.red('Failed to verify token:'), error.message || error);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
exports.login = login;
|
|
57
|
+
const whoami = async () => {
|
|
58
|
+
const user = (0, auth_1.getUserInfo)();
|
|
59
|
+
if (user) {
|
|
60
|
+
console.log(chalk_1.default.green(`Logged in as: ${user.name || 'Unknown'} <${user.email}>`));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
console.log(chalk_1.default.yellow('Not logged in. Run "underlings auth login" to authenticate.'));
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
exports.whoami = whoami;
|
|
67
|
+
const logout = async () => {
|
|
68
|
+
(0, auth_1.clearAuth)();
|
|
69
|
+
console.log(chalk_1.default.green('Logged out successfully.'));
|
|
70
|
+
};
|
|
71
|
+
exports.logout = logout;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.initService = exports.initProject = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
9
|
+
const axios_1 = __importDefault(require("axios"));
|
|
10
|
+
const constants_1 = require("../constants");
|
|
11
|
+
const auth_1 = require("../utils/auth");
|
|
12
|
+
const config_1 = require("../utils/config");
|
|
13
|
+
const initProject = async () => {
|
|
14
|
+
if (!(0, auth_1.isAuthenticated)()) {
|
|
15
|
+
console.error(chalk_1.default.red('Error: You are not logged in.'));
|
|
16
|
+
console.log('Run "underlings auth login" to authenticate.');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const token = (0, auth_1.getAuthToken)();
|
|
20
|
+
const existingConfigPath = (0, config_1.findProjectConfigPath)();
|
|
21
|
+
if (existingConfigPath) {
|
|
22
|
+
console.log(chalk_1.default.yellow(`Found existing project config at ${existingConfigPath}`));
|
|
23
|
+
const response = await (0, prompts_1.default)({
|
|
24
|
+
type: 'confirm',
|
|
25
|
+
name: 'proceed',
|
|
26
|
+
message: 'You are already inside a project. Initialize a new project config here?',
|
|
27
|
+
initial: false
|
|
28
|
+
});
|
|
29
|
+
if (!response.proceed)
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const projectsQuery = `query { projects { id name slug } }`;
|
|
33
|
+
try {
|
|
34
|
+
const result = await axios_1.default.post((0, constants_1.getApiUrl)(), { query: projectsQuery }, {
|
|
35
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
36
|
+
});
|
|
37
|
+
if (result.data.errors)
|
|
38
|
+
throw new Error(result.data.errors[0].message);
|
|
39
|
+
const projects = result.data.data.projects;
|
|
40
|
+
if (!projects?.length) {
|
|
41
|
+
console.log(chalk_1.default.red('No projects found. Please create one on the dashboard first.'));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const response = await (0, prompts_1.default)({
|
|
45
|
+
type: 'select',
|
|
46
|
+
name: 'project',
|
|
47
|
+
message: 'Select a project:',
|
|
48
|
+
choices: projects.map((p) => ({ title: `${p.name} (${p.slug})`, value: p }))
|
|
49
|
+
});
|
|
50
|
+
if (!response.project)
|
|
51
|
+
return;
|
|
52
|
+
(0, config_1.writeProjectConfig)({ project: response.project.slug });
|
|
53
|
+
console.log(chalk_1.default.green(`Successfully initialized ulproject.json for project "${response.project.slug}"`));
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.error(chalk_1.default.red('Failed to fetch projects:'), error.message);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
exports.initProject = initProject;
|
|
60
|
+
const initService = async () => {
|
|
61
|
+
if (!(0, auth_1.isAuthenticated)()) {
|
|
62
|
+
console.error(chalk_1.default.red('Error: You are not logged in.'));
|
|
63
|
+
console.log('Run "underlings auth login" to authenticate.');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const token = (0, auth_1.getAuthToken)();
|
|
67
|
+
const projectConfigPath = (0, config_1.findProjectConfigPath)();
|
|
68
|
+
if (!projectConfigPath) {
|
|
69
|
+
console.error(chalk_1.default.red('Error: No project config found. Run "underlings project init" at the repo root first.'));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const projectConfig = (0, config_1.readProjectConfig)();
|
|
73
|
+
const servicesQuery = `
|
|
74
|
+
query Services($projectSlug: String!) {
|
|
75
|
+
services(projectSlug: $projectSlug) {
|
|
76
|
+
id name slug
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
try {
|
|
81
|
+
const result = await axios_1.default.post((0, constants_1.getApiUrl)(), {
|
|
82
|
+
query: servicesQuery,
|
|
83
|
+
variables: { projectSlug: projectConfig.project }
|
|
84
|
+
}, {
|
|
85
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
86
|
+
});
|
|
87
|
+
if (result.data.errors)
|
|
88
|
+
throw new Error(result.data.errors[0].message);
|
|
89
|
+
const services = result.data.data.services;
|
|
90
|
+
if (!services?.length) {
|
|
91
|
+
console.log(chalk_1.default.red('No services found for this project.'));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const response = await (0, prompts_1.default)({
|
|
95
|
+
type: 'select',
|
|
96
|
+
name: 'service',
|
|
97
|
+
message: 'Select a service to link to this directory:',
|
|
98
|
+
choices: services.map((s) => ({ title: `${s.name} (${s.slug})`, value: s }))
|
|
99
|
+
});
|
|
100
|
+
if (!response.service)
|
|
101
|
+
return;
|
|
102
|
+
(0, config_1.writeServiceConfig)({
|
|
103
|
+
project: projectConfig.project,
|
|
104
|
+
service: response.service.slug
|
|
105
|
+
});
|
|
106
|
+
console.log(chalk_1.default.green(`Successfully initialized ulservice.json for service "${response.service.slug}"`));
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
console.error(chalk_1.default.red('Failed to fetch services:'), error.message);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
exports.initService = initService;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.env = exports.run = void 0;
|
|
7
|
+
const cross_spawn_1 = __importDefault(require("cross-spawn"));
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const axios_1 = __importDefault(require("axios"));
|
|
11
|
+
const constants_1 = require("../constants");
|
|
12
|
+
const config_1 = require("../utils/config");
|
|
13
|
+
const auth_1 = require("../utils/auth");
|
|
14
|
+
const run = async (commandParts) => {
|
|
15
|
+
if (commandParts.length === 0) {
|
|
16
|
+
console.error(chalk_1.default.red('Error: No command provided to run.'));
|
|
17
|
+
console.log('Usage: underlings run <command>');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (!(0, auth_1.isAuthenticated)()) {
|
|
21
|
+
console.error(chalk_1.default.red('Error: You are not logged in.'));
|
|
22
|
+
console.log('Run "underlings auth login" to authenticate.');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const token = (0, auth_1.getAuthToken)();
|
|
26
|
+
// Read service config
|
|
27
|
+
if (!(0, config_1.hasServiceConfig)()) {
|
|
28
|
+
console.error(chalk_1.default.red('Error: No ulservice.json (or ccservice.json) found in the current directory.'));
|
|
29
|
+
console.log('Please run "underlings service init" to configure this directory as a service.');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
let config;
|
|
33
|
+
try {
|
|
34
|
+
config = (0, config_1.readServiceConfig)();
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
console.error(chalk_1.default.red('Error reading service config file.'));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (!config.service) {
|
|
41
|
+
console.error(chalk_1.default.red('Error: Invalid service config. Missing "service" identifier.'));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Get git branch
|
|
45
|
+
let branch;
|
|
46
|
+
try {
|
|
47
|
+
branch = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD').toString().trim();
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
console.error(chalk_1.default.red('Error: Could not determine git branch. Are you in a git repository?'));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!branch) {
|
|
54
|
+
console.error(chalk_1.default.red('Error: Current branch is empty.'));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
console.log(chalk_1.default.dim(`Detected branch: ${branch}`));
|
|
58
|
+
// Fetch service variables
|
|
59
|
+
console.log(chalk_1.default.blue('Fetching service variables...'));
|
|
60
|
+
let envVars = {};
|
|
61
|
+
try {
|
|
62
|
+
envVars = await fetchServiceVariables(config.service, branch, token);
|
|
63
|
+
console.log(chalk_1.default.green(`Successfully loaded ${Object.keys(envVars).length} variables for service: ${config.service}`));
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error(chalk_1.default.red('Failed to fetch variables:'), error.message);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// Execute command
|
|
70
|
+
const [cmd, ...args] = commandParts;
|
|
71
|
+
console.log(chalk_1.default.dim(`> Executing: ${cmd} ${args.join(' ')}`));
|
|
72
|
+
const child = (0, cross_spawn_1.default)(cmd, args, {
|
|
73
|
+
stdio: 'inherit',
|
|
74
|
+
env: {
|
|
75
|
+
...process.env,
|
|
76
|
+
...envVars
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
child.on('exit', (code) => {
|
|
80
|
+
process.exit(code || 0);
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
exports.run = run;
|
|
84
|
+
const env = async () => {
|
|
85
|
+
if (!(0, auth_1.isAuthenticated)()) {
|
|
86
|
+
console.error(chalk_1.default.red('Error: You are not logged in.'));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const token = (0, auth_1.getAuthToken)();
|
|
90
|
+
if (!(0, config_1.hasServiceConfig)()) {
|
|
91
|
+
console.error(chalk_1.default.red('Error: No service config found in the current directory.'));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const config = (0, config_1.readServiceConfig)();
|
|
95
|
+
let branch;
|
|
96
|
+
try {
|
|
97
|
+
branch = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD').toString().trim();
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
console.error(chalk_1.default.red('Error: Could not determine git branch.'));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
console.log(chalk_1.default.blue(`Variables for ${config.service} on branch "${branch}":\n`));
|
|
104
|
+
const envVars = await fetchServiceVariables(config.service, branch, token);
|
|
105
|
+
for (const [key, value] of Object.entries(envVars).sort(([a], [b]) => a.localeCompare(b))) {
|
|
106
|
+
console.log(`${chalk_1.default.cyan(key)}=${value}`);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
exports.env = env;
|
|
110
|
+
async function fetchServiceVariables(serviceSlug, branch, token) {
|
|
111
|
+
const query = `
|
|
112
|
+
query GetServiceVariables($serviceSlug: String!, $branch: String!) {
|
|
113
|
+
serviceVariables(serviceSlug: $serviceSlug, branch: $branch, includeSystem: true) {
|
|
114
|
+
key
|
|
115
|
+
value
|
|
116
|
+
isSecret
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
`;
|
|
120
|
+
const result = await axios_1.default.post((0, constants_1.getApiUrl)(), {
|
|
121
|
+
query,
|
|
122
|
+
variables: { serviceSlug, branch }
|
|
123
|
+
}, {
|
|
124
|
+
headers: {
|
|
125
|
+
'Content-Type': 'application/json',
|
|
126
|
+
'Authorization': `Bearer ${token}`
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
if (result.data.errors) {
|
|
130
|
+
throw new Error(result.data.errors[0].message);
|
|
131
|
+
}
|
|
132
|
+
const variables = result.data.data.serviceVariables;
|
|
133
|
+
const envMap = {};
|
|
134
|
+
variables.forEach((v) => {
|
|
135
|
+
envMap[v.key] = v.value;
|
|
136
|
+
});
|
|
137
|
+
return envMap;
|
|
138
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAuthUrl = exports.getApiUrl = exports.setLocalMode = void 0;
|
|
4
|
+
// Production URLs
|
|
5
|
+
const PRODUCTION_API_URL = 'https://api.underlings.org/graphql';
|
|
6
|
+
const PRODUCTION_AUTH_URL = 'https://team.underlings.org/cli-login';
|
|
7
|
+
// Local URLs
|
|
8
|
+
const LOCAL_API_URL = 'http://localhost:4000/graphql';
|
|
9
|
+
const LOCAL_AUTH_URL = 'http://localhost:5173/cli-login';
|
|
10
|
+
let isLocalMode = false;
|
|
11
|
+
const setLocalMode = (local) => {
|
|
12
|
+
isLocalMode = local;
|
|
13
|
+
};
|
|
14
|
+
exports.setLocalMode = setLocalMode;
|
|
15
|
+
const getApiUrl = () => {
|
|
16
|
+
return isLocalMode ? LOCAL_API_URL : PRODUCTION_API_URL;
|
|
17
|
+
};
|
|
18
|
+
exports.getApiUrl = getApiUrl;
|
|
19
|
+
const getAuthUrl = () => {
|
|
20
|
+
return isLocalMode ? LOCAL_AUTH_URL : PRODUCTION_AUTH_URL;
|
|
21
|
+
};
|
|
22
|
+
exports.getAuthUrl = getAuthUrl;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
const commander_1 = require("commander");
|
|
38
|
+
const constants_1 = require("./constants");
|
|
39
|
+
const program = new commander_1.Command();
|
|
40
|
+
program
|
|
41
|
+
.name('underlings')
|
|
42
|
+
.description('Underlings Developer Utilities CLI')
|
|
43
|
+
.version('0.1.0')
|
|
44
|
+
.option('--local', 'Use local development environment')
|
|
45
|
+
.hook('preAction', (thisCommand) => {
|
|
46
|
+
if (thisCommand.opts().local) {
|
|
47
|
+
(0, constants_1.setLocalMode)(true);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
// ─── Auth ───────────────────────────────────────────────────────────────────
|
|
51
|
+
const auth = program.command('auth').description('Authentication commands');
|
|
52
|
+
auth.command('login')
|
|
53
|
+
.description('Authenticate with Underlings')
|
|
54
|
+
.action(async () => {
|
|
55
|
+
const { login } = await Promise.resolve().then(() => __importStar(require('./commands/login')));
|
|
56
|
+
await login();
|
|
57
|
+
});
|
|
58
|
+
auth.command('whoami')
|
|
59
|
+
.description('Check current authenticated user')
|
|
60
|
+
.action(async () => {
|
|
61
|
+
const { whoami } = await Promise.resolve().then(() => __importStar(require('./commands/login')));
|
|
62
|
+
await whoami();
|
|
63
|
+
});
|
|
64
|
+
auth.command('logout')
|
|
65
|
+
.description('Log out of Underlings')
|
|
66
|
+
.action(async () => {
|
|
67
|
+
const { logout } = await Promise.resolve().then(() => __importStar(require('./commands/login')));
|
|
68
|
+
await logout();
|
|
69
|
+
});
|
|
70
|
+
// ─── Project ────────────────────────────────────────────────────────────────
|
|
71
|
+
const project = program.command('project').description('Project management commands');
|
|
72
|
+
project.command('init')
|
|
73
|
+
.description('Initialize a project in the current directory')
|
|
74
|
+
.action(async () => {
|
|
75
|
+
const { initProject } = await Promise.resolve().then(() => __importStar(require('./commands/project')));
|
|
76
|
+
await initProject();
|
|
77
|
+
});
|
|
78
|
+
// ─── Service ────────────────────────────────────────────────────────────────
|
|
79
|
+
const service = program.command('service').description('Service management commands');
|
|
80
|
+
service.command('init')
|
|
81
|
+
.description('Initialize a service in the current directory')
|
|
82
|
+
.action(async () => {
|
|
83
|
+
const { initService } = await Promise.resolve().then(() => __importStar(require('./commands/project')));
|
|
84
|
+
await initService();
|
|
85
|
+
});
|
|
86
|
+
// ─── Run ────────────────────────────────────────────────────────────────────
|
|
87
|
+
program.command('run [command...]')
|
|
88
|
+
.description('Run a command with injected environment variables')
|
|
89
|
+
.allowUnknownOption()
|
|
90
|
+
.action(async (commandParts) => {
|
|
91
|
+
const { run } = await Promise.resolve().then(() => __importStar(require('./commands/run')));
|
|
92
|
+
await run(commandParts);
|
|
93
|
+
});
|
|
94
|
+
// ─── Env ────────────────────────────────────────────────────────────────────
|
|
95
|
+
program.command('env')
|
|
96
|
+
.description('Print resolved environment variables for current service + branch')
|
|
97
|
+
.action(async () => {
|
|
98
|
+
const { env } = await Promise.resolve().then(() => __importStar(require('./commands/run')));
|
|
99
|
+
await env();
|
|
100
|
+
});
|
|
101
|
+
program.parse();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface UserInfo {
|
|
2
|
+
id: string;
|
|
3
|
+
email: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const getAuthToken: () => string | undefined;
|
|
7
|
+
export declare const setAuthToken: (token: string) => void;
|
|
8
|
+
export declare const clearAuth: () => void;
|
|
9
|
+
export declare const setUserInfo: (user: UserInfo) => void;
|
|
10
|
+
export declare const getUserInfo: () => UserInfo | undefined;
|
|
11
|
+
export declare const isAuthenticated: () => boolean;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isAuthenticated = exports.getUserInfo = exports.setUserInfo = exports.clearAuth = exports.setAuthToken = exports.getAuthToken = void 0;
|
|
7
|
+
const conf_1 = __importDefault(require("conf"));
|
|
8
|
+
const schema = {
|
|
9
|
+
auth: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
token: { type: 'string' },
|
|
13
|
+
user: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
id: { type: 'string' },
|
|
17
|
+
email: { type: 'string' },
|
|
18
|
+
name: { type: 'string' },
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
const config = new conf_1.default({
|
|
25
|
+
projectName: 'underlings-cli',
|
|
26
|
+
schema,
|
|
27
|
+
});
|
|
28
|
+
const getAuthToken = () => {
|
|
29
|
+
if (process.env.UL_TOKEN) {
|
|
30
|
+
return process.env.UL_TOKEN;
|
|
31
|
+
}
|
|
32
|
+
return config.get('auth.token');
|
|
33
|
+
};
|
|
34
|
+
exports.getAuthToken = getAuthToken;
|
|
35
|
+
const setAuthToken = (token) => {
|
|
36
|
+
config.set('auth.token', token);
|
|
37
|
+
};
|
|
38
|
+
exports.setAuthToken = setAuthToken;
|
|
39
|
+
const clearAuth = () => {
|
|
40
|
+
config.delete('auth');
|
|
41
|
+
};
|
|
42
|
+
exports.clearAuth = clearAuth;
|
|
43
|
+
const setUserInfo = (user) => {
|
|
44
|
+
config.set('auth.user', user);
|
|
45
|
+
};
|
|
46
|
+
exports.setUserInfo = setUserInfo;
|
|
47
|
+
const getUserInfo = () => {
|
|
48
|
+
return config.get('auth.user');
|
|
49
|
+
};
|
|
50
|
+
exports.getUserInfo = getUserInfo;
|
|
51
|
+
const isAuthenticated = () => {
|
|
52
|
+
return !!(0, exports.getAuthToken)();
|
|
53
|
+
};
|
|
54
|
+
exports.isAuthenticated = isAuthenticated;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const PROJECT_CONFIG_FILE = "ulproject.json";
|
|
2
|
+
export declare const SERVICE_CONFIG_FILE = "ulservice.json";
|
|
3
|
+
interface ProjectConfig {
|
|
4
|
+
project: string;
|
|
5
|
+
}
|
|
6
|
+
interface ServiceConfig {
|
|
7
|
+
project: string;
|
|
8
|
+
service: string;
|
|
9
|
+
}
|
|
10
|
+
/** Search up the directory tree for ulproject.json (or ccproject.json) */
|
|
11
|
+
export declare const findProjectConfigPath: (startDir?: string) => string | null;
|
|
12
|
+
export declare const hasProjectConfig: () => boolean;
|
|
13
|
+
export declare const readProjectConfig: () => ProjectConfig;
|
|
14
|
+
/** Check for ulservice.json or ccservice.json in cwd */
|
|
15
|
+
export declare const findServiceConfigPath: () => string | null;
|
|
16
|
+
export declare const hasServiceConfig: () => boolean;
|
|
17
|
+
export declare const readServiceConfig: () => ServiceConfig;
|
|
18
|
+
export declare const writeProjectConfig: (config: ProjectConfig) => void;
|
|
19
|
+
export declare const writeServiceConfig: (config: ServiceConfig) => void;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.writeServiceConfig = exports.writeProjectConfig = exports.readServiceConfig = exports.hasServiceConfig = exports.findServiceConfigPath = exports.readProjectConfig = exports.hasProjectConfig = exports.findProjectConfigPath = exports.SERVICE_CONFIG_FILE = exports.PROJECT_CONFIG_FILE = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
exports.PROJECT_CONFIG_FILE = 'ulproject.json';
|
|
10
|
+
exports.SERVICE_CONFIG_FILE = 'ulservice.json';
|
|
11
|
+
// Also check legacy CC config files for backward compat
|
|
12
|
+
const LEGACY_PROJECT_CONFIG = 'ccproject.json';
|
|
13
|
+
const LEGACY_SERVICE_CONFIG = 'ccservice.json';
|
|
14
|
+
/** Search up the directory tree for ulproject.json (or ccproject.json) */
|
|
15
|
+
const findProjectConfigPath = (startDir = process.cwd()) => {
|
|
16
|
+
let currentDir = startDir;
|
|
17
|
+
while (true) {
|
|
18
|
+
// Check new name first
|
|
19
|
+
const ulPath = path_1.default.resolve(currentDir, exports.PROJECT_CONFIG_FILE);
|
|
20
|
+
if (fs_1.default.existsSync(ulPath))
|
|
21
|
+
return ulPath;
|
|
22
|
+
// Check legacy name
|
|
23
|
+
const ccPath = path_1.default.resolve(currentDir, LEGACY_PROJECT_CONFIG);
|
|
24
|
+
if (fs_1.default.existsSync(ccPath))
|
|
25
|
+
return ccPath;
|
|
26
|
+
const parentDir = path_1.default.dirname(currentDir);
|
|
27
|
+
if (parentDir === currentDir)
|
|
28
|
+
return null;
|
|
29
|
+
currentDir = parentDir;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
exports.findProjectConfigPath = findProjectConfigPath;
|
|
33
|
+
const hasProjectConfig = () => {
|
|
34
|
+
return !!(0, exports.findProjectConfigPath)();
|
|
35
|
+
};
|
|
36
|
+
exports.hasProjectConfig = hasProjectConfig;
|
|
37
|
+
const readProjectConfig = () => {
|
|
38
|
+
const configPath = (0, exports.findProjectConfigPath)();
|
|
39
|
+
if (!configPath) {
|
|
40
|
+
throw new Error(`Project config file not found in ancestors`);
|
|
41
|
+
}
|
|
42
|
+
return JSON.parse(fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
43
|
+
};
|
|
44
|
+
exports.readProjectConfig = readProjectConfig;
|
|
45
|
+
/** Check for ulservice.json or ccservice.json in cwd */
|
|
46
|
+
const findServiceConfigPath = () => {
|
|
47
|
+
const ulPath = path_1.default.resolve(process.cwd(), exports.SERVICE_CONFIG_FILE);
|
|
48
|
+
if (fs_1.default.existsSync(ulPath))
|
|
49
|
+
return ulPath;
|
|
50
|
+
const ccPath = path_1.default.resolve(process.cwd(), LEGACY_SERVICE_CONFIG);
|
|
51
|
+
if (fs_1.default.existsSync(ccPath))
|
|
52
|
+
return ccPath;
|
|
53
|
+
return null;
|
|
54
|
+
};
|
|
55
|
+
exports.findServiceConfigPath = findServiceConfigPath;
|
|
56
|
+
const hasServiceConfig = () => {
|
|
57
|
+
return !!(0, exports.findServiceConfigPath)();
|
|
58
|
+
};
|
|
59
|
+
exports.hasServiceConfig = hasServiceConfig;
|
|
60
|
+
const readServiceConfig = () => {
|
|
61
|
+
const configPath = (0, exports.findServiceConfigPath)();
|
|
62
|
+
if (!configPath) {
|
|
63
|
+
throw new Error(`Service config file not found in current directory`);
|
|
64
|
+
}
|
|
65
|
+
return JSON.parse(fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
66
|
+
};
|
|
67
|
+
exports.readServiceConfig = readServiceConfig;
|
|
68
|
+
const writeProjectConfig = (config) => {
|
|
69
|
+
const configPath = path_1.default.resolve(process.cwd(), exports.PROJECT_CONFIG_FILE);
|
|
70
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
71
|
+
};
|
|
72
|
+
exports.writeProjectConfig = writeProjectConfig;
|
|
73
|
+
const writeServiceConfig = (config) => {
|
|
74
|
+
const configPath = path_1.default.resolve(process.cwd(), exports.SERVICE_CONFIG_FILE);
|
|
75
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
76
|
+
};
|
|
77
|
+
exports.writeServiceConfig = writeServiceConfig;
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@underlingscloud/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Underlings Developer Utilities CLI",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"underlings": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "tsx src/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"axios": "^1.6.0",
|
|
15
|
+
"chalk": "^4.1.2",
|
|
16
|
+
"commander": "^11.1.0",
|
|
17
|
+
"conf": "^10.2.0",
|
|
18
|
+
"cross-spawn": "^7.0.3",
|
|
19
|
+
"open": "^9.1.0",
|
|
20
|
+
"prompts": "^2.4.2"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/cross-spawn": "^6.0.6",
|
|
24
|
+
"@types/prompts": "^2.4.9",
|
|
25
|
+
"typescript": "^5.0.0",
|
|
26
|
+
"tsx": "^4.0.0"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/underlings/ul-mono.git",
|
|
37
|
+
"directory": "packages/cli"
|
|
38
|
+
},
|
|
39
|
+
"license": "MIT"
|
|
40
|
+
}
|