@falldownthesystem/devtool 0.4.0 → 0.4.1
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/LICENSE +15 -0
- package/README.md +1 -2
- package/package.json +22 -4
- package/src/clients/confluence.js +12 -8
- package/src/clients/serviceDesk.js +181 -119
- package/src/commands/config.js +10 -27
- package/src/commands/sd.js +135 -135
- package/src/config/store.js +20 -16
- package/src/formatters/serviceDesk.js +267 -267
- package/src/index.js +1 -1
- package/CLAUDE.md +0 -54
package/src/commands/sd.js
CHANGED
|
@@ -1,135 +1,135 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import {
|
|
3
|
-
getIssue,
|
|
4
|
-
getIssueComments,
|
|
5
|
-
searchIssues,
|
|
6
|
-
getMyIssues,
|
|
7
|
-
listQueues,
|
|
8
|
-
searchByQueue,
|
|
9
|
-
} from '../clients/serviceDesk.js';
|
|
10
|
-
import {
|
|
11
|
-
formatIssueView, formatIssueComments, formatIssueSearch,
|
|
12
|
-
plainIssueView, plainIssueComments, plainIssueSearch,
|
|
13
|
-
curateIssueJson, curateCommentsJson, curateSearchJson,
|
|
14
|
-
formatQueueList, plainQueueList, curateQueuesJson,
|
|
15
|
-
} from '../formatters/serviceDesk.js';
|
|
16
|
-
import { spinner } from '../utils/output.js';
|
|
17
|
-
import { handleError } from '../utils/errors.js';
|
|
18
|
-
import { getOutputMode, outputJson } from '../utils/mode.js';
|
|
19
|
-
|
|
20
|
-
const sd = new Command('sd')
|
|
21
|
-
.alias('servicedesk')
|
|
22
|
-
.description('Jira Service Desk Cloud tickets');
|
|
23
|
-
|
|
24
|
-
sd.command('view <key>')
|
|
25
|
-
.alias('read')
|
|
26
|
-
.description('View ticket details')
|
|
27
|
-
.action(async function (key) {
|
|
28
|
-
const mode = getOutputMode(this);
|
|
29
|
-
const s = mode === 'rich' ? spinner(`Fetching ${key}...`).start() : null;
|
|
30
|
-
try {
|
|
31
|
-
const issue = await getIssue(key);
|
|
32
|
-
if (s) s.success({ text: key });
|
|
33
|
-
if (mode === 'json') outputJson(curateIssueJson(issue));
|
|
34
|
-
else if (mode === 'plain') plainIssueView(issue);
|
|
35
|
-
else formatIssueView(issue);
|
|
36
|
-
} catch (err) {
|
|
37
|
-
if (s) s.error({ text: `Failed to fetch ${key}` });
|
|
38
|
-
handleError(err, `sd view ${key}`);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
sd.command('comments <key>')
|
|
43
|
-
.description('List ticket comments')
|
|
44
|
-
.action(async function (key) {
|
|
45
|
-
const mode = getOutputMode(this);
|
|
46
|
-
const s = mode === 'rich' ? spinner(`Fetching comments for ${key}...`).start() : null;
|
|
47
|
-
try {
|
|
48
|
-
const comments = await getIssueComments(key);
|
|
49
|
-
if (s) s.success({ text: `${comments.length} comment(s)` });
|
|
50
|
-
if (mode === 'json') outputJson(curateCommentsJson(comments));
|
|
51
|
-
else if (mode === 'plain') plainIssueComments(comments);
|
|
52
|
-
else formatIssueComments(comments);
|
|
53
|
-
} catch (err) {
|
|
54
|
-
if (s) s.error({ text: `Failed to fetch comments for ${key}` });
|
|
55
|
-
handleError(err, `sd comments ${key}`);
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
sd.command('search <jql>')
|
|
60
|
-
.alias('find')
|
|
61
|
-
.description('Search Service Desk tickets with JQL')
|
|
62
|
-
.option('-n, --per-page <number>', 'Results per page', '20')
|
|
63
|
-
.option('--next <token>', 'Continuation token from a previous page')
|
|
64
|
-
.action(async function (jql, opts) {
|
|
65
|
-
const mode = getOutputMode(this);
|
|
66
|
-
const perPage = parseInt(opts.perPage, 10);
|
|
67
|
-
const s = mode === 'rich' ? spinner('Searching...').start() : null;
|
|
68
|
-
try {
|
|
69
|
-
const result = await searchIssues(jql, perPage, opts.next || '');
|
|
70
|
-
if (s) s.success({ text: `${result.issues.length} issue(s) returned` });
|
|
71
|
-
if (mode === 'json') outputJson(curateSearchJson(result));
|
|
72
|
-
else if (mode === 'plain') plainIssueSearch(result);
|
|
73
|
-
else formatIssueSearch(result);
|
|
74
|
-
} catch (err) {
|
|
75
|
-
if (s) s.error({ text: 'Search failed' });
|
|
76
|
-
handleError(err, `sd search "${jql}"`);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
sd.command('queues')
|
|
81
|
-
.description('List Service Desk queues for the default project')
|
|
82
|
-
.action(async function () {
|
|
83
|
-
const mode = getOutputMode(this);
|
|
84
|
-
const s = mode === 'rich' ? spinner('Fetching queues...').start() : null;
|
|
85
|
-
try {
|
|
86
|
-
const queues = await listQueues();
|
|
87
|
-
if (s) s.success({ text: `${queues.length} queue(s)` });
|
|
88
|
-
if (mode === 'json') outputJson(curateQueuesJson(queues));
|
|
89
|
-
else if (mode === 'plain') plainQueueList(queues);
|
|
90
|
-
else formatQueueList(queues);
|
|
91
|
-
} catch (err) {
|
|
92
|
-
if (s) s.error({ text: 'Failed to fetch queues' });
|
|
93
|
-
handleError(err, 'sd queues');
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
sd.command('queue <nameOrId>')
|
|
98
|
-
.description('Show issues in a Service Desk queue (by name or id)')
|
|
99
|
-
.option('-n, --per-page <number>', 'Results per page', '20')
|
|
100
|
-
.option('--next <token>', 'Continuation token from a previous page')
|
|
101
|
-
.action(async function (nameOrId, opts) {
|
|
102
|
-
const mode = getOutputMode(this);
|
|
103
|
-
const perPage = parseInt(opts.perPage, 10);
|
|
104
|
-
const s = mode === 'rich' ? spinner(`Fetching queue "${nameOrId}"...`).start() : null;
|
|
105
|
-
try {
|
|
106
|
-
const result = await searchByQueue(nameOrId, perPage, opts.next || '');
|
|
107
|
-
if (s) s.success({ text: `${result.issues.length} issue(s) in "${result.queue.name}"` });
|
|
108
|
-
if (mode === 'json') outputJson(curateSearchJson(result));
|
|
109
|
-
else if (mode === 'plain') plainIssueSearch(result);
|
|
110
|
-
else formatIssueSearch(result);
|
|
111
|
-
} catch (err) {
|
|
112
|
-
if (s) s.error({ text: `Failed to fetch queue "${nameOrId}"` });
|
|
113
|
-
handleError(err, `sd queue ${nameOrId}`);
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
sd.command('mine')
|
|
118
|
-
.alias('my')
|
|
119
|
-
.description('Show my open Service Desk tickets')
|
|
120
|
-
.action(async function () {
|
|
121
|
-
const mode = getOutputMode(this);
|
|
122
|
-
const s = mode === 'rich' ? spinner('Fetching my tickets...').start() : null;
|
|
123
|
-
try {
|
|
124
|
-
const result = await getMyIssues();
|
|
125
|
-
if (s) s.success({ text: `${result.issues.length} ticket(s)` });
|
|
126
|
-
if (mode === 'json') outputJson(curateSearchJson(result));
|
|
127
|
-
else if (mode === 'plain') plainIssueSearch(result);
|
|
128
|
-
else formatIssueSearch(result);
|
|
129
|
-
} catch (err) {
|
|
130
|
-
if (s) s.error({ text: 'Failed to fetch tickets' });
|
|
131
|
-
handleError(err, 'sd my tickets');
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
export default sd;
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import {
|
|
3
|
+
getIssue,
|
|
4
|
+
getIssueComments,
|
|
5
|
+
searchIssues,
|
|
6
|
+
getMyIssues,
|
|
7
|
+
listQueues,
|
|
8
|
+
searchByQueue,
|
|
9
|
+
} from '../clients/serviceDesk.js';
|
|
10
|
+
import {
|
|
11
|
+
formatIssueView, formatIssueComments, formatIssueSearch,
|
|
12
|
+
plainIssueView, plainIssueComments, plainIssueSearch,
|
|
13
|
+
curateIssueJson, curateCommentsJson, curateSearchJson,
|
|
14
|
+
formatQueueList, plainQueueList, curateQueuesJson,
|
|
15
|
+
} from '../formatters/serviceDesk.js';
|
|
16
|
+
import { spinner } from '../utils/output.js';
|
|
17
|
+
import { handleError } from '../utils/errors.js';
|
|
18
|
+
import { getOutputMode, outputJson } from '../utils/mode.js';
|
|
19
|
+
|
|
20
|
+
const sd = new Command('sd')
|
|
21
|
+
.alias('servicedesk')
|
|
22
|
+
.description('Jira Service Desk Cloud tickets');
|
|
23
|
+
|
|
24
|
+
sd.command('view <key>')
|
|
25
|
+
.alias('read')
|
|
26
|
+
.description('View ticket details')
|
|
27
|
+
.action(async function (key) {
|
|
28
|
+
const mode = getOutputMode(this);
|
|
29
|
+
const s = mode === 'rich' ? spinner(`Fetching ${key}...`).start() : null;
|
|
30
|
+
try {
|
|
31
|
+
const issue = await getIssue(key);
|
|
32
|
+
if (s) s.success({ text: key });
|
|
33
|
+
if (mode === 'json') outputJson(curateIssueJson(issue));
|
|
34
|
+
else if (mode === 'plain') plainIssueView(issue);
|
|
35
|
+
else formatIssueView(issue);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
if (s) s.error({ text: `Failed to fetch ${key}` });
|
|
38
|
+
handleError(err, `sd view ${key}`);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
sd.command('comments <key>')
|
|
43
|
+
.description('List ticket comments')
|
|
44
|
+
.action(async function (key) {
|
|
45
|
+
const mode = getOutputMode(this);
|
|
46
|
+
const s = mode === 'rich' ? spinner(`Fetching comments for ${key}...`).start() : null;
|
|
47
|
+
try {
|
|
48
|
+
const comments = await getIssueComments(key);
|
|
49
|
+
if (s) s.success({ text: `${comments.length} comment(s)` });
|
|
50
|
+
if (mode === 'json') outputJson(curateCommentsJson(comments));
|
|
51
|
+
else if (mode === 'plain') plainIssueComments(comments);
|
|
52
|
+
else formatIssueComments(comments);
|
|
53
|
+
} catch (err) {
|
|
54
|
+
if (s) s.error({ text: `Failed to fetch comments for ${key}` });
|
|
55
|
+
handleError(err, `sd comments ${key}`);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
sd.command('search <jql>')
|
|
60
|
+
.alias('find')
|
|
61
|
+
.description('Search Service Desk tickets with JQL')
|
|
62
|
+
.option('-n, --per-page <number>', 'Results per page', '20')
|
|
63
|
+
.option('--next <token>', 'Continuation token from a previous page')
|
|
64
|
+
.action(async function (jql, opts) {
|
|
65
|
+
const mode = getOutputMode(this);
|
|
66
|
+
const perPage = parseInt(opts.perPage, 10);
|
|
67
|
+
const s = mode === 'rich' ? spinner('Searching...').start() : null;
|
|
68
|
+
try {
|
|
69
|
+
const result = await searchIssues(jql, perPage, opts.next || '');
|
|
70
|
+
if (s) s.success({ text: `${result.issues.length} issue(s) returned` });
|
|
71
|
+
if (mode === 'json') outputJson(curateSearchJson(result));
|
|
72
|
+
else if (mode === 'plain') plainIssueSearch(result);
|
|
73
|
+
else formatIssueSearch(result);
|
|
74
|
+
} catch (err) {
|
|
75
|
+
if (s) s.error({ text: 'Search failed' });
|
|
76
|
+
handleError(err, `sd search "${jql}"`);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
sd.command('queues')
|
|
81
|
+
.description('List Service Desk queues for the default project')
|
|
82
|
+
.action(async function () {
|
|
83
|
+
const mode = getOutputMode(this);
|
|
84
|
+
const s = mode === 'rich' ? spinner('Fetching queues...').start() : null;
|
|
85
|
+
try {
|
|
86
|
+
const queues = await listQueues();
|
|
87
|
+
if (s) s.success({ text: `${queues.length} queue(s)` });
|
|
88
|
+
if (mode === 'json') outputJson(curateQueuesJson(queues));
|
|
89
|
+
else if (mode === 'plain') plainQueueList(queues);
|
|
90
|
+
else formatQueueList(queues);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
if (s) s.error({ text: 'Failed to fetch queues' });
|
|
93
|
+
handleError(err, 'sd queues');
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
sd.command('queue <nameOrId>')
|
|
98
|
+
.description('Show issues in a Service Desk queue (by name or id)')
|
|
99
|
+
.option('-n, --per-page <number>', 'Results per page', '20')
|
|
100
|
+
.option('--next <token>', 'Continuation token from a previous page')
|
|
101
|
+
.action(async function (nameOrId, opts) {
|
|
102
|
+
const mode = getOutputMode(this);
|
|
103
|
+
const perPage = parseInt(opts.perPage, 10);
|
|
104
|
+
const s = mode === 'rich' ? spinner(`Fetching queue "${nameOrId}"...`).start() : null;
|
|
105
|
+
try {
|
|
106
|
+
const result = await searchByQueue(nameOrId, perPage, opts.next || '');
|
|
107
|
+
if (s) s.success({ text: `${result.issues.length} issue(s) in "${result.queue.name}"` });
|
|
108
|
+
if (mode === 'json') outputJson(curateSearchJson(result));
|
|
109
|
+
else if (mode === 'plain') plainIssueSearch(result);
|
|
110
|
+
else formatIssueSearch(result);
|
|
111
|
+
} catch (err) {
|
|
112
|
+
if (s) s.error({ text: `Failed to fetch queue "${nameOrId}"` });
|
|
113
|
+
handleError(err, `sd queue ${nameOrId}`);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
sd.command('mine')
|
|
118
|
+
.alias('my')
|
|
119
|
+
.description('Show my open Service Desk tickets')
|
|
120
|
+
.action(async function () {
|
|
121
|
+
const mode = getOutputMode(this);
|
|
122
|
+
const s = mode === 'rich' ? spinner('Fetching my tickets...').start() : null;
|
|
123
|
+
try {
|
|
124
|
+
const result = await getMyIssues();
|
|
125
|
+
if (s) s.success({ text: `${result.issues.length} ticket(s)` });
|
|
126
|
+
if (mode === 'json') outputJson(curateSearchJson(result));
|
|
127
|
+
else if (mode === 'plain') plainIssueSearch(result);
|
|
128
|
+
else formatIssueSearch(result);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
if (s) s.error({ text: 'Failed to fetch tickets' });
|
|
131
|
+
handleError(err, 'sd my tickets');
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
export default sd;
|
package/src/config/store.js
CHANGED
|
@@ -2,40 +2,44 @@ import Conf from 'conf';
|
|
|
2
2
|
import * as p from '@clack/prompts';
|
|
3
3
|
import pc from 'picocolors';
|
|
4
4
|
|
|
5
|
-
const schema = {
|
|
5
|
+
export const schema = {
|
|
6
6
|
bitbucket: {
|
|
7
7
|
type: 'object',
|
|
8
|
+
title: 'Bitbucket',
|
|
8
9
|
properties: {
|
|
9
|
-
workspace: { type: 'string' },
|
|
10
|
-
defaultRepo: { type: 'string' },
|
|
11
|
-
email: { type: 'string' },
|
|
12
|
-
token: { type: 'string' },
|
|
10
|
+
workspace: { type: 'string', title: 'Workspace' },
|
|
11
|
+
defaultRepo: { type: 'string', title: 'Default repo' },
|
|
12
|
+
email: { type: 'string', title: 'Email' },
|
|
13
|
+
token: { type: 'string', title: 'Token', writeOnly: true },
|
|
13
14
|
},
|
|
14
15
|
},
|
|
15
16
|
jira: {
|
|
16
17
|
type: 'object',
|
|
18
|
+
title: 'Jira',
|
|
17
19
|
properties: {
|
|
18
|
-
baseUrl: { type: 'string' },
|
|
19
|
-
token: { type: 'string' },
|
|
20
|
-
defaultProject: { type: 'string' },
|
|
20
|
+
baseUrl: { type: 'string', title: 'Base URL' },
|
|
21
|
+
token: { type: 'string', title: 'Token', writeOnly: true },
|
|
22
|
+
defaultProject: { type: 'string', title: 'Default project' },
|
|
21
23
|
},
|
|
22
24
|
},
|
|
23
25
|
confluence: {
|
|
24
26
|
type: 'object',
|
|
27
|
+
title: 'Confluence',
|
|
25
28
|
properties: {
|
|
26
|
-
baseUrl: { type: 'string' },
|
|
27
|
-
email: { type: 'string' },
|
|
28
|
-
token: { type: 'string' },
|
|
29
|
-
defaultSpace: { type: 'string' },
|
|
29
|
+
baseUrl: { type: 'string', title: 'Base URL' },
|
|
30
|
+
email: { type: 'string', title: 'Email' },
|
|
31
|
+
token: { type: 'string', title: 'Token', writeOnly: true },
|
|
32
|
+
defaultSpace: { type: 'string', title: 'Default space' },
|
|
30
33
|
},
|
|
31
34
|
},
|
|
32
35
|
serviceDesk: {
|
|
33
36
|
type: 'object',
|
|
37
|
+
title: 'Service Desk',
|
|
34
38
|
properties: {
|
|
35
|
-
baseUrl: { type: 'string' },
|
|
36
|
-
email: { type: 'string' },
|
|
37
|
-
token: { type: 'string' },
|
|
38
|
-
defaultProject: { type: 'string' },
|
|
39
|
+
baseUrl: { type: 'string', title: 'Base URL' },
|
|
40
|
+
email: { type: 'string', title: 'Email' },
|
|
41
|
+
token: { type: 'string', title: 'Token', writeOnly: true },
|
|
42
|
+
defaultProject: { type: 'string', title: 'Default project' },
|
|
39
43
|
},
|
|
40
44
|
},
|
|
41
45
|
};
|