wattpm 3.14.0 → 3.16.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/index.js +7 -0
- package/lib/commands/applications.js +194 -0
- package/lib/commands/create.js +1 -0
- package/lib/commands/execution.js +34 -16
- package/lib/commands/help.js +12 -1
- package/lib/commands/inject.js +3 -3
- package/lib/commands/logs.js +3 -1
- package/lib/commands/management.js +12 -8
- package/lib/commands/metrics.js +3 -2
- package/lib/commands/pprof.js +28 -16
- package/package.json +5 -5
- package/schema.json +21 -1
package/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { loadApplicationsCommands } from '@platformatic/runtime'
|
|
|
3
3
|
import * as colorette from 'colorette'
|
|
4
4
|
import { bold } from 'colorette'
|
|
5
5
|
import { adminCommand } from './lib/commands/admin.js'
|
|
6
|
+
import { applicationsAddCommand, applicationsRemoveCommand } from './lib/commands/applications.js'
|
|
6
7
|
import { buildCommand } from './lib/commands/build.js'
|
|
7
8
|
import { createCommand } from './lib/commands/create.js'
|
|
8
9
|
import { devCommand, reloadCommand, restartCommand, startCommand, stopCommand } from './lib/commands/execution.js'
|
|
@@ -105,6 +106,12 @@ export async function main () {
|
|
|
105
106
|
case 'pprof':
|
|
106
107
|
command = pprofCommand
|
|
107
108
|
break
|
|
109
|
+
case 'applications:add':
|
|
110
|
+
command = applicationsAddCommand
|
|
111
|
+
break
|
|
112
|
+
case 'applications:remove':
|
|
113
|
+
command = applicationsRemoveCommand
|
|
114
|
+
break
|
|
108
115
|
case 'admin':
|
|
109
116
|
command = adminCommand
|
|
110
117
|
break
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { getMatchingRuntime, RuntimeApiClient } from '@platformatic/control'
|
|
2
|
+
import { ensureLoggableError, logFatalError, parseArgs } from '@platformatic/foundation'
|
|
3
|
+
import { bold } from 'colorette'
|
|
4
|
+
import { readFile, stat, writeFile } from 'node:fs/promises'
|
|
5
|
+
import { basename, isAbsolute, relative, resolve } from 'node:path'
|
|
6
|
+
|
|
7
|
+
async function updateConfigFile (path, update) {
|
|
8
|
+
const contents = JSON.parse(await readFile(path, 'utf-8'))
|
|
9
|
+
await update(contents)
|
|
10
|
+
await writeFile(path, JSON.stringify(contents, null, 2), 'utf-8')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function applicationsAddCommand (logger, args) {
|
|
14
|
+
const {
|
|
15
|
+
values: { save },
|
|
16
|
+
positionals: allPositionals
|
|
17
|
+
} = parseArgs(
|
|
18
|
+
args,
|
|
19
|
+
{
|
|
20
|
+
save: {
|
|
21
|
+
type: 'boolean',
|
|
22
|
+
short: 's'
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
false
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
const client = new RuntimeApiClient()
|
|
29
|
+
try {
|
|
30
|
+
const [runtime, applications] = await getMatchingRuntime(client, allPositionals)
|
|
31
|
+
const config = await client.getRuntimeConfig(runtime.pid, true)
|
|
32
|
+
const root = config.__metadata.root
|
|
33
|
+
|
|
34
|
+
let toAdd = []
|
|
35
|
+
let added = 0
|
|
36
|
+
|
|
37
|
+
for (let app of applications) {
|
|
38
|
+
let spec
|
|
39
|
+
|
|
40
|
+
// Determine if app is a path to a directory or file, and load accordingly
|
|
41
|
+
try {
|
|
42
|
+
if (!isAbsolute(app)) {
|
|
43
|
+
app = resolve(root, app)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const pathStat = await stat(app)
|
|
47
|
+
if (pathStat.isDirectory()) {
|
|
48
|
+
spec = {
|
|
49
|
+
id: basename(app),
|
|
50
|
+
path: relative(root, app)
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
spec = JSON.parse(await readFile(app, 'utf-8'))
|
|
54
|
+
}
|
|
55
|
+
} catch (err) {
|
|
56
|
+
logFatalError(logger, `The path "${bold(app)}" does not exist or is not valid JSON.`)
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const response = await client.addApplications(runtime.pid, spec, true)
|
|
61
|
+
added += response.length
|
|
62
|
+
toAdd = toAdd.concat(spec)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (save) {
|
|
66
|
+
await updateConfigFile(config.__metadata.path, async config => {
|
|
67
|
+
config.applications = (config.applications ?? []).concat(toAdd)
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
logger.done(`Successfully added ${added} application${added > 1 ? 's' : ''} to the application.`)
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (error.code === 'PLT_CTR_RUNTIME_NOT_FOUND') {
|
|
74
|
+
return logFatalError(logger, 'Cannot find a matching runtime.')
|
|
75
|
+
/* c8 ignore next 7 - Hard to test */
|
|
76
|
+
} else {
|
|
77
|
+
return logFatalError(
|
|
78
|
+
logger,
|
|
79
|
+
{ error: ensureLoggableError(error) },
|
|
80
|
+
`Cannot add applications to the application: ${error.message}`
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
} finally {
|
|
84
|
+
await client.close()
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function applicationsRemoveCommand (logger, args) {
|
|
89
|
+
const {
|
|
90
|
+
values: { save },
|
|
91
|
+
positionals
|
|
92
|
+
} = parseArgs(
|
|
93
|
+
args,
|
|
94
|
+
{
|
|
95
|
+
save: {
|
|
96
|
+
type: 'boolean',
|
|
97
|
+
short: 's'
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
false
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
const client = new RuntimeApiClient()
|
|
104
|
+
try {
|
|
105
|
+
const [runtime, applications] = await getMatchingRuntime(client, positionals)
|
|
106
|
+
|
|
107
|
+
const removed = await client.removeApplications(runtime.pid, applications)
|
|
108
|
+
|
|
109
|
+
if (save) {
|
|
110
|
+
const config = await client.getRuntimeConfig(runtime.pid, true)
|
|
111
|
+
const absoluteAutoloadPath = resolve(config.__metadata.path, config.autoload.path)
|
|
112
|
+
|
|
113
|
+
await updateConfigFile(config.__metadata.path, async config => {
|
|
114
|
+
// Remove applications from all relevant sections
|
|
115
|
+
for (const app of removed) {
|
|
116
|
+
for (const section of ['applications', 'services', 'web']) {
|
|
117
|
+
if (Array.isArray(config[section])) {
|
|
118
|
+
config[section] = config[section].filter(a => a.id !== app.id)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (config.autoload) {
|
|
123
|
+
if (app.path.startsWith(absoluteAutoloadPath)) {
|
|
124
|
+
config.autoload.exclude ??= []
|
|
125
|
+
config.autoload.exclude.push(relative(absoluteAutoloadPath, app.path))
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
logger.done(
|
|
133
|
+
`Successfully removed ${applications.length} application${applications.length > 1 ? 's' : ''} from the application.`
|
|
134
|
+
)
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (error.code === 'PLT_CTR_RUNTIME_NOT_FOUND') {
|
|
137
|
+
return logFatalError(logger, 'Cannot find a matching runtime.')
|
|
138
|
+
/* c8 ignore next 7 - Hard to test */
|
|
139
|
+
} else {
|
|
140
|
+
return logFatalError(
|
|
141
|
+
logger,
|
|
142
|
+
{ error: ensureLoggableError(error) },
|
|
143
|
+
`Cannot remove applications from the application: ${error.message}`
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
} finally {
|
|
147
|
+
await client.close()
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export const help = {
|
|
152
|
+
'applications:add': {
|
|
153
|
+
usage: 'applications:add [id] <path>',
|
|
154
|
+
description: 'Add new applications to a running application',
|
|
155
|
+
args: [
|
|
156
|
+
{
|
|
157
|
+
name: 'id',
|
|
158
|
+
description:
|
|
159
|
+
'The process ID or the name of the application (it can be omitted only if there is a single application running)'
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'path',
|
|
163
|
+
description: 'A folder containing an application or a JSON file containing the applications to add'
|
|
164
|
+
}
|
|
165
|
+
],
|
|
166
|
+
options: [
|
|
167
|
+
{
|
|
168
|
+
usage: '-s, --save',
|
|
169
|
+
description: 'Save the added applications to the application configuration file'
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
},
|
|
173
|
+
'applications:remove': {
|
|
174
|
+
usage: 'applications:remove [id] [applications...]',
|
|
175
|
+
description: 'Remove applications from a running application',
|
|
176
|
+
args: [
|
|
177
|
+
{
|
|
178
|
+
name: 'id',
|
|
179
|
+
description:
|
|
180
|
+
'The process ID or the name of the application (it can be omitted only if there is a single application running)'
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: 'applications',
|
|
184
|
+
description: 'The list of applications to remove'
|
|
185
|
+
}
|
|
186
|
+
],
|
|
187
|
+
options: [
|
|
188
|
+
{
|
|
189
|
+
usage: '-s, --save',
|
|
190
|
+
description: 'Remove the removed applications from the application configuration file'
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
}
|
package/lib/commands/create.js
CHANGED
|
@@ -19,6 +19,7 @@ export async function runDelegatedCommand (logger, packageManager, args) {
|
|
|
19
19
|
|
|
20
20
|
const options = { stdio: 'inherit' }
|
|
21
21
|
|
|
22
|
+
/* c8 ignore next 4 - Covered by CI */
|
|
22
23
|
if (platform() === 'win32') {
|
|
23
24
|
options.shell = true
|
|
24
25
|
options.windowsVerbatimArguments = true
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import { create } from '@platformatic/runtime'
|
|
11
11
|
import { bold } from 'colorette'
|
|
12
12
|
import { spawn } from 'node:child_process'
|
|
13
|
-
import {
|
|
13
|
+
import { createInterface } from 'node:readline'
|
|
14
14
|
|
|
15
15
|
export async function devCommand (logger, args) {
|
|
16
16
|
const {
|
|
@@ -38,16 +38,32 @@ export async function devCommand (logger, args) {
|
|
|
38
38
|
|
|
39
39
|
let runtime = await create(root, configurationFile, { start: true })
|
|
40
40
|
|
|
41
|
-
//
|
|
42
|
-
const
|
|
43
|
-
watcher.startWatching()
|
|
41
|
+
// Handle reloading via either file changes or stdin "rs" command
|
|
42
|
+
const { promise, reject } = Promise.withResolvers()
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
for await (const _ of on(watcher, 'update')) {
|
|
47
|
-
runtime.logger.info('The configuration file has changed, reloading the application ...')
|
|
44
|
+
async function reloadApplication () {
|
|
48
45
|
await runtime.close()
|
|
49
|
-
runtime = await create(root, configurationFile, { start: true })
|
|
46
|
+
runtime = await create(root, configurationFile, { start: true, reloaded: true })
|
|
50
47
|
}
|
|
48
|
+
|
|
49
|
+
const watcher = new FileWatcher({ path: configurationFile })
|
|
50
|
+
watcher.startWatching()
|
|
51
|
+
watcher.on('update', () => {
|
|
52
|
+
runtime.logger.info('The configuration file has changed, reloading the application ...')
|
|
53
|
+
reloadApplication().catch(reject)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const rl = createInterface({ input: process.stdin })
|
|
57
|
+
rl.on('line', line => {
|
|
58
|
+
if (line.trim() !== 'rs') {
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
runtime.logger.info('Received "rs" from the stdin, Reloading the application ...')
|
|
63
|
+
reloadApplication().catch(reject)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
return promise
|
|
51
67
|
}
|
|
52
68
|
|
|
53
69
|
export async function startCommand (logger, args) {
|
|
@@ -83,12 +99,11 @@ export async function startCommand (logger, args) {
|
|
|
83
99
|
export async function stopCommand (logger, args) {
|
|
84
100
|
const { positionals } = parseArgs(args, {}, false)
|
|
85
101
|
|
|
102
|
+
const client = new RuntimeApiClient()
|
|
86
103
|
try {
|
|
87
|
-
const client = new RuntimeApiClient()
|
|
88
104
|
const [runtime] = await getMatchingRuntime(client, positionals)
|
|
89
105
|
|
|
90
106
|
await client.stopRuntime(runtime.pid)
|
|
91
|
-
await client.close()
|
|
92
107
|
|
|
93
108
|
logger.done(`Runtime ${bold(runtime.packageName)} have been stopped.`)
|
|
94
109
|
} catch (error) {
|
|
@@ -98,18 +113,19 @@ export async function stopCommand (logger, args) {
|
|
|
98
113
|
} else {
|
|
99
114
|
return logFatalError(logger, { error: ensureLoggableError(error) }, `Cannot stop the runtime: ${error.message}`)
|
|
100
115
|
}
|
|
116
|
+
} finally {
|
|
117
|
+
await client.close()
|
|
101
118
|
}
|
|
102
119
|
}
|
|
103
120
|
|
|
104
121
|
export async function restartCommand (logger, args) {
|
|
105
122
|
const { positionals } = parseArgs(args, {}, false)
|
|
106
123
|
|
|
124
|
+
const client = new RuntimeApiClient()
|
|
107
125
|
try {
|
|
108
|
-
const client = new RuntimeApiClient()
|
|
109
126
|
const [runtime, applications] = await getMatchingRuntime(client, positionals)
|
|
110
127
|
|
|
111
128
|
await client.restartRuntime(runtime.pid, ...applications)
|
|
112
|
-
await client.close()
|
|
113
129
|
|
|
114
130
|
logger.done(`Runtime ${bold(runtime.packageName)} has been restarted.`)
|
|
115
131
|
} catch (error) {
|
|
@@ -123,14 +139,16 @@ export async function restartCommand (logger, args) {
|
|
|
123
139
|
`Cannot restart the runtime: ${error.message}`
|
|
124
140
|
)
|
|
125
141
|
}
|
|
142
|
+
} finally {
|
|
143
|
+
await client.close()
|
|
126
144
|
}
|
|
127
145
|
}
|
|
128
146
|
|
|
129
147
|
export async function reloadCommand (logger, args) {
|
|
130
148
|
const { positionals } = parseArgs(args, {}, false)
|
|
131
149
|
|
|
150
|
+
const client = new RuntimeApiClient()
|
|
132
151
|
try {
|
|
133
|
-
const client = new RuntimeApiClient()
|
|
134
152
|
const [runtime] = await getMatchingRuntime(client, positionals)
|
|
135
153
|
|
|
136
154
|
// Stop the previous runtime
|
|
@@ -147,7 +165,6 @@ export async function reloadCommand (logger, args) {
|
|
|
147
165
|
})
|
|
148
166
|
|
|
149
167
|
child.unref()
|
|
150
|
-
await client.close()
|
|
151
168
|
|
|
152
169
|
logger.done(`Runtime ${bold(runtime.packageName)} have been reloaded and it is now running as PID ${child.pid}.`)
|
|
153
170
|
} catch (error) {
|
|
@@ -157,6 +174,8 @@ export async function reloadCommand (logger, args) {
|
|
|
157
174
|
} else {
|
|
158
175
|
return logFatalError(logger, { error: ensureLoggableError(error) }, `Cannot reload the runtime: ${error.message}`)
|
|
159
176
|
}
|
|
177
|
+
} finally {
|
|
178
|
+
await client.close()
|
|
160
179
|
}
|
|
161
180
|
}
|
|
162
181
|
|
|
@@ -221,8 +240,7 @@ export const help = {
|
|
|
221
240
|
},
|
|
222
241
|
{
|
|
223
242
|
name: 'application',
|
|
224
|
-
description:
|
|
225
|
-
'The name of the application to restart (if omitted, all applications are restarted)'
|
|
243
|
+
description: 'The name of the application to restart (if omitted, all applications are restarted)'
|
|
226
244
|
}
|
|
227
245
|
]
|
|
228
246
|
},
|
package/lib/commands/help.js
CHANGED
|
@@ -9,7 +9,18 @@ function sanitizeHelp (raw) {
|
|
|
9
9
|
async function loadCommands () {
|
|
10
10
|
const commands = {}
|
|
11
11
|
|
|
12
|
-
for (const file of [
|
|
12
|
+
for (const file of [
|
|
13
|
+
'build',
|
|
14
|
+
'create',
|
|
15
|
+
'execution',
|
|
16
|
+
'applications',
|
|
17
|
+
'management',
|
|
18
|
+
'admin',
|
|
19
|
+
'logs',
|
|
20
|
+
'inject',
|
|
21
|
+
'metrics',
|
|
22
|
+
'pprof'
|
|
23
|
+
]) {
|
|
13
24
|
const category = await import(`./${file}.js`)
|
|
14
25
|
Object.assign(commands, category.help)
|
|
15
26
|
}
|
package/lib/commands/inject.js
CHANGED
|
@@ -70,8 +70,8 @@ export async function injectCommand (logger, args) {
|
|
|
70
70
|
)
|
|
71
71
|
|
|
72
72
|
const outputStream = output ? createWriteStream(resolve(process.cwd(), output)) : process.stdout
|
|
73
|
+
const client = new RuntimeApiClient()
|
|
73
74
|
try {
|
|
74
|
-
const client = new RuntimeApiClient()
|
|
75
75
|
const [runtime, positionals] = await getMatchingRuntime(client, allPositionals)
|
|
76
76
|
let application = positionals[0]
|
|
77
77
|
|
|
@@ -137,8 +137,6 @@ export async function injectCommand (logger, args) {
|
|
|
137
137
|
outputStream.end()
|
|
138
138
|
await finished(outputStream)
|
|
139
139
|
}
|
|
140
|
-
|
|
141
|
-
await client.close()
|
|
142
140
|
} catch (error) {
|
|
143
141
|
if (error.code === 'PLT_CTR_RUNTIME_NOT_FOUND') {
|
|
144
142
|
return logFatalError(logger, 'Cannot find a matching runtime.')
|
|
@@ -148,6 +146,8 @@ export async function injectCommand (logger, args) {
|
|
|
148
146
|
} else {
|
|
149
147
|
return logFatalError(logger, { error: ensureLoggableError(error) }, `Cannot perform a request: ${error.message}`)
|
|
150
148
|
}
|
|
149
|
+
} finally {
|
|
150
|
+
await client.close()
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
|
package/lib/commands/logs.js
CHANGED
|
@@ -18,8 +18,8 @@ export async function logsCommand (logger, args) {
|
|
|
18
18
|
const output = values.pretty ? pinoPretty({ colorize: true }) : process.stdout
|
|
19
19
|
|
|
20
20
|
let application
|
|
21
|
+
const client = new RuntimeApiClient()
|
|
21
22
|
try {
|
|
22
|
-
const client = new RuntimeApiClient()
|
|
23
23
|
const [runtime, positionals] = await getMatchingRuntime(client, allPositionals)
|
|
24
24
|
application = positionals[0]
|
|
25
25
|
|
|
@@ -54,6 +54,8 @@ export async function logsCommand (logger, args) {
|
|
|
54
54
|
`Cannot stream ${application ? 'application' : 'runtime'} logs: ${error.message}`
|
|
55
55
|
)
|
|
56
56
|
}
|
|
57
|
+
} finally {
|
|
58
|
+
await client.close()
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
61
|
|
|
@@ -61,10 +61,9 @@ function formatDuration (duration) {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
export async function psCommand (logger) {
|
|
64
|
+
const client = new RuntimeApiClient()
|
|
64
65
|
try {
|
|
65
|
-
const client = new RuntimeApiClient()
|
|
66
66
|
const runtimes = await client.getRuntimes()
|
|
67
|
-
await client.close()
|
|
68
67
|
|
|
69
68
|
if (runtimes.length === 0) {
|
|
70
69
|
logger.warn('No runtimes found.')
|
|
@@ -86,17 +85,18 @@ export async function psCommand (logger) {
|
|
|
86
85
|
/* c8 ignore next 3 - Hard to test */
|
|
87
86
|
} catch (error) {
|
|
88
87
|
return logFatalError(logger, { error: ensureLoggableError(error) }, `Cannot list runtime: ${error.message}`)
|
|
88
|
+
} finally {
|
|
89
|
+
await client.close()
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
export async function applicationsCommand (logger, args) {
|
|
93
94
|
const { positionals } = parseArgs(args, {}, false)
|
|
95
|
+
const client = new RuntimeApiClient()
|
|
94
96
|
|
|
95
97
|
try {
|
|
96
|
-
const client = new RuntimeApiClient()
|
|
97
98
|
const [runtime] = await getMatchingRuntime(client, positionals)
|
|
98
99
|
const { production, applications } = await client.getRuntimeApplications(runtime.pid)
|
|
99
|
-
await client.close()
|
|
100
100
|
|
|
101
101
|
const headers = production
|
|
102
102
|
? [bold('Name'), bold('Workers'), bold('Type'), bold('Entrypoint')]
|
|
@@ -121,6 +121,8 @@ export async function applicationsCommand (logger, args) {
|
|
|
121
121
|
`Cannot list runtime applications: ${error.message}`
|
|
122
122
|
)
|
|
123
123
|
}
|
|
124
|
+
} finally {
|
|
125
|
+
await client.close()
|
|
124
126
|
}
|
|
125
127
|
}
|
|
126
128
|
|
|
@@ -128,15 +130,14 @@ export async function envCommand (logger, args) {
|
|
|
128
130
|
const { values, positionals: allPositionals } = parseArgs(args, { table: { type: 'boolean', short: 't' } }, false)
|
|
129
131
|
|
|
130
132
|
let application
|
|
133
|
+
const client = new RuntimeApiClient()
|
|
131
134
|
try {
|
|
132
|
-
const client = new RuntimeApiClient()
|
|
133
135
|
const [runtime, positionals] = await getMatchingRuntime(client, allPositionals)
|
|
134
136
|
application = positionals[0]
|
|
135
137
|
|
|
136
138
|
const env = application
|
|
137
139
|
? await client.getRuntimeApplicationEnv(runtime.pid, application)
|
|
138
140
|
: await client.getRuntimeEnv(runtime.pid)
|
|
139
|
-
await client.close()
|
|
140
141
|
|
|
141
142
|
if (values.table) {
|
|
142
143
|
console.log(
|
|
@@ -166,6 +167,8 @@ export async function envCommand (logger, args) {
|
|
|
166
167
|
`Cannot get ${application ? 'application' : 'runtime'} environment variables: ${error.message}`
|
|
167
168
|
)
|
|
168
169
|
}
|
|
170
|
+
} finally {
|
|
171
|
+
await client.close()
|
|
169
172
|
}
|
|
170
173
|
}
|
|
171
174
|
|
|
@@ -173,15 +176,14 @@ export async function configCommand (logger, args) {
|
|
|
173
176
|
const { positionals: allPositionals } = parseArgs(args, {}, false)
|
|
174
177
|
|
|
175
178
|
let application
|
|
179
|
+
const client = new RuntimeApiClient()
|
|
176
180
|
try {
|
|
177
|
-
const client = new RuntimeApiClient()
|
|
178
181
|
const [runtime, positionals] = await getMatchingRuntime(client, allPositionals)
|
|
179
182
|
application = positionals[0]
|
|
180
183
|
|
|
181
184
|
const config = application
|
|
182
185
|
? await client.getRuntimeApplicationConfig(runtime.pid, application)
|
|
183
186
|
: await client.getRuntimeConfig(runtime.pid)
|
|
184
|
-
await client.close()
|
|
185
187
|
|
|
186
188
|
console.log(JSON.stringify(config, null, 2))
|
|
187
189
|
} catch (error) {
|
|
@@ -197,6 +199,8 @@ export async function configCommand (logger, args) {
|
|
|
197
199
|
`Cannot get ${application ? 'application' : 'runtime'} configuration: ${error.message}`
|
|
198
200
|
)
|
|
199
201
|
}
|
|
202
|
+
} finally {
|
|
203
|
+
await client.close()
|
|
200
204
|
}
|
|
201
205
|
}
|
|
202
206
|
|
package/lib/commands/metrics.js
CHANGED
|
@@ -3,15 +3,14 @@ import { ensureLoggableError, logFatalError, parseArgs } from '@platformatic/fou
|
|
|
3
3
|
import { bold } from 'colorette'
|
|
4
4
|
|
|
5
5
|
export async function metricsCommand (logger, args) {
|
|
6
|
+
const client = new RuntimeApiClient()
|
|
6
7
|
try {
|
|
7
8
|
const { values, positionals } = parseArgs(args, { format: { type: 'string', short: 'f', default: 'json' } }, false)
|
|
8
9
|
|
|
9
|
-
const client = new RuntimeApiClient()
|
|
10
10
|
const [runtime] = await getMatchingRuntime(client, positionals)
|
|
11
11
|
const metrics = await client.getRuntimeMetrics(runtime.pid, { format: values.format })
|
|
12
12
|
const result = values.format === 'text' ? metrics : JSON.stringify(metrics, null, 2)
|
|
13
13
|
console.log(result)
|
|
14
|
-
await client.close()
|
|
15
14
|
|
|
16
15
|
logger.done(`Runtime ${bold(runtime.packageName)} have been stopped.`)
|
|
17
16
|
} catch (error) {
|
|
@@ -21,6 +20,8 @@ export async function metricsCommand (logger, args) {
|
|
|
21
20
|
} else {
|
|
22
21
|
return logFatalError(logger, { error: ensureLoggableError(error) }, `Cannot reload the runtime: ${error.message}`)
|
|
23
22
|
}
|
|
23
|
+
} finally {
|
|
24
|
+
client.close()
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
|
package/lib/commands/pprof.js
CHANGED
|
@@ -4,10 +4,16 @@ import { bold } from 'colorette'
|
|
|
4
4
|
import { writeFile } from 'node:fs/promises'
|
|
5
5
|
|
|
6
6
|
export async function pprofStartCommand (logger, args) {
|
|
7
|
+
const client = new RuntimeApiClient()
|
|
8
|
+
|
|
7
9
|
try {
|
|
8
|
-
const { positionals, values } = parseArgs(
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
const { positionals, values } = parseArgs(
|
|
11
|
+
args,
|
|
12
|
+
{
|
|
13
|
+
type: { type: 'string', short: 't', default: 'cpu' }
|
|
14
|
+
},
|
|
15
|
+
false
|
|
16
|
+
)
|
|
11
17
|
|
|
12
18
|
// Validate profile type
|
|
13
19
|
const type = values.type
|
|
@@ -15,7 +21,6 @@ export async function pprofStartCommand (logger, args) {
|
|
|
15
21
|
return logFatalError(logger, `Invalid profile type: ${type}. Must be 'cpu' or 'heap'.`)
|
|
16
22
|
}
|
|
17
23
|
|
|
18
|
-
const client = new RuntimeApiClient()
|
|
19
24
|
const [runtime, remainingPositionals] = await getMatchingRuntime(client, positionals)
|
|
20
25
|
const { applications: runtimeApplications } = await client.getRuntimeApplications(runtime.pid)
|
|
21
26
|
|
|
@@ -28,7 +33,6 @@ export async function pprofStartCommand (logger, args) {
|
|
|
28
33
|
// Start profiling for specific application
|
|
29
34
|
const application = runtimeApplications.find(s => s.id === applicationId)
|
|
30
35
|
if (!application) {
|
|
31
|
-
await client.close()
|
|
32
36
|
return logFatalError(logger, `Application not found: ${applicationId}`)
|
|
33
37
|
}
|
|
34
38
|
|
|
@@ -45,8 +49,6 @@ export async function pprofStartCommand (logger, args) {
|
|
|
45
49
|
}
|
|
46
50
|
}
|
|
47
51
|
}
|
|
48
|
-
|
|
49
|
-
await client.close()
|
|
50
52
|
} catch (error) {
|
|
51
53
|
if (error.code === 'PLT_CTR_RUNTIME_NOT_FOUND') {
|
|
52
54
|
return logFatalError(logger, 'Cannot find a matching runtime.')
|
|
@@ -55,14 +57,22 @@ export async function pprofStartCommand (logger, args) {
|
|
|
55
57
|
} else {
|
|
56
58
|
return logFatalError(logger, { error: ensureLoggableError(error) }, `Cannot start profiling: ${error.message}`)
|
|
57
59
|
}
|
|
60
|
+
} finally {
|
|
61
|
+
await client.close()
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
export async function pprofStopCommand (logger, args) {
|
|
66
|
+
const client = new RuntimeApiClient()
|
|
67
|
+
|
|
62
68
|
try {
|
|
63
|
-
const { positionals, values } = parseArgs(
|
|
64
|
-
|
|
65
|
-
|
|
69
|
+
const { positionals, values } = parseArgs(
|
|
70
|
+
args,
|
|
71
|
+
{
|
|
72
|
+
type: { type: 'string', short: 't', default: 'cpu' }
|
|
73
|
+
},
|
|
74
|
+
false
|
|
75
|
+
)
|
|
66
76
|
|
|
67
77
|
// Validate profile type
|
|
68
78
|
const type = values.type
|
|
@@ -70,7 +80,6 @@ export async function pprofStopCommand (logger, args) {
|
|
|
70
80
|
return logFatalError(logger, `Invalid profile type: ${type}. Must be 'cpu' or 'heap'.`)
|
|
71
81
|
}
|
|
72
82
|
|
|
73
|
-
const client = new RuntimeApiClient()
|
|
74
83
|
const [runtime, remainingPositionals] = await getMatchingRuntime(client, positionals)
|
|
75
84
|
const { applications: runtimeApplications } = await client.getRuntimeApplications(runtime.pid)
|
|
76
85
|
|
|
@@ -84,14 +93,15 @@ export async function pprofStopCommand (logger, args) {
|
|
|
84
93
|
// Stop profiling for specific application
|
|
85
94
|
const application = runtimeApplications.find(s => s.id === applicationId)
|
|
86
95
|
if (!application) {
|
|
87
|
-
await client.close()
|
|
88
96
|
return logFatalError(logger, `Application not found: ${applicationId}`)
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
const profileData = await client.stopApplicationProfiling(runtime.pid, applicationId, options)
|
|
92
100
|
const filename = `pprof-${type}-${applicationId}-${timestamp}.pb`
|
|
93
101
|
await writeFile(filename, Buffer.from(profileData))
|
|
94
|
-
logger.info(
|
|
102
|
+
logger.info(
|
|
103
|
+
`${type.toUpperCase()} profiling stopped for application ${bold(applicationId)}, profile saved to ${bold(filename)}`
|
|
104
|
+
)
|
|
95
105
|
} else {
|
|
96
106
|
// Stop profiling for all applications
|
|
97
107
|
for (const application of runtimeApplications) {
|
|
@@ -99,14 +109,14 @@ export async function pprofStopCommand (logger, args) {
|
|
|
99
109
|
const profileData = await client.stopApplicationProfiling(runtime.pid, application.id, options)
|
|
100
110
|
const filename = `pprof-${type}-${application.id}-${timestamp}.pb`
|
|
101
111
|
await writeFile(filename, Buffer.from(profileData))
|
|
102
|
-
logger.info(
|
|
112
|
+
logger.info(
|
|
113
|
+
`${type.toUpperCase()} profiling stopped for application ${bold(application.id)}, profile saved to ${bold(filename)}`
|
|
114
|
+
)
|
|
103
115
|
} catch (error) {
|
|
104
116
|
logger.warn(`Failed to stop profiling for application ${application.id}: ${error.message}`)
|
|
105
117
|
}
|
|
106
118
|
}
|
|
107
119
|
}
|
|
108
|
-
|
|
109
|
-
await client.close()
|
|
110
120
|
} catch (error) {
|
|
111
121
|
if (error.code === 'PLT_CTR_RUNTIME_NOT_FOUND') {
|
|
112
122
|
return logFatalError(logger, 'Cannot find a matching runtime.')
|
|
@@ -115,6 +125,8 @@ export async function pprofStopCommand (logger, args) {
|
|
|
115
125
|
} else {
|
|
116
126
|
return logFatalError(logger, { error: ensureLoggableError(error) }, `Cannot stop profiling: ${error.message}`)
|
|
117
127
|
}
|
|
128
|
+
} finally {
|
|
129
|
+
await client.close()
|
|
118
130
|
}
|
|
119
131
|
}
|
|
120
132
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wattpm",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.16.0",
|
|
4
4
|
"description": "The Node.js Application Server",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"pino-pretty": "^13.0.0",
|
|
30
30
|
"split2": "^4.2.0",
|
|
31
31
|
"table": "^6.8.2",
|
|
32
|
-
"@platformatic/
|
|
33
|
-
"@platformatic/
|
|
34
|
-
"@platformatic/
|
|
32
|
+
"@platformatic/control": "3.16.0",
|
|
33
|
+
"@platformatic/runtime": "3.16.0",
|
|
34
|
+
"@platformatic/foundation": "3.16.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"cleaner-spec-reporter": "^0.5.0",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"neostandard": "^0.12.0",
|
|
43
43
|
"typescript": "^5.5.4",
|
|
44
44
|
"undici": "^7.0.0",
|
|
45
|
-
"@platformatic/node": "3.
|
|
45
|
+
"@platformatic/node": "3.16.0"
|
|
46
46
|
},
|
|
47
47
|
"engines": {
|
|
48
48
|
"node": ">=22.19.0"
|
package/schema.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://schemas.platformatic.dev/wattpm/3.
|
|
2
|
+
"$id": "https://schemas.platformatic.dev/wattpm/3.16.0.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"title": "Platformatic Runtime Config",
|
|
5
5
|
"type": "object",
|
|
@@ -64,6 +64,10 @@
|
|
|
64
64
|
"useHttp": {
|
|
65
65
|
"type": "boolean"
|
|
66
66
|
},
|
|
67
|
+
"reuseTcpPorts": {
|
|
68
|
+
"type": "boolean",
|
|
69
|
+
"default": true
|
|
70
|
+
},
|
|
67
71
|
"workers": {
|
|
68
72
|
"anyOf": [
|
|
69
73
|
{
|
|
@@ -342,6 +346,10 @@
|
|
|
342
346
|
"useHttp": {
|
|
343
347
|
"type": "boolean"
|
|
344
348
|
},
|
|
349
|
+
"reuseTcpPorts": {
|
|
350
|
+
"type": "boolean",
|
|
351
|
+
"default": true
|
|
352
|
+
},
|
|
345
353
|
"workers": {
|
|
346
354
|
"anyOf": [
|
|
347
355
|
{
|
|
@@ -618,6 +626,10 @@
|
|
|
618
626
|
"useHttp": {
|
|
619
627
|
"type": "boolean"
|
|
620
628
|
},
|
|
629
|
+
"reuseTcpPorts": {
|
|
630
|
+
"type": "boolean",
|
|
631
|
+
"default": true
|
|
632
|
+
},
|
|
621
633
|
"workers": {
|
|
622
634
|
"anyOf": [
|
|
623
635
|
{
|
|
@@ -894,6 +906,10 @@
|
|
|
894
906
|
"useHttp": {
|
|
895
907
|
"type": "boolean"
|
|
896
908
|
},
|
|
909
|
+
"reuseTcpPorts": {
|
|
910
|
+
"type": "boolean",
|
|
911
|
+
"default": true
|
|
912
|
+
},
|
|
897
913
|
"workers": {
|
|
898
914
|
"anyOf": [
|
|
899
915
|
{
|
|
@@ -1464,6 +1480,10 @@
|
|
|
1464
1480
|
},
|
|
1465
1481
|
"additionalProperties": false
|
|
1466
1482
|
},
|
|
1483
|
+
"reuseTcpPorts": {
|
|
1484
|
+
"type": "boolean",
|
|
1485
|
+
"default": true
|
|
1486
|
+
},
|
|
1467
1487
|
"startTimeout": {
|
|
1468
1488
|
"default": 30000,
|
|
1469
1489
|
"type": "number",
|