@ptkl/toolkit 0.6.2 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -0
- package/dist/bin/toolkit.js +20 -1
- package/dist/commands/apps.js +10 -4
- package/dist/commands/profile.js +63 -19
- package/dist/lib/util.js +16 -3
- package/package.json +25 -8
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Protokol Toolkit
|
|
2
|
+
|
|
3
|
+
A command-line toolkit for managing Protokol platform applications, profiles, functions, and components.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @ptkl/toolkit
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Getting Started
|
|
12
|
+
|
|
13
|
+
### 1. Initialize the Toolkit
|
|
14
|
+
|
|
15
|
+
Before using the toolkit, initialize it to create the configuration directory:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
ptkl init
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This creates a `~/.ptkl` directory where profiles and settings are stored.
|
|
22
|
+
|
|
23
|
+
### 2. Create a Profile
|
|
24
|
+
|
|
25
|
+
A profile stores your authentication credentials and connection settings for a Protokol instance.
|
|
26
|
+
|
|
27
|
+
#### Secure Method (Recommended)
|
|
28
|
+
|
|
29
|
+
Omit the password or use `--password` flag without a value to be prompted securely:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
ptkl profile new \
|
|
33
|
+
-n production \
|
|
34
|
+
-u user@example.com \
|
|
35
|
+
-P my-project \
|
|
36
|
+
-h https://api.example.com \
|
|
37
|
+
--password
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
You'll be prompted to enter your password securely (hidden input).
|
|
41
|
+
|
|
42
|
+
#### With Password in CLI (Not Recommended)
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
ptkl profile new \
|
|
46
|
+
-n production \
|
|
47
|
+
-u user@example.com \
|
|
48
|
+
-P my-project \
|
|
49
|
+
-h https://api.example.com \
|
|
50
|
+
--password "myPassword"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
⚠️ **Security Warning**: This will show a warning as the password may be visible in shell history.
|
|
54
|
+
|
|
55
|
+
#### Options
|
|
56
|
+
|
|
57
|
+
- `-n, --name <name>` - Profile name (e.g., "production", "staging")
|
|
58
|
+
- `-u, --username <username>` - Email or API username
|
|
59
|
+
- `-P, --project <project>` - Project identifier
|
|
60
|
+
- `-h, --host <host>` - API host URL
|
|
61
|
+
- `-p, --password [password]` - Password (optional value for secure prompt)
|
|
62
|
+
|
|
63
|
+
## Profile Management
|
|
64
|
+
|
|
65
|
+
### List All Profiles
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
ptkl profile list
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Shows all available profiles. The currently active profile is marked with `*`.
|
|
72
|
+
|
|
73
|
+
### View Current Profile
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
ptkl profile
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Switch to a Different Profile
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
ptkl profile use staging
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Sets `staging` as the active profile for all subsequent commands.
|
|
86
|
+
|
|
87
|
+
### Inspect Profile Details
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
ptkl profile inspect production
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Re-authenticate a Profile
|
|
94
|
+
|
|
95
|
+
If your token expires or you need to update credentials:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
ptkl profile auth --password
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Or for a specific profile:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
ptkl profile auth --profile production --password
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### Options
|
|
108
|
+
|
|
109
|
+
- `-p, --password [password]` - Password (omit value for secure prompt)
|
|
110
|
+
- `-t, --token <token>` - Directly provide a token
|
|
111
|
+
- `-P, --project <project>` - Update the project
|
|
112
|
+
|
|
113
|
+
### Delete a Profile
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
ptkl profile delete staging
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Using the --profile Flag
|
|
120
|
+
|
|
121
|
+
You can override the active profile for any command using the global `--profile` flag:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
ptkl --profile production apps upload -a app -d ./my-app
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Version
|
|
128
|
+
|
|
129
|
+
Current version: 0.7.0
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
GNU General Public License v3.0 (GPL-3.0)
|
|
134
|
+
|
|
135
|
+
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
136
|
+
|
|
137
|
+
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
138
|
+
|
|
139
|
+
Any modifications or derivative works must also be licensed under GPL-3.0 and the source code must be made available.
|
package/dist/bin/toolkit.js
CHANGED
|
@@ -2,8 +2,27 @@
|
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { commands } from "../commands/index.js";
|
|
4
4
|
const program = new Command();
|
|
5
|
+
// Add global --profile option
|
|
6
|
+
program.option('--profile <profileName>', 'Use a specific profile for this command');
|
|
7
|
+
// Add a preAction hook to handle the global --profile flag
|
|
8
|
+
program.hook('preAction', (thisCommand, actionCommand) => {
|
|
9
|
+
const opts = thisCommand.opts();
|
|
10
|
+
if (opts.profile) {
|
|
11
|
+
// Store the profile name in a global variable or process.env so commands can access it
|
|
12
|
+
process.env.PTKL_ACTIVE_PROFILE = opts.profile;
|
|
13
|
+
}
|
|
14
|
+
});
|
|
5
15
|
commands.forEach(c => program.addCommand(c));
|
|
6
|
-
program.parseAsync(process.argv)
|
|
16
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
17
|
+
const { response } = err;
|
|
18
|
+
if (response && response.data) {
|
|
19
|
+
console.error(response.data.message);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
console.error(err.message || err);
|
|
23
|
+
}
|
|
24
|
+
process.exit(1);
|
|
25
|
+
});
|
|
7
26
|
process.on('uncaughtException', (err) => {
|
|
8
27
|
const { response } = err;
|
|
9
28
|
if (response && response.data) {
|
package/dist/commands/apps.js
CHANGED
|
@@ -76,7 +76,7 @@ class AppsCommand {
|
|
|
76
76
|
async upload(options) {
|
|
77
77
|
const { apptype, directory, build } = options;
|
|
78
78
|
const profile = util.getCurrentProfile();
|
|
79
|
-
const
|
|
79
|
+
const api = new Api({ token: profile.token, host: profile.host }).app(apptype);
|
|
80
80
|
try {
|
|
81
81
|
let buffer = Buffer.alloc(0);
|
|
82
82
|
const bufferStream = new Writable({
|
|
@@ -100,7 +100,12 @@ class AppsCommand {
|
|
|
100
100
|
if (build) {
|
|
101
101
|
formData.append('build', 'true');
|
|
102
102
|
}
|
|
103
|
-
return await client.upload
|
|
103
|
+
return await api.client.post(`/v3/system/gateway/app-service/${apptype}/upload`, formData, {
|
|
104
|
+
timeout: 60000,
|
|
105
|
+
headers: {
|
|
106
|
+
'Content-Type': 'multipart/form-data'
|
|
107
|
+
}
|
|
108
|
+
});
|
|
104
109
|
}
|
|
105
110
|
catch (error) {
|
|
106
111
|
throw error;
|
|
@@ -164,14 +169,15 @@ class AppsCommand {
|
|
|
164
169
|
const manifestPath = `${distPath}/manifest.json`;
|
|
165
170
|
writeFileSync(manifestPath, JSON.stringify(manifest, null, 4));
|
|
166
171
|
if (upload) {
|
|
167
|
-
|
|
172
|
+
const chunks = [];
|
|
168
173
|
const bufferStream = new Writable({
|
|
169
174
|
write(chunk, encoding, callback) {
|
|
170
|
-
|
|
175
|
+
chunks.push(chunk);
|
|
171
176
|
callback();
|
|
172
177
|
},
|
|
173
178
|
});
|
|
174
179
|
c({ gzip: true, cwd: distPath }, ['.']).pipe(bufferStream).on('finish', () => {
|
|
180
|
+
const buffer = Buffer.concat(chunks);
|
|
175
181
|
client.app().bundleUpload(buffer).then(() => {
|
|
176
182
|
console.log('Bundle uploaded successfully');
|
|
177
183
|
})
|
package/dist/commands/profile.js
CHANGED
|
@@ -2,6 +2,7 @@ import { Command } from "commander";
|
|
|
2
2
|
import util from "../lib/util.js";
|
|
3
3
|
import cli from "../lib/cli.js";
|
|
4
4
|
import { APIUser, User } from "@ptkl/sdk";
|
|
5
|
+
import password from '@inquirer/password';
|
|
5
6
|
class ProfileCommand {
|
|
6
7
|
register() {
|
|
7
8
|
return new Command('profile')
|
|
@@ -12,14 +13,15 @@ class ProfileCommand {
|
|
|
12
13
|
.description('Create new profile')
|
|
13
14
|
.option("-n, --name <name>")
|
|
14
15
|
.option("-u, --username <username>")
|
|
15
|
-
.option("-p, --password
|
|
16
|
+
.option("-p, --password [password]", "Password (optional value - if omitted, you will be prompted securely)")
|
|
16
17
|
.option("-P, --project <project>")
|
|
17
18
|
.option("-h, --host <host>")
|
|
18
19
|
.action(this.new))
|
|
19
20
|
.addCommand(new Command('auth')
|
|
20
21
|
.description('Profile auth')
|
|
21
|
-
.option("-p, --password
|
|
22
|
+
.option("-p, --password [password]", "Password (optional value - if omitted, you will be prompted securely)")
|
|
22
23
|
.option("-t, --token <token>")
|
|
24
|
+
.option("-P, --project <project>")
|
|
23
25
|
.action(this.auth))
|
|
24
26
|
.addCommand(new Command('list')
|
|
25
27
|
.description('List all available profiles')
|
|
@@ -43,23 +45,39 @@ class ProfileCommand {
|
|
|
43
45
|
}
|
|
44
46
|
async new(options) {
|
|
45
47
|
// try to find profile with the name
|
|
46
|
-
const { name, username,
|
|
48
|
+
const { name, username, project, host } = options;
|
|
49
|
+
let userPassword = options.password;
|
|
50
|
+
// Handle password input
|
|
51
|
+
if (typeof userPassword === 'string' && userPassword.length > 0) {
|
|
52
|
+
// Password was provided as CLI argument - show security warning
|
|
53
|
+
console.warn('⚠️ WARNING: Providing password via CLI argument is insecure and may be visible in shell history.');
|
|
54
|
+
console.warn('⚠️ Consider using --password flag without a value to be prompted securely.\n');
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
// Prompt for password securely (either --password flag was used without value, or not provided at all)
|
|
58
|
+
userPassword = await password({
|
|
59
|
+
message: 'Enter password:',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
47
62
|
let token = "";
|
|
48
63
|
// if username is email then login to platform as user
|
|
49
64
|
if (username && username.includes('@')) {
|
|
50
65
|
const user = new User({
|
|
51
|
-
username,
|
|
52
|
-
password,
|
|
53
66
|
host,
|
|
54
67
|
});
|
|
55
|
-
|
|
56
|
-
|
|
68
|
+
try {
|
|
69
|
+
const { data } = await user.auth(username, userPassword, project);
|
|
70
|
+
token = "SESSION:" + data.token;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
57
75
|
}
|
|
58
76
|
else {
|
|
59
77
|
const user = new APIUser({
|
|
60
78
|
host,
|
|
61
79
|
});
|
|
62
|
-
const { data } = await user.auth(username,
|
|
80
|
+
const { data } = await user.auth(username, userPassword, project);
|
|
63
81
|
token = data.Token;
|
|
64
82
|
}
|
|
65
83
|
const profiles = util.getProfiles() ?? [];
|
|
@@ -72,25 +90,51 @@ class ProfileCommand {
|
|
|
72
90
|
name,
|
|
73
91
|
username,
|
|
74
92
|
token,
|
|
75
|
-
host
|
|
93
|
+
host,
|
|
94
|
+
project,
|
|
76
95
|
});
|
|
77
96
|
util.updateProfiles(profiles);
|
|
78
97
|
}
|
|
79
98
|
async auth(options) {
|
|
80
99
|
// try to find profile with the name
|
|
81
|
-
|
|
100
|
+
let { token, project } = options;
|
|
101
|
+
let userPassword = options.password;
|
|
82
102
|
const profile = util.getCurrentProfile();
|
|
83
103
|
if (!token) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
104
|
+
// Handle password input
|
|
105
|
+
if (typeof userPassword === 'string' && userPassword.length > 0) {
|
|
106
|
+
// Password was provided as CLI argument - show security warning
|
|
107
|
+
console.warn('⚠️ WARNING: Providing password via CLI argument is insecure and may be visible in shell history.');
|
|
108
|
+
console.warn('⚠️ Consider using --password flag without a value to be prompted securely.\n');
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// Prompt for password securely
|
|
112
|
+
userPassword = await password({
|
|
113
|
+
message: 'Enter password:',
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (profile.username && profile.username.includes('@')) {
|
|
117
|
+
const user = new User({
|
|
118
|
+
host: profile.host,
|
|
119
|
+
});
|
|
120
|
+
try {
|
|
121
|
+
const { data } = await user.auth(profile.username, userPassword, project ?? profile.project);
|
|
122
|
+
token = "SESSION:" + data.token;
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const user = new APIUser({
|
|
130
|
+
host: profile.host,
|
|
131
|
+
});
|
|
132
|
+
const { data } = await user.auth(profile.username, userPassword, project ?? profile.project);
|
|
133
|
+
token = data.Token;
|
|
134
|
+
}
|
|
93
135
|
}
|
|
136
|
+
profile.project = project ?? profile.project;
|
|
137
|
+
profile.token = token;
|
|
94
138
|
util.updateProfile(profile);
|
|
95
139
|
}
|
|
96
140
|
list() {
|
package/dist/lib/util.js
CHANGED
|
@@ -48,9 +48,22 @@ export default class Util {
|
|
|
48
48
|
}
|
|
49
49
|
static getCurrentProfile() {
|
|
50
50
|
const profiles = Util.getProfiles();
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
// Check if --profile flag was used globally
|
|
52
|
+
const profileNameFromFlag = process.env.PTKL_ACTIVE_PROFILE;
|
|
53
|
+
let profile;
|
|
54
|
+
if (profileNameFromFlag) {
|
|
55
|
+
// Use the profile specified via --profile flag
|
|
56
|
+
profile = profiles?.find(p => p.name === profileNameFromFlag);
|
|
57
|
+
if (!profile) {
|
|
58
|
+
throw new Error(`Profile '${profileNameFromFlag}' not found. Available profiles: ${profiles?.map(p => p.name).join(', ')}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// Fall back to current profile
|
|
63
|
+
profile = profiles?.find(p => p.current);
|
|
64
|
+
if (!profile) {
|
|
65
|
+
throw new Error("Current profile is not set. Run command `profile use {profile-name}` or use --profile flag");
|
|
66
|
+
}
|
|
54
67
|
}
|
|
55
68
|
return profile;
|
|
56
69
|
}
|
package/package.json
CHANGED
|
@@ -1,19 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ptkl/toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.2",
|
|
4
|
+
"description": "A command-line toolkit for managing Protokol platform applications, profiles, functions, and components",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"protokol",
|
|
7
|
+
"cli",
|
|
8
|
+
"toolkit",
|
|
9
|
+
"deployment",
|
|
10
|
+
"serverless",
|
|
11
|
+
"functions"
|
|
12
|
+
],
|
|
13
|
+
"author": "Protokol",
|
|
14
|
+
"license": "GPL-3.0",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://your-gitea-instance.com/your-org/protokol-toolkit.git"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://your-gitea-instance.com/your-org/protokol-toolkit/issues"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://your-gitea-instance.com/your-org/protokol-toolkit#readme",
|
|
23
|
+
"type": "module",
|
|
4
24
|
"scripts": {
|
|
5
25
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
6
26
|
"build": "npx tsc",
|
|
7
|
-
"toolkit": "tsc --build && node dist/bin/toolkit"
|
|
27
|
+
"toolkit": "tsc --build && node dist/bin/toolkit",
|
|
28
|
+
"prepublishOnly": "npm run build"
|
|
8
29
|
},
|
|
9
|
-
"keywords": [],
|
|
10
|
-
"author": "",
|
|
11
|
-
"license": "MIT",
|
|
12
|
-
"description": "",
|
|
13
|
-
"type": "module",
|
|
14
30
|
"dependencies": {
|
|
15
31
|
"@babel/standalone": "^7.26.10",
|
|
16
|
-
"@
|
|
32
|
+
"@inquirer/password": "^4.0.21",
|
|
33
|
+
"@ptkl/sdk": "^0.9.12",
|
|
17
34
|
"@types/axios": "^0.14.0",
|
|
18
35
|
"@types/commander": "^2.12.2",
|
|
19
36
|
"@types/js-yaml": "^4.0.9",
|