@get-dx/cli 0.1.2 → 0.1.3
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 +14 -22
- package/dist/cli.js +2 -0
- package/dist/commands/auth.js +3 -2
- package/dist/commands/authRendering.js +2 -0
- package/dist/commands/init.js +193 -0
- package/dist/logger.js +14 -1
- package/dist/runtime.js +17 -3
- package/dist/secrets.js +19 -6
- package/dist/ui/blocks.js +10 -0
- package/dist/ui.js +7 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -2,27 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
AI-Native CLI for interacting with DX.
|
|
4
4
|
|
|
5
|
+
<img width="800" height="423" alt="cli-demo" src="https://github.com/user-attachments/assets/a89f7d53-2617-413e-84df-07293149e9c8" />
|
|
6
|
+
|
|
5
7
|
## Install
|
|
6
8
|
|
|
7
|
-
```
|
|
8
|
-
npm install
|
|
9
|
+
```shell
|
|
10
|
+
npm install -g @get-dx/cli
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
- `dx auth login --token <account-web-api-token>`
|
|
14
|
-
- `dx auth logout`
|
|
15
|
-
- `dx auth status`
|
|
16
|
-
- `dx catalog entities info <identifier>`
|
|
13
|
+
## Getting started
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
Interactively login and install the AI agent skill:
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
```shell
|
|
18
|
+
dx init
|
|
19
|
+
```
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
- `DX_AGENT_SESSION_ID`
|
|
21
|
+
Show help text:
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
```shell
|
|
24
|
+
dx --help
|
|
25
|
+
dx <subcommand> --help
|
|
26
|
+
```
|
|
26
27
|
|
|
27
28
|
## Logging
|
|
28
29
|
|
|
@@ -30,15 +31,6 @@ Set `DX_LOG_LEVEL` to one of `debug`, `info`, `warn`, or `error` to enable CLI l
|
|
|
30
31
|
|
|
31
32
|
Logs are always written to `stderr`. They are human-readable by default, and switch to JSON when `--json` is present or `stderr` is not a TTY.
|
|
32
33
|
|
|
33
|
-
Deferred in this repo version:
|
|
34
|
-
|
|
35
|
-
- `dx init`
|
|
36
|
-
- `dx scorecards list|get|create|update`
|
|
37
|
-
- `dx catalog entities list|create|update`
|
|
38
|
-
- `dx workflows list`
|
|
39
|
-
- `dx workflows trigger`
|
|
40
|
-
- `dx studio query`
|
|
41
|
-
|
|
42
34
|
## Developing the CLI
|
|
43
35
|
|
|
44
36
|
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Option } from "commander";
|
|
2
2
|
import { authCommand } from "./commands/auth.js";
|
|
3
3
|
import { catalogCommand } from "./commands/catalog.js";
|
|
4
|
+
import { initCommand } from "./commands/init.js";
|
|
4
5
|
import { scorecardsCommand } from "./commands/scorecards.js";
|
|
5
6
|
import { studioCommand } from "./commands/studio.js";
|
|
6
7
|
import { handleError } from "./commandHelpers.js";
|
|
@@ -29,6 +30,7 @@ function createProgram() {
|
|
|
29
30
|
.addOption(new Option("--agent-session-id <id>", "Agent session ID to send as an HTTP header"));
|
|
30
31
|
program.addCommand(authCommand());
|
|
31
32
|
program.addCommand(catalogCommand());
|
|
33
|
+
program.addCommand(initCommand());
|
|
32
34
|
program.addCommand(scorecardsCommand());
|
|
33
35
|
program.addCommand(studioCommand());
|
|
34
36
|
applyExitOverride(program);
|
package/dist/commands/auth.js
CHANGED
|
@@ -15,7 +15,7 @@ export function authCommand() {
|
|
|
15
15
|
.description("Manage DX authentication");
|
|
16
16
|
auth
|
|
17
17
|
.command("login")
|
|
18
|
-
.requiredOption("--token <token>", "Account web API token")
|
|
18
|
+
.requiredOption("--token <token>", "Account web API token or personal access token")
|
|
19
19
|
.action(wrapAction(async (commandOptions, command) => {
|
|
20
20
|
const context = getContext(command);
|
|
21
21
|
const baseUrl = resolveBaseUrl();
|
|
@@ -62,7 +62,8 @@ export function authCommand() {
|
|
|
62
62
|
}));
|
|
63
63
|
return auth;
|
|
64
64
|
}
|
|
65
|
-
|
|
65
|
+
// TODO: should we move this somewhere more central, since `init` needs it?
|
|
66
|
+
export async function getAuthInfo(runtime) {
|
|
66
67
|
const response = await request(runtime, "/auth.info", {
|
|
67
68
|
method: "GET",
|
|
68
69
|
});
|
|
@@ -34,6 +34,8 @@ function tokenTypeName(tokenType) {
|
|
|
34
34
|
switch (tokenType) {
|
|
35
35
|
case "account_web_api_token":
|
|
36
36
|
return "Account-level web API token";
|
|
37
|
+
case "personal_access_token":
|
|
38
|
+
return "Personal access token";
|
|
37
39
|
default:
|
|
38
40
|
throw new Error(`Unknown token type: ${tokenType}`);
|
|
39
41
|
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { input, password, confirm } from "@inquirer/prompts";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import { execa } from "execa";
|
|
7
|
+
import { buildRuntime, buildRuntimeSafe } from "../runtime.js";
|
|
8
|
+
import { getAuthInfo } from "./auth.js";
|
|
9
|
+
import { renderAuthInfo } from "./authRendering.js";
|
|
10
|
+
import { getContext } from "../commandHelpers.js";
|
|
11
|
+
import { wrapAction } from "../commandHelpers.js";
|
|
12
|
+
import { renderRichText } from "../renderers.js";
|
|
13
|
+
import * as ui from "../ui.js";
|
|
14
|
+
import { CliError } from "../errors.js";
|
|
15
|
+
import { persistBaseUrl } from "../config.js";
|
|
16
|
+
import { setToken } from "../secrets.js";
|
|
17
|
+
function parseHostname(raw) {
|
|
18
|
+
const normalized = raw.trim().replace(/\/$/, "");
|
|
19
|
+
if (!normalized || normalized === "app.getdx.com") {
|
|
20
|
+
return { type: "cloud" };
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const url = new URL(normalized.startsWith("http") ? normalized : `https://${normalized}`);
|
|
24
|
+
const host = url.hostname;
|
|
25
|
+
const dedicatedMatch = host.match(/^(.+)\.getdx\.io$/);
|
|
26
|
+
if (dedicatedMatch) {
|
|
27
|
+
return { type: "dedicated", accountName: dedicatedMatch[1] };
|
|
28
|
+
}
|
|
29
|
+
if (host) {
|
|
30
|
+
return { type: "managed", uiBaseUrl: url.origin };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// fall through to invalid
|
|
35
|
+
}
|
|
36
|
+
return { type: "invalid" };
|
|
37
|
+
}
|
|
38
|
+
// FIXME: make this WAY more glam
|
|
39
|
+
// Choose a more interesting ASCII art font from https://patorjk.com/software/taag/
|
|
40
|
+
const WELCOME_BANNER = ui.indent(`\
|
|
41
|
+
▄▄▄▄▄ ▄▄▄ ▄▄▄
|
|
42
|
+
██▀▀▀██ ██▄▄██
|
|
43
|
+
██ ██ ████
|
|
44
|
+
██ ██ ██
|
|
45
|
+
██ ██ ████
|
|
46
|
+
██▄▄▄██ ██ ██
|
|
47
|
+
▀▀▀▀▀ ▀▀▀ ▀▀▀
|
|
48
|
+
`, 2);
|
|
49
|
+
export function initCommand() {
|
|
50
|
+
const init = new Command()
|
|
51
|
+
.name("init")
|
|
52
|
+
.description("Initialize the DX CLI")
|
|
53
|
+
.action(wrapAction(async (_commandOptions, command) => {
|
|
54
|
+
const context = getContext(command);
|
|
55
|
+
let runtime = buildRuntimeSafe(context);
|
|
56
|
+
ensureInteractive();
|
|
57
|
+
showWelcomeBanner();
|
|
58
|
+
runtime = await ensureLoggedIn(runtime);
|
|
59
|
+
await optionallySetupSkill(runtime);
|
|
60
|
+
renderRichText([
|
|
61
|
+
ui.blankLine(),
|
|
62
|
+
ui.p(`${ui.success(ui.GLYPHS.CHECK)} Done! You are now ready to use the DX CLI.`),
|
|
63
|
+
]);
|
|
64
|
+
}));
|
|
65
|
+
return init;
|
|
66
|
+
}
|
|
67
|
+
function ensureInteractive() {
|
|
68
|
+
if (!process.stdin.isTTY || !process.stderr.isTTY) {
|
|
69
|
+
throw new CliError("`dx init` must be run interactively");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function showWelcomeBanner() {
|
|
73
|
+
renderRichText([ui.p(ui.success(WELCOME_BANNER))]);
|
|
74
|
+
}
|
|
75
|
+
async function ensureLoggedIn(runtime) {
|
|
76
|
+
renderRichText([ui.h1(`Checking if you are logged in...`), ui.blankLine()]);
|
|
77
|
+
if (runtime) {
|
|
78
|
+
let authInfo = null;
|
|
79
|
+
try {
|
|
80
|
+
authInfo = await getAuthInfo(runtime);
|
|
81
|
+
}
|
|
82
|
+
catch (_error) {
|
|
83
|
+
// Do nothing, we don't mind if this fails
|
|
84
|
+
}
|
|
85
|
+
if (authInfo) {
|
|
86
|
+
renderAuthInfo(authInfo, runtime.token, runtime.baseUrl);
|
|
87
|
+
return runtime;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
renderRichText([ui.p(`You are not logged in yet.`), ui.blankLine()]);
|
|
91
|
+
let parsed = { type: "invalid" };
|
|
92
|
+
while (parsed.type === "invalid") {
|
|
93
|
+
const raw = await input({
|
|
94
|
+
message: "What is your DX hostname? (leave blank for app.getdx.com)",
|
|
95
|
+
});
|
|
96
|
+
parsed = parseHostname(raw);
|
|
97
|
+
if (parsed.type === "invalid") {
|
|
98
|
+
renderRichText([
|
|
99
|
+
ui.p(ui.error(`Could not recognise that hostname. Please try again.`)),
|
|
100
|
+
]);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
switch (parsed.type) {
|
|
104
|
+
case "cloud":
|
|
105
|
+
return await attemptLogin("https://api.getdx.com", "https://app.getdx.com");
|
|
106
|
+
case "dedicated": {
|
|
107
|
+
const { accountName } = parsed;
|
|
108
|
+
return await attemptLogin(`https://api.${accountName}.getdx.io`, `https://${accountName}.getdx.io`);
|
|
109
|
+
}
|
|
110
|
+
case "managed": {
|
|
111
|
+
const apiBaseUrl = await input({
|
|
112
|
+
message: "What is your API base URL?",
|
|
113
|
+
});
|
|
114
|
+
if (!apiBaseUrl) {
|
|
115
|
+
throw new CliError("API base URL is required");
|
|
116
|
+
}
|
|
117
|
+
return await attemptLogin(apiBaseUrl, parsed.uiBaseUrl);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async function attemptLogin(apiBaseUrl, _uiBaseUrl) {
|
|
122
|
+
const token = await password({
|
|
123
|
+
message: "Paste your account web API token here:",
|
|
124
|
+
mask: true,
|
|
125
|
+
});
|
|
126
|
+
if (!token) {
|
|
127
|
+
throw new CliError("Account web API token is required");
|
|
128
|
+
}
|
|
129
|
+
const context = createEmptyContext();
|
|
130
|
+
const runtime = buildRuntime(context, {
|
|
131
|
+
baseUrl: apiBaseUrl,
|
|
132
|
+
token,
|
|
133
|
+
});
|
|
134
|
+
renderRichText([ui.blankLine(), ui.p(`Attempting login...`), ui.blankLine()]);
|
|
135
|
+
const response = await getAuthInfo(runtime);
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
throw new CliError(`Login failed`);
|
|
138
|
+
}
|
|
139
|
+
persistBaseUrl(apiBaseUrl);
|
|
140
|
+
setToken(apiBaseUrl, token);
|
|
141
|
+
renderAuthInfo(response, token, apiBaseUrl);
|
|
142
|
+
return runtime;
|
|
143
|
+
}
|
|
144
|
+
async function optionallySetupSkill(runtime) {
|
|
145
|
+
renderRichText([
|
|
146
|
+
ui.blankLine(),
|
|
147
|
+
ui.h1(`Checking for the DX skill...`),
|
|
148
|
+
ui.blankLine(),
|
|
149
|
+
]);
|
|
150
|
+
const skillPath = join(homedir(), ".agents", "skills", "dx-cli");
|
|
151
|
+
const skillInstalled = await access(skillPath)
|
|
152
|
+
.then(() => true)
|
|
153
|
+
.catch(() => false);
|
|
154
|
+
if (skillInstalled) {
|
|
155
|
+
renderRichText([ui.p(`The DX skill is already installed.`)]);
|
|
156
|
+
runtime.logger.debug("To update: npx skills update dx-cli --global");
|
|
157
|
+
runtime.logger.debug("To uninstall: npx skills remove dx-cli --global");
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
renderRichText([
|
|
161
|
+
ui.p(`The DX skill is not installed. Would you like to install it?`),
|
|
162
|
+
ui.p("This will run the following command:", false),
|
|
163
|
+
ui.codeBlock("npx --yes -- skills@latest add get-dx/dx-cli --global"),
|
|
164
|
+
ui.blankLine(),
|
|
165
|
+
]);
|
|
166
|
+
const setupSkill = await confirm({
|
|
167
|
+
message: "Continue?",
|
|
168
|
+
default: true,
|
|
169
|
+
});
|
|
170
|
+
if (!setupSkill) {
|
|
171
|
+
renderRichText([
|
|
172
|
+
ui.p(`Skipping the DX skill setup. You can always do this later.`),
|
|
173
|
+
]);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
renderRichText([ui.p(`Setting up the DX skill...`)]);
|
|
177
|
+
const result = await execa({
|
|
178
|
+
stdout: "inherit",
|
|
179
|
+
stderr: "inherit",
|
|
180
|
+
stdin: "inherit",
|
|
181
|
+
}) `npx --yes -- skills@latest add get-dx/dx-cli --global`;
|
|
182
|
+
runtime.logger.debug("To update: npx skills update dx-cli --global");
|
|
183
|
+
runtime.logger.debug("To uninstall: npx skills remove dx-cli --global");
|
|
184
|
+
if (result.exitCode !== 0) {
|
|
185
|
+
throw new CliError(`Failed to setup the DX skill: ${result.stderr}`);
|
|
186
|
+
}
|
|
187
|
+
renderRichText([ui.p(`DX skill setup successful.`)]);
|
|
188
|
+
}
|
|
189
|
+
function createEmptyContext() {
|
|
190
|
+
return {
|
|
191
|
+
json: false,
|
|
192
|
+
};
|
|
193
|
+
}
|
package/dist/logger.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import pc from "picocolors";
|
|
1
2
|
const LOG_LEVELS = ["debug", "info", "warn", "error"];
|
|
2
3
|
const LOG_LEVEL_PRIORITY = {
|
|
3
4
|
debug: 10,
|
|
@@ -66,5 +67,17 @@ function writeLog(configuredLevel, options, level, message, fields) {
|
|
|
66
67
|
const renderedFields = entry.fields && Object.keys(entry.fields).length > 0
|
|
67
68
|
? ` ${JSON.stringify(entry.fields)}`
|
|
68
69
|
: "";
|
|
69
|
-
process.stderr.write(`${entry.time} ${entry.level
|
|
70
|
+
process.stderr.write(`${pc.dim(entry.time)} ${colorizeLevel(entry.level)} ${entry.message}${pc.dim(renderedFields)}\n`);
|
|
71
|
+
}
|
|
72
|
+
function colorizeLevel(level) {
|
|
73
|
+
switch (level) {
|
|
74
|
+
case "debug":
|
|
75
|
+
return pc.blue("DEBUG");
|
|
76
|
+
case "info":
|
|
77
|
+
return pc.bold("INFO");
|
|
78
|
+
case "warn":
|
|
79
|
+
return pc.yellow("WARN");
|
|
80
|
+
case "error":
|
|
81
|
+
return pc.red("ERROR");
|
|
82
|
+
}
|
|
70
83
|
}
|
package/dist/runtime.js
CHANGED
|
@@ -6,9 +6,9 @@ import cliPackage from "../package.json" with { type: "json" };
|
|
|
6
6
|
export function buildLogger(context) {
|
|
7
7
|
return createLogger({ json: context.json || !process.stderr.isTTY });
|
|
8
8
|
}
|
|
9
|
-
export function buildRuntime(context) {
|
|
10
|
-
const baseUrl = resolveBaseUrl();
|
|
11
|
-
const token = getToken(baseUrl);
|
|
9
|
+
export function buildRuntime(context, overrides) {
|
|
10
|
+
const baseUrl = overrides?.baseUrl ?? resolveBaseUrl();
|
|
11
|
+
const token = overrides?.token ?? getToken(baseUrl);
|
|
12
12
|
if (!token) {
|
|
13
13
|
throw new CliError("No API token configured. Run `dx auth login --token <token>` or set DX_API_TOKEN.");
|
|
14
14
|
}
|
|
@@ -20,3 +20,17 @@ export function buildRuntime(context) {
|
|
|
20
20
|
logger: buildLogger(context),
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
+
export function buildRuntimeSafe(context, overrides) {
|
|
24
|
+
const baseUrl = overrides?.baseUrl ?? resolveBaseUrl();
|
|
25
|
+
const token = overrides?.token ?? getToken(baseUrl);
|
|
26
|
+
if (!token) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
baseUrl,
|
|
31
|
+
token,
|
|
32
|
+
context,
|
|
33
|
+
version: cliPackage.version,
|
|
34
|
+
logger: buildLogger(context),
|
|
35
|
+
};
|
|
36
|
+
}
|
package/dist/secrets.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execaSync } from "execa";
|
|
2
2
|
import { CliError } from "./errors.js";
|
|
3
3
|
const SERVICE = "dx-cli";
|
|
4
4
|
export function getToken(baseUrl) {
|
|
@@ -26,14 +26,21 @@ function macosSecretStore() {
|
|
|
26
26
|
return {
|
|
27
27
|
get(baseUrl) {
|
|
28
28
|
try {
|
|
29
|
-
return
|
|
29
|
+
return execaSync("security", [
|
|
30
|
+
"find-generic-password",
|
|
31
|
+
"-s",
|
|
32
|
+
SERVICE,
|
|
33
|
+
"-a",
|
|
34
|
+
baseUrl,
|
|
35
|
+
"-w",
|
|
36
|
+
]).stdout.trim();
|
|
30
37
|
}
|
|
31
38
|
catch {
|
|
32
39
|
return null;
|
|
33
40
|
}
|
|
34
41
|
},
|
|
35
42
|
set(baseUrl, token) {
|
|
36
|
-
|
|
43
|
+
execaSync("security", [
|
|
37
44
|
"add-generic-password",
|
|
38
45
|
"-U",
|
|
39
46
|
"-s",
|
|
@@ -46,7 +53,7 @@ function macosSecretStore() {
|
|
|
46
53
|
},
|
|
47
54
|
delete(baseUrl) {
|
|
48
55
|
try {
|
|
49
|
-
|
|
56
|
+
execaSync("security", [
|
|
50
57
|
"delete-generic-password",
|
|
51
58
|
"-s",
|
|
52
59
|
SERVICE,
|
|
@@ -64,14 +71,20 @@ function linuxSecretStore() {
|
|
|
64
71
|
return {
|
|
65
72
|
get(baseUrl) {
|
|
66
73
|
try {
|
|
67
|
-
return
|
|
74
|
+
return execaSync("secret-tool", [
|
|
75
|
+
"lookup",
|
|
76
|
+
"service",
|
|
77
|
+
SERVICE,
|
|
78
|
+
"account",
|
|
79
|
+
baseUrl,
|
|
80
|
+
]).stdout.trim();
|
|
68
81
|
}
|
|
69
82
|
catch {
|
|
70
83
|
return null;
|
|
71
84
|
}
|
|
72
85
|
},
|
|
73
86
|
set(baseUrl, token) {
|
|
74
|
-
|
|
87
|
+
execaSync("secret-tool", ["store", "--label=dx-cli", "service", SERVICE, "account", baseUrl], { input: token });
|
|
75
88
|
},
|
|
76
89
|
delete(_baseUrl) {
|
|
77
90
|
throw new CliError("Linux token deletion is not supported by secret-tool in this implementation");
|
package/dist/ui/blocks.js
CHANGED
|
@@ -82,6 +82,16 @@ export class DescriptionListItemContainer extends AbstractBlock {
|
|
|
82
82
|
this.options = options;
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
+
export class CodeBlock extends AbstractBlock {
|
|
86
|
+
text;
|
|
87
|
+
constructor(text) {
|
|
88
|
+
super();
|
|
89
|
+
this.text = text;
|
|
90
|
+
}
|
|
91
|
+
render() {
|
|
92
|
+
return pc.cyan(this.text) + "\n";
|
|
93
|
+
}
|
|
94
|
+
}
|
|
85
95
|
export class JsonBlock extends AbstractBlock {
|
|
86
96
|
value;
|
|
87
97
|
constructor(value) {
|
package/dist/ui.js
CHANGED
|
@@ -2,7 +2,7 @@ import dayjs from "dayjs";
|
|
|
2
2
|
import relativeTimePlugin from "dayjs/plugin/relativeTime.js";
|
|
3
3
|
import pc from "picocolors";
|
|
4
4
|
export { GLYPHS } from "./ui/glyphs.js";
|
|
5
|
-
import { DescriptionListBlock, DescriptionListItemContainer, HeadingBlock, JsonBlock, ListItemContainer, ParagraphBlock, UnorderedListBlock, } from "./ui/blocks.js";
|
|
5
|
+
import { DescriptionListBlock, DescriptionListItemContainer, HeadingBlock, JsonBlock, ListItemContainer, ParagraphBlock, UnorderedListBlock, CodeBlock, } from "./ui/blocks.js";
|
|
6
6
|
dayjs.extend(relativeTimePlugin);
|
|
7
7
|
// Block-level elements
|
|
8
8
|
export function h1(text) {
|
|
@@ -49,9 +49,15 @@ export function dli(term, detailItems) {
|
|
|
49
49
|
return new DescriptionListItemContainer(term, detailItems);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
export function codeBlock(text) {
|
|
53
|
+
return new CodeBlock(text);
|
|
54
|
+
}
|
|
52
55
|
export function json(value) {
|
|
53
56
|
return new JsonBlock(value);
|
|
54
57
|
}
|
|
58
|
+
export function blankLine() {
|
|
59
|
+
return new ParagraphBlock("", false);
|
|
60
|
+
}
|
|
55
61
|
// Inline elements
|
|
56
62
|
export function bold(text) {
|
|
57
63
|
return pc.bold(text);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@get-dx/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "DX CLI",
|
|
5
5
|
"author": "DX <developers@getdx.com>",
|
|
6
6
|
"bugs": {
|
|
@@ -38,8 +38,10 @@
|
|
|
38
38
|
"typecheck": "tsc --noEmit"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
+
"@inquirer/prompts": "^8.4.1",
|
|
41
42
|
"commander": "^14.0.1",
|
|
42
43
|
"dayjs": "^1.11.20",
|
|
44
|
+
"execa": "^9.6.1",
|
|
43
45
|
"picocolors": "^1.1.1",
|
|
44
46
|
"yaml": "^2.8.3"
|
|
45
47
|
},
|