@get-dx/cli 0.1.6 → 0.1.7
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 +20 -0
- package/dist/commands/init.js +8 -14
- package/dist/commands/teams.js +106 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,6 +18,26 @@ Interactively login and install the AI agent skill:
|
|
|
18
18
|
dx init
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
The CLI uses `app.getdx.com` as the hostname and `api.getdx.com` as the api base url by default.
|
|
22
|
+
|
|
23
|
+
### Dedicated or managed deployments
|
|
24
|
+
|
|
25
|
+
If your organization is on a dedicated or managed DX deployment, pass your custom hostname at init time:
|
|
26
|
+
|
|
27
|
+
```shell
|
|
28
|
+
# Flag (dedicated instance at mycompany.getdx.io)
|
|
29
|
+
dx init --host mycompany.getdx.io
|
|
30
|
+
|
|
31
|
+
# Env var — useful for CI or scripted setups
|
|
32
|
+
DX_HOST=mycompany.getdx.io dx init
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The `DX_HOST` and `--host` values accept:
|
|
36
|
+
|
|
37
|
+
- `app.getdx.com` — DX Cloud (default)
|
|
38
|
+
- `<account>.getdx.io` — DX dedicated (derives `https://api.<account>.getdx.io` automatically)
|
|
39
|
+
- Any custom domain — you will be prompted for the separate API base URL
|
|
40
|
+
|
|
21
41
|
Show help text:
|
|
22
42
|
|
|
23
43
|
```shell
|
package/dist/commands/init.js
CHANGED
|
@@ -50,12 +50,14 @@ export function initCommand() {
|
|
|
50
50
|
const init = new Command()
|
|
51
51
|
.name("init")
|
|
52
52
|
.description("Initialize the DX CLI")
|
|
53
|
-
.
|
|
53
|
+
.option("--host <hostname>", "DX hostname for dedicated or managed deployments (e.g. mycompany.getdx.io)")
|
|
54
|
+
.action(wrapAction(async (commandOptions, command) => {
|
|
54
55
|
const context = getContext(command);
|
|
55
56
|
let runtime = buildRuntimeSafe(context);
|
|
56
57
|
ensureInteractive();
|
|
57
58
|
showWelcomeBanner();
|
|
58
|
-
|
|
59
|
+
const host = commandOptions.host || process.env.DX_HOST;
|
|
60
|
+
runtime = await ensureLoggedIn(runtime, host);
|
|
59
61
|
await optionallySetupSkill(runtime);
|
|
60
62
|
renderRichText([
|
|
61
63
|
ui.blankLine(),
|
|
@@ -72,7 +74,7 @@ function ensureInteractive() {
|
|
|
72
74
|
function showWelcomeBanner() {
|
|
73
75
|
renderRichText([ui.p(ui.success(WELCOME_BANNER))]);
|
|
74
76
|
}
|
|
75
|
-
async function ensureLoggedIn(runtime) {
|
|
77
|
+
async function ensureLoggedIn(runtime, host) {
|
|
76
78
|
renderRichText([ui.h1(`Checking if you are logged in...`), ui.blankLine()]);
|
|
77
79
|
if (runtime) {
|
|
78
80
|
let authInfo = null;
|
|
@@ -88,17 +90,9 @@ async function ensureLoggedIn(runtime) {
|
|
|
88
90
|
}
|
|
89
91
|
}
|
|
90
92
|
renderRichText([ui.p(`You are not logged in yet.`), ui.blankLine()]);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
}
|
|
93
|
+
const parsed = host ? parseHostname(host) : { type: "cloud" };
|
|
94
|
+
if (parsed.type === "invalid") {
|
|
95
|
+
throw new CliError(`Could not recognize hostname "${host}". Expected app.getdx.com, <account>.getdx.io, or a custom domain.`);
|
|
102
96
|
}
|
|
103
97
|
switch (parsed.type) {
|
|
104
98
|
case "cloud":
|
package/dist/commands/teams.js
CHANGED
|
@@ -32,6 +32,36 @@ export function teamsCommand() {
|
|
|
32
32
|
renderTeamByMembers(response.team);
|
|
33
33
|
}
|
|
34
34
|
}));
|
|
35
|
+
teams
|
|
36
|
+
.command("info")
|
|
37
|
+
.description("Retrieve details for an individual team")
|
|
38
|
+
.option("--team-id <id>", "DX team ID")
|
|
39
|
+
.option("--reference-id <id>", "Team ID that is internal to your organization")
|
|
40
|
+
.addHelpText("afterAll", createExampleText([
|
|
41
|
+
{
|
|
42
|
+
label: "Fetch info for a team by DX team ID",
|
|
43
|
+
command: "dx teams info --team-id NTA4Nzc",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
label: "Fetch info for a team by your organization's reference ID",
|
|
47
|
+
command: "dx teams info --reference-id 06BEC4E0-5A61-354E-08A6-C39D756058AB",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
label: "Fetch team info and return JSON",
|
|
51
|
+
command: "dx --json teams info --team-id NTA4Nzc",
|
|
52
|
+
},
|
|
53
|
+
]))
|
|
54
|
+
.action(wrapAction(async (options, command) => {
|
|
55
|
+
const lookup = parseTeamInfoLookup(options);
|
|
56
|
+
const runtime = buildRuntime(getContext(command));
|
|
57
|
+
const response = await getTeamInfo(runtime, lookup);
|
|
58
|
+
if (runtime.context.json) {
|
|
59
|
+
renderJson(response);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
renderTeamInfo(response.team);
|
|
63
|
+
}
|
|
64
|
+
}));
|
|
35
65
|
teams
|
|
36
66
|
.command("list")
|
|
37
67
|
.description("List all teams in DX")
|
|
@@ -72,6 +102,13 @@ async function findTeamByMembers(runtime, teamEmails) {
|
|
|
72
102
|
throw buildFindByMembersError(error, teamEmails);
|
|
73
103
|
}
|
|
74
104
|
}
|
|
105
|
+
async function getTeamInfo(runtime, lookup) {
|
|
106
|
+
const response = await request(runtime, "/teams.info", {
|
|
107
|
+
method: "GET",
|
|
108
|
+
query: lookup,
|
|
109
|
+
});
|
|
110
|
+
return response.body;
|
|
111
|
+
}
|
|
75
112
|
async function listTeams(runtime) {
|
|
76
113
|
const response = await request(runtime, "/teams.list", {
|
|
77
114
|
method: "GET",
|
|
@@ -93,6 +130,31 @@ function renderTeamByMembers(team) {
|
|
|
93
130
|
];
|
|
94
131
|
renderRichText(blocks);
|
|
95
132
|
}
|
|
133
|
+
function renderTeamInfo(team) {
|
|
134
|
+
const blocks = [ui.h1("Team Information")];
|
|
135
|
+
const heading = formatTeamHeading(team);
|
|
136
|
+
if (heading) {
|
|
137
|
+
blocks.push(ui.h2(heading));
|
|
138
|
+
}
|
|
139
|
+
const details = [];
|
|
140
|
+
pushDetail(details, "ID", formatText(team.id), team.id !== undefined);
|
|
141
|
+
pushDetail(details, "Name", team.name ?? ui.dim("(None)"), team.name !== undefined);
|
|
142
|
+
pushDetail(details, "Reference ID", formatText(team.reference_id), team.reference_id !== undefined);
|
|
143
|
+
pushDetail(details, "Parent ID", formatText(team.parent_id), team.parent_id !== undefined);
|
|
144
|
+
pushDetail(details, "Manager ID", formatText(team.manager_id), team.manager_id !== undefined);
|
|
145
|
+
pushDetail(details, "Parent team", typeof team.parent === "boolean" ? (team.parent ? "Yes" : "No") : "", team.parent !== undefined);
|
|
146
|
+
pushDetail(details, "Last changed", team.last_changed_at
|
|
147
|
+
? ui.timestampSummary(team.last_changed_at)
|
|
148
|
+
: ui.dim("(None)"), team.last_changed_at !== undefined);
|
|
149
|
+
pushDetail(details, "Lead", formatTeamMember(team.lead), team.lead !== undefined);
|
|
150
|
+
pushDetail(details, "Manager", formatTeamMember(team.manager), team.manager !== undefined);
|
|
151
|
+
if (details.length > 0) {
|
|
152
|
+
blocks.push(ui.dl(details, { termWidth: 14 }));
|
|
153
|
+
}
|
|
154
|
+
blocks.push(ui.h2("Contributors"));
|
|
155
|
+
blocks.push(formatTeamMemberList(team.contributors));
|
|
156
|
+
renderRichText(blocks);
|
|
157
|
+
}
|
|
96
158
|
function renderTeams(teams) {
|
|
97
159
|
const blocks = [ui.h1("Teams")];
|
|
98
160
|
blocks.push(ui.p(`Displaying ${ui.bold(teams.length.toString())} teams.`));
|
|
@@ -123,12 +185,56 @@ function renderTeams(teams) {
|
|
|
123
185
|
function formatText(value) {
|
|
124
186
|
return value ? ui.code(value) : ui.dim("(None)");
|
|
125
187
|
}
|
|
188
|
+
function formatTeamHeading(team) {
|
|
189
|
+
if (team.name && team.id) {
|
|
190
|
+
return `${team.name} (${ui.code(team.id)})`;
|
|
191
|
+
}
|
|
192
|
+
if (team.name) {
|
|
193
|
+
return team.name;
|
|
194
|
+
}
|
|
195
|
+
if (team.id) {
|
|
196
|
+
return ui.code(team.id);
|
|
197
|
+
}
|
|
198
|
+
if (team.reference_id) {
|
|
199
|
+
return ui.code(team.reference_id);
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
126
203
|
function formatTeamMember(member) {
|
|
127
204
|
if (!member) {
|
|
128
205
|
return ui.dim("(None)");
|
|
129
206
|
}
|
|
130
207
|
return `${member.name} <${member.email}> (${ui.code(member.id)})`;
|
|
131
208
|
}
|
|
209
|
+
function formatTeamMemberList(members) {
|
|
210
|
+
if (!members || members.length === 0) {
|
|
211
|
+
return ui.p(ui.dim("(None)"));
|
|
212
|
+
}
|
|
213
|
+
return ui.ul(members.map((member) => ui.li(formatTeamMember(member))));
|
|
214
|
+
}
|
|
215
|
+
function pushDetail(details, term, value, shouldInclude) {
|
|
216
|
+
if (shouldInclude) {
|
|
217
|
+
details.push(ui.dli(term, value));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function parseTeamInfoLookup(options) {
|
|
221
|
+
const teamId = parseOptionalTextOption(options.teamId);
|
|
222
|
+
const referenceId = parseOptionalTextOption(options.referenceId);
|
|
223
|
+
if (!teamId && !referenceId) {
|
|
224
|
+
throw new CliError("One of --team-id or --reference-id is required", EXIT_CODES.ARGUMENT_ERROR);
|
|
225
|
+
}
|
|
226
|
+
if (teamId && referenceId) {
|
|
227
|
+
throw new CliError("--team-id and --reference-id are mutually exclusive", EXIT_CODES.ARGUMENT_ERROR);
|
|
228
|
+
}
|
|
229
|
+
return teamId ? { team_id: teamId } : { reference_id: referenceId };
|
|
230
|
+
}
|
|
231
|
+
function parseOptionalTextOption(value) {
|
|
232
|
+
if (typeof value !== "string") {
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
235
|
+
const trimmed = value.trim();
|
|
236
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
237
|
+
}
|
|
132
238
|
function parseTeamEmails(value) {
|
|
133
239
|
if (typeof value !== "string") {
|
|
134
240
|
throw new CliError("--team-emails is required", EXIT_CODES.ARGUMENT_ERROR);
|