@osaas/cli 0.2.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.
@@ -0,0 +1 @@
1
+ dist/
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Eyevinn Technology Open Source Software Center
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export default function cmdAdmin(): Command;
3
+ //# sourceMappingURL=cmd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cmd.d.ts","sourceRoot":"","sources":["../../src/admin/cmd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,MAAM,CAAC,OAAO,UAAU,QAAQ,YA2D/B"}
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const commander_1 = require("commander");
4
+ const token_1 = require("./token");
5
+ const client_core_1 = require("@osaas/client-core");
6
+ const instance_1 = require("./instance");
7
+ const util_1 = require("../user/util");
8
+ function cmdAdmin() {
9
+ const admin = new commander_1.Command('admin');
10
+ admin
11
+ .command('gen-pat')
12
+ .description('Generate a personal access token')
13
+ .argument('<tenantId>', 'The tenant ID')
14
+ .argument('<username>', 'The username')
15
+ .action((tenantId, username) => {
16
+ try {
17
+ const pat = (0, token_1.generatePat)(tenantId, username);
18
+ console.log(pat);
19
+ }
20
+ catch (err) {
21
+ console.log(err.message);
22
+ }
23
+ });
24
+ admin
25
+ .command('list-instances')
26
+ .description('List all instances for a service and tenant')
27
+ .argument('<tenantId>', 'The tenant ID')
28
+ .argument('<serviceId>', 'The service ID')
29
+ .action(async (tenantId, serviceId, options, command) => {
30
+ try {
31
+ const globalOpts = command.optsWithGlobals();
32
+ const environment = globalOpts?.env || 'prod';
33
+ (0, client_core_1.Log)().info(`Listing instance for tenant ${tenantId} and service ${serviceId} in ${environment}`);
34
+ const instances = await (0, instance_1.listInstancesForTenant)(tenantId, serviceId, environment);
35
+ instances.forEach((instance) => console.log(instance));
36
+ }
37
+ catch (err) {
38
+ console.log(err.message);
39
+ }
40
+ });
41
+ admin
42
+ .command('remove-instance')
43
+ .description('Remove an instance')
44
+ .argument('<tenantId>', 'The Tenant Id')
45
+ .argument('<serviceId>', 'The Service Id')
46
+ .argument('<name>', 'The instance name')
47
+ .action(async (tenantId, serviceId, name, options, command) => {
48
+ try {
49
+ const globalOpts = command.optsWithGlobals();
50
+ const environment = globalOpts?.env || 'prod';
51
+ (0, client_core_1.Log)().info(`Removing instance ${name} for tenant ${tenantId} and service ${serviceId} in ${environment}`);
52
+ await (0, util_1.confirm)('Are you sure you want to remove this instance? (yes/no) ');
53
+ await (0, instance_1.removeInstanceForTenant)(tenantId, serviceId, name, environment);
54
+ }
55
+ catch (err) {
56
+ console.log(err.message);
57
+ }
58
+ });
59
+ return admin;
60
+ }
61
+ exports.default = cmdAdmin;
62
+ //# sourceMappingURL=cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cmd.js","sourceRoot":"","sources":["../../src/admin/cmd.ts"],"names":[],"mappings":";;AAAA,yCAAoC;AACpC,mCAAsC;AACtC,oDAAyC;AACzC,yCAA6E;AAC7E,uCAAuC;AAEvC,SAAwB,QAAQ;IAC9B,MAAM,KAAK,GAAG,IAAI,mBAAO,CAAC,OAAO,CAAC,CAAC;IACnC,KAAK;SACF,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,kCAAkC,CAAC;SAC/C,QAAQ,CAAC,YAAY,EAAE,eAAe,CAAC;SACvC,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC;SACtC,MAAM,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE;QAC7B,IAAI;YACF,MAAM,GAAG,GAAG,IAAA,mBAAW,EAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC5C,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;SAClB;QAAC,OAAO,GAAG,EAAE;YACZ,OAAO,CAAC,GAAG,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;SACrC;IACH,CAAC,CAAC,CAAC;IACL,KAAK;SACF,OAAO,CAAC,gBAAgB,CAAC;SACzB,WAAW,CAAC,6CAA6C,CAAC;SAC1D,QAAQ,CAAC,YAAY,EAAE,eAAe,CAAC;SACvC,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC;SACzC,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;QACtD,IAAI;YACF,MAAM,UAAU,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;YAC7C,MAAM,WAAW,GAAG,UAAU,EAAE,GAAG,IAAI,MAAM,CAAC;YAC9C,IAAA,iBAAG,GAAE,CAAC,IAAI,CACR,+BAA+B,QAAQ,gBAAgB,SAAS,OAAO,WAAW,EAAE,CACrF,CAAC;YACF,MAAM,SAAS,GAAG,MAAM,IAAA,iCAAsB,EAC5C,QAAQ,EACR,SAAS,EACT,WAAW,CACZ,CAAC;YACF,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;SACxD;QAAC,OAAO,GAAG,EAAE;YACZ,OAAO,CAAC,GAAG,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;SACrC;IACH,CAAC,CAAC,CAAC;IACL,KAAK;SACF,OAAO,CAAC,iBAAiB,CAAC;SAC1B,WAAW,CAAC,oBAAoB,CAAC;SACjC,QAAQ,CAAC,YAAY,EAAE,eAAe,CAAC;SACvC,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC;SACzC,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;SACvC,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;QAC5D,IAAI;YACF,MAAM,UAAU,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;YAC7C,MAAM,WAAW,GAAG,UAAU,EAAE,GAAG,IAAI,MAAM,CAAC;YAC9C,IAAA,iBAAG,GAAE,CAAC,IAAI,CACR,qBAAqB,IAAI,eAAe,QAAQ,gBAAgB,SAAS,OAAO,WAAW,EAAE,CAC9F,CAAC;YACF,MAAM,IAAA,cAAO,EACX,0DAA0D,CAC3D,CAAC;YACF,MAAM,IAAA,kCAAuB,EAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;SACvE;QAAC,OAAO,GAAG,EAAE;YACZ,OAAO,CAAC,GAAG,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;SACrC;IACH,CAAC,CAAC,CAAC;IACL,OAAO,KAAK,CAAC;AACf,CAAC;AA3DD,2BA2DC"}
@@ -0,0 +1,3 @@
1
+ export declare function listInstancesForTenant(tenantId: string, serviceId: string, environment: string): Promise<string[]>;
2
+ export declare function removeInstanceForTenant(tenantId: string, serviceId: string, name: string, environment: string): Promise<void>;
3
+ //# sourceMappingURL=instance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/admin/instance.ts"],"names":[],"mappings":"AAGA,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,EAAE,CAAC,CAQnB;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,iBAQpB"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.removeInstanceForTenant = exports.listInstancesForTenant = void 0;
4
+ const client_core_1 = require("@osaas/client-core");
5
+ const token_1 = require("./token");
6
+ async function listInstancesForTenant(tenantId, serviceId, environment) {
7
+ const pat = (0, token_1.generatePat)(tenantId, 'osc-admin');
8
+ const ctx = new client_core_1.Context({ personalAccessToken: pat, environment });
9
+ const serviceAccessToken = await ctx.getServiceAccessToken(serviceId);
10
+ const instances = await (0, client_core_1.listInstances)(ctx, serviceId, serviceAccessToken);
11
+ return instances.map((instance) => instance.name);
12
+ }
13
+ exports.listInstancesForTenant = listInstancesForTenant;
14
+ async function removeInstanceForTenant(tenantId, serviceId, name, environment) {
15
+ const pat = (0, token_1.generatePat)(tenantId, 'osc-admin');
16
+ const ctx = new client_core_1.Context({ personalAccessToken: pat, environment });
17
+ const serviceAccessToken = await ctx.getServiceAccessToken(serviceId);
18
+ await (0, client_core_1.removeInstance)(ctx, serviceId, name, serviceAccessToken);
19
+ }
20
+ exports.removeInstanceForTenant = removeInstanceForTenant;
21
+ //# sourceMappingURL=instance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance.js","sourceRoot":"","sources":["../../src/admin/instance.ts"],"names":[],"mappings":";;;AAAA,oDAA4E;AAC5E,mCAAsC;AAE/B,KAAK,UAAU,sBAAsB,CAC1C,QAAgB,EAChB,SAAiB,EACjB,WAAmB;IAEnB,MAAM,GAAG,GAAG,IAAA,mBAAW,EAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAE/C,MAAM,GAAG,GAAG,IAAI,qBAAO,CAAC,EAAE,mBAAmB,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;IACnE,MAAM,kBAAkB,GAAG,MAAM,GAAG,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAEtE,MAAM,SAAS,GAAG,MAAM,IAAA,2BAAa,EAAC,GAAG,EAAE,SAAS,EAAE,kBAAkB,CAAC,CAAC;IAC1E,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,QAA0B,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACtE,CAAC;AAZD,wDAYC;AAEM,KAAK,UAAU,uBAAuB,CAC3C,QAAgB,EAChB,SAAiB,EACjB,IAAY,EACZ,WAAmB;IAEnB,MAAM,GAAG,GAAG,IAAA,mBAAW,EAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAE/C,MAAM,GAAG,GAAG,IAAI,qBAAO,CAAC,EAAE,mBAAmB,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;IACnE,MAAM,kBAAkB,GAAG,MAAM,GAAG,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAEtE,MAAM,IAAA,4BAAc,EAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,kBAAkB,CAAC,CAAC;AACjE,CAAC;AAZD,0DAYC"}
@@ -0,0 +1,2 @@
1
+ export declare function generatePat(tenantId: string, userId: string, serviceLimits?: number): string;
2
+ //# sourceMappingURL=token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/admin/token.ts"],"names":[],"mappings":"AAUA,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,UAkBvB"}
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generatePat = void 0;
4
+ const fast_jwt_1 = require("fast-jwt");
5
+ const node_crypto_1 = require("node:crypto");
6
+ function readPatSecret() {
7
+ if (!process.env.PAT_SECRET) {
8
+ throw new Error('PAT_SECRET must be set');
9
+ }
10
+ return process.env.PAT_SECRET;
11
+ }
12
+ function generatePat(tenantId, userId, serviceLimits) {
13
+ const signSync = (0, fast_jwt_1.createSigner)({
14
+ key: readPatSecret(),
15
+ iss: 'token.osaas.eyevinn.se'
16
+ });
17
+ const iat = Math.round(Date.now() / 1000);
18
+ const patId = (0, node_crypto_1.randomUUID)();
19
+ const jwtClaims = {
20
+ iat,
21
+ tenantId,
22
+ userId,
23
+ patId,
24
+ serviceLimits
25
+ };
26
+ const token = signSync(jwtClaims);
27
+ return token;
28
+ }
29
+ exports.generatePat = generatePat;
30
+ //# sourceMappingURL=token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/admin/token.ts"],"names":[],"mappings":";;;AAAA,uCAAwC;AACxC,6CAAyC;AAEzC,SAAS,aAAa;IACpB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE;QAC3B,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;KAC3C;IACD,OAAO,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;AAChC,CAAC;AAED,SAAgB,WAAW,CACzB,QAAgB,EAChB,MAAc,EACd,aAAsB;IAEtB,MAAM,QAAQ,GAAG,IAAA,uBAAY,EAAC;QAC5B,GAAG,EAAE,aAAa,EAAE;QACpB,GAAG,EAAE,wBAAwB;KAC9B,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,IAAA,wBAAU,GAAE,CAAC;IAC3B,MAAM,SAAS,GAAG;QAChB,GAAG;QACH,QAAQ;QACR,MAAM;QACN,KAAK;QACL,aAAa;KACd,CAAC;IACF,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IAClC,OAAO,KAAK,CAAC;AACf,CAAC;AArBD,kCAqBC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #! /usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,42 @@
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 (mod) {
20
+ if (mod && mod.__esModule) return mod;
21
+ var result = {};
22
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
23
+ __setModuleDefault(result, mod);
24
+ return result;
25
+ };
26
+ var __importDefault = (this && this.__importDefault) || function (mod) {
27
+ return (mod && mod.__esModule) ? mod : { "default": mod };
28
+ };
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ const commander_1 = require("commander");
31
+ const cmd_1 = __importDefault(require("./admin/cmd"));
32
+ const cmdUser = __importStar(require("./user/cmd"));
33
+ const cli = new commander_1.Command();
34
+ cli
35
+ .configureHelp({ showGlobalOptions: true })
36
+ .option('--env <environment>', 'Environment to use');
37
+ cli.addCommand((0, cmd_1.default)());
38
+ cli.addCommand(cmdUser.cmdList());
39
+ cli.addCommand(cmdUser.cmdCreate());
40
+ cli.addCommand(cmdUser.cmdRemove());
41
+ cli.parse(process.argv);
42
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,yCAAoC;AACpC,sDAAmC;AACnC,oDAAsC;AAEtC,MAAM,GAAG,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE1B,GAAG;KACA,aAAa,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC;KAC1C,MAAM,CAAC,qBAAqB,EAAE,oBAAoB,CAAC,CAAC;AACvD,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,SAAS,EAAE,CAAC,CAAC;AACpC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { Command } from 'commander';
2
+ export declare function cmdList(): Command;
3
+ export declare function cmdCreate(): Command;
4
+ export declare function cmdRemove(): Command;
5
+ //# sourceMappingURL=cmd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cmd.d.ts","sourceRoot":"","sources":["../../src/user/cmd.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,wBAAgB,OAAO,YA0BtB;AAED,wBAAgB,SAAS,YAiCxB;AAED,wBAAgB,SAAS,YAsBxB"}
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cmdRemove = exports.cmdCreate = exports.cmdList = void 0;
4
+ const client_core_1 = require("@osaas/client-core");
5
+ const commander_1 = require("commander");
6
+ const util_1 = require("./util");
7
+ function cmdList() {
8
+ const list = new commander_1.Command('list');
9
+ list
10
+ .description('List all my service instances')
11
+ .argument('<serviceId>', 'The Service Id')
12
+ .action(async (serviceId, options, command) => {
13
+ try {
14
+ const globalOpts = command.optsWithGlobals();
15
+ const environment = globalOpts?.env || 'prod';
16
+ const ctx = new client_core_1.Context({ environment });
17
+ const serviceAccessToken = await ctx.getServiceAccessToken(serviceId);
18
+ const instances = await (0, client_core_1.listInstances)(ctx, serviceId, serviceAccessToken);
19
+ return instances.map((instance) => console.log(instance.name));
20
+ }
21
+ catch (err) {
22
+ console.log(err.message);
23
+ }
24
+ });
25
+ return list;
26
+ }
27
+ exports.cmdList = cmdList;
28
+ function cmdCreate() {
29
+ const create = new commander_1.Command('create');
30
+ create
31
+ .description('Create a service instance')
32
+ .argument('<serviceId>', 'The Service Id')
33
+ .argument('<name>', 'The instance name')
34
+ .option('-o, --instance-options [instanceOptions...]', 'Instance options as key value pairs (e.g. -o key1=value1 -o key2=value2)')
35
+ .action(async (serviceId, name, options, command) => {
36
+ try {
37
+ const globalOpts = command.optsWithGlobals();
38
+ const payload = (0, util_1.instanceOptsToPayload)(options.instanceOptions);
39
+ payload['name'] = name;
40
+ const environment = globalOpts?.env || 'prod';
41
+ const ctx = new client_core_1.Context({ environment });
42
+ const serviceAccessToken = await ctx.getServiceAccessToken(serviceId);
43
+ const instance = await (0, client_core_1.createInstance)(ctx, serviceId, serviceAccessToken, payload);
44
+ console.log('Instance created:');
45
+ console.log(instance);
46
+ }
47
+ catch (err) {
48
+ console.log(err.message);
49
+ }
50
+ });
51
+ return create;
52
+ }
53
+ exports.cmdCreate = cmdCreate;
54
+ function cmdRemove() {
55
+ const remove = new commander_1.Command('remove');
56
+ remove
57
+ .description('Remove a service instance')
58
+ .argument('<serviceId>', 'The Service Id')
59
+ .argument('<name>', 'The instance name')
60
+ .action(async (serviceId, name, options, command) => {
61
+ try {
62
+ const globalOpts = command.optsWithGlobals();
63
+ const environment = globalOpts?.env || 'prod';
64
+ const ctx = new client_core_1.Context({ environment });
65
+ const serviceAccessToken = await ctx.getServiceAccessToken(serviceId);
66
+ await (0, util_1.confirm)(`Are you sure you want to remove ${name}? (yes/no) `);
67
+ await (0, client_core_1.removeInstance)(ctx, serviceId, name, serviceAccessToken);
68
+ console.log('Instance removed');
69
+ }
70
+ catch (err) {
71
+ console.log(err.message);
72
+ }
73
+ });
74
+ return remove;
75
+ }
76
+ exports.cmdRemove = cmdRemove;
77
+ //# sourceMappingURL=cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cmd.js","sourceRoot":"","sources":["../../src/user/cmd.ts"],"names":[],"mappings":";;;AAAA,oDAK4B;AAC5B,yCAAoC;AACpC,iCAAwD;AAExD,SAAgB,OAAO;IACrB,MAAM,IAAI,GAAG,IAAI,mBAAO,CAAC,MAAM,CAAC,CAAC;IAEjC,IAAI;SACD,WAAW,CAAC,+BAA+B,CAAC;SAC5C,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC;SACzC,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;QAC5C,IAAI;YACF,MAAM,UAAU,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;YAC7C,MAAM,WAAW,GAAG,UAAU,EAAE,GAAG,IAAI,MAAM,CAAC;YAC9C,MAAM,GAAG,GAAG,IAAI,qBAAO,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC;YACzC,MAAM,kBAAkB,GAAG,MAAM,GAAG,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC;YAEtE,MAAM,SAAS,GAAG,MAAM,IAAA,2BAAa,EACnC,GAAG,EACH,SAAS,EACT,kBAAkB,CACnB,CAAC;YACF,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,QAA0B,EAAE,EAAE,CAClD,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAC3B,CAAC;SACH;QAAC,OAAO,GAAG,EAAE;YACZ,OAAO,CAAC,GAAG,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;SACrC;IACH,CAAC,CAAC,CAAC;IACL,OAAO,IAAI,CAAC;AACd,CAAC;AA1BD,0BA0BC;AAED,SAAgB,SAAS;IACvB,MAAM,MAAM,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC,CAAC;IAErC,MAAM;SACH,WAAW,CAAC,2BAA2B,CAAC;SACxC,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC;SACzC,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;SACvC,MAAM,CACL,6CAA6C,EAC7C,0EAA0E,CAC3E;SACA,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;QAClD,IAAI;YACF,MAAM,UAAU,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;YAC7C,MAAM,OAAO,GAAG,IAAA,4BAAqB,EAAC,OAAO,CAAC,eAAe,CAAC,CAAC;YAC/D,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;YACvB,MAAM,WAAW,GAAG,UAAU,EAAE,GAAG,IAAI,MAAM,CAAC;YAC9C,MAAM,GAAG,GAAG,IAAI,qBAAO,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC;YACzC,MAAM,kBAAkB,GAAG,MAAM,GAAG,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC;YAEtE,MAAM,QAAQ,GAAG,MAAM,IAAA,4BAAc,EACnC,GAAG,EACH,SAAS,EACT,kBAAkB,EAClB,OAAO,CACR,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;SACvB;QAAC,OAAO,GAAG,EAAE;YACZ,OAAO,CAAC,GAAG,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;SACrC;IACH,CAAC,CAAC,CAAC;IACL,OAAO,MAAM,CAAC;AAChB,CAAC;AAjCD,8BAiCC;AAED,SAAgB,SAAS;IACvB,MAAM,MAAM,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC,CAAC;IAErC,MAAM;SACH,WAAW,CAAC,2BAA2B,CAAC;SACxC,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC;SACzC,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;SACvC,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;QAClD,IAAI;YACF,MAAM,UAAU,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;YAC7C,MAAM,WAAW,GAAG,UAAU,EAAE,GAAG,IAAI,MAAM,CAAC;YAC9C,MAAM,GAAG,GAAG,IAAI,qBAAO,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC;YACzC,MAAM,kBAAkB,GAAG,MAAM,GAAG,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC;YAEtE,MAAM,IAAA,cAAO,EAAC,mCAAmC,IAAI,aAAa,CAAC,CAAC;YACpE,MAAM,IAAA,4BAAc,EAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,kBAAkB,CAAC,CAAC;YAC/D,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;SACjC;QAAC,OAAO,GAAG,EAAE;YACZ,OAAO,CAAC,GAAG,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;SACrC;IACH,CAAC,CAAC,CAAC;IACL,OAAO,MAAM,CAAC;AAChB,CAAC;AAtBD,8BAsBC"}
@@ -0,0 +1,5 @@
1
+ export declare function instanceOptsToPayload(opts: string[] | undefined): {
2
+ [key: string]: any;
3
+ };
4
+ export declare function confirm(message: string | undefined): Promise<void>;
5
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +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;;EAwB/D;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBxE"}
@@ -0,0 +1,55 @@
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.confirm = exports.instanceOptsToPayload = void 0;
7
+ const node_readline_1 = __importDefault(require("node:readline"));
8
+ function instanceOptsToPayload(opts) {
9
+ const payload = {};
10
+ if (opts) {
11
+ opts.map((kv) => {
12
+ const [key, value] = kv.split('=');
13
+ const subkeys = key.split('.');
14
+ if (subkeys.length > 1) {
15
+ let current = payload;
16
+ subkeys.forEach((subkey, index) => {
17
+ if (index === subkeys.length - 1) {
18
+ current[subkey] = value;
19
+ }
20
+ else {
21
+ if (!current[subkey]) {
22
+ current[subkey] = {};
23
+ }
24
+ current = current[subkey];
25
+ }
26
+ });
27
+ }
28
+ else {
29
+ payload[key] = value;
30
+ }
31
+ });
32
+ }
33
+ return payload;
34
+ }
35
+ exports.instanceOptsToPayload = instanceOptsToPayload;
36
+ async function confirm(message) {
37
+ const rl = node_readline_1.default.createInterface({
38
+ input: process.stdin,
39
+ output: process.stdout
40
+ });
41
+ return new Promise((resolve, reject) => {
42
+ rl.question(message || 'Are you sure? (yes/no) ', (answer) => {
43
+ rl.close();
44
+ const cleaned = answer.trim().toLowerCase();
45
+ if (cleaned === 'yes') {
46
+ resolve();
47
+ }
48
+ else {
49
+ reject(new Error('aborted by user'));
50
+ }
51
+ });
52
+ });
53
+ }
54
+ exports.confirm = confirm;
55
+ //# sourceMappingURL=util.js.map
@@ -0,0 +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,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACnC,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;AAxBD,sDAwBC;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"}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@osaas/cli",
3
+ "version": "0.2.0",
4
+ "description": "Open Source Cloud CLI",
5
+ "author": "Eyevinn Technology <work@eyevinn.se>",
6
+ "homepage": "https://www.osaas.io",
7
+ "license": "MIT",
8
+ "main": "dist/cli.js",
9
+ "bin": {
10
+ "osc": "./dist/cli.js"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/Eyevinn/osaas-client-ts.git"
15
+ },
16
+ "scripts": {
17
+ "build": "tsc -p tsconfig.json",
18
+ "lint": "eslint .",
19
+ "pretty": "prettier --check --ignore-unknown .",
20
+ "typecheck": "tsc --noEmit -p tsconfig.json",
21
+ "test": "jest --pass-with-no-tests"
22
+ },
23
+ "dependencies": {
24
+ "@osaas/client-core": "^0.4.0",
25
+ "chalk": "4.1.2",
26
+ "commander": "^12.1.0",
27
+ "fast-jwt": "^4.0.1"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "gitHead": "71d6b915ca0ff5bfdcc1a0886ed7c9f816343fa1"
33
+ }
package/readme.md ADDED
@@ -0,0 +1,87 @@
1
+ # Command Line Utility for Open Source Cloud
2
+
3
+ CLI for working and scripting with [Open Source Cloud](www.osaas.io)
4
+
5
+ Prerequisites:
6
+
7
+ - Node >18
8
+ - An Open Source Cloud account and a Personal Access Token at hand
9
+
10
+ ## Install
11
+
12
+ ```
13
+ npm install -g @osaas/cli
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ ```
19
+ % osc
20
+ Usage: osc [options] [command]
21
+
22
+ Options:
23
+ --env <environment> Environment to use
24
+ -h, --help display help for command
25
+
26
+ Commands:
27
+ admin
28
+ list <serviceId> List all my service instances
29
+ create [options] <serviceId> <name> Create a service instance
30
+ remove <serviceId> <name> Remove a service instance
31
+ help [command] display help for command
32
+ ```
33
+
34
+ To display help for subcommand `admin` you enter
35
+
36
+ ```
37
+ % osc help admin
38
+ Usage: osc admin [options] [command]
39
+
40
+ Options:
41
+ -h, --help display help for command
42
+
43
+ Commands:
44
+ gen-pat <tenantId> <username> Generate a personal access token
45
+ list-instances <tenantId> <serviceId> List all instances for a service and tenant
46
+ remove-instance <tenantId> <serviceId> <name> Remove an instance
47
+ help [command] display help for command
48
+ ```
49
+
50
+ ## Examples
51
+
52
+ ### List all my channel-engine instances
53
+
54
+ ```
55
+ % export OSC_ACCESS_TOKEN=<pat>
56
+ % osc list channel-engine
57
+ ```
58
+
59
+ ### Create a channel from a VOD on loop and insert ad breaks
60
+
61
+ ```
62
+ % osc create channel-engine cli -o \
63
+ type=Loop \
64
+ url=https://lab.cdn.eyevinn.technology/stswetvplus-promo-2023-5GBm231Mkz.mov/manifest.m3u8 \
65
+ opts.useDemuxedAudio=false \
66
+ opts.useVttSubtitles=false \
67
+ opts.preroll.url=http://maitv-vod.lab.eyevinn.technology/VINN.mp4/master.m3u8 \
68
+ opts.preroll.duration=10500
69
+ ```
70
+
71
+ ### Remove a channel-engine instance with name `clidemo`
72
+
73
+ ```
74
+ % osc remove channel-engine clidemo
75
+ ```
76
+
77
+ ### List all channel-engine instance for tenant `eyevinn` as an OSC super admin
78
+
79
+ ```
80
+ PAT_SECRET=<pat-secret> osc admin list-instances eyevinn channel-engine
81
+ ```
82
+
83
+ ### Remove a channel-engine instance in dev env for tenant `asdasd` as an OSC super admin
84
+
85
+ ```
86
+ PAT_SECRET=<pat-secret> osc --env dev admin remove-instance asdasd channel-engine mychannel
87
+ ```
@@ -0,0 +1,66 @@
1
+ import { Command } from 'commander';
2
+ import { generatePat } from './token';
3
+ import { Log } from '@osaas/client-core';
4
+ import { listInstancesForTenant, removeInstanceForTenant } from './instance';
5
+ import { confirm } from '../user/util';
6
+
7
+ export default function cmdAdmin() {
8
+ const admin = new Command('admin');
9
+ admin
10
+ .command('gen-pat')
11
+ .description('Generate a personal access token')
12
+ .argument('<tenantId>', 'The tenant ID')
13
+ .argument('<username>', 'The username')
14
+ .action((tenantId, username) => {
15
+ try {
16
+ const pat = generatePat(tenantId, username);
17
+ console.log(pat);
18
+ } catch (err) {
19
+ console.log((err as Error).message);
20
+ }
21
+ });
22
+ admin
23
+ .command('list-instances')
24
+ .description('List all instances for a service and tenant')
25
+ .argument('<tenantId>', 'The tenant ID')
26
+ .argument('<serviceId>', 'The service ID')
27
+ .action(async (tenantId, serviceId, options, command) => {
28
+ try {
29
+ const globalOpts = command.optsWithGlobals();
30
+ const environment = globalOpts?.env || 'prod';
31
+ Log().info(
32
+ `Listing instance for tenant ${tenantId} and service ${serviceId} in ${environment}`
33
+ );
34
+ const instances = await listInstancesForTenant(
35
+ tenantId,
36
+ serviceId,
37
+ environment
38
+ );
39
+ instances.forEach((instance) => console.log(instance));
40
+ } catch (err) {
41
+ console.log((err as Error).message);
42
+ }
43
+ });
44
+ admin
45
+ .command('remove-instance')
46
+ .description('Remove an instance')
47
+ .argument('<tenantId>', 'The Tenant Id')
48
+ .argument('<serviceId>', 'The Service Id')
49
+ .argument('<name>', 'The instance name')
50
+ .action(async (tenantId, serviceId, name, options, command) => {
51
+ try {
52
+ const globalOpts = command.optsWithGlobals();
53
+ const environment = globalOpts?.env || 'prod';
54
+ Log().info(
55
+ `Removing instance ${name} for tenant ${tenantId} and service ${serviceId} in ${environment}`
56
+ );
57
+ await confirm(
58
+ 'Are you sure you want to remove this instance? (yes/no) '
59
+ );
60
+ await removeInstanceForTenant(tenantId, serviceId, name, environment);
61
+ } catch (err) {
62
+ console.log((err as Error).message);
63
+ }
64
+ });
65
+ return admin;
66
+ }
@@ -0,0 +1,30 @@
1
+ import { Context, listInstances, removeInstance } from '@osaas/client-core';
2
+ import { generatePat } from './token';
3
+
4
+ export async function listInstancesForTenant(
5
+ tenantId: string,
6
+ serviceId: string,
7
+ environment: string
8
+ ): Promise<string[]> {
9
+ const pat = generatePat(tenantId, 'osc-admin');
10
+
11
+ const ctx = new Context({ personalAccessToken: pat, environment });
12
+ const serviceAccessToken = await ctx.getServiceAccessToken(serviceId);
13
+
14
+ const instances = await listInstances(ctx, serviceId, serviceAccessToken);
15
+ return instances.map((instance: { name: string }) => instance.name);
16
+ }
17
+
18
+ export async function removeInstanceForTenant(
19
+ tenantId: string,
20
+ serviceId: string,
21
+ name: string,
22
+ environment: string
23
+ ) {
24
+ const pat = generatePat(tenantId, 'osc-admin');
25
+
26
+ const ctx = new Context({ personalAccessToken: pat, environment });
27
+ const serviceAccessToken = await ctx.getServiceAccessToken(serviceId);
28
+
29
+ await removeInstance(ctx, serviceId, name, serviceAccessToken);
30
+ }
@@ -0,0 +1,32 @@
1
+ import { createSigner } from 'fast-jwt';
2
+ import { randomUUID } from 'node:crypto';
3
+
4
+ function readPatSecret(): string {
5
+ if (!process.env.PAT_SECRET) {
6
+ throw new Error('PAT_SECRET must be set');
7
+ }
8
+ return process.env.PAT_SECRET;
9
+ }
10
+
11
+ export function generatePat(
12
+ tenantId: string,
13
+ userId: string,
14
+ serviceLimits?: number
15
+ ) {
16
+ const signSync = createSigner({
17
+ key: readPatSecret(),
18
+ iss: 'token.osaas.eyevinn.se'
19
+ });
20
+
21
+ const iat = Math.round(Date.now() / 1000);
22
+ const patId = randomUUID();
23
+ const jwtClaims = {
24
+ iat,
25
+ tenantId,
26
+ userId,
27
+ patId,
28
+ serviceLimits
29
+ };
30
+ const token = signSync(jwtClaims);
31
+ return token;
32
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,16 @@
1
+ #! /usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import cmdAdmin from './admin/cmd';
5
+ import * as cmdUser from './user/cmd';
6
+
7
+ const cli = new Command();
8
+
9
+ cli
10
+ .configureHelp({ showGlobalOptions: true })
11
+ .option('--env <environment>', 'Environment to use');
12
+ cli.addCommand(cmdAdmin());
13
+ cli.addCommand(cmdUser.cmdList());
14
+ cli.addCommand(cmdUser.cmdCreate());
15
+ cli.addCommand(cmdUser.cmdRemove());
16
+ cli.parse(process.argv);
@@ -0,0 +1,95 @@
1
+ import {
2
+ Context,
3
+ createInstance,
4
+ listInstances,
5
+ removeInstance
6
+ } from '@osaas/client-core';
7
+ import { Command } from 'commander';
8
+ import { confirm, instanceOptsToPayload } from './util';
9
+
10
+ export function cmdList() {
11
+ const list = new Command('list');
12
+
13
+ list
14
+ .description('List all my service instances')
15
+ .argument('<serviceId>', 'The Service Id')
16
+ .action(async (serviceId, options, command) => {
17
+ try {
18
+ const globalOpts = command.optsWithGlobals();
19
+ const environment = globalOpts?.env || 'prod';
20
+ const ctx = new Context({ environment });
21
+ const serviceAccessToken = await ctx.getServiceAccessToken(serviceId);
22
+
23
+ const instances = await listInstances(
24
+ ctx,
25
+ serviceId,
26
+ serviceAccessToken
27
+ );
28
+ return instances.map((instance: { name: string }) =>
29
+ console.log(instance.name)
30
+ );
31
+ } catch (err) {
32
+ console.log((err as Error).message);
33
+ }
34
+ });
35
+ return list;
36
+ }
37
+
38
+ export function cmdCreate() {
39
+ const create = new Command('create');
40
+
41
+ create
42
+ .description('Create a service instance')
43
+ .argument('<serviceId>', 'The Service Id')
44
+ .argument('<name>', 'The instance name')
45
+ .option(
46
+ '-o, --instance-options [instanceOptions...]',
47
+ 'Instance options as key value pairs (e.g. -o key1=value1 -o key2=value2)'
48
+ )
49
+ .action(async (serviceId, name, options, command) => {
50
+ try {
51
+ const globalOpts = command.optsWithGlobals();
52
+ const payload = instanceOptsToPayload(options.instanceOptions);
53
+ payload['name'] = name;
54
+ const environment = globalOpts?.env || 'prod';
55
+ const ctx = new Context({ environment });
56
+ const serviceAccessToken = await ctx.getServiceAccessToken(serviceId);
57
+
58
+ const instance = await createInstance(
59
+ ctx,
60
+ serviceId,
61
+ serviceAccessToken,
62
+ payload
63
+ );
64
+ console.log('Instance created:');
65
+ console.log(instance);
66
+ } catch (err) {
67
+ console.log((err as Error).message);
68
+ }
69
+ });
70
+ return create;
71
+ }
72
+
73
+ export function cmdRemove() {
74
+ const remove = new Command('remove');
75
+
76
+ remove
77
+ .description('Remove a service instance')
78
+ .argument('<serviceId>', 'The Service Id')
79
+ .argument('<name>', 'The instance name')
80
+ .action(async (serviceId, name, options, command) => {
81
+ try {
82
+ const globalOpts = command.optsWithGlobals();
83
+ const environment = globalOpts?.env || 'prod';
84
+ const ctx = new Context({ environment });
85
+ const serviceAccessToken = await ctx.getServiceAccessToken(serviceId);
86
+
87
+ await confirm(`Are you sure you want to remove ${name}? (yes/no) `);
88
+ await removeInstance(ctx, serviceId, name, serviceAccessToken);
89
+ console.log('Instance removed');
90
+ } catch (err) {
91
+ console.log((err as Error).message);
92
+ }
93
+ });
94
+ return remove;
95
+ }
@@ -0,0 +1,46 @@
1
+ import readline from 'node:readline';
2
+
3
+ export function instanceOptsToPayload(opts: string[] | undefined) {
4
+ const payload: { [key: string]: any } = {};
5
+ if (opts) {
6
+ opts.map((kv: string) => {
7
+ const [key, value] = kv.split('=');
8
+ const subkeys = key.split('.');
9
+ if (subkeys.length > 1) {
10
+ let current = payload;
11
+ subkeys.forEach((subkey, index) => {
12
+ if (index === subkeys.length - 1) {
13
+ current[subkey] = value;
14
+ } else {
15
+ if (!current[subkey]) {
16
+ current[subkey] = {};
17
+ }
18
+ current = current[subkey];
19
+ }
20
+ });
21
+ } else {
22
+ payload[key] = value;
23
+ }
24
+ });
25
+ }
26
+ return payload;
27
+ }
28
+
29
+ export async function confirm(message: string | undefined): Promise<void> {
30
+ const rl = readline.createInterface({
31
+ input: process.stdin,
32
+ output: process.stdout
33
+ });
34
+
35
+ return new Promise((resolve, reject) => {
36
+ rl.question(message || 'Are you sure? (yes/no) ', (answer) => {
37
+ rl.close();
38
+ const cleaned = answer.trim().toLowerCase();
39
+ if (cleaned === 'yes') {
40
+ resolve();
41
+ } else {
42
+ reject(new Error('aborted by user'));
43
+ }
44
+ });
45
+ });
46
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist"
5
+ },
6
+ "include": ["./src"]
7
+ }