@get-dx/cli 0.1.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 +44 -0
- package/dist/cli.js +50 -0
- package/dist/commandHelpers.js +110 -0
- package/dist/commands/auth.js +70 -0
- package/dist/commands/authRendering.js +40 -0
- package/dist/commands/catalog/entities.js +596 -0
- package/dist/commands/catalog/entitiesRendering.js +286 -0
- package/dist/commands/catalog/entity-type-blank-template.yaml +51 -0
- package/dist/commands/catalog/entityTypes.js +402 -0
- package/dist/commands/catalog/entityTypesRendering.js +159 -0
- package/dist/commands/catalog.js +11 -0
- package/dist/commands/scorecard-blank-template.yaml +82 -0
- package/dist/commands/scorecards.js +480 -0
- package/dist/commands/scorecardsRendering.js +219 -0
- package/dist/commands/studio/query.js +270 -0
- package/dist/commands/studio/queryRendering.js +99 -0
- package/dist/commands/studio.js +9 -0
- package/dist/config.js +38 -0
- package/dist/errors.js +24 -0
- package/dist/http.js +128 -0
- package/dist/index.js +3 -0
- package/dist/logger.js +70 -0
- package/dist/renderers.js +29 -0
- package/dist/runtime.js +22 -0
- package/dist/secrets.js +80 -0
- package/dist/types.js +1 -0
- package/dist/ui/blocks.js +94 -0
- package/dist/ui/glyphs.js +5 -0
- package/dist/ui.js +109 -0
- package/package.json +56 -0
package/dist/secrets.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { CliError } from "./errors.js";
|
|
3
|
+
const SERVICE = "dx-cli";
|
|
4
|
+
export function getToken(baseUrl) {
|
|
5
|
+
if (process.env.DX_API_TOKEN) {
|
|
6
|
+
return process.env.DX_API_TOKEN;
|
|
7
|
+
}
|
|
8
|
+
return getSecretStore().get(baseUrl);
|
|
9
|
+
}
|
|
10
|
+
export function setToken(baseUrl, token) {
|
|
11
|
+
getSecretStore().set(baseUrl, token);
|
|
12
|
+
}
|
|
13
|
+
export function deleteToken(baseUrl) {
|
|
14
|
+
getSecretStore().delete(baseUrl);
|
|
15
|
+
}
|
|
16
|
+
function getSecretStore(platform = process.platform) {
|
|
17
|
+
if (platform === "darwin") {
|
|
18
|
+
return macosSecretStore();
|
|
19
|
+
}
|
|
20
|
+
if (platform === "linux") {
|
|
21
|
+
return linuxSecretStore();
|
|
22
|
+
}
|
|
23
|
+
throw new CliError(`Unsupported platform for secure token storage: ${platform}`);
|
|
24
|
+
}
|
|
25
|
+
function macosSecretStore() {
|
|
26
|
+
return {
|
|
27
|
+
get(baseUrl) {
|
|
28
|
+
try {
|
|
29
|
+
return execFileSync("security", ["find-generic-password", "-s", SERVICE, "-a", baseUrl, "-w"], { encoding: "utf8" }).trim();
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
set(baseUrl, token) {
|
|
36
|
+
execFileSync("security", [
|
|
37
|
+
"add-generic-password",
|
|
38
|
+
"-U",
|
|
39
|
+
"-s",
|
|
40
|
+
SERVICE,
|
|
41
|
+
"-a",
|
|
42
|
+
baseUrl,
|
|
43
|
+
"-w",
|
|
44
|
+
token,
|
|
45
|
+
]);
|
|
46
|
+
},
|
|
47
|
+
delete(baseUrl) {
|
|
48
|
+
try {
|
|
49
|
+
execFileSync("security", [
|
|
50
|
+
"delete-generic-password",
|
|
51
|
+
"-s",
|
|
52
|
+
SERVICE,
|
|
53
|
+
"-a",
|
|
54
|
+
baseUrl,
|
|
55
|
+
]);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// No-op if missing.
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function linuxSecretStore() {
|
|
64
|
+
return {
|
|
65
|
+
get(baseUrl) {
|
|
66
|
+
try {
|
|
67
|
+
return execFileSync("secret-tool", ["lookup", "service", SERVICE, "account", baseUrl], { encoding: "utf8" }).trim();
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
set(baseUrl, token) {
|
|
74
|
+
execFileSync("secret-tool", ["store", "--label=dx-cli", "service", SERVICE, "account", baseUrl], { input: token });
|
|
75
|
+
},
|
|
76
|
+
delete(_baseUrl) {
|
|
77
|
+
throw new CliError("Linux token deletion is not supported by secret-tool in this implementation");
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import pc from "picocolors";
|
|
2
|
+
import { padEnd, indent } from "../ui.js";
|
|
3
|
+
export class AbstractBlock {
|
|
4
|
+
}
|
|
5
|
+
export class HeadingBlock extends AbstractBlock {
|
|
6
|
+
text;
|
|
7
|
+
level;
|
|
8
|
+
constructor(text, level) {
|
|
9
|
+
super();
|
|
10
|
+
this.text = text;
|
|
11
|
+
this.level = level;
|
|
12
|
+
}
|
|
13
|
+
render() {
|
|
14
|
+
return pc.bold(pc.blue("#".repeat(this.level) + " " + this.text)) + "\n";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class ParagraphBlock extends AbstractBlock {
|
|
18
|
+
text;
|
|
19
|
+
finalSpace;
|
|
20
|
+
constructor(text, finalSpace = true) {
|
|
21
|
+
super();
|
|
22
|
+
this.text = text;
|
|
23
|
+
this.finalSpace = finalSpace;
|
|
24
|
+
}
|
|
25
|
+
render() {
|
|
26
|
+
return this.text + (this.finalSpace ? "\n" : "");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export class UnorderedListBlock extends AbstractBlock {
|
|
30
|
+
items;
|
|
31
|
+
constructor(items) {
|
|
32
|
+
super();
|
|
33
|
+
this.items = items;
|
|
34
|
+
}
|
|
35
|
+
render() {
|
|
36
|
+
const renderedItems = this.items.map((item) => indent(item.render(), 1));
|
|
37
|
+
return (renderedItems
|
|
38
|
+
.map((renderedItem) => "- " + renderedItem.substring(2))
|
|
39
|
+
.join("\n") + "\n");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export class ListItemContainer extends AbstractBlock {
|
|
43
|
+
items;
|
|
44
|
+
constructor(items) {
|
|
45
|
+
super();
|
|
46
|
+
this.items = items;
|
|
47
|
+
}
|
|
48
|
+
render() {
|
|
49
|
+
return this.items.map((item) => item.render()).join("\n");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export class DescriptionListBlock extends AbstractBlock {
|
|
53
|
+
items;
|
|
54
|
+
options;
|
|
55
|
+
constructor(items, options) {
|
|
56
|
+
super();
|
|
57
|
+
this.items = items;
|
|
58
|
+
this.options = options;
|
|
59
|
+
}
|
|
60
|
+
render() {
|
|
61
|
+
this.items.forEach((item) => item.setOptions(this.options));
|
|
62
|
+
return this.items.map((item) => item.render()).join("\n") + "\n";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export class DescriptionListItemContainer extends AbstractBlock {
|
|
66
|
+
term;
|
|
67
|
+
detailItems;
|
|
68
|
+
options;
|
|
69
|
+
constructor(term, detailItems) {
|
|
70
|
+
super();
|
|
71
|
+
this.term = term;
|
|
72
|
+
this.detailItems = detailItems;
|
|
73
|
+
}
|
|
74
|
+
render() {
|
|
75
|
+
if (!this.options) {
|
|
76
|
+
throw new Error("Options are not set");
|
|
77
|
+
}
|
|
78
|
+
return (pc.bold(padEnd(`${this.term}:`, this.options.termWidth)) +
|
|
79
|
+
this.detailItems.map((item) => item.render()).join("\n"));
|
|
80
|
+
}
|
|
81
|
+
setOptions(options) {
|
|
82
|
+
this.options = options;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export class JsonBlock extends AbstractBlock {
|
|
86
|
+
value;
|
|
87
|
+
constructor(value) {
|
|
88
|
+
super();
|
|
89
|
+
this.value = value;
|
|
90
|
+
}
|
|
91
|
+
render() {
|
|
92
|
+
return pc.cyan(JSON.stringify(this.value, null, 2)) + "\n";
|
|
93
|
+
}
|
|
94
|
+
}
|
package/dist/ui.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
import relativeTimePlugin from "dayjs/plugin/relativeTime.js";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
export { GLYPHS } from "./ui/glyphs.js";
|
|
5
|
+
import { DescriptionListBlock, DescriptionListItemContainer, HeadingBlock, JsonBlock, ListItemContainer, ParagraphBlock, UnorderedListBlock, } from "./ui/blocks.js";
|
|
6
|
+
dayjs.extend(relativeTimePlugin);
|
|
7
|
+
// Block-level elements
|
|
8
|
+
export function h1(text) {
|
|
9
|
+
return new HeadingBlock(text, 1);
|
|
10
|
+
}
|
|
11
|
+
export function h2(text) {
|
|
12
|
+
return new HeadingBlock(text, 2);
|
|
13
|
+
}
|
|
14
|
+
export function h3(text) {
|
|
15
|
+
return new HeadingBlock(text, 3);
|
|
16
|
+
}
|
|
17
|
+
export function p(text, finalSpace = true) {
|
|
18
|
+
return new ParagraphBlock(text, finalSpace);
|
|
19
|
+
}
|
|
20
|
+
export function ul(items) {
|
|
21
|
+
return new UnorderedListBlock(items);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Create a list item. Supports both simple text and multi-block content.
|
|
25
|
+
*/
|
|
26
|
+
export function li(items) {
|
|
27
|
+
if (typeof items === "string") {
|
|
28
|
+
const wrapper = p(items, false);
|
|
29
|
+
return new ListItemContainer([wrapper]);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
return new ListItemContainer(items);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function dl(items, options) {
|
|
36
|
+
return new DescriptionListBlock(items, options);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Create an item for a description list: a combination of `<dt>` and `<dd>` elements.
|
|
40
|
+
*
|
|
41
|
+
* Supports both simple text and multi-block content.
|
|
42
|
+
*/
|
|
43
|
+
export function dli(term, detailItems) {
|
|
44
|
+
if (typeof detailItems === "string") {
|
|
45
|
+
const wrapper = p(detailItems, false);
|
|
46
|
+
return new DescriptionListItemContainer(term, [wrapper]);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
return new DescriptionListItemContainer(term, detailItems);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function json(value) {
|
|
53
|
+
return new JsonBlock(value);
|
|
54
|
+
}
|
|
55
|
+
// Inline elements
|
|
56
|
+
export function bold(text) {
|
|
57
|
+
return pc.bold(text);
|
|
58
|
+
}
|
|
59
|
+
export function dim(text) {
|
|
60
|
+
return pc.dim(text);
|
|
61
|
+
}
|
|
62
|
+
export function code(text) {
|
|
63
|
+
return pc.cyan(text);
|
|
64
|
+
}
|
|
65
|
+
export function link(url) {
|
|
66
|
+
return pc.magenta(url);
|
|
67
|
+
}
|
|
68
|
+
export function success(text) {
|
|
69
|
+
return pc.green(text);
|
|
70
|
+
}
|
|
71
|
+
export function warning(text) {
|
|
72
|
+
return pc.yellow(text);
|
|
73
|
+
}
|
|
74
|
+
export function error(text) {
|
|
75
|
+
return pc.red(text);
|
|
76
|
+
}
|
|
77
|
+
export function padEnd(text, width) {
|
|
78
|
+
if (text.length >= width) {
|
|
79
|
+
return text;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
return text + " ".repeat(width - text.length);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Prints relative time, then a dim timestamp
|
|
87
|
+
*
|
|
88
|
+
* Example: "1 day ago (2026-01-01:00:00:00Z)"
|
|
89
|
+
*/
|
|
90
|
+
export function timestampSummary(timestamp) {
|
|
91
|
+
const ts = dayjs(timestamp);
|
|
92
|
+
return `${ts.fromNow()} ${dim(`(${timestamp})`)}`;
|
|
93
|
+
}
|
|
94
|
+
// Other helpers
|
|
95
|
+
export function indent(text, level) {
|
|
96
|
+
return text
|
|
97
|
+
.split("\n")
|
|
98
|
+
.map((line) => " ".repeat(level) + line)
|
|
99
|
+
.join("\n");
|
|
100
|
+
}
|
|
101
|
+
export function maskToken(token) {
|
|
102
|
+
if (!token) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
if (token.length <= 8) {
|
|
106
|
+
return "*".repeat(token.length);
|
|
107
|
+
}
|
|
108
|
+
return `${token.slice(0, 4)}${"*".repeat(token.length - 8)}${token.slice(-4)}`;
|
|
109
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@get-dx/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "DX CLI",
|
|
5
|
+
"author": "DX <developers@getdx.com>",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/get-dx/dx-cli/issues"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"bin": {
|
|
11
|
+
"dx": "dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://github.com/get-dx/dx-cli#readme",
|
|
17
|
+
"license": "Apache-2.0",
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": "20.x"
|
|
20
|
+
},
|
|
21
|
+
"private": false,
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/get-dx/dx-cli.git",
|
|
28
|
+
"directory": "."
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "./bin/build",
|
|
32
|
+
"clean": "rm -rf dist",
|
|
33
|
+
"lint": "eslint .",
|
|
34
|
+
"format": "prettier --write .",
|
|
35
|
+
"format:check": "prettier --check .",
|
|
36
|
+
"prepack": "pnpm build",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"typecheck": "tsc --noEmit"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"commander": "^14.0.1",
|
|
42
|
+
"dayjs": "^1.11.20",
|
|
43
|
+
"picocolors": "^1.1.1",
|
|
44
|
+
"yaml": "^2.8.3"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@eslint/js": "^10.0.1",
|
|
48
|
+
"@types/node": "^24.5.2",
|
|
49
|
+
"eslint": "^10.1.0",
|
|
50
|
+
"eslint-config-prettier": "^10.1.8",
|
|
51
|
+
"prettier": "3.8.1",
|
|
52
|
+
"typescript": "^5.9.2",
|
|
53
|
+
"typescript-eslint": "^8.58.0",
|
|
54
|
+
"vitest": "^3.2.4"
|
|
55
|
+
}
|
|
56
|
+
}
|