Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d861bbb

Browse files
committedOct 30, 2023
feat(tp): commits got messed up
1 parent bf15bff commit d861bbb

File tree

1 file changed

+527
-301
lines changed

1 file changed

+527
-301
lines changed
 

‎src/tctl.ts

+527-301
Original file line numberDiff line numberDiff line change
@@ -1,185 +1,287 @@
1-
import { type } from "node:os";
2-
31
// Barebones Teleport resource definitions, ensuring type safety and autocomplete
42
type GenericResource = {
3+
kind?: string;
54
metadata: {
65
name: string;
76
description: string;
87
};
98
};
109

11-
type User = object & GenericResource;
10+
type Node = {
11+
spec: {
12+
hostname: string;
13+
};
14+
} & GenericResource;
1215

13-
type Role = object & GenericResource;
16+
enum AccessRequestState {
17+
"NONE",
18+
"PENDING",
19+
"DENIED",
20+
"APPROVED",
21+
}
22+
23+
type AccessRequest = {
24+
spec: {
25+
user: string;
26+
roles: string[];
27+
request_reason: string;
28+
state: number;
29+
};
30+
} & GenericResource;
1431

15-
type App = object & GenericResource;
32+
type User = {
33+
spec: {
34+
created_by: {
35+
time: string;
36+
user: {
37+
name: string;
38+
};
39+
};
40+
status: {
41+
is_locked: boolean;
42+
lock_expires: string;
43+
};
44+
};
45+
} & GenericResource;
1646

17-
type Db = object & GenericResource;
47+
type Lock = {
48+
spec: {
49+
target: {
50+
user: string;
51+
};
52+
created_by: string;
53+
expires: string;
54+
};
55+
} & GenericResource;
1856

19-
type AccessRequest = object & GenericResource;
57+
type Role = object & GenericResource;
2058

2159
type Token = object & GenericResource;
2260

2361
type Bot = object & GenericResource;
2462

25-
type WindowsDesktop = object & GenericResource;
26-
2763
type ACL = object & GenericResource;
2864

2965
type Alert = object & GenericResource;
3066

31-
type Device = object & GenericResource;
32-
33-
type Connector = object & GenericResource;
34-
3567
type Namespace = object & GenericResource;
3668

3769
type Cluster = {
3870
kube_cluster_name: string;
3971
};
4072

