@osaas/cli 4.24.1 → 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 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;AAEnC,MAAM,GAAG,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE1B,oBAAoB;AACpB,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAC5B,IAAA,iBAAY,EAAC,IAAA,WAAI,EAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAC5D,CAAC;AAEF,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,eAAe,EAAE,4BAA4B,CAAC,CAAC;AAEhF,GAAG;KACA,aAAa,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC;KAC1C,MAAM,CACL,qBAAqB,EACrB,oDAAoD,EACpD,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,MAAM,CAClC,CAAC;AAEJ,GAAG,CAAC,UAAU,CAAC,IAAA,aAAQ,GAAE,CAAC,CAAC;AAC3B,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;AAClC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;AACpC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;AACtC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;AACpC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;AAClC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;AACrC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAC;AACjD,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAC;AACjD,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC;AAChD,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;AAC3C,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;AACrC,GAAG,CAAC,UAAU,CAAC,IAAA,aAAW,GAAE,CAAC,CAAC;AAC9B,GAAG,CAAC,UAAU,CAAC,IAAA,aAAU,GAAE,CAAC,CAAC;AAC7B,GAAG,CAAC,UAAU,CAAC,IAAA,aAAO,GAAE,CAAC,CAAC;AAC1B,GAAG,CAAC,UAAU,CAAC,IAAA,aAAW,GAAE,CAAC,CAAC;AAC9B,GAAG,CAAC,UAAU,CAAC,IAAA,aAAa,GAAE,CAAC,CAAC;AAChC,GAAG,CAAC,UAAU,CAAC,IAAA,aAAK,GAAE,CAAC,CAAC;AACxB,GAAG,CAAC,UAAU,CAAC,IAAA,aAAO,GAAE,CAAC,CAAC;AAC1B,GAAG,CAAC,UAAU,CAAC,IAAA,YAAM,GAAE,CAAC,CAAC;AACzB,GAAG,CAAC,UAAU,CAAC,IAAA,aAAM,GAAE,CAAC,CAAC;AACzB,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -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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"}
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/user/util.ts"],"names":[],"mappings":"AAEA,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"}
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
@@ -1 +1 @@
1
- {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/user/util.ts"],"names":[],"mappings":";;;;;;AAAA,kEAAqC;AAErC,SAAgB,qBAAqB,CAAC,IAA0B;IAC9D,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,IAAI,IAAI,EAAE;QACR,IAAI,CAAC,GAAG,CAAC,CAAC,EAAU,EAAE,EAAE;YACtB,MAAM,KAAK,GAAG,8BAA8B,CAAC;YAC7C,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC7B,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC/B,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE;gBACtB,IAAI,OAAO,GAAG,OAAO,CAAC;gBACtB,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;oBAChC,IAAI,KAAK,KAAK,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE;wBAChC,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;qBACzB;yBAAM;wBACL,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;4BACpB,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;yBACtB;wBACD,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;qBAC3B;gBACH,CAAC,CAAC,CAAC;aACJ;iBAAM;gBACL,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;aACtB;QACH,CAAC,CAAC,CAAC;KACJ;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AA1BD,sDA0BC;AAED,SAAgB,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;AACtD,CAAC;AAFD,oCAEC;AAEM,KAAK,UAAU,OAAO,CAAC,OAA2B;IACvD,MAAM,EAAE,GAAG,uBAAQ,CAAC,eAAe,CAAC;QAClC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IAEH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,EAAE,CAAC,QAAQ,CAAC,OAAO,IAAI,yBAAyB,EAAE,CAAC,MAAM,EAAE,EAAE;YAC3D,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC5C,IAAI,OAAO,KAAK,KAAK,EAAE;gBACrB,OAAO,EAAE,CAAC;aACX;iBAAM;gBACL,MAAM,CAAC,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;aACtC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAjBD,0BAiBC"}
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.24.1",
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",
@@ -37,5 +37,5 @@
37
37
  "publishConfig": {
38
38
  "access": "public"
39
39
  },
40
- "gitHead": "0c2c66c87a143e3c6a1c9f26934eaeaffbd91f1b"
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 and a Personal Access Token at hand
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 Output the current version
24
- --env <environment> Environment to use
25
- -h, --help display help for command
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
- admin Administrative commands for OSC super admins
29
- list <serviceId> List all my service instances
30
- create [options] <serviceId> <name> Create a service instance
31
- describe <serviceId> <name> Get details for a service instance
32
- remove [options] <serviceId> <name> Remove a service instance
33
- restart <serviceId> <name> Restart a service instance
34
- logs <serviceId> <name> Get logs for a service instance
35
- list-reserved-nodes List all my reserved nodes
36
- service-access-token <serviceId> Generate a service access token for a service
37
- secrets Manage secrets for services (list, create, update, remove)
38
- packager [options] <source> <dest> Create streaming package from ABR bundle on S3
39
- and store on another S3 bucket
40
- compare vmaf <reference> <distorted> <result> Compare two video files using VMAF
41
- live Live streaming commands
42
- intercom Intercom messaging commands
43
- transcribe [options] <source> Generate subtitles from video or audio using OpenAI Whisper
44
- db Database management commands
45
- architect AI chat assistant for architecture guidance
46
- vod create [options] <name> <source> Create a VOD file ready for streaming
47
- vod cleanup <name> Remove VOD pipeline but keep VOD files
48
- web publish [options] <name> <dir> Publish a website
49
- web cdn-create [options] <serviceId> <instanceName> Create a CDN distribution
50
- web config-create <name> Create a configuration service instance
51
- web config-delete [options] <name> Delete a configuration service instance
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
- ### List all channel-engine instance for tenant `eyevinn` as an OSC super admin
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
- PAT_SECRET=<pat-secret> osc admin list-instances eyevinn channel-engine
240
+
241
+ Stop and remove a single bitrate live stream:
242
+
243
+ ```bash
244
+ % osc live stop-single mystreamname
168
245
  ```
169
246
 
170
- ### Remove a channel-engine instance in dev env for tenant `asdasd` as an OSC super admin
247
+ List all single bitrate live streams:
171
248
 
249
+ ```bash
250
+ % osc live list-single
172
251
  ```
173
- PAT_SECRET=<pat-secret> osc --env dev admin remove-instance asdasd channel-engine mychannel
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
- ### Create application configuration service instance
271
+ ### Database management
177
272
 
178
- To manage configuration values for an application we can create an instance of an Application Config Service.
273
+ Create a Valkey database:
179
274
 
180
275
  ```bash
181
- % osc web config-create jonastest
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
- And to remove it (with the data)
279
+ List all databases of a given type:
186
280
 
187
281
  ```bash
188
- % osc web config-delete --data jonastest
282
+ % osc db list valkey
189
283
  ```
190
284
 
191
- ### Store application configuration values as environment variables
285
+ Remove a database:
192
286
 
193
- Configuration values managed by the Application Config Service can be stored as environment variable using this commmand.
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 web config-to-env <config-instance-name>
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
- And storing it in the shell.
299
+ Create a production with lines:
202
300
 
203
301
  ```bash
204
- % eval `osc web config-to-env <config-instance-name>`
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
- ### Service instance management
379
+ ### Web publishing and configuration
251
380
 
252
- Restart a service instance:
381
+ Publish a website:
253
382
 
254
383
  ```bash
255
- % osc restart minio-minio mystore
384
+ % osc web publish mysite ./dist
256
385
  ```
257
386
 
258
- List your reserved nodes:
387
+ Create a CDN distribution:
259
388
 
260
389
  ```bash
261
- % osc list-reserved-nodes
390
+ % osc web cdn-create --provider=cloudfront --origin-path=/mybucket minio-minio mystore
262
391
  ```
263
392
 
264
- Generate a service access token:
393
+ ### Application configuration service
394
+
395
+ Create a configuration service instance:
265
396
 
266
397
  ```bash
267
- % osc service-access-token minio-minio
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
- ### Create a service instance with safe name option
402
+ And to remove it (with the data):
271
403
 
272
- Create a service instance with invalid characters stripped from the name:
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 create minio-minio "my store with spaces!" --safe-name -o RootUser=admin -o RootPassword=abC12345678
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
+ }