@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 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.
@@ -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) {
@@ -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 client = new Api({ token: profile.token, host: profile.host }).app(apptype);
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(formData);
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
- let buffer = Buffer.alloc(0);
172
+ const chunks = [];
168
173
  const bufferStream = new Writable({
169
174
  write(chunk, encoding, callback) {
170
- buffer = Buffer.concat([buffer, chunk]);
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
  })
@@ -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 <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 <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, password, project, host } = options;
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
- const { data } = await user.auth(username, password, project);
56
- token = "SESSION:" + data.token;
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, password, project);
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
- const { password, token } = options;
100
+ let { token, project } = options;
101
+ let userPassword = options.password;
82
102
  const profile = util.getCurrentProfile();
83
103
  if (!token) {
84
- const user = new APIUser({
85
- host: profile.host,
86
- });
87
- // authenticate to protokol
88
- const { data } = await user.auth(profile.username, password, profile.project);
89
- profile.token = data.Token;
90
- }
91
- else {
92
- profile.token = token;
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
- const profile = profiles?.find(p => p.current);
52
- if (!profile) {
53
- throw new Error("Current profile is not set. Run command `profile use {profile-name}`");
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.6.2",
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
- "@ptkl/sdk": "^0.9.5",
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",