@osaas/cli 4.24.0 → 4.25.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/cli.js +11 -0
- package/dist/cli.js.map +1 -1
- package/dist/login.d.ts +6 -0
- package/dist/login.d.ts.map +1 -0
- package/dist/login.js +165 -0
- package/dist/login.js.map +1 -0
- package/dist/user/util.d.ts +1 -0
- package/dist/user/util.d.ts.map +1 -1
- package/dist/user/util.js +33 -1
- package/dist/user/util.js.map +1 -1
- package/package.json +5 -5
- package/readme.md +221 -63
- package/src/cli.ts +12 -0
- package/src/login.ts +190 -0
- package/src/user/util.ts +38 -0
package/dist/cli.js
CHANGED
|
@@ -41,6 +41,8 @@ const cmd_7 = __importDefault(require("./db/cmd"));
|
|
|
41
41
|
const cmd_8 = require("./architect/cmd");
|
|
42
42
|
const cmd_9 = require("./vod/cmd");
|
|
43
43
|
const cmd_10 = require("./web/cmd");
|
|
44
|
+
const login_1 = require("./login");
|
|
45
|
+
const util_1 = require("./user/util");
|
|
44
46
|
const cli = new commander_1.Command();
|
|
45
47
|
// Read package.json
|
|
46
48
|
const packageJson = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '..', 'package.json'), 'utf8'));
|
|
@@ -48,6 +50,15 @@ cli.version(packageJson.version, '-v, --version', 'Output the current version');
|
|
|
48
50
|
cli
|
|
49
51
|
.configureHelp({ showGlobalOptions: true })
|
|
50
52
|
.option('--env <environment>', 'Environment to use (overrides ENVIRONMENT env var)', process.env.ENVIRONMENT || 'prod');
|
|
53
|
+
cli.addCommand((0, login_1.cmdLogin)());
|
|
54
|
+
cli.hook('preAction', async (thisCommand) => {
|
|
55
|
+
const commandName = thisCommand.args?.[0] || thisCommand.name();
|
|
56
|
+
if (commandName !== 'login') {
|
|
57
|
+
const globalOpts = thisCommand.optsWithGlobals();
|
|
58
|
+
const environment = globalOpts?.env || 'prod';
|
|
59
|
+
await (0, util_1.ensureToken)(environment);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
51
62
|
cli.addCommand((0, cmd_1.default)());
|
|
52
63
|
cli.addCommand(cmdUser.cmdList());
|
|
53
64
|
cli.addCommand(cmdUser.cmdCreate());
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,2BAAkC;AAClC,+BAA4B;AAC5B,yCAAoC;AACpC,sDAAmC;AACnC,oDAAsC;AACtC,qDAAoC;AACpC,yDAAyC;AACzC,qDAAiC;AACjC,yDAAyC;AACzC,2DAA6C;AAC7C,mDAA6B;AAC7B,yCAA0C;AAC1C,mCAAmC;AACnC,oCAAmC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/login.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
export declare function saveToken(token: string, environment?: string): void;
|
|
3
|
+
export declare function loadToken(environment?: string): string | undefined;
|
|
4
|
+
export declare function login(environment?: string): Promise<string>;
|
|
5
|
+
export declare function cmdLogin(): Command;
|
|
6
|
+
//# sourceMappingURL=login.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,SAAS,GAAG,IAAI,CAKnE;AAED,wBAAgB,SAAS,CAAC,WAAW,SAAS,GAAG,MAAM,GAAG,SAAS,CAMlE;AA2BD,wBAAsB,KAAK,CAAC,WAAW,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CA+GjE;AAED,wBAAgB,QAAQ,YAmBvB"}
|
package/dist/login.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
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.cmdLogin = exports.login = exports.loadToken = exports.saveToken = void 0;
|
|
7
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
8
|
+
const node_http_1 = __importDefault(require("node:http"));
|
|
9
|
+
const node_child_process_1 = require("node:child_process");
|
|
10
|
+
const node_fs_1 = require("node:fs");
|
|
11
|
+
const node_path_1 = require("node:path");
|
|
12
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
13
|
+
const commander_1 = require("commander");
|
|
14
|
+
const CLIENT_ID = 'cli';
|
|
15
|
+
const TOKEN_DIR = (0, node_path_1.join)(node_os_1.default.homedir(), '.osc');
|
|
16
|
+
function getTokenPath(environment) {
|
|
17
|
+
const suffix = !environment || environment === 'prod' ? '' : `-${environment}`;
|
|
18
|
+
return (0, node_path_1.join)(TOKEN_DIR, `token${suffix}`);
|
|
19
|
+
}
|
|
20
|
+
function saveToken(token, environment = 'prod') {
|
|
21
|
+
if (!(0, node_fs_1.existsSync)(TOKEN_DIR)) {
|
|
22
|
+
(0, node_fs_1.mkdirSync)(TOKEN_DIR, { mode: 0o700 });
|
|
23
|
+
}
|
|
24
|
+
(0, node_fs_1.writeFileSync)(getTokenPath(environment), token, { mode: 0o600 });
|
|
25
|
+
}
|
|
26
|
+
exports.saveToken = saveToken;
|
|
27
|
+
function loadToken(environment = 'prod') {
|
|
28
|
+
const tokenPath = getTokenPath(environment);
|
|
29
|
+
if ((0, node_fs_1.existsSync)(tokenPath)) {
|
|
30
|
+
return (0, node_fs_1.readFileSync)(tokenPath, 'utf-8').trim();
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
exports.loadToken = loadToken;
|
|
35
|
+
function getBaseUrl(environment) {
|
|
36
|
+
if (!environment || environment === 'prod') {
|
|
37
|
+
return 'https://app.osaas.io';
|
|
38
|
+
}
|
|
39
|
+
return `https://app.${environment}.osaas.io`;
|
|
40
|
+
}
|
|
41
|
+
function generateCodeVerifier() {
|
|
42
|
+
return node_crypto_1.default.randomBytes(32).toString('base64url');
|
|
43
|
+
}
|
|
44
|
+
function generateCodeChallenge(verifier) {
|
|
45
|
+
return node_crypto_1.default.createHash('sha256').update(verifier).digest('base64url');
|
|
46
|
+
}
|
|
47
|
+
function openBrowser(url) {
|
|
48
|
+
const command = process.platform === 'darwin'
|
|
49
|
+
? 'open'
|
|
50
|
+
: process.platform === 'win32'
|
|
51
|
+
? 'start'
|
|
52
|
+
: 'xdg-open';
|
|
53
|
+
(0, node_child_process_1.exec)(`${command} "${url}"`);
|
|
54
|
+
}
|
|
55
|
+
async function login(environment = 'prod') {
|
|
56
|
+
const baseUrl = getBaseUrl(environment);
|
|
57
|
+
const authUrlBase = `${baseUrl}/api/connect/authorize`;
|
|
58
|
+
const tokenUrl = `${baseUrl}/api/connect/token`;
|
|
59
|
+
const codeVerifier = generateCodeVerifier();
|
|
60
|
+
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
const server = node_http_1.default.createServer(async (req, res) => {
|
|
63
|
+
try {
|
|
64
|
+
const url = new URL(req.url || '', `http://localhost`);
|
|
65
|
+
if (url.pathname !== '/callback') {
|
|
66
|
+
res.writeHead(404);
|
|
67
|
+
res.end('Not found');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const code = url.searchParams.get('code');
|
|
71
|
+
const error = url.searchParams.get('error');
|
|
72
|
+
if (error) {
|
|
73
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
74
|
+
res.end('<html><body><h1>Login failed</h1><p>You can close this window.</p></body></html>');
|
|
75
|
+
server.close();
|
|
76
|
+
reject(new Error(`Authorization failed: ${error}`));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (!code) {
|
|
80
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
81
|
+
res.end('<html><body><h1>Login failed</h1><p>No authorization code received.</p></body></html>');
|
|
82
|
+
server.close();
|
|
83
|
+
reject(new Error('No authorization code received'));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const address = server.address();
|
|
87
|
+
const port = address && typeof address === 'object' ? address.port : null;
|
|
88
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
89
|
+
const tokenResponse = await fetch(tokenUrl, {
|
|
90
|
+
method: 'POST',
|
|
91
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
92
|
+
body: new URLSearchParams({
|
|
93
|
+
grant_type: 'authorization_code',
|
|
94
|
+
client_id: CLIENT_ID,
|
|
95
|
+
code,
|
|
96
|
+
redirect_uri: redirectUri,
|
|
97
|
+
code_verifier: codeVerifier
|
|
98
|
+
}).toString()
|
|
99
|
+
});
|
|
100
|
+
if (!tokenResponse.ok) {
|
|
101
|
+
const errorText = await tokenResponse.text();
|
|
102
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
103
|
+
res.end('<html><body><h1>Login failed</h1><p>Token exchange failed. You can close this window.</p></body></html>');
|
|
104
|
+
server.close();
|
|
105
|
+
reject(new Error(`Token exchange failed: ${errorText}`));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const tokenData = (await tokenResponse.json());
|
|
109
|
+
const accessToken = tokenData.access_token;
|
|
110
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
111
|
+
res.end('<html><body><h1>Login successful!</h1><p>You can close this window and return to the terminal.</p></body></html>');
|
|
112
|
+
server.close();
|
|
113
|
+
resolve(accessToken);
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
res.writeHead(500, { 'Content-Type': 'text/html' });
|
|
117
|
+
res.end('<html><body><h1>Login failed</h1><p>An unexpected error occurred.</p></body></html>');
|
|
118
|
+
server.close();
|
|
119
|
+
reject(err);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
server.listen(0, () => {
|
|
123
|
+
const address = server.address();
|
|
124
|
+
const port = address && typeof address === 'object' ? address.port : null;
|
|
125
|
+
if (!port) {
|
|
126
|
+
server.close();
|
|
127
|
+
reject(new Error('Failed to start local server'));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
131
|
+
const authUrl = new URL(authUrlBase);
|
|
132
|
+
authUrl.searchParams.set('response_type', 'code');
|
|
133
|
+
authUrl.searchParams.set('client_id', CLIENT_ID);
|
|
134
|
+
authUrl.searchParams.set('redirect_uri', redirectUri);
|
|
135
|
+
authUrl.searchParams.set('code_challenge', codeChallenge);
|
|
136
|
+
authUrl.searchParams.set('code_challenge_method', 'S256');
|
|
137
|
+
console.log('Opening browser for login...');
|
|
138
|
+
console.log(`If the browser does not open, visit: ${authUrl.toString()}`);
|
|
139
|
+
openBrowser(authUrl.toString());
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
exports.login = login;
|
|
144
|
+
function cmdLogin() {
|
|
145
|
+
const loginCmd = new commander_1.Command('login');
|
|
146
|
+
loginCmd
|
|
147
|
+
.description('Login to Open Source Cloud using OAuth')
|
|
148
|
+
.action(async (options, command) => {
|
|
149
|
+
try {
|
|
150
|
+
const globalOpts = command.optsWithGlobals();
|
|
151
|
+
const environment = globalOpts?.env || 'prod';
|
|
152
|
+
const token = await login(environment);
|
|
153
|
+
saveToken(token, environment);
|
|
154
|
+
process.env.OSC_ACCESS_TOKEN = token;
|
|
155
|
+
console.log('Login successful! Token saved to ~/.osc');
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
console.log(err.message);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
return loginCmd;
|
|
163
|
+
}
|
|
164
|
+
exports.cmdLogin = cmdLogin;
|
|
165
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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"}
|
package/dist/user/util.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ export declare function instanceOptsToPayload(opts: string[] | undefined): {
|
|
|
3
3
|
};
|
|
4
4
|
export declare function makeSafeName(name: string): string;
|
|
5
5
|
export declare function confirm(message: string | undefined): Promise<void>;
|
|
6
|
+
export declare function ensureToken(environment?: string): Promise<void>;
|
|
6
7
|
//# sourceMappingURL=util.d.ts.map
|
package/dist/user/util.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/user/util.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/user/util.ts"],"names":[],"mappings":"AAGA,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAS;;EA0B/D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBxE;AAED,wBAAsB,WAAW,CAAC,WAAW,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCrE"}
|
package/dist/user/util.js
CHANGED
|
@@ -3,8 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.confirm = exports.makeSafeName = exports.instanceOptsToPayload = void 0;
|
|
6
|
+
exports.ensureToken = exports.confirm = exports.makeSafeName = exports.instanceOptsToPayload = void 0;
|
|
7
7
|
const node_readline_1 = __importDefault(require("node:readline"));
|
|
8
|
+
const login_1 = require("../login");
|
|
8
9
|
function instanceOptsToPayload(opts) {
|
|
9
10
|
const payload = {};
|
|
10
11
|
if (opts) {
|
|
@@ -58,4 +59,35 @@ async function confirm(message) {
|
|
|
58
59
|
});
|
|
59
60
|
}
|
|
60
61
|
exports.confirm = confirm;
|
|
62
|
+
async function ensureToken(environment = 'prod') {
|
|
63
|
+
if (process.env.OSC_ACCESS_TOKEN) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const savedToken = (0, login_1.loadToken)(environment);
|
|
67
|
+
if (savedToken) {
|
|
68
|
+
process.env.OSC_ACCESS_TOKEN = savedToken;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const rl = node_readline_1.default.createInterface({
|
|
72
|
+
input: process.stdin,
|
|
73
|
+
output: process.stdout
|
|
74
|
+
});
|
|
75
|
+
const answer = await new Promise((resolve) => {
|
|
76
|
+
rl.question('OSC_ACCESS_TOKEN is not set. Do you want to login? (yes/no) ', (ans) => {
|
|
77
|
+
rl.close();
|
|
78
|
+
resolve(ans.trim().toLowerCase());
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
if (answer === 'yes') {
|
|
82
|
+
const token = await (0, login_1.login)(environment);
|
|
83
|
+
(0, login_1.saveToken)(token, environment);
|
|
84
|
+
process.env.OSC_ACCESS_TOKEN = token;
|
|
85
|
+
console.log('Login successful!');
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
console.error('OSC_ACCESS_TOKEN is required. Set it or run: osc login');
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.ensureToken = ensureToken;
|
|
61
93
|
//# sourceMappingURL=util.js.map
|
package/dist/user/util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/user/util.ts"],"names":[],"mappings":";;;;;;AAAA,kEAAqC;
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/user/util.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@osaas/cli",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.25.0",
|
|
4
4
|
"description": "Open Source Cloud CLI",
|
|
5
5
|
"author": "Eyevinn Open Source Cloud <osc@eyevinn.se>",
|
|
6
6
|
"homepage": "https://www.osaas.io",
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@osaas/client-ai": "^0.4.10",
|
|
25
25
|
"@osaas/client-core": "^0.20.0",
|
|
26
|
-
"@osaas/client-db": "^0.6.
|
|
26
|
+
"@osaas/client-db": "^0.6.8",
|
|
27
27
|
"@osaas/client-intercom": "^0.3.5",
|
|
28
|
-
"@osaas/client-transcode": "^0.27.
|
|
29
|
-
"@osaas/client-web": "^0.5.
|
|
28
|
+
"@osaas/client-transcode": "^0.27.1",
|
|
29
|
+
"@osaas/client-web": "^0.5.12",
|
|
30
30
|
"@types/promptly": "^3.0.5",
|
|
31
31
|
"chalk": "4.1.2",
|
|
32
32
|
"commander": "^12.1.0",
|
|
@@ -37,5 +37,5 @@
|
|
|
37
37
|
"publishConfig": {
|
|
38
38
|
"access": "public"
|
|
39
39
|
},
|
|
40
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "256415ebd4ee729710330c7760e936b45d0188a3"
|
|
41
41
|
}
|
package/readme.md
CHANGED
|
@@ -5,7 +5,7 @@ CLI for working and scripting with [Open Source Cloud](www.osaas.io)
|
|
|
5
5
|
Prerequisites:
|
|
6
6
|
|
|
7
7
|
- Node >18
|
|
8
|
-
- An Open Source Cloud account
|
|
8
|
+
- An Open Source Cloud account
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
@@ -13,6 +13,38 @@ Prerequisites:
|
|
|
13
13
|
npm install -g @osaas/cli
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
## Authentication
|
|
17
|
+
|
|
18
|
+
You can authenticate with Open Source Cloud in two ways:
|
|
19
|
+
|
|
20
|
+
### Option 1: Login via browser (recommended)
|
|
21
|
+
|
|
22
|
+
Run the `login` command to authenticate using your browser. The token is saved to `~/.osc` and used automatically for subsequent commands.
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
% osc login
|
|
26
|
+
Opening browser for login...
|
|
27
|
+
Login successful! Token saved to ~/.osc
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
When using a non-production environment:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
% osc --env dev login
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Option 2: Personal Access Token
|
|
37
|
+
|
|
38
|
+
Set the environment variable `OSC_ACCESS_TOKEN` with your personal access token. Obtain the personal access token in the Eyevinn Open Source Cloud web console.
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
% export OSC_ACCESS_TOKEN=<personal-access-token>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Auto-prompt
|
|
45
|
+
|
|
46
|
+
If neither a saved token nor `OSC_ACCESS_TOKEN` is found when running a command, the CLI will prompt you to login interactively.
|
|
47
|
+
|
|
16
48
|
## Usage
|
|
17
49
|
|
|
18
50
|
```
|
|
@@ -20,47 +52,39 @@ npm install -g @osaas/cli
|
|
|
20
52
|
Usage: osc [options] [command]
|
|
21
53
|
|
|
22
54
|
Options:
|
|
23
|
-
-v, --version
|
|
24
|
-
--env <environment>
|
|
25
|
-
-h, --help
|
|
55
|
+
-v, --version Output the current version
|
|
56
|
+
--env <environment> Environment to use
|
|
57
|
+
-h, --help display help for command
|
|
26
58
|
|
|
27
59
|
Commands:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
logs <serviceId> <name>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
web
|
|
51
|
-
|
|
52
|
-
web config-to-env <name> Export configuration values as environment variables
|
|
53
|
-
help [command] display help for command
|
|
60
|
+
login Login to Open Source Cloud using OAuth
|
|
61
|
+
admin Administrative commands for OSC super admins
|
|
62
|
+
list <serviceId> List all my service instances
|
|
63
|
+
create [options] <serviceId> <name> Create a service instance
|
|
64
|
+
describe <serviceId> <name> Get details for a service instance
|
|
65
|
+
remove [options] <serviceId> <name> Remove a service instance
|
|
66
|
+
logs <serviceId> <name> Get logs for a service instance
|
|
67
|
+
restart <serviceId> <name> Restart a service instance
|
|
68
|
+
get-instance-replicas <serviceId> <name> Get the number of replicas for a service instance
|
|
69
|
+
set-instance-replicas <serviceId> <name> <n> Set the number of replicas for a service instance
|
|
70
|
+
service-access-token <serviceId> Generate a service access token for a service
|
|
71
|
+
list-reserved-nodes List all my reserved nodes
|
|
72
|
+
secrets Manage secrets for services (list, create, update, remove)
|
|
73
|
+
packager [options] <source> <dest> Create streaming package from ABR bundle on S3
|
|
74
|
+
compare vmaf [options] <reference> <distorted> <result>
|
|
75
|
+
Compare two video files using VMAF
|
|
76
|
+
live Live streaming commands
|
|
77
|
+
intercom Intercom messaging commands
|
|
78
|
+
transcribe [options] <source> Generate subtitles from video or audio using OpenAI Whisper
|
|
79
|
+
db Database management commands
|
|
80
|
+
architect AI chat assistant for architecture guidance
|
|
81
|
+
vod VOD transcoding and packaging commands
|
|
82
|
+
web Web publishing and configuration commands
|
|
83
|
+
help [command] display help for command
|
|
54
84
|
```
|
|
55
85
|
|
|
56
86
|
## Examples
|
|
57
87
|
|
|
58
|
-
First set the environment variable `OSC_ACCESS_TOKEN` with your personal access token. Obtain the personal access token in the Eyevinn Open Source Cloud web console.
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
% export OSC_ACCESS_TOKEN=<personal-access-token>
|
|
62
|
-
```
|
|
63
|
-
|
|
64
88
|
### Create a MinIO storage service and bucket
|
|
65
89
|
|
|
66
90
|
Create a MinIO storage server instance called `mystore` with the given credentials.
|
|
@@ -141,6 +165,50 @@ Setup CDN property in AWS Cloudfront
|
|
|
141
165
|
Are you sure you want to remove mystore? (yes/no) yes
|
|
142
166
|
```
|
|
143
167
|
|
|
168
|
+
### Create a service instance with safe name option
|
|
169
|
+
|
|
170
|
+
Create a service instance with invalid characters stripped from the name:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
% osc create minio-minio "my store with spaces!" --safe-name -o RootUser=admin -o RootPassword=abC12345678
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Instance replica management
|
|
177
|
+
|
|
178
|
+
Get the number of replicas for a service instance:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
% osc get-instance-replicas minio-minio mystore
|
|
182
|
+
Instance mystore has 1 actual replicas and 1 desired replicas.
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Set the number of replicas:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
% osc set-instance-replicas minio-minio mystore 3
|
|
189
|
+
Set desired replicas for instance mystore to 3.
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Service instance management
|
|
193
|
+
|
|
194
|
+
Restart a service instance:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
% osc restart minio-minio mystore
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
List your reserved nodes:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
% osc list-reserved-nodes
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Generate a service access token:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
% osc service-access-token minio-minio
|
|
210
|
+
```
|
|
211
|
+
|
|
144
212
|
### Create ABR file for VOD using SVT Encore
|
|
145
213
|
|
|
146
214
|
Create a VOD file for streaming from using a pipeline named `demo`. Follow the steps in the [Eyevinn Open Source Cloud documentation](https://docs.osaas.io/osaas.wiki/Solution%3A-VOD-Transcoding.html#vod-transcoding-and-packaging) on how to setup a pipeline.
|
|
@@ -161,47 +229,91 @@ Remove a VOD pipeline but keep the VOD files:
|
|
|
161
229
|
% osc vod cleanup demo
|
|
162
230
|
```
|
|
163
231
|
|
|
164
|
-
###
|
|
232
|
+
### Live streaming
|
|
233
|
+
|
|
234
|
+
Start a single bitrate live stream:
|
|
165
235
|
|
|
236
|
+
```bash
|
|
237
|
+
% osc live start-single mystreamname
|
|
238
|
+
Start streaming to rtmp://... and watch at https://...
|
|
166
239
|
```
|
|
167
|
-
|
|
240
|
+
|
|
241
|
+
Stop and remove a single bitrate live stream:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
% osc live stop-single mystreamname
|
|
168
245
|
```
|
|
169
246
|
|
|
170
|
-
|
|
247
|
+
List all single bitrate live streams:
|
|
171
248
|
|
|
249
|
+
```bash
|
|
250
|
+
% osc live list-single
|
|
172
251
|
```
|
|
173
|
-
|
|
252
|
+
|
|
253
|
+
Create and start a multi bitrate live stream:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
% osc live create-multi mystreamname
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Start an existing multi bitrate live stream:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
% osc live start-multi mystreamname
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Stop a multi bitrate live stream:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
% osc live stop-multi mystreamname
|
|
174
269
|
```
|
|
175
270
|
|
|
176
|
-
###
|
|
271
|
+
### Database management
|
|
177
272
|
|
|
178
|
-
|
|
273
|
+
Create a Valkey database:
|
|
179
274
|
|
|
180
275
|
```bash
|
|
181
|
-
% osc
|
|
182
|
-
Configuration service instance available at https://eyevinnlab-jonastest.eyevinn-app-config-svc.auto.prod.osaas.io
|
|
276
|
+
% osc db create valkey mydb
|
|
183
277
|
```
|
|
184
278
|
|
|
185
|
-
|
|
279
|
+
List all databases of a given type:
|
|
186
280
|
|
|
187
281
|
```bash
|
|
188
|
-
% osc
|
|
282
|
+
% osc db list valkey
|
|
189
283
|
```
|
|
190
284
|
|
|
191
|
-
|
|
285
|
+
Remove a database:
|
|
192
286
|
|
|
193
|
-
|
|
287
|
+
```bash
|
|
288
|
+
% osc db remove valkey mydb
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Intercom
|
|
292
|
+
|
|
293
|
+
List productions in an intercom system:
|
|
194
294
|
|
|
195
295
|
```bash
|
|
196
|
-
% osc
|
|
197
|
-
export AWS_ACCESS_KEY_ID=admin
|
|
198
|
-
export CHANNELURL=https://eyevinnlab.ce.prod.osaas.io/channels/mychannel/master.m3u8
|
|
296
|
+
% osc intercom list-productions myintercom
|
|
199
297
|
```
|
|
200
298
|
|
|
201
|
-
|
|
299
|
+
Create a production with lines:
|
|
202
300
|
|
|
203
301
|
```bash
|
|
204
|
-
%
|
|
302
|
+
% osc intercom create-production myintercom "My Production" line1 line2
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Delete a production:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
% osc intercom delete-production myintercom <productionId>
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### AI architect chat
|
|
312
|
+
|
|
313
|
+
Start an interactive chat session with the AI assistant:
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
% osc architect chat
|
|
205
317
|
```
|
|
206
318
|
|
|
207
319
|
### Compare video files using VMAF
|
|
@@ -212,6 +324,15 @@ Compare two video files and get a VMAF quality score:
|
|
|
212
324
|
% osc compare vmaf s3://bucket/reference.mp4 s3://bucket/distorted.mp4 s3://bucket/vmaf-result.json
|
|
213
325
|
```
|
|
214
326
|
|
|
327
|
+
Custom AWS credentials and S3 endpoint can be provided:
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
% osc compare vmaf \
|
|
331
|
+
--aws-access-key-id=AKID --aws-secret-access-key=SECRET \
|
|
332
|
+
--s3-endpoint-url=https://s3.example.com \
|
|
333
|
+
s3://bucket/reference.mp4 s3://bucket/distorted.mp4 s3://bucket/vmaf-result.json
|
|
334
|
+
```
|
|
335
|
+
|
|
215
336
|
### Generate subtitles from video or audio
|
|
216
337
|
|
|
217
338
|
Generate subtitles using OpenAI Whisper:
|
|
@@ -221,6 +342,14 @@ Generate subtitles using OpenAI Whisper:
|
|
|
221
342
|
% osc transcribe https://example.com/video.mp4 --format vtt --language en
|
|
222
343
|
```
|
|
223
344
|
|
|
345
|
+
### Create streaming package
|
|
346
|
+
|
|
347
|
+
Create a streaming package from an ABR bundle on S3:
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
% osc packager -v video_1.mp4 video_2.mp4 -a audio.mp4 s3://source-bucket/abr s3://dest-bucket/package
|
|
351
|
+
```
|
|
352
|
+
|
|
224
353
|
### Manage service secrets
|
|
225
354
|
|
|
226
355
|
List secrets for a service:
|
|
@@ -247,30 +376,59 @@ Remove a secret:
|
|
|
247
376
|
% osc secrets remove minio-minio mysecret
|
|
248
377
|
```
|
|
249
378
|
|
|
250
|
-
###
|
|
379
|
+
### Web publishing and configuration
|
|
251
380
|
|
|
252
|
-
|
|
381
|
+
Publish a website:
|
|
253
382
|
|
|
254
383
|
```bash
|
|
255
|
-
% osc
|
|
384
|
+
% osc web publish mysite ./dist
|
|
256
385
|
```
|
|
257
386
|
|
|
258
|
-
|
|
387
|
+
Create a CDN distribution:
|
|
259
388
|
|
|
260
389
|
```bash
|
|
261
|
-
% osc
|
|
390
|
+
% osc web cdn-create --provider=cloudfront --origin-path=/mybucket minio-minio mystore
|
|
262
391
|
```
|
|
263
392
|
|
|
264
|
-
|
|
393
|
+
### Application configuration service
|
|
394
|
+
|
|
395
|
+
Create a configuration service instance:
|
|
265
396
|
|
|
266
397
|
```bash
|
|
267
|
-
% osc
|
|
398
|
+
% osc web config-create jonastest
|
|
399
|
+
Configuration service instance available at https://eyevinnlab-jonastest.eyevinn-app-config-svc.auto.prod.osaas.io
|
|
268
400
|
```
|
|
269
401
|
|
|
270
|
-
|
|
402
|
+
And to remove it (with the data):
|
|
271
403
|
|
|
272
|
-
|
|
404
|
+
```bash
|
|
405
|
+
% osc web config-delete --data jonastest
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Store application configuration values as environment variables:
|
|
273
409
|
|
|
274
410
|
```bash
|
|
275
|
-
% osc
|
|
411
|
+
% osc web config-to-env <config-instance-name>
|
|
412
|
+
export AWS_ACCESS_KEY_ID=admin
|
|
413
|
+
export CHANNELURL=https://eyevinnlab.ce.prod.osaas.io/channels/mychannel/master.m3u8
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
And storing it in the shell:
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
% eval `osc web config-to-env <config-instance-name>`
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Admin commands
|
|
423
|
+
|
|
424
|
+
List all channel-engine instance for tenant `eyevinn` as an OSC super admin:
|
|
425
|
+
|
|
426
|
+
```
|
|
427
|
+
PAT_SECRET=<pat-secret> osc admin list-instances eyevinn channel-engine
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Remove a channel-engine instance in dev env for tenant `asdasd` as an OSC super admin:
|
|
431
|
+
|
|
432
|
+
```
|
|
433
|
+
PAT_SECRET=<pat-secret> osc --env dev admin remove-instance asdasd channel-engine mychannel
|
|
276
434
|
```
|
package/src/cli.ts
CHANGED
|
@@ -14,6 +14,8 @@ import cmdDb from './db/cmd';
|
|
|
14
14
|
import { cmdChat } from './architect/cmd';
|
|
15
15
|
import { cmdVod } from './vod/cmd';
|
|
16
16
|
import { cmdWeb } from './web/cmd';
|
|
17
|
+
import { cmdLogin } from './login';
|
|
18
|
+
import { ensureToken } from './user/util';
|
|
17
19
|
|
|
18
20
|
const cli = new Command();
|
|
19
21
|
|
|
@@ -32,6 +34,16 @@ cli
|
|
|
32
34
|
process.env.ENVIRONMENT || 'prod'
|
|
33
35
|
);
|
|
34
36
|
|
|
37
|
+
cli.addCommand(cmdLogin());
|
|
38
|
+
cli.hook('preAction', async (thisCommand) => {
|
|
39
|
+
const commandName = thisCommand.args?.[0] || thisCommand.name();
|
|
40
|
+
if (commandName !== 'login') {
|
|
41
|
+
const globalOpts = thisCommand.optsWithGlobals();
|
|
42
|
+
const environment = globalOpts?.env || 'prod';
|
|
43
|
+
await ensureToken(environment);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
35
47
|
cli.addCommand(cmdAdmin());
|
|
36
48
|
cli.addCommand(cmdUser.cmdList());
|
|
37
49
|
cli.addCommand(cmdUser.cmdCreate());
|
package/src/login.ts
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import http from 'node:http';
|
|
3
|
+
import { exec } from 'node:child_process';
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
|
|
9
|
+
const CLIENT_ID = 'cli';
|
|
10
|
+
const TOKEN_DIR = join(os.homedir(), '.osc');
|
|
11
|
+
|
|
12
|
+
function getTokenPath(environment: string): string {
|
|
13
|
+
const suffix =
|
|
14
|
+
!environment || environment === 'prod' ? '' : `-${environment}`;
|
|
15
|
+
return join(TOKEN_DIR, `token${suffix}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function saveToken(token: string, environment = 'prod'): void {
|
|
19
|
+
if (!existsSync(TOKEN_DIR)) {
|
|
20
|
+
mkdirSync(TOKEN_DIR, { mode: 0o700 });
|
|
21
|
+
}
|
|
22
|
+
writeFileSync(getTokenPath(environment), token, { mode: 0o600 });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function loadToken(environment = 'prod'): string | undefined {
|
|
26
|
+
const tokenPath = getTokenPath(environment);
|
|
27
|
+
if (existsSync(tokenPath)) {
|
|
28
|
+
return readFileSync(tokenPath, 'utf-8').trim();
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getBaseUrl(environment: string): string {
|
|
34
|
+
if (!environment || environment === 'prod') {
|
|
35
|
+
return 'https://app.osaas.io';
|
|
36
|
+
}
|
|
37
|
+
return `https://app.${environment}.osaas.io`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function generateCodeVerifier(): string {
|
|
41
|
+
return crypto.randomBytes(32).toString('base64url');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function generateCodeChallenge(verifier: string): string {
|
|
45
|
+
return crypto.createHash('sha256').update(verifier).digest('base64url');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function openBrowser(url: string): void {
|
|
49
|
+
const command =
|
|
50
|
+
process.platform === 'darwin'
|
|
51
|
+
? 'open'
|
|
52
|
+
: process.platform === 'win32'
|
|
53
|
+
? 'start'
|
|
54
|
+
: 'xdg-open';
|
|
55
|
+
exec(`${command} "${url}"`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function login(environment = 'prod'): Promise<string> {
|
|
59
|
+
const baseUrl = getBaseUrl(environment);
|
|
60
|
+
const authUrlBase = `${baseUrl}/api/connect/authorize`;
|
|
61
|
+
const tokenUrl = `${baseUrl}/api/connect/token`;
|
|
62
|
+
const codeVerifier = generateCodeVerifier();
|
|
63
|
+
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
64
|
+
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
const server = http.createServer(async (req, res) => {
|
|
67
|
+
try {
|
|
68
|
+
const url = new URL(req.url || '', `http://localhost`);
|
|
69
|
+
if (url.pathname !== '/callback') {
|
|
70
|
+
res.writeHead(404);
|
|
71
|
+
res.end('Not found');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const code = url.searchParams.get('code');
|
|
76
|
+
const error = url.searchParams.get('error');
|
|
77
|
+
|
|
78
|
+
if (error) {
|
|
79
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
80
|
+
res.end(
|
|
81
|
+
'<html><body><h1>Login failed</h1><p>You can close this window.</p></body></html>'
|
|
82
|
+
);
|
|
83
|
+
server.close();
|
|
84
|
+
reject(new Error(`Authorization failed: ${error}`));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!code) {
|
|
89
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
90
|
+
res.end(
|
|
91
|
+
'<html><body><h1>Login failed</h1><p>No authorization code received.</p></body></html>'
|
|
92
|
+
);
|
|
93
|
+
server.close();
|
|
94
|
+
reject(new Error('No authorization code received'));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const address = server.address();
|
|
99
|
+
const port =
|
|
100
|
+
address && typeof address === 'object' ? address.port : null;
|
|
101
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
102
|
+
|
|
103
|
+
const tokenResponse = await fetch(tokenUrl, {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
106
|
+
body: new URLSearchParams({
|
|
107
|
+
grant_type: 'authorization_code',
|
|
108
|
+
client_id: CLIENT_ID,
|
|
109
|
+
code,
|
|
110
|
+
redirect_uri: redirectUri,
|
|
111
|
+
code_verifier: codeVerifier
|
|
112
|
+
}).toString()
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
if (!tokenResponse.ok) {
|
|
116
|
+
const errorText = await tokenResponse.text();
|
|
117
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
118
|
+
res.end(
|
|
119
|
+
'<html><body><h1>Login failed</h1><p>Token exchange failed. You can close this window.</p></body></html>'
|
|
120
|
+
);
|
|
121
|
+
server.close();
|
|
122
|
+
reject(new Error(`Token exchange failed: ${errorText}`));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const tokenData = (await tokenResponse.json()) as {
|
|
127
|
+
access_token: string;
|
|
128
|
+
};
|
|
129
|
+
const accessToken = tokenData.access_token;
|
|
130
|
+
|
|
131
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
132
|
+
res.end(
|
|
133
|
+
'<html><body><h1>Login successful!</h1><p>You can close this window and return to the terminal.</p></body></html>'
|
|
134
|
+
);
|
|
135
|
+
server.close();
|
|
136
|
+
resolve(accessToken);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
res.writeHead(500, { 'Content-Type': 'text/html' });
|
|
139
|
+
res.end(
|
|
140
|
+
'<html><body><h1>Login failed</h1><p>An unexpected error occurred.</p></body></html>'
|
|
141
|
+
);
|
|
142
|
+
server.close();
|
|
143
|
+
reject(err);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
server.listen(0, () => {
|
|
148
|
+
const address = server.address();
|
|
149
|
+
const port = address && typeof address === 'object' ? address.port : null;
|
|
150
|
+
if (!port) {
|
|
151
|
+
server.close();
|
|
152
|
+
reject(new Error('Failed to start local server'));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
157
|
+
const authUrl = new URL(authUrlBase);
|
|
158
|
+
authUrl.searchParams.set('response_type', 'code');
|
|
159
|
+
authUrl.searchParams.set('client_id', CLIENT_ID);
|
|
160
|
+
authUrl.searchParams.set('redirect_uri', redirectUri);
|
|
161
|
+
authUrl.searchParams.set('code_challenge', codeChallenge);
|
|
162
|
+
authUrl.searchParams.set('code_challenge_method', 'S256');
|
|
163
|
+
|
|
164
|
+
console.log('Opening browser for login...');
|
|
165
|
+
console.log(`If the browser does not open, visit: ${authUrl.toString()}`);
|
|
166
|
+
openBrowser(authUrl.toString());
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function cmdLogin() {
|
|
172
|
+
const loginCmd = new Command('login');
|
|
173
|
+
|
|
174
|
+
loginCmd
|
|
175
|
+
.description('Login to Open Source Cloud using OAuth')
|
|
176
|
+
.action(async (options, command) => {
|
|
177
|
+
try {
|
|
178
|
+
const globalOpts = command.optsWithGlobals();
|
|
179
|
+
const environment = globalOpts?.env || 'prod';
|
|
180
|
+
const token = await login(environment);
|
|
181
|
+
saveToken(token, environment);
|
|
182
|
+
process.env.OSC_ACCESS_TOKEN = token;
|
|
183
|
+
console.log('Login successful! Token saved to ~/.osc');
|
|
184
|
+
} catch (err) {
|
|
185
|
+
console.log((err as Error).message);
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
return loginCmd;
|
|
190
|
+
}
|
package/src/user/util.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import readline from 'node:readline';
|
|
2
|
+
import { login, loadToken, saveToken } from '../login';
|
|
2
3
|
|
|
3
4
|
export function instanceOptsToPayload(opts: string[] | undefined) {
|
|
4
5
|
const payload: { [key: string]: any } = {};
|
|
@@ -50,3 +51,40 @@ export async function confirm(message: string | undefined): Promise<void> {
|
|
|
50
51
|
});
|
|
51
52
|
});
|
|
52
53
|
}
|
|
54
|
+
|
|
55
|
+
export async function ensureToken(environment = 'prod'): Promise<void> {
|
|
56
|
+
if (process.env.OSC_ACCESS_TOKEN) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const savedToken = loadToken(environment);
|
|
61
|
+
if (savedToken) {
|
|
62
|
+
process.env.OSC_ACCESS_TOKEN = savedToken;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const rl = readline.createInterface({
|
|
67
|
+
input: process.stdin,
|
|
68
|
+
output: process.stdout
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const answer = await new Promise<string>((resolve) => {
|
|
72
|
+
rl.question(
|
|
73
|
+
'OSC_ACCESS_TOKEN is not set. Do you want to login? (yes/no) ',
|
|
74
|
+
(ans) => {
|
|
75
|
+
rl.close();
|
|
76
|
+
resolve(ans.trim().toLowerCase());
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
if (answer === 'yes') {
|
|
82
|
+
const token = await login(environment);
|
|
83
|
+
saveToken(token, environment);
|
|
84
|
+
process.env.OSC_ACCESS_TOKEN = token;
|
|
85
|
+
console.log('Login successful!');
|
|
86
|
+
} else {
|
|
87
|
+
console.error('OSC_ACCESS_TOKEN is required. Set it or run: osc login');
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
}
|