41-
const teleportAccountsGenerator: Fig.Generator = {
42-
script: "tctl get users --format json",
43-
postProcess: function (out) {
44-
const users = JSON.parse(out);
45-
46-
return users.map((user: User) => {
47-
return {
48-
name: user.metadata.name,
49-
description: user.metadata.description,
50-
};
51-
});
52-
},
73+
const commaQueryTerm = (curr) => {
74+
return curr.split(",").pop();
5375
};
5476

55-
const teleportKubernetesClustersGenerator: Fig.Generator = {
56-
script: "tsh kube ls --format json",
57-
postProcess: function (out) {
58-
const clusters = JSON.parse(out);
77+
// Prefix is used as sometimes Teleport wants the "type" with the request, e.g. tctl get user/USERNAME (user is the prefix here)
78+
const resourcePostProcesserBuilder = (prefix: string = "") => {
79+
return (out: string, tokens: string[]): Fig.Suggestion[] => {
80+
const resources = JSON.parse(out);
5981

60-
return clusters.map((cluster: Cluster) => {
61-
return {
62-
name: cluster.kube_cluster_name,
63-
description: "Kubernetes cluster connected to Teleport",
64-
};
65-
});
66-
},
67-
};
82+
const postProcesser = resources
83+
.map((resource: GenericResource): Fig.Suggestion => {
84+
if (resource.kind === "node") {
85+
const node = resource as Node;
6886

69-
const commaQueryTerm = (curr) => {
70-
if (curr.includes(",")) {
71-
return curr.slice(curr.lastIndexOf(",") + 1);
72-
} else {
73-
return curr;
74-
}
75-
};
87+
return {
88+
name: `${prefix}${node.spec.hostname}`,
7689

77-
const filterRoles = (currentRoles: string, allRoles: Role[]) => {
78-
let currentRolesString = currentRoles;
90+
// If there is a resource prefix, we do not need to use the UID (name) because its already unique
91+
insertValue: prefix
92+
? `${prefix}${node.spec.hostname}`
93+
: node.metadata.name,
7994

80-
if (currentRoles.includes("=")) {
81-
currentRolesString = currentRoles.split("=")[1];
82-
}
95+
description: "Inserts UUID: " + node.metadata.name,
96+
priority: 65,
97+
};
98+
}
8399

84-
const filterable = currentRolesString.split(",");
100+
if (resource.kind === "lock") {
101+
const lock = resource as Lock;
85102

86-
return allRoles.filter((role: Role) => {
87-
return !filterable.includes(role.metadata.name);
88-
});
103+
return {
104+
name: `${resource.kind}/${lock.metadata.name}`,
105+
description: "Created by: " + lock.spec.created_by,
106+
priority: 65,
107+
};
108+
}
109+
110+
if (resource.kind === "access_request") {
111+
const request = resource as AccessRequest;
112+
113+
return {
114+
name: `${prefix}${AccessRequestState[request.spec.state]} ${
115+
request.spec.user
116+
}`,
117+
insertValue: request.metadata.name,
118+
description: `Requests: ${request.spec.roles.join(", ")}`,
119+
priority: 76,
120+
};
121+
}
122+
123+
if (resource.kind === "user") {
124+
const user = resource as User;
125+
const locked = user.spec.status.is_locked;
126+
const lockedExpires = user.spec.status.lock_expires;
127+
128+
let description = locked
129+
? `User locked until ${lockedExpires}`
130+
: user.metadata.description;
131+
132+
if (!description) {
133+
const created = new Date(
134+
user.spec.created_by.time
135+
).toLocaleDateString();
136+
const creator = user.spec.created_by.user.name;
137+
138+
description = `${creator} created this user on ${created}`;
139+
}
140+
141+
return {
142+
name: `${prefix}${user.metadata.name}`,
143+
icon: locked
144+
? "fig://icon?type=box&=FF0000&badge=🚫"
145+
: "fig://icon?type=box",
146+
description,
147+
priority: 65,
148+
};
149+
}
150+
151+
return {
152+
name: `${prefix}${resource.metadata.name}`,
153+
description: resource.metadata.description,
154+
priority: 65,
155+
};
156+
})
157+
.filter((suggestion: Fig.Suggestion) => {
158+
// The last token always contains the resources
159+
const lastToken = tokens[tokens.length - 1];
160+
161+
// We remove the resources that we are already listing, this wont impact single resource selection options
162+
return !lastToken.includes(suggestion.name.toString());
163+
});
164+
165+
return postProcesser;
166+
};
89167
};
90168

91-
const teleportRolesGenerator: Fig.Generator = {
92-
script: "tctl get roles --format json",
93-
getQueryTerm: commaQueryTerm,
94-
postProcess: function (out, tokens) {
95-
const roles = JSON.parse(out);
169+
const tctlGetGenerator = (
170+
resource: string,
171+
canBeMultiple: boolean = false
172+
): Fig.Generator => {
173+
return {
174+
script: `tctl get ${resource} --format json`,
175+
getQueryTerm: canBeMultiple ? commaQueryTerm : undefined,
176+
postProcess: resourcePostProcesserBuilder(),
177+
};
178+
};
96179

97-
const filteredRoles = filterRoles(tokens[tokens.length - 1], roles);
180+
const tshListGenerator = (resource: string): Fig.Generator => {
181+
return {
182+
script: `tsh ${resource} ls --format json`,
183+
postProcess: resourcePostProcesserBuilder(),
184+
};
185+
};
98186

99-
return filteredRoles.map((role: Role) => {
100-
return {
101-
name: role.metadata.name,
102-
description: role.metadata.description,
103-
};
104-
});
187+
const teleportGenerators: Record<string, Fig.Generator> = {
188+
yamlFiles: {
189+
template: "filepaths",
190+
// Only show YAML files and directories
191+
filterTemplateSuggestions: function (paths) {
192+
return paths.filter(
193+
(file) =>
194+
file.name.endsWith("/") ||
195+
file.name.endsWith(".yaml") ||
196+
file.name.endsWith(".yml")
197+
);
198+
},
105199
},
200+
201+
// Shorthand suggestion generators
202+
role: tctlGetGenerator("roles"),
203+
roles: tctlGetGenerator("roles", true),
204+
user: tctlGetGenerator("user"),
205+
windows_desktop: tctlGetGenerator("windows_desktop"),
206+
node: tctlGetGenerator("node"),
207+
device: tctlGetGenerator("device"),
208+
connector: tctlGetGenerator("connector"),
209+
kube: tshListGenerator("kube"),
210+
apps: tshListGenerator("apps"),
211+
db: tshListGenerator("db"),
212+
request: tshListGenerator("request"),
213+
tokens: tshListGenerator("tokens"),
106214
};
107215

108-
const teleportAppGenerator: Fig.Generator = {
109-
script: "tsh apps ls --format json",
110-
postProcess: function (out) {
111-
const apps = JSON.parse(out);
112-
return apps.map((app: App) => {
113-
return {
114-
name: app.metadata.name,
115-
description: app.metadata.description,
116-
};
117-
});
216+
const teleportOptions: Record<string, Fig.Option> = {
217+
ttl: {
218+
name: "--ttl",
219+
description: "Set the time to live, default is 1h0m0s, maximum is 48h0m0s",
220+
args: {
221+
name: "10h10m10s",
222+
description: "Relative duration like 5s, can be chained like 1h10m10s",
223+
},
118224
},
119-
};
120225

121-
const teleportDatabaseGenerator: Fig.Generator = {
122-
script: "tsh db ls --format json",
123-
postProcess: function (out) {
124-
const dbs = JSON.parse(out);
125-
return dbs.map((db: Db) => {
126-
return {
127-
name: db.metadata.name,
128-
description: db.metadata.description,
129-
};
130-
});
226+
format: {
227+
name: "--format",
228+
description: "Output format. One of: [text, json, yaml]",
229+
args: {
230+
name: "format",
231+
suggestions: ["text", "json", "yaml"],
232+
default: "yaml",
233+
},
131234
},
132-
};
133235

134-
const teleportRequestGenerator: Fig.Generator = {
135-
script: "tctl request ls --format json",
136-
postProcess: function (out) {
137-
const requests = JSON.parse(out);
138-
return requests.map((request: AccessRequest) => {
139-
return {
140-
name: request.metadata.name,
141-
description: request.metadata.description,
142-
};
143-
});
236+
labels: {
237+
name: "--labels",
238+
description: "Which labels to add to the resource",
239+
args: {
240+
name: "label1=value1,label2=value2",
241+
},
144242
},
145-
};
146243

147-
const teleportTokenGenerator: Fig.Generator = {
148-
script: "tctl tokens ls --format json",
149-
postProcess: function (out) {
150-
const tokens = JSON.parse(out);
151-
return tokens.map((token: Token) => {
152-
return {
153-
name: token.metadata.name,
154-
description: token.metadata.description,
155-
};
156-
});
244+
reason: {
245+
name: "--reason",
246+
description: "Optional reason message",
247+
insertValue: "--reason '{cursor}'",
248+
args: {
249+
name: "reason",
250+
},
157251
},
158-
};
159252

160-
const teleportWindowsDesktopGenerator: Fig.Generator = {
161-
script: "tctl get windows_desktop --format json",
162-
postProcess: function (out) {
163-
const desktops = JSON.parse(out);
164-
return desktops.map((desktop: WindowsDesktop) => {
165-
return {
166-
name: desktop.metadata.name,
167-
description: desktop.metadata.description,
168-
};
169-
});
253+
roles: {
254+
name: "--roles",
255+
description: "Comma seperated list of roles",
256+
args: {
257+
name: "role1,role2",
258+
generators: teleportGenerators.roles,
259+
},
170260
},
171-
};
172261

173-
const teleportFormatOption: Fig.Option = {
174-
name: "--format",
175-
description: "Output format. One of: [text, json, yaml]",
176-
args: {
177-
name: "format",
178-
suggestions: ["text", "json", "yaml"],
179-
default: "text",
262+
logins: {
263+
name: "--logins",
264+
description: "List of allowed SSH logins",
265+
args: {
266+
name: "login1,login2",
267+
},
180268
},
181269
};
182270

271+
const filterRoles = (currentRoles: string, allRoles: Role[]) => {
272+
let currentRolesString = currentRoles;
273+
274+
if (currentRoles.includes("=")) {
275+
currentRolesString = currentRoles.split("=")[1];
276+
}
277+
278+
const filterable = currentRolesString.split(",");
279+
280+
return allRoles.filter((role: Role) => {
281+
return !filterable.includes(role.metadata.name);
282+
});
283+
};
284+
183285
const teleportBotsGenerator: Fig.Generator = {
184286
script: "tctl bots ls --format json",
185287
postProcess: function (out) {
@@ -219,37 +321,11 @@ const teleportAlertGenerator: Fig.Generator = {
219321
},
220322
};
221323

222-
const teleportDeviceGenerator: Fig.Generator = {
223-
script: "tctl get device --format json",
224-
postProcess: function (out) {
225-
const devices = JSON.parse(out);
226-
return devices.map((device: Device) => {
227-
return {
228-
name: device.metadata.name,
229-
description: device.metadata.description,
230-
};
231-
});
232-
},
233-
};
234-
235-
const teleportSAMLConnectorGenerator: Fig.Generator = {
236-
script: "tctl get connector --format json",
237-
postProcess: function (out) {
238-
const connectors = JSON.parse(out);
239-
return connectors.map((connector: Connector) => {
240-
return {
241-
name: connector.metadata.name,
242-
description: connector.metadata.description,
243-
};
244-
});
245-
},
246-
};
247-
248324
const teleportGetResourcesGenerator: Fig.Generator = {
249325
trigger: (current, old) => {
250-
return true;
326+
return current.lastIndexOf("/") > old.lastIndexOf("/");
251327
},
252-
custom: async (tokens, executeShellCommand) => {
328+
custom: async (tokens, executeShellCommand): Promise<Fig.Suggestion[]> => {
253329
const standardSuggestions = [
254330
"user",
255331
"role",
@@ -264,10 +340,12 @@ const teleportGetResourcesGenerator: Fig.Generator = {
264340
"lock",
265341
"all",
266342
];
343+
267344
const respondSuggestions = standardSuggestions.map((suggestion) => {
268345
return {
269346
name: suggestion,
270347
description: "Get a " + suggestion,
348+
priority: 100,
271349
};
272350
});
273351

@@ -281,52 +359,68 @@ const teleportGetResourcesGenerator: Fig.Generator = {
281359
.find((token) => standardSuggestions.includes(token.split("/")[0]))
282360
.split("/")[0];
283361

362+
// Only show suggestions for resources that are supported by tctl
284363
if (standardSuggestions.find((sug) => sug === resource) == undefined)
285364
return respondSuggestions;
365+
286366
if (["cluster_auth_preference", "all"].includes(resource)) return []; // This is what tctl expects
287367

288368
const resources = await executeShellCommand(
289369
`tctl get ${resource} --format json`
290370
);
371+
291372
const parsedResources = JSON.parse(resources);
292373

293-
return parsedResources.map((parsedResource: GenericResource) => {
294-
return {
295-
name: `${resource}/${parsedResource.metadata.name}`,
296-
};
297-
});
374+
let parsedLocks: Lock[] = [];
375+
376+
if (["lock", "user"].includes(resource)) {
377+
const locks = await executeShellCommand(`tctl get locks --format json`);
378+
parsedLocks = JSON.parse(locks);
379+
}
380+
381+
const postProcessResource = resourcePostProcesserBuilder(`${resource}/`);
382+
383+
if (resource === "user") {
384+
const users = parsedResources as User[];
385+
386+
users.forEach((user) => {
387+
parsedLocks.find((lock) => {
388+
if (lock.spec.target.user === user.metadata.name) {
389+
user.spec.status.is_locked = true;
390+
user.spec.status.lock_expires = lock.spec.expires;
391+
}
392+
});
393+
});
394+
395+
return postProcessResource(JSON.stringify(users), tokens);
396+
}
397+
398+
return postProcessResource(JSON.stringify(parsedResources), tokens);
298399
}
299400

300401
return respondSuggestions;
301402
},
302403
};
303404

304-
/* tctl lock --help
305-
--user Name of a Teleport user to disable.
306-
--role Name of a Teleport role to disable.
307-
--login Name of a local UNIX user to disable.
308-
--mfa-device UUID of a user MFA device to disable.
309-
--windows-desktop Name of a Windows desktop to disable.
310-
--access-request UUID of an access request to disable.
311-
--device UUID of a trusted device to disable.
312-
--message Message to display to locked-out users.
313-
--expires Time point (RFC3339) when the lock expires.
314-
--ttl Time duration after which the lock expires.
315-
--server-id UUID of a Teleport server to disable.
316-
*/
317-
318405
const completionSpec: Fig.Spec = {
319406
name: "tctl",
320407
description: "Admin tool for the Teleport Access Platform",
408+
args: {},
409+
requiresSubcommand: true,
321410
subcommands: [
322411
/* tctl help */
323412
{
324413
name: "help",
325414
description: "Show help",
415+
priority: 100,
326416
},
327417
/* tctl users */
328418
{
329419
name: "users",
420+
description: "Manage user accounts",
421+
requiresSubcommand: true,
422+
args: {},
423+
priority: 100,
330424
subcommands: [
331425
{
332426
name: "add",
@@ -336,69 +430,86 @@ const completionSpec: Fig.Spec = {
336430
description: "Teleport user account name",
337431
},
338432
options: [
339-
{
340-
name: "--logins",
341-
description: "List of allowed SSH logins for the new user",
342-
},
433+
teleportOptions.ttl,
434+
teleportOptions.roles,
435+
teleportOptions.logins,
343436
{
344437
name: "--windows-logins",
345438
description: "List of allowed Windows logins for the new user",
439+
args: {
440+
name: "login1,login2",
441+
},
346442
},
347443
{
348444
name: "--kubernetes-users",
349445
description: "List of allowed Kubernetes users for the new user",
446+
args: {
447+
name: "value1,value2",
448+
},
350449
},
351450
{
352451
name: "--kubernetes-groups",
353452
description: "List of allowed Kubernetes groups for the new user",
453+
args: {
454+
name: "group1,group2",
455+
},
354456
},
355457
{
356458
name: "--db-users",
357459
description: "List of allowed database users for the new user",
460+
args: {
461+
name: "user1,user2",
462+
},
358463
},
359464
{
360465
name: "--db-names",
361466
description: "List of allowed database names for the new user",
467+
args: {
468+
name: "value1,value2",
469+
},
362470
},
363471
{
364472
name: "--db-roles",
365473
description:
366474
"List of database roles for automatic database user provisioning",
475+
args: {
476+
name: "name1,name2",
477+
},
367478
},
368479
{
369480
name: "--aws-role-arns",
370481
description: "List of allowed AWS role ARNs for the new user",
482+
args: {
483+
name: "value1,value2",
484+
},
371485
},
372486
{
373487
name: "--azure-identities",
374488
description: "List of allowed Azure identities for the new user",
489+
args: {
490+
name: "identity1,identity2",
491+
},
375492
},
376493
{
377494
name: "--gcp-service-accounts",
378495
description:
379496
"List of allowed GCP service accounts for the new user",
497+
args: {
498+
name: "account1,account2",
499+
},
380500
},
381501
{
382502
name: "--host-user-uid",
383503
description: "UID for auto provisioned host users to use",
504+
args: {
505+
name: "user-id",
506+
},
384507
},
385508
{
386509
name: "--host-user-gid",
387510
description: "GID for auto provisioned host users to use",
388-
},
389-
{
390-
name: "--ttl",
391-
description:
392-
"Set expiration time for token, default is 1h0m0s, maximum is 48h0m0s",
393-
},
394-
{
395-
name: "--roles",
396-
description:
397-
"List of roles for the new user to assume. Comma seperated",
398-
isRequired: true,
399-
isRepeatable: true,
400511
args: {
401-
generators: teleportRolesGenerator,
512+
name: "group-id",
402513
},
403514
},
404515
],
@@ -410,81 +521,119 @@ const completionSpec: Fig.Spec = {
410521
{
411522
name: "--set-roles",
412523
description:
413-
"List of roles for the user to assume, replaces current roles. Comma seperated",
524+
"List of roles for the user to assume, replaces current roles",
414525
args: {
415-
generators: teleportRolesGenerator,
526+
name: "role1,role2",
527+
generators: teleportGenerators.roles,
416528
},
417529
},
418530
{
419531
name: "--set-logins",
420532
description:
421533
"List of allowed SSH logins for the user, replaces current logins",
534+
args: {
535+
name: "value1,value2",
536+
},
422537
},
423538
{
424539
name: "--set-windows-logins",
425540
description:
426541
"List of allowed Windows logins for the user, replaces current Windows logins",
542+
args: {
543+
name: "value1,value2",
544+
},
427545
},
428546
{
429547
name: "--set-kubernetes-users",
430548
description:
431549
"List of allowed Kubernetes users for the user, replaces current Kubernetes users",
550+
args: {
551+
name: "value1,value2",
552+
},
432553
},
433554
{
434555
name: "--set-kubernetes-groups",
435556
description:
436557
"List of allowed Kubernetes groups for the user, replaces current Kubernetes groups",
558+
args: {
559+
name: "value1,value2",
560+
},
437561
},
438562
{
439563
name: "--set-db-users",
440564
description:
441565
"List of allowed database users for the user, replaces current database users",
566+
args: {
567+
name: "value1,value2",
568+
},
442569
},
443570
{
444571
name: "--set-db-names",
445572
description:
446573
"List of allowed database names for the user, replaces current database names",
574+
args: {
575+
name: "value1,value2",
576+
},
447577
},
448578
{
449579
name: "--set-db-roles",
450580
description:
451581
"List of allowed database roles for automatic database user provisioning, replaces current database roles",
582+
args: {
583+
name: "value1,value2",
584+
},
452585
},
453586
{
454587
name: "--set-aws-role-arns",
455588
description:
456589
"List of allowed AWS role ARNs for the user, replaces current AWS role ARNs",
590+
args: {
591+
name: "value1,value2",
592+
},
457593
},
458594
{
459595
name: "--set-azure-identities",
460596
description:
461597
"List of allowed Azure identities for the user, replaces current Azure identities",
598+
args: {
599+
name: "value1,value2",
600+
},
462601
},
463602
{
464603
name: "--set-gcp-service-accounts",
465604
description:
466605
"List of allowed GCP service accounts for the user, replaces current service accounts",
606+
args: {
607+
name: "value1,value2",
608+
},
467609
},
468610
{
469611
name: "--set-host-user-uid",
470612
description:
471613
"UID for auto provisioned host users to use. Value can be reset by providing an empty string",
614+
args: {
615+
name: "user-id",
616+
},
472617
},
473618
{
474619
name: "--set-host-user-gid",
475620
description:
476621
"GID for auto provisioned host users to use. Value can be reset by providing an empty string",
622+
args: {
623+
name: "group-id",
624+
},
477625
},
478626
],
479627
args: {
480628
name: "account",
481629
description: "Teleport user account name",
482-
generators: teleportAccountsGenerator,
630+
generators: teleportGenerators.user,
483631
},
484632
},
485633
{
486634
name: "ls",
487635
description: "Lists all user accounts",
636+
options: [teleportOptions.format],
488637
},
489638
{
490639
name: "rm",
@@ -493,7 +642,7 @@ const completionSpec: Fig.Spec = {
493642
name: "account",
494643
description: "Teleport user account name",
495644
isVariadic: true,
496-
generators: teleportAccountsGenerator,
645+
generators: teleportGenerators.user,
497646
},
498647
},
499648
{
@@ -503,25 +652,30 @@ const completionSpec: Fig.Spec = {
503652
args: {
504653
name: "account",
505654
description: "Teleport user account name",
506-
generators: teleportAccountsGenerator,
655+
generators: teleportGenerators.user,
507656
},
508657
},
509658
],
510659
},
511660
/* tctl nodes */
512661
{
513662
name: "nodes",
663+
priority: 100,
514664
description: "Issue invites for other nodes to join the cluster",
665+
requiresSubcommand: true,
515666
subcommands: [
516667
{
517668
name: "add",
518669
description: "Generate a node invitation token",
670+
args: {},
519671
options: [
672+
teleportOptions.ttl,
520673
{
521674
name: "--roles",
522675
description:
523676
"Comma-separated list of roles for the new node to assume",
524677
args: {
678+
name: "role1,role2",
525679
generators: {
526680
getQueryTerm: commaQueryTerm,
527681
trigger: (current, old) => {
@@ -552,17 +706,13 @@ const completionSpec: Fig.Spec = {
552706
},
553707
},
554708
},
555-
{
556-
name: "--ttl",
557-
description:
558-
"Time to live for a generated token, default is 0h30m0s, maximum is 48h0m0s",
559-
},
560709
],
561710
},
562711
{
563712
name: "ls",
564713
description: "List all active SSH nodes within the cluster",
565714
options: [
715+
teleportOptions.format,
566716
{
567717
name: "--namespace",
568718
description: "Namespace of the nodes",
@@ -592,14 +742,18 @@ const completionSpec: Fig.Spec = {
592742
/* tctl tokens */
593743
{
594744
name: "tokens",
745+
priority: 100,
595746
description: "Manage invitation tokens",
747+
requiresSubcommand: true,
748+
args: {},
596749
subcommands: [
597750
{
598751
name: "add",
599752
description: "Create a invitation token",
600753
args: {},
601754
options: [
602-
teleportFormatOption,
755+
teleportOptions.format,
756+
teleportOptions.ttl,
603757
{
604758
name: "--type",
605759
description: "Type(s) of token to add",
@@ -699,14 +853,6 @@ const completionSpec: Fig.Spec = {
699853
name: "label1=value1,label2=value2",
700854
},
701855
},
702-
{
703-
name: "--ttl",
704-
description:
705-
"Set expiration time for token, default is 30 minutes",
706-
args: {
707-
name: "30m",
708-
},
709-
},
710856
{
711857
name: "--app-name",
712858
description: "Name of the application to add",
@@ -779,7 +925,7 @@ const completionSpec: Fig.Spec = {
779925
{
780926
name: "ls",
781927
description: "List node and user invitation tokens",
782-
options: [teleportFormatOption],
928+
options: [teleportOptions.format],
783929
},
784930
],
785931
},
@@ -788,6 +934,7 @@ const completionSpec: Fig.Spec = {
788934
name: "auth",
789935
description:
790936
"Operations with user and host certificate authorities (CAs)",
937+
priority: 100,
791938
args: {},
792939
subcommands: [
793940
{
@@ -801,6 +948,9 @@ const completionSpec: Fig.Spec = {
801948
{
802949
name: "--fingerprint",
803950
description: "Filter authority by fingerprint",
951+
args: {
952+
name: "fingerprint",
953+
},
804954
},
805955
{
806956
name: "--compat",
@@ -841,10 +991,11 @@ const completionSpec: Fig.Spec = {
841991
{
842992
name: "--user",
843993
description: "Teleport user name",
994+
priority: 100,
844995
isRequired: true,
845996
args: {
846997
name: "user",
847-
generators: teleportAccountsGenerator,
998+
generators: teleportGenerators.user,
848999
},
8491000
},
8501001
{
@@ -857,6 +1008,7 @@ const completionSpec: Fig.Spec = {
8571008
{
8581009
name: ["--out", "-o"],
8591010
description: "Identity output",
1011+
priority: 99,
8601012
isRequired: true,
8611013
args: {
8621014
name: "out",
@@ -873,11 +1025,8 @@ const completionSpec: Fig.Spec = {
8731025
},
8741026
},
8751027
{
876-
name: "--ttl",
1028+
...teleportOptions.ttl,
8771029
description: "TTL (time to live) for the generated certificate",
878-
args: {
879-
name: "ttl",
880-
},
8811030
},
8821031
{
8831032
name: "--compat",
@@ -920,66 +1069,95 @@ const completionSpec: Fig.Spec = {
9201069
'Kubernetes cluster to generate identity file for when --format is set to "kubernetes"',
9211070
args: {
9221071
name: "name",
923-
generators: teleportKubernetesClustersGenerator,
1072+
generators: {
1073+
...teleportGenerators.kube,
1074+
postProcess: function (out) {
1075+
const clusters = JSON.parse(out);
1076+
1077+
return clusters.map((cluster: Cluster) => {
1078+
return {
1079+
name: cluster.kube_cluster_name,
1080+
description: "Kubernetes cluster connected to Teleport",
1081+
};
1082+
});
1083+
},
1084+
},
9241085
},
9251086
},
9261087
{
9271088
name: "--app-name",
9281089
description:
9291090
'Application to generate identity file for. Mutually exclusive with "--db-service"',
1091+
exclusiveOn: ["--db-service"],
9301092
args: {
9311093
name: "name",
932-
generators: teleportAppGenerator,
1094+
generators: teleportGenerators.apps,
9331095
},
9341096
},
9351097
{
9361098
name: "--db-service",
9371099
description:
9381100
'Database to generate identity file for. Mutually exclusive with "--app-name"',
1101+
exclusiveOn: ["--app-name"],
9391102
args: {
9401103
name: "service",
941-
generators: teleportDatabaseGenerator,
1104+
generators: teleportGenerators.db,
9421105
},
9431106
},
9441107
{
9451108
name: "--db-user",
9461109
description:
9471110
'Database user placed on the identity file. Only used when "--db-service" is set',
1111+
dependsOn: ["--db-service"],
1112+
args: {
1113+
name: "user",
1114+
},
9481115
},
9491116
{
9501117
name: "--db-name",
9511118
description:
9521119
'Database name placed on the identity file. Only used when "--db-service" is set',
1120+
dependsOn: ["--db-service"],
1121+
args: {
1122+
name: "name",
1123+
},
9531124
},
9541125
{
9551126
name: "--windows-user",
9561127
description:
9571128
'Window user placed on the identity file. Only used when --format is set to "windows"',
1129+
args: {
1130+
name: "user",
1131+
},
9581132
},
9591133
{
9601134
name: "--windows-domain",
9611135
description:
9621136
'Active Directory domain for which this cert is valid. Only used when --format is set to "windows"',
1137+
args: {
1138+
name: "domain",
1139+
},
9631140
},
9641141
{
9651142
name: "--windows-sid",
9661143
description:
9671144
'Optional Security Identifier to embed in the certificate. Only used when --format is set to "windows"',
1145+
args: {
1146+
name: "security-id",
1147+
},
9681148
},
9691149
],
9701150
},
9711151
{
9721152
name: "rotate",
9731153
description: "Rotate certificate authorities in the cluster",
1154+
args: {},
1155+
isDangerous: true,
9741156
options: [
9751157
{
1158+
...teleportOptions.ttl,
9761159
name: "--grace-period",
977-
description:
978-
"Grace period keeps previous certificate authorities signatures valid, if set to 0 will force users to re-login and nodes to re-register",
979-
args: {
980-
name: "duration",
981-
description: "Relative duration like 5s, 2m, or 3h",
982-
},
1160+
description: "Grace period keeps previous CA valid",
9831161
},
9841162
{
9851163
name: "--manual",
@@ -1025,12 +1203,13 @@ const completionSpec: Fig.Spec = {
10251203
{
10261204
name: "ls",
10271205
description: "List connected auth servers",
1028-
options: [teleportFormatOption],
1206+
options: [teleportOptions.format],
10291207
},
10301208
{
10311209
name: "crl",
10321210
description:
10331211
"Export empty certificate revocation list (CRL) for certificate authorities",
1212+
args: {},
10341213
options: [
10351214
{
10361215
name: "--type",
@@ -1050,13 +1229,14 @@ const completionSpec: Fig.Spec = {
10501229
{
10511230
name: "get",
10521231
description: "Get a resource",
1232+
priority: 100,
10531233
args: {
1054-
name: "resource",
1055-
description: "Resource to get",
1234+
name: "type/name",
1235+
description: "Resource to get (e.g. user/bob)",
10561236
generators: teleportGetResourcesGenerator,
10571237
},
10581238
options: [
1059-
teleportFormatOption,
1239+
teleportOptions.format,
10601240
{
10611241
name: "--with-secrets",
10621242
description:
@@ -1072,11 +1252,13 @@ const completionSpec: Fig.Spec = {
10721252
{
10731253
name: "status",
10741254
description: "Report cluster status",
1255+
priority: 100,
10751256
},
10761257
/* tctl top */
10771258
{
10781259
name: "top",
10791260
description: "Report cluster status",
1261+
priority: 100,
10801262
args: [
10811263
{
10821264
name: "diag-address",
@@ -1093,27 +1275,30 @@ const completionSpec: Fig.Spec = {
10931275
name: ["requests", "request"],
10941276
description: "Manage access requests",
10951277
args: {},
1278+
priority: 100,
10961279
subcommands: [
10971280
{
10981281
name: "ls",
10991282
description: "Show active access requests",
1283+
options: [teleportOptions.format],
11001284
},
11011285
{
11021286
name: "get",
11031287
description: "Show access request details",
11041288
args: {
11051289
name: "request",
11061290
description: "Access request ID",
1107-
generators: teleportRequestGenerator,
1291+
generators: teleportGenerators.request,
11081292
},
1293+
options: [teleportOptions.format],
11091294
},
11101295
{
11111296
name: "approve",
11121297
description: "Approve pending access request",
11131298
args: {
11141299
name: "request",
11151300
description: "Access request ID",
1116-
generators: teleportRequestGenerator,
1301+
generators: teleportGenerators.request,
11171302
},
11181303
},
11191304
{
@@ -1122,7 +1307,7 @@ const completionSpec: Fig.Spec = {
11221307
args: {
11231308
name: "request",
11241309
description: "Access request ID",
1125-
generators: teleportRequestGenerator,
1310+
generators: teleportGenerators.request,
11261311
},
11271312
},
11281313
{
@@ -1131,21 +1316,11 @@ const completionSpec: Fig.Spec = {
11311316
args: {
11321317
name: "username",
11331318
description: "Name of target user",
1134-
generators: teleportAccountsGenerator,
1319+
generators: teleportGenerators.user,
11351320
},
11361321
options: [
1137-
{
1138-
name: "--roles",
1139-
description: "Roles to be requested",
1140-
args: {
1141-
name: "roles",
1142-
generators: teleportRolesGenerator,
1143-
},
1144-
},
1145-
{
1146-
name: "--reason",
1147-
description: "Optional reason message",
1148-
},
1322+
teleportOptions.reason,
1323+
teleportOptions.roles,
11491324
{
11501325
name: "--resource",
11511326
description: "Resource ID to be requested",
@@ -1172,7 +1347,7 @@ const completionSpec: Fig.Spec = {
11721347
args: {
11731348
name: "request-id",
11741349
description: "Access request ID",
1175-
generators: teleportRequestGenerator,
1350+
generators: teleportGenerators.request,
11761351
},
11771352
},
11781353
{
@@ -1185,7 +1360,7 @@ const completionSpec: Fig.Spec = {
11851360
isRequired: true,
11861361
args: {
11871362
name: "author",
1188-
generators: teleportAccountsGenerator,
1363+
generators: teleportGenerators.user,
11891364
},
11901365
},
11911366
{
@@ -1200,7 +1375,7 @@ const completionSpec: Fig.Spec = {
12001375
args: {
12011376
name: "request-id",
12021377
description: "Access request ID",
1203-
generators: teleportRequestGenerator,
1378+
generators: teleportGenerators.request,
12041379
},
12051380
},
12061381
],
@@ -1209,66 +1384,70 @@ const completionSpec: Fig.Spec = {
12091384
{
12101385
name: "apps",
12111386
description: "Operate on applications registered with the cluster",
1212-
args: {},
12131387
requiresSubcommand: true,
1388+
priority: 100,
12141389
subcommands: [
12151390
{
12161391
name: "ls",
12171392
description: "List all applications registered with the cluster",
1393+
options: [teleportOptions.format],
12181394
},
12191395
],
12201396
},
12211397
/* tctl db */
12221398
{
12231399
name: "db",
12241400
description: "Operate on databases registered with the cluster",
1225-
args: {},
12261401
requiresSubcommand: true,
1402+
priority: 100,
12271403
subcommands: [
12281404
{
12291405
name: "ls",
12301406
description: "List all databases registered with the cluster",
1407+
options: [teleportOptions.format],
12311408
},
12321409
],
12331410
},
12341411
/* tctl kube */
12351412
{
12361413
name: "kube",
12371414
description: "Operate on registered Kubernetes clusters",
1238-
args: {},
12391415
requiresSubcommand: true,
1416+
priority: 100,
12401417
subcommands: [
12411418
{
12421419
name: "ls",
12431420
description:
12441421
"List all Kubernetes clusters registered with the cluster",
1422+
options: [teleportOptions.format],
12451423
},
12461424
],
12471425
},
12481426
/* tctl windows_desktops */
12491427
{
12501428
name: "windows_desktops",
12511429
description: "Operate on registered Windows desktops",
1252-
args: {},
12531430
requiresSubcommand: true,
1431+
priority: 100,
12541432
subcommands: [
12551433
{
12561434
name: "ls",
12571435
description: "List all Windows desktops registered with the cluster",
1436+
options: [teleportOptions.format],
12581437
},
12591438
],
12601439
},
12611440
/* tctl proxy */
12621441
{
12631442
name: "proxy",
12641443
description: "Operations with information for cluster proxies",
1265-
args: {},
12661444
requiresSubcommand: true,
1445+
priority: 100,
12671446
subcommands: [
12681447
{
12691448
name: "ls",
12701449
description: "Lists proxies connected to the cluster",
1271-
options: [teleportFormatOption],
1450+
options: [teleportOptions.format],
12721451
},
12731452
],
12741453
},
@@ -1287,45 +1466,52 @@ const completionSpec: Fig.Spec = {
12871466
name: "lock",
12881467
description: "Create a new lock",
12891468
args: {},
1469+
priority: 100,
12901470
options: [
12911471
{
12921472
name: "--user",
12931473
description: "Name of a Teleport user to disable",
12941474
args: {
12951475
name: "user",
1296-
generators: teleportAccountsGenerator,
1476+
generators: teleportGenerators.user,
12971477
},
12981478
},
12991479
{
13001480
name: "--role",
13011481
description: "Name of a Teleport role to disable",
13021482
args: {
13031483
name: "role",
1304-
generators: teleportRolesGenerator,
1484+
generators: teleportGenerators.role,
13051485
},
13061486
},
13071487
{
13081488
name: "--login",
13091489
description: "Name of a local UNIX user to disable",
1490+
args: {
1491+
name: "login",
1492+
},
13101493
},
13111494
{
13121495
name: "--mfa-device",
13131496
description: "UUID of a user MFA device to disable",
1497+
args: {
1498+
name: "device",
1499+
},
13141500
},
13151501
{
13161502
name: "--windows-desktop",
13171503
description: "Name of a Windows desktop to disable",
13181504
args: {
13191505
name: "desktop",
1320-
generators: teleportWindowsDesktopGenerator,
1506+
generators: teleportGenerators.windows_desktop,
13211507
},
13221508
},
13231509
{
13241510
name: "--access-request",
13251511
description: "UUID of an access request to disable",
13261512
args: {
13271513
name: "request",
1328-
generators: teleportRequestGenerator,
1514+
generators: teleportGenerators.request,
13291515
},
13301516
},
13311517
{
@@ -1345,16 +1531,16 @@ const completionSpec: Fig.Spec = {
13451531
},
13461532
},
13471533
{
1348-
name: "--ttl",
1534+
...teleportOptions.ttl,
13491535
description: "Time duration after which the lock expires",
1350-
args: {
1351-
name: "duration",
1352-
description: "Time duration after which the lock expires",
1353-
},
13541536
},
13551537
{
13561538
name: "--server-id",
13571539
description: "UUID of a Teleport server to disable",
1540+
args: {
1541+
name: "server-uuid",
1542+
generators: teleportGenerators.node,
1543+
},
13581544
},
13591545
],
13601546
},
@@ -1364,45 +1550,46 @@ const completionSpec: Fig.Spec = {
13641550
description:
13651551
"Operate on certificate renewal bots registered with the cluster",
13661552
requiresSubcommand: true,
1553+
args: {},
1554+
priority: 100,
13671555
subcommands: [
13681556
{
13691557
name: "ls",
13701558
description:
13711559
"List all certificate renewal bots registered with the cluster",
1560+
options: [teleportOptions.format],
13721561
},
13731562
{
13741563
name: "add",
13751564
description: "Add a new certificate renewal bot to the cluster",
13761565
args: {
13771566
name: "name",
13781567
description: "A name to uniquely identify this bot in the cluster",
1379-
},
1380-
options: [
1381-
{
1382-
name: "--roles",
1383-
description: "Roles the bot is able to assume",
1384-
isRequired: true,
1385-
args: {
1386-
name: "roles",
1387-
generators: teleportRolesGenerator,
1568+
generators: {
1569+
...teleportBotsGenerator,
1570+
postProcess: function (out) {
1571+
const bots = JSON.parse(out);
1572+
return bots.map((bot: Bot) => {
1573+
return {
1574+
name: bot.metadata.name.slice(4),
1575+
description: "A bot with this name already exists",
1576+
};
1577+
});
13881578
},
13891579
},
1390-
{
1391-
name: "--ttl",
1392-
description: "TTL for the bot join token",
1393-
},
1580+
},
1581+
options: [
1582+
teleportOptions.ttl,
1583+
teleportOptions.roles,
1584+
teleportOptions.logins,
13941585
{
13951586
name: "--token",
13961587
description: "Name of an existing token to use",
13971588
args: {
13981589
name: "token",
1399-
generators: teleportTokenGenerator,
1590+
generators: teleportGenerators.tokens,
14001591
},
14011592
},
1402-
{
1403-
name: "--logins",
1404-
description: "List of allowed SSH logins for the bot user",
1405-
},
14061593
],
14071594
},
14081595
{
@@ -1422,6 +1609,7 @@ const completionSpec: Fig.Spec = {
14221609
name: "inventory",
14231610
description: "Manage Teleport instance inventory",
14241611
requiresSubcommand: true,
1612+
priority: 100,
14251613
subcommands: [
14261614
{
14271615
name: "status",
@@ -1496,6 +1684,7 @@ const completionSpec: Fig.Spec = {
14961684
name: "recordings",
14971685
description: "View and control session recordings",
14981686
requiresSubcommand: true,
1687+
priority: 100,
14991688
subcommands: [
15001689
{
15011690
name: "ls",
@@ -1508,15 +1697,17 @@ const completionSpec: Fig.Spec = {
15081697
name: "alerts",
15091698
description: "Manage cluster alerts",
15101699
requiresSubcommand: true,
1700+
args: {},
1701+
priority: 100,
15111702
subcommands: [
15121703
{
15131704
name: "list",
15141705
description: "List cluster alerts",
15151706
options: [
1516-
teleportFormatOption,
1707+
teleportOptions.format,
15171708
{
15181709
name: "--labels",
1519-
description: "List of comma separated labels to filter by labels",
1710+
description: "Filter by labels",
15201711
args: {
15211712
name: "label1=value1,label2=value2",
15221713
},
@@ -1534,7 +1725,7 @@ const completionSpec: Fig.Spec = {
15341725
options: [
15351726
{
15361727
name: "--labels",
1537-
description: "List of comma separated labels to filter by labels",
1728+
description: "Which labels should this alert have",
15381729
args: {
15391730
name: "label1=value1,label2=value2",
15401731
},
@@ -1548,7 +1739,7 @@ const completionSpec: Fig.Spec = {
15481739
},
15491740
},
15501741
{
1551-
name: "--ttl",
1742+
...teleportOptions.ttl,
15521743
description:
15531744
"Time duration after which the alert expires (default 24h)",
15541745
},
@@ -1573,12 +1764,9 @@ const completionSpec: Fig.Spec = {
15731764
generators: teleportAlertGenerator,
15741765
},
15751766
options: [
1767+
...[teleportOptions.reason],
15761768
{
1577-
name: "--reason",
1578-
description: "The reason for acknowledging the cluster alert",
1579-
},
1580-
{
1581-
name: "--ttl",
1769+
...teleportOptions.ttl,
15821770
description:
15831771
"Time duration after which the alert expires (default 24h)",
15841772
},
@@ -1594,9 +1782,10 @@ const completionSpec: Fig.Spec = {
15941782
{
15951783
name: "create",
15961784
description: "Create or update a Teleport resource from a YAML file",
1785+
priority: 100,
15971786
args: {
15981787
name: "filename",
1599-
template: "filepaths",
1788+
generators: teleportGenerators.yamlFiles,
16001789
},
16011790
options: [
16021791
{
@@ -1609,6 +1798,7 @@ const completionSpec: Fig.Spec = {
16091798
{
16101799
name: "update",
16111800
description: "Update resource fields",
1801+
priority: 100,
16121802
args: {
16131803
name: "resource type/resource name",
16141804
description: "Resource to update",
@@ -1617,18 +1807,23 @@ const completionSpec: Fig.Spec = {
16171807
options: [
16181808
{
16191809
name: "--set-labels",
1620-
description: "Set labels",
1810+
description: "Replace labels",
1811+
args: {
1812+
name: "label1=value1,label2=value2",
1813+
},
16211814
},
16221815
{
1816+
...teleportOptions.ttl,
16231817
name: "--set-ttl",
1624-
description: "Set TTL",
1818+
description: "Replace TTL",
16251819
},
16261820
],
16271821
},
16281822
/* tctl edit */
16291823
{
16301824
name: "edit",
16311825
description: "Edit a Teleport resource",
1826+
priority: 100,
16321827
args: {
16331828
name: "resource type/resource name",
16341829
description: "Resource to edit",
@@ -1639,7 +1834,9 @@ const completionSpec: Fig.Spec = {
16391834
{
16401835
name: "devices",
16411836
description: "Register and manage trusted devices",
1837+
priority: 100,
16421838
requiresSubcommand: true,
1839+
args: {},
16431840
subcommands: [
16441841
{
16451842
name: "add",
@@ -1683,7 +1880,7 @@ const completionSpec: Fig.Spec = {
16831880
args: {
16841881
name: "device",
16851882
description: "Device ID",
1686-
generators: teleportDeviceGenerator,
1883+
generators: teleportGenerators.device,
16871884
},
16881885
},
16891886
{
@@ -1710,7 +1907,7 @@ const completionSpec: Fig.Spec = {
17101907
args: {
17111908
name: "device",
17121909
description: "Device ID",
1713-
generators: teleportDeviceGenerator,
1910+
generators: teleportGenerators.device,
17141911
},
17151912
},
17161913
],
@@ -1720,14 +1917,17 @@ const completionSpec: Fig.Spec = {
17201917
name: "saml",
17211918
description: "Operations on SAML auth connectors",
17221919
requiresSubcommand: true,
1920+
priority: 100,
1921+
args: {},
17231922
subcommands: [
17241923
{
17251924
name: "export",
17261925
description: "Export a SAML signing key in .crt format",
17271926
args: {
17281927
name: "connector_name",
1928+
isOptional: true,
17291929
description: "Name of the SAML connector to export the key from",
1730-
generators: teleportSAMLConnectorGenerator,
1930+
generators: teleportGenerators.connector,
17311931
},
17321932
},
17331933
],
@@ -1737,6 +1937,8 @@ const completionSpec: Fig.Spec = {
17371937
name: ["acl", "access-lists"],
17381938
description: "Manage access lists",
17391939
requiresSubcommand: true,
1940+
priority: 100,
1941+
args: {},
17401942
subcommands: [
17411943
{
17421944
name: "ls",
@@ -1768,7 +1970,7 @@ const completionSpec: Fig.Spec = {
17681970
{
17691971
name: "user",
17701972
description: "The user name",
1771-
generators: teleportAccountsGenerator,
1973+
generators: teleportGenerators.user,
17721974
},
17731975
{
17741976
name: "expires",
@@ -1796,7 +1998,7 @@ const completionSpec: Fig.Spec = {
17961998
{
17971999
name: "user",
17982000
description: "The user name",
1799-
generators: teleportAccountsGenerator,
2001+
generators: teleportGenerators.user,
18002002
},
18012003
],
18022004
},
@@ -1818,6 +2020,7 @@ const completionSpec: Fig.Spec = {
18182020
name: "login_rule",
18192021
description: "Test login rules",
18202022
requiresSubcommand: true,
2023+
priority: 100,
18212024
subcommands: [
18222025
{
18232026
name: "test",
@@ -1831,6 +2034,8 @@ const completionSpec: Fig.Spec = {
18312034
description:
18322035
"A family of commands for configuring and testing auth connectors (SSO)",
18332036
requiresSubcommand: true,
2037+
priority: 100,
2038+
args: {},
18342039
subcommands: [
18352040
{
18362041
name: "configure",
@@ -1844,6 +2049,9 @@ const completionSpec: Fig.Spec = {
18442049
{
18452050
name: "--name",
18462051
description: "Connector name",
2052+
args: {
2053+
name: "name",
2054+
},
18472055
},
18482056
{
18492057
name: "--teams-to-roles",
@@ -1856,26 +2064,44 @@ const completionSpec: Fig.Spec = {
18562064
{
18572065
name: "--display",
18582066
description: "Sets the connector display name",
2067+
args: {
2068+
name: "display-name",
2069+
},
18592070
},
18602071
{
18612072
name: "--id",
18622073
description: "GitHub app client ID",
2074+
args: {
2075+
name: "id",
2076+
},
18632077
},
18642078
{
18652079
name: "--secret",
18662080
description: "GitHub app client secret",
2081+
args: {
2082+
name: "secret",
2083+
},
18672084
},
18682085
{
18692086
name: "--endpoint-url",
18702087
description: "Endpoint URL for GitHub instance",
2088+
args: {
2089+
name: "endpoint-url",
2090+
},
18712091
},
18722092
{
18732093
name: "--api-endpoint-url",
18742094
description: "API endpoint URL for GitHub instance",
2095+
args: {
2096+
name: "api-endpoint-url",
2097+
},
18752098
},
18762099
{
18772100
name: "--redirect-url",
18782101
description: "Authorization callback URL",
2102+
args: {
2103+
name: "redirect-url",
2104+
},
18792105
},
18802106
{
18812107
name: "--ignore-missing-roles",
@@ -1895,15 +2121,15 @@ const completionSpec: Fig.Spec = {
18952121
name: "filename",
18962122
description:
18972123
"Connector resource definition filename. Empty for stdin",
1898-
isOptional: true,
1899-
template: "filepaths",
2124+
generators: teleportGenerators.yamlFiles,
19002125
},
19012126
},
19022127
],
19032128
},
19042129
/* tctl version */
19052130
{
19062131
name: "version",
2132+
priority: 100,
19072133
description: "Print the version of your tctl binary",
19082134
},
19092135
],
@@ -1924,7 +2150,7 @@ const completionSpec: Fig.Spec = {
19242150
isPersistent: true,
19252151
args: {
19262152
name: "config",
1927-
template: "filepaths",
2153+
generators: teleportGenerators.yamlFiles,
19282154
},
19292155
},
19302156
{

0 commit comments

Comments
 (0)
Please sign in to comment.