@pmate/cli 0.31.0 → 0.32.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/README.md +7 -7
- package/dist/index.d.ts +5 -0
- package/dist/index.js +103 -85
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -234,19 +234,19 @@ Notes:
|
|
|
234
234
|
- `pmate vercel` is a thin wrapper around the installed `vercel` CLI.
|
|
235
235
|
- v1 only supports `vercel.scope` in PMate config.
|
|
236
236
|
- `pmate vercel deploys` requires `--project <name>`.
|
|
237
|
-
#
|
|
237
|
+
# Service logs
|
|
238
238
|
|
|
239
|
-
|
|
239
|
+
Service logs use the same authenticated API as `dev.pmate.chat`. Each registered Service has `app` (application/deployment) and `runtime` (PM2/Nginx) Table topics on the dedicated `log` chain.
|
|
240
240
|
|
|
241
241
|
```sh
|
|
242
|
-
pmate service log pmate:
|
|
243
|
-
pmate service log pmate:
|
|
242
|
+
pmate service log pmate:issues-api --topic app --page 0 --json
|
|
243
|
+
pmate service log pmate:issues-api --topic runtime --source nginx --level error --env prod
|
|
244
|
+
pmate service log pmate:issues-api --request-id <id> --follow
|
|
244
245
|
```
|
|
245
246
|
|
|
246
|
-
Pages are zero-based. `--search`
|
|
247
|
-
|
|
248
|
-
A current CLI session is exchanged in memory for `pmate:developers`; the active App and session file are not changed. If login is required, run `pmate login --app pmate:developers`. Access denial never falls back to anonymous log reads. Neither source nor exchanged tokens are printed.
|
|
247
|
+
Pages are zero-based. Lists and `--search` cover the chain's configured retention window (30 days in production). `--since-ms` adds an epoch-millisecond lower bound. `--type`, `--level`, `--source`, `--request-id` and `--env` filter on the server. The legacy `--app` argument is optional and does not select a different source.
|
|
249
248
|
|
|
249
|
+
A current CLI session is exchanged in memory for `pmate:developers`; the active App and session file are unchanged. If needed, run `pmate login --app pmate:developers`. Permission denial never falls back to anonymous Indexer reads. Credentials are never printed.
|
|
250
250
|
|
|
251
251
|
### Package a desktop app
|
|
252
252
|
|
package/dist/index.d.ts
CHANGED
|
@@ -970,6 +970,11 @@ type ServiceCommandArgs = {
|
|
|
970
970
|
} | {
|
|
971
971
|
type: Command.Service;
|
|
972
972
|
action: "log";
|
|
973
|
+
topic?: "app" | "runtime";
|
|
974
|
+
level?: string;
|
|
975
|
+
source?: string;
|
|
976
|
+
requestId?: string;
|
|
977
|
+
logType?: string;
|
|
973
978
|
app?: string;
|
|
974
979
|
sinceMs?: number;
|
|
975
980
|
namespace: string;
|
package/dist/index.js
CHANGED
|
@@ -18740,6 +18740,50 @@ function capitalize(value) {
|
|
|
18740
18740
|
return value.slice(0, 1).toUpperCase() + value.slice(1);
|
|
18741
18741
|
}
|
|
18742
18742
|
|
|
18743
|
+
// src/services/DeveloperLogsClient.ts
|
|
18744
|
+
init_sessionStore();
|
|
18745
|
+
init_namespacedRef();
|
|
18746
|
+
var appRef = "pmate:developers";
|
|
18747
|
+
async function queryDeveloperLogs(input, fetcher = fetch) {
|
|
18748
|
+
if (input.app) parseNamespacedRef(input.app, { entityLabel: "app ref" });
|
|
18749
|
+
parseNamespacedRef(input.service, { entityLabel: "service ref" });
|
|
18750
|
+
if (!Number.isSafeInteger(input.page) || input.page < 0) throw new Error("Invalid log page");
|
|
18751
|
+
if (input.sinceMs !== void 0 && (!Number.isSafeInteger(input.sinceMs) || input.sinceMs < 0)) throw new Error("Invalid log time");
|
|
18752
|
+
if (input.keyword && input.keyword.length > 256) throw new Error("Log search must be at most 256 characters");
|
|
18753
|
+
const session = readRequiredSession({ preferredApp: appRef, fallbackToActiveApp: true });
|
|
18754
|
+
if (Date.parse(session.expiresAt) <= Date.now() || !Number.isFinite(Date.parse(session.expiresAt)))
|
|
18755
|
+
throw new Error("Session expired. Run pmate login --app pmate:developers");
|
|
18756
|
+
async function request2(url, init) {
|
|
18757
|
+
let response;
|
|
18758
|
+
try {
|
|
18759
|
+
response = await fetcher(url, { ...init, redirect: "error", signal: AbortSignal.timeout(3e4) });
|
|
18760
|
+
} catch {
|
|
18761
|
+
throw new Error("Developers log request failed; no anonymous fallback was attempted");
|
|
18762
|
+
}
|
|
18763
|
+
if (!response.ok) throw new Error(`Developers log request failed (HTTP ${response.status}); check login, pmate:dev role`);
|
|
18764
|
+
let body;
|
|
18765
|
+
try {
|
|
18766
|
+
body = await response.json();
|
|
18767
|
+
} catch {
|
|
18768
|
+
throw new Error("Invalid Developers API response");
|
|
18769
|
+
}
|
|
18770
|
+
if (!body?.success) throw new Error("Developers API rejected the request");
|
|
18771
|
+
return body.data;
|
|
18772
|
+
}
|
|
18773
|
+
const exchanged = await request2("https://auth-api-v2.pmate.chat/session/exchange", {
|
|
18774
|
+
method: "POST",
|
|
18775
|
+
headers: { Authorization: `Bearer ${session.token}`, "Content-Type": "application/json" },
|
|
18776
|
+
body: JSON.stringify({ app: appRef })
|
|
18777
|
+
});
|
|
18778
|
+
if (exchanged?.session?.identity?.app !== appRef || exchanged.session.identity.accountId !== session.accountId || typeof exchanged.token !== "string" || !exchanged.token)
|
|
18779
|
+
throw new Error("Developers session identity mismatch");
|
|
18780
|
+
const params = new URLSearchParams({ service: input.service, page: String(input.page), topic: input.topic ?? "app" });
|
|
18781
|
+
for (const key of ["type", "level", "source", "requestId", "environment"]) if (input[key]) params.set(key, input[key]);
|
|
18782
|
+
if (input.keyword) params.set("keyword", input.keyword);
|
|
18783
|
+
if (input.sinceMs !== void 0) params.set("sinceMs", String(input.sinceMs));
|
|
18784
|
+
return request2(`https://dev-api.pmate.chat/api/logs?${params}`, { headers: { Authorization: `Bearer ${exchanged.token}` } });
|
|
18785
|
+
}
|
|
18786
|
+
|
|
18743
18787
|
// src/services/ServiceRuntimeRunner.ts
|
|
18744
18788
|
var import_node_fs22 = require("fs");
|
|
18745
18789
|
var import_node_os12 = __toESM(require("os"));
|
|
@@ -19111,15 +19155,21 @@ var ServiceRuntimeRunner = class {
|
|
|
19111
19155
|
const namespace = service.namespace;
|
|
19112
19156
|
const appName = service.name;
|
|
19113
19157
|
const page = await this.fetchLogPage(namespace, appName, {
|
|
19158
|
+
...options,
|
|
19114
19159
|
page: options.page ?? 0,
|
|
19115
19160
|
history: options.history ?? false,
|
|
19116
19161
|
search: options.search
|
|
19117
19162
|
});
|
|
19118
|
-
|
|
19163
|
+
if (options.json) {
|
|
19164
|
+
console.log(JSON.stringify(page));
|
|
19165
|
+
return;
|
|
19166
|
+
}
|
|
19167
|
+
ui.title(`Logs for ${namespace}/${appName} \xB7 ${options.topic ?? "app"}`);
|
|
19119
19168
|
ui.info("Source", { mode: options.history ? "history" : "online" });
|
|
19120
19169
|
this.renderPage(page);
|
|
19121
19170
|
if (options.follow) {
|
|
19122
19171
|
await this.followLogs(namespace, appName, {
|
|
19172
|
+
...options,
|
|
19123
19173
|
page: options.page ?? 0,
|
|
19124
19174
|
history: options.history ?? false,
|
|
19125
19175
|
search: options.search,
|
|
@@ -19136,7 +19186,7 @@ var ServiceRuntimeRunner = class {
|
|
|
19136
19186
|
interactive?.rl.on("close", () => {
|
|
19137
19187
|
running = false;
|
|
19138
19188
|
});
|
|
19139
|
-
const seen = new Set(options.seedItems.map((item) => item.hash));
|
|
19189
|
+
const seen = new Set(options.seedItems.map((item) => item.id ?? item.hash));
|
|
19140
19190
|
ui.info(
|
|
19141
19191
|
"Follow",
|
|
19142
19192
|
interactive ? "enabled (poll every 2s, Ctrl+C to stop, /help for filters)" : "enabled (poll every 2s, Ctrl+C to stop)"
|
|
@@ -19147,6 +19197,7 @@ var ServiceRuntimeRunner = class {
|
|
|
19147
19197
|
let page;
|
|
19148
19198
|
try {
|
|
19149
19199
|
page = await this.fetchLogPage(namespace, appName, {
|
|
19200
|
+
...options,
|
|
19150
19201
|
page: options.page,
|
|
19151
19202
|
history: options.history,
|
|
19152
19203
|
search: options.search
|
|
@@ -19156,12 +19207,12 @@ var ServiceRuntimeRunner = class {
|
|
|
19156
19207
|
ui.warn("Follow fetch failed", { message });
|
|
19157
19208
|
continue;
|
|
19158
19209
|
}
|
|
19159
|
-
const newItems = page.data.filter((item) => !seen.has(item.hash));
|
|
19210
|
+
const newItems = page.data.filter((item) => !seen.has(item.id ?? item.hash));
|
|
19160
19211
|
if (newItems.length === 0) {
|
|
19161
19212
|
continue;
|
|
19162
19213
|
}
|
|
19163
19214
|
for (const item of newItems) {
|
|
19164
|
-
seen.add(item.hash);
|
|
19215
|
+
seen.add(item.id ?? item.hash);
|
|
19165
19216
|
recent.unshift(item);
|
|
19166
19217
|
if (recent.length > 200) {
|
|
19167
19218
|
recent.pop();
|
|
@@ -19747,35 +19798,19 @@ var ServiceRuntimeRunner = class {
|
|
|
19747
19798
|
await this.runRemoteCommand(server, command);
|
|
19748
19799
|
}
|
|
19749
19800
|
async fetchLogPage(namespace, appName, options) {
|
|
19750
|
-
|
|
19751
|
-
|
|
19752
|
-
|
|
19753
|
-
|
|
19754
|
-
|
|
19755
|
-
|
|
19756
|
-
|
|
19757
|
-
|
|
19758
|
-
|
|
19759
|
-
|
|
19760
|
-
|
|
19761
|
-
|
|
19762
|
-
const response = await fetch(url.toString(), {
|
|
19763
|
-
method: "GET",
|
|
19764
|
-
headers: { Accept: "application/json" }
|
|
19801
|
+
resolveLogChainId();
|
|
19802
|
+
return await queryDeveloperLogs({
|
|
19803
|
+
service: `${namespace}:${appName}`,
|
|
19804
|
+
page: options.page,
|
|
19805
|
+
keyword: options.search,
|
|
19806
|
+
topic: options.topic,
|
|
19807
|
+
type: options.logType,
|
|
19808
|
+
level: options.level,
|
|
19809
|
+
source: options.source,
|
|
19810
|
+
requestId: options.requestId,
|
|
19811
|
+
environment: options.envName,
|
|
19812
|
+
sinceMs: options.sinceMs
|
|
19765
19813
|
});
|
|
19766
|
-
const payload = await response.json();
|
|
19767
|
-
if (response.status === 404 && /resource not found/i.test(payload.message || "")) {
|
|
19768
|
-
return {
|
|
19769
|
-
page: options.page,
|
|
19770
|
-
totalPage: 0,
|
|
19771
|
-
pageSize: 100,
|
|
19772
|
-
data: []
|
|
19773
|
-
};
|
|
19774
|
-
}
|
|
19775
|
-
if (!response.ok || !payload.success || !payload.data) {
|
|
19776
|
-
throw new Error(payload.message || `Log request failed: ${response.status}`);
|
|
19777
|
-
}
|
|
19778
|
-
return payload.data;
|
|
19779
19814
|
}
|
|
19780
19815
|
envNameToStage(envName) {
|
|
19781
19816
|
if (envName === "dev" || envName === "preview" || envName === "prod") {
|
|
@@ -19801,8 +19836,8 @@ var ServiceRuntimeRunner = class {
|
|
|
19801
19836
|
const type = isErrorLike ? import_chalk3.default.bold.red(`[${item.type}]`) : import_chalk3.default.bold.blue(`[${item.type}]`);
|
|
19802
19837
|
const level = resolvedLevel ? `${isErrorLike ? import_chalk3.default.bold.red(resolvedLevel.toUpperCase()) : import_chalk3.default.bold.yellow(resolvedLevel.toUpperCase())} ` : "";
|
|
19803
19838
|
const data = this.formatLogData(item.data);
|
|
19804
|
-
const hash = import_chalk3.default.gray(`#${item.hash}`);
|
|
19805
|
-
return `${ts} ${level}${type} ${data} ${hash}`;
|
|
19839
|
+
const hash = import_chalk3.default.gray(`#${item.id ?? item.hash}`);
|
|
19840
|
+
return `${ts} ${level}${type} ${item.source ?? ""} ${item.message ?? ""} ${data} ${hash}`;
|
|
19806
19841
|
}
|
|
19807
19842
|
formatLogData(value) {
|
|
19808
19843
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -20459,7 +20494,8 @@ var ServiceRuntimeRunner = class {
|
|
|
20459
20494
|
}
|
|
20460
20495
|
};
|
|
20461
20496
|
function resolveLogChainId(env = process.env) {
|
|
20462
|
-
|
|
20497
|
+
if (env.APP_LOG_CHAIN_ID && env.APP_LOG_CHAIN_ID !== "log") throw new Error("Logs require log chain");
|
|
20498
|
+
return "log";
|
|
20463
20499
|
}
|
|
20464
20500
|
function shellQuote2(value) {
|
|
20465
20501
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
@@ -20474,51 +20510,6 @@ function parseServiceRef(serviceId) {
|
|
|
20474
20510
|
|
|
20475
20511
|
// src/commands/ServiceCommandHandler.ts
|
|
20476
20512
|
init_namespacedRef();
|
|
20477
|
-
|
|
20478
|
-
// src/services/DeveloperLogsClient.ts
|
|
20479
|
-
init_sessionStore();
|
|
20480
|
-
init_namespacedRef();
|
|
20481
|
-
var appRef = "pmate:developers";
|
|
20482
|
-
async function queryDeveloperLogs(input, fetcher = fetch) {
|
|
20483
|
-
parseNamespacedRef(input.app, { entityLabel: "app ref" });
|
|
20484
|
-
parseNamespacedRef(input.service, { entityLabel: "service ref" });
|
|
20485
|
-
if (!Number.isSafeInteger(input.page) || input.page < 0) throw new Error("Invalid log page");
|
|
20486
|
-
if (input.sinceMs !== void 0 && (!Number.isSafeInteger(input.sinceMs) || input.sinceMs < 0)) throw new Error("Invalid log time");
|
|
20487
|
-
if (input.keyword && input.keyword.length > 256) throw new Error("Log search must be at most 256 characters");
|
|
20488
|
-
const session = readRequiredSession({ preferredApp: appRef, fallbackToActiveApp: true });
|
|
20489
|
-
if (Date.parse(session.expiresAt) <= Date.now() || !Number.isFinite(Date.parse(session.expiresAt)))
|
|
20490
|
-
throw new Error("Session expired. Run pmate login --app pmate:developers");
|
|
20491
|
-
async function request2(url, init) {
|
|
20492
|
-
let response;
|
|
20493
|
-
try {
|
|
20494
|
-
response = await fetcher(url, { ...init, redirect: "error", signal: AbortSignal.timeout(3e4) });
|
|
20495
|
-
} catch {
|
|
20496
|
-
throw new Error("Developers log request failed; no anonymous fallback was attempted");
|
|
20497
|
-
}
|
|
20498
|
-
if (!response.ok) throw new Error(`Developers log request failed (HTTP ${response.status}); check login, pmate:dev role and App-Service access`);
|
|
20499
|
-
let body;
|
|
20500
|
-
try {
|
|
20501
|
-
body = await response.json();
|
|
20502
|
-
} catch {
|
|
20503
|
-
throw new Error("Invalid Developers API response");
|
|
20504
|
-
}
|
|
20505
|
-
if (!body?.success) throw new Error("Developers API rejected the request");
|
|
20506
|
-
return body.data;
|
|
20507
|
-
}
|
|
20508
|
-
const exchanged = await request2("https://auth-api-v2.pmate.chat/session/exchange", {
|
|
20509
|
-
method: "POST",
|
|
20510
|
-
headers: { Authorization: `Bearer ${session.token}`, "Content-Type": "application/json" },
|
|
20511
|
-
body: JSON.stringify({ app: appRef })
|
|
20512
|
-
});
|
|
20513
|
-
if (exchanged?.session?.identity?.app !== appRef || exchanged.session.identity.accountId !== session.accountId || typeof exchanged.token !== "string" || !exchanged.token)
|
|
20514
|
-
throw new Error("Developers session identity mismatch");
|
|
20515
|
-
const params = new URLSearchParams({ app: input.app, service: input.service, page: String(input.page) });
|
|
20516
|
-
if (input.keyword) params.set("keyword", input.keyword);
|
|
20517
|
-
if (input.sinceMs !== void 0) params.set("sinceMs", String(input.sinceMs));
|
|
20518
|
-
return request2(`https://dev-api.pmate.chat/api/logs?${params}`, { headers: { Authorization: `Bearer ${exchanged.token}` } });
|
|
20519
|
-
}
|
|
20520
|
-
|
|
20521
|
-
// src/commands/ServiceCommandHandler.ts
|
|
20522
20513
|
var ServiceCommandHandler = class {
|
|
20523
20514
|
async handle(args) {
|
|
20524
20515
|
const baseUrl = this.resolveBaseUrl(args.baseUrl);
|
|
@@ -20716,7 +20707,7 @@ var ServiceCommandHandler = class {
|
|
|
20716
20707
|
}
|
|
20717
20708
|
case "log": {
|
|
20718
20709
|
if (args.app) {
|
|
20719
|
-
const result = await queryDeveloperLogs({ app: args.app, service: `${args.namespace}:${args.name}`, page: args.page, keyword: args.search, sinceMs: args.sinceMs });
|
|
20710
|
+
const result = await queryDeveloperLogs({ app: args.app, service: `${args.namespace}:${args.name}`, page: args.page, keyword: args.search, sinceMs: args.sinceMs, topic: args.topic, type: args.logType, level: args.level, source: args.source, requestId: args.requestId });
|
|
20720
20711
|
console.log(JSON.stringify(result, null, args.json ? void 0 : 2));
|
|
20721
20712
|
return;
|
|
20722
20713
|
}
|
|
@@ -20727,7 +20718,14 @@ var ServiceCommandHandler = class {
|
|
|
20727
20718
|
history: args.history,
|
|
20728
20719
|
search: args.search,
|
|
20729
20720
|
follow: args.follow,
|
|
20730
|
-
envName: args.env
|
|
20721
|
+
envName: args.env,
|
|
20722
|
+
topic: args.topic,
|
|
20723
|
+
level: args.level,
|
|
20724
|
+
source: args.source,
|
|
20725
|
+
requestId: args.requestId,
|
|
20726
|
+
logType: args.logType,
|
|
20727
|
+
sinceMs: args.sinceMs,
|
|
20728
|
+
json: args.json
|
|
20731
20729
|
});
|
|
20732
20730
|
return;
|
|
20733
20731
|
}
|
|
@@ -23998,6 +23996,7 @@ function parseServiceArgs(tokens) {
|
|
|
23998
23996
|
let page = 0;
|
|
23999
23997
|
let app;
|
|
24000
23998
|
let sinceMs;
|
|
23999
|
+
const logFilters = {};
|
|
24001
24000
|
let history = false;
|
|
24002
24001
|
let search;
|
|
24003
24002
|
let follow = false;
|
|
@@ -24005,6 +24004,26 @@ function parseServiceArgs(tokens) {
|
|
|
24005
24004
|
while (rest.length > 0) {
|
|
24006
24005
|
const token = rest.shift();
|
|
24007
24006
|
switch (token) {
|
|
24007
|
+
case "--topic":
|
|
24008
|
+
case "--level":
|
|
24009
|
+
case "--source":
|
|
24010
|
+
case "--request-id":
|
|
24011
|
+
case "--type": {
|
|
24012
|
+
if (action !== "log") throw new Error(`Unknown service ${action} option: ${token}`);
|
|
24013
|
+
const value = requireValue4(rest, token);
|
|
24014
|
+
if (token === "--topic") {
|
|
24015
|
+
if (value !== "app" && value !== "runtime") throw new Error("Invalid --topic");
|
|
24016
|
+
logFilters.topic = value;
|
|
24017
|
+
} else if (token === "--level") {
|
|
24018
|
+
if (!["debug", "info", "warn", "error"].includes(value)) throw new Error("Invalid --level");
|
|
24019
|
+
logFilters.level = value;
|
|
24020
|
+
} else if (token === "--source") {
|
|
24021
|
+
if (!["pm2", "nginx", "application", "deployment"].includes(value)) throw new Error("Invalid --source");
|
|
24022
|
+
logFilters.source = value;
|
|
24023
|
+
} else if (token === "--request-id") logFilters.requestId = value;
|
|
24024
|
+
else logFilters.logType = value;
|
|
24025
|
+
break;
|
|
24026
|
+
}
|
|
24008
24027
|
case "--app":
|
|
24009
24028
|
if (action !== "log") throw new Error(`Unknown service ${action} option: ${token}`);
|
|
24010
24029
|
app = parseNamespacedRef(requireValue4(rest, token), { entityLabel: "app ref" }).fullId;
|
|
@@ -24055,8 +24074,7 @@ function parseServiceArgs(tokens) {
|
|
|
24055
24074
|
const name = parsedRef.objectId;
|
|
24056
24075
|
if (action === "log") {
|
|
24057
24076
|
if (app && (history || follow || env || baseUrl)) throw new Error("App logs do not support --history, --follow, --env or --base-url; use --page, --search and --since-ms");
|
|
24058
|
-
|
|
24059
|
-
return { type: "service" /* Service */, action, namespace, name, baseUrl, json, page, history, search, follow, env, ...app ? { app } : {}, ...sinceMs !== void 0 ? { sinceMs } : {} };
|
|
24077
|
+
return { type: "service" /* Service */, action, namespace, name, baseUrl, json, page, history, search, follow, env, ...logFilters, ...app ? { app } : {}, ...sinceMs !== void 0 ? { sinceMs } : {} };
|
|
24060
24078
|
}
|
|
24061
24079
|
if (action === "deploy-logs" || action === "deploy-history") {
|
|
24062
24080
|
return { type: "service" /* Service */, action, namespace, name, env, page, baseUrl, json };
|
|
@@ -26303,7 +26321,7 @@ var HELP_TEXT = [
|
|
|
26303
26321
|
` ${import_chalk4.default.gray(" init <ns:objectId> [--cwd <dir>] [--base-url <url>]")}`,
|
|
26304
26322
|
` ${import_chalk4.default.gray(" ssl set <ns:objectId> --cert <key> [--base-url <url>] [--json]")}`,
|
|
26305
26323
|
` ${import_chalk4.default.gray(" ssl refresh <ns:objectId> [--cert <key>] [--dry-run] [--base-url <url>] [--json]")}`,
|
|
26306
|
-
` ${import_chalk4.default.gray(" log <ns:objectId> [--
|
|
26324
|
+
` ${import_chalk4.default.gray(" log <ns:objectId> [--topic app|runtime] [--env <env>] [--level <level>] [--source <source>] [--type <type>] [--request-id <id>] [--search <keyword>] [--since-ms <ms>] [--page <n>] [--follow|-f] [--json]")}`,
|
|
26307
26325
|
` ${import_chalk4.default.gray(" default base URL: https://apps-api.pmate.chat")}`,
|
|
26308
26326
|
` ${import_chalk4.default.cyan("host")} Manage host-level environment variables`,
|
|
26309
26327
|
` ${import_chalk4.default.gray(" env list [--host <id>] [--json]")}`,
|