@urugus/slack-cli 0.2.12 → 0.3.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 +45 -0
- package/dist/commands/history-display.d.ts +5 -1
- package/dist/commands/history-display.d.ts.map +1 -1
- package/dist/commands/history-display.js +3 -3
- package/dist/commands/history-display.js.map +1 -1
- package/dist/commands/history.d.ts.map +1 -1
- package/dist/commands/history.js +28 -11
- package/dist/commands/history.js.map +1 -1
- package/dist/commands/search.d.ts +3 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +51 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/types/commands.d.ts +10 -0
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/utils/constants.d.ts +5 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +5 -0
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/formatters/search-formatters.d.ts +10 -0
- package/dist/utils/formatters/search-formatters.d.ts.map +1 -0
- package/dist/utils/formatters/search-formatters.js +91 -0
- package/dist/utils/formatters/search-formatters.js.map +1 -0
- package/dist/utils/slack-api-client.d.ts +5 -0
- package/dist/utils/slack-api-client.d.ts.map +1 -1
- package/dist/utils/slack-api-client.js +8 -0
- package/dist/utils/slack-api-client.js.map +1 -1
- package/dist/utils/slack-operations/index.d.ts +1 -0
- package/dist/utils/slack-operations/index.d.ts.map +1 -1
- package/dist/utils/slack-operations/index.js +3 -1
- package/dist/utils/slack-operations/index.js.map +1 -1
- package/dist/utils/slack-operations/message-operations.d.ts +1 -0
- package/dist/utils/slack-operations/message-operations.d.ts.map +1 -1
- package/dist/utils/slack-operations/message-operations.js +21 -0
- package/dist/utils/slack-operations/message-operations.js.map +1 -1
- package/dist/utils/slack-operations/search-operations.d.ts +29 -0
- package/dist/utils/slack-operations/search-operations.d.ts.map +1 -0
- package/dist/utils/slack-operations/search-operations.js +37 -0
- package/dist/utils/slack-operations/search-operations.js.map +1 -0
- package/dist/utils/validators.d.ts +16 -0
- package/dist/utils/validators.d.ts.map +1 -1
- package/dist/utils/validators.js +50 -0
- package/dist/utils/validators.js.map +1 -1
- package/package.json +5 -2
- package/.claude/settings.local.json +0 -75
- package/.github/dependabot.yml +0 -18
- package/.github/workflows/ci.yml +0 -70
- package/.github/workflows/pr-validation.yml +0 -41
- package/.prettierignore +0 -11
- package/.prettierrc +0 -10
- package/CHANGELOG.md +0 -61
- package/CLAUDE.md +0 -16
- package/eslint.config.js +0 -38
- package/src/commands/channels.ts +0 -50
- package/src/commands/config-subcommands.ts +0 -63
- package/src/commands/config.ts +0 -50
- package/src/commands/history-display.ts +0 -19
- package/src/commands/history-validators.ts +0 -46
- package/src/commands/history.ts +0 -61
- package/src/commands/scheduled.ts +0 -71
- package/src/commands/send.ts +0 -69
- package/src/commands/unread.ts +0 -122
- package/src/index.ts +0 -27
- package/src/types/commands.ts +0 -58
- package/src/types/config.ts +0 -20
- package/src/utils/channel-formatter.ts +0 -45
- package/src/utils/channel-resolver.ts +0 -82
- package/src/utils/client-factory.ts +0 -10
- package/src/utils/command-wrapper.ts +0 -27
- package/src/utils/config/config-file-manager.ts +0 -56
- package/src/utils/config/profile-manager.ts +0 -79
- package/src/utils/config/token-crypto-service.ts +0 -80
- package/src/utils/config-helper.ts +0 -21
- package/src/utils/constants.ts +0 -78
- package/src/utils/date-utils.ts +0 -8
- package/src/utils/error-utils.ts +0 -6
- package/src/utils/errors.ts +0 -33
- package/src/utils/format-utils.ts +0 -9
- package/src/utils/formatters/base-formatter.ts +0 -34
- package/src/utils/formatters/channel-formatters.ts +0 -71
- package/src/utils/formatters/channels-list-formatters.ts +0 -55
- package/src/utils/formatters/history-formatters.ts +0 -123
- package/src/utils/formatters/message-formatters.ts +0 -85
- package/src/utils/mention-utils.ts +0 -47
- package/src/utils/option-parsers.ts +0 -100
- package/src/utils/profile-config.ts +0 -161
- package/src/utils/schedule-utils.ts +0 -41
- package/src/utils/slack-api-client.ts +0 -135
- package/src/utils/slack-operations/base-client.ts +0 -30
- package/src/utils/slack-operations/channel-operations.ts +0 -161
- package/src/utils/slack-operations/index.ts +0 -3
- package/src/utils/slack-operations/message-operations.ts +0 -176
- package/src/utils/slack-patterns.ts +0 -9
- package/src/utils/token-utils.ts +0 -17
- package/src/utils/validators.ts +0 -263
- package/tests/commands/channels.test.ts +0 -250
- package/tests/commands/config.test.ts +0 -158
- package/tests/commands/history.test.ts +0 -403
- package/tests/commands/scheduled.test.ts +0 -131
- package/tests/commands/send.test.ts +0 -414
- package/tests/commands/unread.test.ts +0 -492
- package/tests/index.test.ts +0 -40
- package/tests/test-utils.ts +0 -28
- package/tests/utils/channel-resolver.test.ts +0 -161
- package/tests/utils/config/config-file-manager.test.ts +0 -118
- package/tests/utils/config/profile-manager.test.ts +0 -266
- package/tests/utils/config/token-crypto-service.test.ts +0 -98
- package/tests/utils/config.test.ts +0 -400
- package/tests/utils/date-utils.test.ts +0 -30
- package/tests/utils/error-utils.test.ts +0 -34
- package/tests/utils/format-utils.test.ts +0 -61
- package/tests/utils/mention-utils.test.ts +0 -100
- package/tests/utils/option-parsers.test.ts +0 -173
- package/tests/utils/profile-config.test.ts +0 -282
- package/tests/utils/schedule-utils.test.ts +0 -63
- package/tests/utils/slack-api-client.test.ts +0 -313
- package/tests/utils/slack-operations/channel-operations.test.ts +0 -248
- package/tests/utils/slack-operations/message-operations.test.ts +0 -163
- package/tests/utils/token-utils.test.ts +0 -33
- package/tests/utils/validators.test.ts +0 -307
- package/tsconfig.json +0 -22
- package/vitest.config.ts +0 -27
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { AbstractFormatter, JsonFormatter, createFormatterFactory } from './base-formatter';
|
|
2
|
-
import { ChannelInfo } from '../channel-formatter';
|
|
3
|
-
|
|
4
|
-
export interface ChannelsListFormatterOptions {
|
|
5
|
-
channels: ChannelInfo[];
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
class ChannelsTableFormatter extends AbstractFormatter<ChannelsListFormatterOptions> {
|
|
9
|
-
format({ channels }: ChannelsListFormatterOptions): void {
|
|
10
|
-
// Print table header
|
|
11
|
-
console.log('Name Type Members Created Description');
|
|
12
|
-
console.log('─'.repeat(65));
|
|
13
|
-
|
|
14
|
-
// Print channel rows
|
|
15
|
-
channels.forEach((channel) => {
|
|
16
|
-
const name = channel.name.padEnd(17);
|
|
17
|
-
const type = channel.type.padEnd(9);
|
|
18
|
-
const members = channel.members.toString().padEnd(8);
|
|
19
|
-
const created = channel.created.padEnd(12);
|
|
20
|
-
const purpose =
|
|
21
|
-
channel.purpose.length > 30 ? channel.purpose.substring(0, 27) + '...' : channel.purpose;
|
|
22
|
-
|
|
23
|
-
console.log(`${name} ${type} ${members} ${created} ${purpose}`);
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
class ChannelsSimpleFormatter extends AbstractFormatter<ChannelsListFormatterOptions> {
|
|
29
|
-
format({ channels }: ChannelsListFormatterOptions): void {
|
|
30
|
-
channels.forEach((channel) => console.log(channel.name));
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
class ChannelsJsonFormatter extends JsonFormatter<ChannelsListFormatterOptions> {
|
|
35
|
-
protected transform({ channels }: ChannelsListFormatterOptions) {
|
|
36
|
-
return channels.map((channel) => ({
|
|
37
|
-
id: channel.id,
|
|
38
|
-
name: channel.name,
|
|
39
|
-
type: channel.type,
|
|
40
|
-
members: channel.members,
|
|
41
|
-
created: channel.created + 'T00:00:00Z',
|
|
42
|
-
purpose: channel.purpose,
|
|
43
|
-
}));
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const channelsListFormatterFactory = createFormatterFactory<ChannelsListFormatterOptions>({
|
|
48
|
-
table: new ChannelsTableFormatter(),
|
|
49
|
-
simple: new ChannelsSimpleFormatter(),
|
|
50
|
-
json: new ChannelsJsonFormatter(),
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
export function createChannelsListFormatter(format: string) {
|
|
54
|
-
return channelsListFormatterFactory.create(format);
|
|
55
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { AbstractFormatter, JsonFormatter, createFormatterFactory } from './base-formatter';
|
|
3
|
-
import { formatSlackTimestamp } from '../date-utils';
|
|
4
|
-
|
|
5
|
-
function formatTimestampFixed(slackTimestamp: string): string {
|
|
6
|
-
const timestamp = parseFloat(slackTimestamp);
|
|
7
|
-
const date = new Date(timestamp * 1000);
|
|
8
|
-
const year = date.getUTCFullYear();
|
|
9
|
-
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
|
|
10
|
-
const day = String(date.getUTCDate()).padStart(2, '0');
|
|
11
|
-
const hours = String(date.getUTCHours()).padStart(2, '0');
|
|
12
|
-
const minutes = String(date.getUTCMinutes()).padStart(2, '0');
|
|
13
|
-
const seconds = String(date.getUTCSeconds()).padStart(2, '0');
|
|
14
|
-
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
15
|
-
}
|
|
16
|
-
import { formatMessageWithMentions } from '../format-utils';
|
|
17
|
-
import { Message as SlackMessage } from '../slack-api-client';
|
|
18
|
-
|
|
19
|
-
export interface HistoryFormatterOptions {
|
|
20
|
-
channelName: string;
|
|
21
|
-
messages: SlackMessage[];
|
|
22
|
-
users: Map<string, string>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
class TableHistoryFormatter extends AbstractFormatter<HistoryFormatterOptions> {
|
|
26
|
-
format(options: HistoryFormatterOptions): void {
|
|
27
|
-
const { channelName, messages, users } = options;
|
|
28
|
-
|
|
29
|
-
console.log(chalk.bold(`\nMessage History for #${channelName}:`));
|
|
30
|
-
|
|
31
|
-
if (messages.length === 0) {
|
|
32
|
-
console.log(chalk.yellow('No messages found'));
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
console.log('');
|
|
37
|
-
messages.forEach((message) => {
|
|
38
|
-
const timestamp = formatTimestampFixed(message.ts);
|
|
39
|
-
const username = this.getUsername(message, users);
|
|
40
|
-
|
|
41
|
-
console.log(`${chalk.gray(`[${timestamp}]`)} ${chalk.cyan(username)}`);
|
|
42
|
-
const text = message.text ? formatMessageWithMentions(message.text, users) : '(no text)';
|
|
43
|
-
console.log(text);
|
|
44
|
-
console.log('');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
console.log(chalk.green(`✓ Displayed ${messages.length} message(s)`));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
private getUsername(message: SlackMessage, users: Map<string, string>): string {
|
|
51
|
-
if (message.user) {
|
|
52
|
-
return users.get(message.user) || 'Unknown User';
|
|
53
|
-
}
|
|
54
|
-
if (message.bot_id) {
|
|
55
|
-
return 'Bot';
|
|
56
|
-
}
|
|
57
|
-
return 'Unknown';
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
class SimpleHistoryFormatter extends AbstractFormatter<HistoryFormatterOptions> {
|
|
62
|
-
format(options: HistoryFormatterOptions): void {
|
|
63
|
-
const { messages, users } = options;
|
|
64
|
-
|
|
65
|
-
if (messages.length === 0) {
|
|
66
|
-
console.log('No messages found');
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
messages.forEach((message) => {
|
|
71
|
-
const timestamp = formatTimestampFixed(message.ts);
|
|
72
|
-
const username = this.getUsername(message, users);
|
|
73
|
-
const text = message.text ? formatMessageWithMentions(message.text, users) : '(no text)';
|
|
74
|
-
console.log(`[${timestamp}] ${username}: ${text}`);
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
private getUsername(message: SlackMessage, users: Map<string, string>): string {
|
|
79
|
-
if (message.user) {
|
|
80
|
-
return users.get(message.user) || 'Unknown User';
|
|
81
|
-
}
|
|
82
|
-
if (message.bot_id) {
|
|
83
|
-
return 'Bot';
|
|
84
|
-
}
|
|
85
|
-
return 'Unknown';
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
class JsonHistoryFormatter extends JsonFormatter<HistoryFormatterOptions> {
|
|
90
|
-
protected transform(options: HistoryFormatterOptions) {
|
|
91
|
-
const { channelName, messages, users } = options;
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
channel: channelName,
|
|
95
|
-
messages: messages.map((message) => ({
|
|
96
|
-
timestamp: formatTimestampFixed(message.ts),
|
|
97
|
-
user: this.getUsername(message, users),
|
|
98
|
-
text: message.text || '(no text)',
|
|
99
|
-
})),
|
|
100
|
-
total: messages.length,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
private getUsername(message: SlackMessage, users: Map<string, string>): string {
|
|
105
|
-
if (message.user) {
|
|
106
|
-
return users.get(message.user) || 'Unknown User';
|
|
107
|
-
}
|
|
108
|
-
if (message.bot_id) {
|
|
109
|
-
return 'Bot';
|
|
110
|
-
}
|
|
111
|
-
return 'Unknown';
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const historyFormatterFactory = createFormatterFactory<HistoryFormatterOptions>({
|
|
116
|
-
table: new TableHistoryFormatter(),
|
|
117
|
-
simple: new SimpleHistoryFormatter(),
|
|
118
|
-
json: new JsonHistoryFormatter(),
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
export function createHistoryFormatter(format: string) {
|
|
122
|
-
return historyFormatterFactory.create(format);
|
|
123
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { AbstractFormatter, JsonFormatter, createFormatterFactory } from './base-formatter';
|
|
3
|
-
import { formatSlackTimestamp } from '../date-utils';
|
|
4
|
-
import { formatMessageWithMentions } from '../format-utils';
|
|
5
|
-
import { formatChannelName } from '../channel-formatter';
|
|
6
|
-
|
|
7
|
-
export interface MessageFormatterOptions {
|
|
8
|
-
channel: any;
|
|
9
|
-
messages: any[];
|
|
10
|
-
users: Map<string, string>;
|
|
11
|
-
countOnly: boolean;
|
|
12
|
-
format: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
class TableMessageFormatter extends AbstractFormatter<MessageFormatterOptions> {
|
|
16
|
-
format(options: MessageFormatterOptions): void {
|
|
17
|
-
const { channel, messages, users, countOnly } = options;
|
|
18
|
-
const channelName = formatChannelName(channel.name);
|
|
19
|
-
|
|
20
|
-
console.log(chalk.bold(`${channelName}: ${channel.unread_count || 0} unread messages`));
|
|
21
|
-
|
|
22
|
-
if (!countOnly && messages.length > 0) {
|
|
23
|
-
console.log('');
|
|
24
|
-
messages.forEach((message) => {
|
|
25
|
-
const timestamp = formatSlackTimestamp(message.ts);
|
|
26
|
-
const author = message.user ? users.get(message.user) || message.user : 'unknown';
|
|
27
|
-
console.log(`${chalk.gray(timestamp)} ${chalk.cyan(author)}`);
|
|
28
|
-
const text = message.text ? formatMessageWithMentions(message.text, users) : '(no text)';
|
|
29
|
-
console.log(text);
|
|
30
|
-
console.log('');
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
class SimpleMessageFormatter extends AbstractFormatter<MessageFormatterOptions> {
|
|
37
|
-
format(options: MessageFormatterOptions): void {
|
|
38
|
-
const { channel, messages, users, countOnly } = options;
|
|
39
|
-
const channelName = formatChannelName(channel.name);
|
|
40
|
-
|
|
41
|
-
console.log(`${channelName} (${channel.unread_count || 0})`);
|
|
42
|
-
|
|
43
|
-
if (!countOnly && messages.length > 0) {
|
|
44
|
-
messages.forEach((message) => {
|
|
45
|
-
const timestamp = formatSlackTimestamp(message.ts);
|
|
46
|
-
const author = message.user ? users.get(message.user) || message.user : 'unknown';
|
|
47
|
-
const text = message.text ? formatMessageWithMentions(message.text, users) : '(no text)';
|
|
48
|
-
console.log(`[${timestamp}] ${author}: ${text}`);
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
class JsonMessageFormatter extends JsonFormatter<MessageFormatterOptions> {
|
|
55
|
-
protected transform(options: MessageFormatterOptions) {
|
|
56
|
-
const { channel, messages, users, countOnly } = options;
|
|
57
|
-
const channelName = formatChannelName(channel.name);
|
|
58
|
-
|
|
59
|
-
const output: any = {
|
|
60
|
-
channel: channelName,
|
|
61
|
-
channelId: channel.id,
|
|
62
|
-
unreadCount: channel.unread_count || 0,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
if (!countOnly && messages.length > 0) {
|
|
66
|
-
output.messages = messages.map((message) => ({
|
|
67
|
-
timestamp: formatSlackTimestamp(message.ts),
|
|
68
|
-
author: message.user ? users.get(message.user) || message.user : 'unknown',
|
|
69
|
-
text: message.text || '(no text)',
|
|
70
|
-
}));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return output;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const messageFormatterFactory = createFormatterFactory<MessageFormatterOptions>({
|
|
78
|
-
table: new TableMessageFormatter(),
|
|
79
|
-
simple: new SimpleMessageFormatter(),
|
|
80
|
-
json: new JsonMessageFormatter(),
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
export function createMessageFormatter(format: string) {
|
|
84
|
-
return messageFormatterFactory.create(format);
|
|
85
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { USER_MENTION_PATTERN } from './slack-patterns';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Extracts all user IDs from mentions in a text
|
|
5
|
-
* @param text - The text containing Slack mentions
|
|
6
|
-
* @returns Array of unique user IDs found in mentions
|
|
7
|
-
*/
|
|
8
|
-
export function extractUserIdsFromMentions(text: string): string[] {
|
|
9
|
-
const userIds: string[] = [];
|
|
10
|
-
const matches = text.matchAll(USER_MENTION_PATTERN);
|
|
11
|
-
|
|
12
|
-
for (const match of matches) {
|
|
13
|
-
const userId = match[1];
|
|
14
|
-
if (userId) {
|
|
15
|
-
userIds.push(userId);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return userIds;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Extracts all unique user IDs from an array of messages
|
|
24
|
-
* Includes both message authors and mentioned users
|
|
25
|
-
* @param messages - Array of messages to extract user IDs from
|
|
26
|
-
* @returns Array of unique user IDs
|
|
27
|
-
*/
|
|
28
|
-
export function extractAllUserIds(messages: Array<{ user?: string; text?: string }>): string[] {
|
|
29
|
-
const userIds = new Set<string>();
|
|
30
|
-
|
|
31
|
-
for (const message of messages) {
|
|
32
|
-
// Add message author
|
|
33
|
-
if (message.user) {
|
|
34
|
-
userIds.add(message.user);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Add mentioned users
|
|
38
|
-
if (message.text) {
|
|
39
|
-
const mentionedIds = extractUserIdsFromMentions(message.text);
|
|
40
|
-
for (const id of mentionedIds) {
|
|
41
|
-
userIds.add(id);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return Array.from(userIds);
|
|
47
|
-
}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Common option parsing utilities to reduce duplication
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Parse format option with default value
|
|
7
|
-
*/
|
|
8
|
-
export function parseFormat(format?: string, defaultFormat = 'table'): string {
|
|
9
|
-
return format || defaultFormat;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Parse limit option with default value
|
|
14
|
-
*/
|
|
15
|
-
export function parseLimit(limit: string | undefined, defaultLimit: number): number {
|
|
16
|
-
return parseInt(limit || defaultLimit.toString(), 10);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Parse boolean option with default value
|
|
21
|
-
*/
|
|
22
|
-
export function parseBoolean(value?: boolean, defaultValue = false): boolean {
|
|
23
|
-
return value !== undefined ? value : defaultValue;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Parse count option with default value
|
|
28
|
-
*/
|
|
29
|
-
export function parseCount(
|
|
30
|
-
count: string | undefined,
|
|
31
|
-
defaultCount: number,
|
|
32
|
-
min?: number,
|
|
33
|
-
max?: number
|
|
34
|
-
): number {
|
|
35
|
-
const parsed = parseInt(count || defaultCount.toString(), 10);
|
|
36
|
-
|
|
37
|
-
if (isNaN(parsed)) {
|
|
38
|
-
return defaultCount;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (min !== undefined && parsed < min) {
|
|
42
|
-
return min;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (max !== undefined && parsed > max) {
|
|
46
|
-
return max;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return parsed;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Parse profile option
|
|
54
|
-
*/
|
|
55
|
-
export function parseProfile(profile?: string): string | undefined {
|
|
56
|
-
return profile;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Common option defaults
|
|
61
|
-
*/
|
|
62
|
-
export const OPTION_DEFAULTS = {
|
|
63
|
-
format: 'table',
|
|
64
|
-
limit: 100,
|
|
65
|
-
countOnly: false,
|
|
66
|
-
includeArchived: false,
|
|
67
|
-
} as const;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Parse common list options
|
|
71
|
-
*/
|
|
72
|
-
export interface ListOptions {
|
|
73
|
-
format?: string;
|
|
74
|
-
limit?: string;
|
|
75
|
-
countOnly?: boolean;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export interface ParsedListOptions {
|
|
79
|
-
format: string;
|
|
80
|
-
limit: number;
|
|
81
|
-
countOnly: boolean;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function parseListOptions(
|
|
85
|
-
options: ListOptions,
|
|
86
|
-
defaults?: Partial<ParsedListOptions>
|
|
87
|
-
): ParsedListOptions {
|
|
88
|
-
const mergedDefaults = {
|
|
89
|
-
format: OPTION_DEFAULTS.format,
|
|
90
|
-
limit: OPTION_DEFAULTS.limit,
|
|
91
|
-
countOnly: OPTION_DEFAULTS.countOnly,
|
|
92
|
-
...defaults,
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
return {
|
|
96
|
-
format: parseFormat(options.format, mergedDefaults.format),
|
|
97
|
-
limit: parseLimit(options.limit, mergedDefaults.limit),
|
|
98
|
-
countOnly: parseBoolean(options.countOnly, mergedDefaults.countOnly),
|
|
99
|
-
};
|
|
100
|
-
}
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs/promises';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import * as os from 'os';
|
|
4
|
-
import type { Config, ConfigOptions, ConfigStore, Profile } from '../types/config';
|
|
5
|
-
import { DEFAULT_PROFILE_NAME, ERROR_MESSAGES, FILE_PERMISSIONS } from './constants';
|
|
6
|
-
import { maskToken } from './token-utils';
|
|
7
|
-
|
|
8
|
-
export class ProfileConfigManager {
|
|
9
|
-
private configPath: string;
|
|
10
|
-
|
|
11
|
-
constructor(options: ConfigOptions = {}) {
|
|
12
|
-
const configDir = options.configDir || path.join(os.homedir(), '.slack-cli');
|
|
13
|
-
this.configPath = path.join(configDir, 'config.json');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async setToken(token: string, profile?: string): Promise<void> {
|
|
17
|
-
const store = await this.getConfigStore();
|
|
18
|
-
const profileName = profile || store.defaultProfile || DEFAULT_PROFILE_NAME;
|
|
19
|
-
const config: Config = {
|
|
20
|
-
token,
|
|
21
|
-
updatedAt: new Date().toISOString(),
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
store.profiles[profileName] = config;
|
|
25
|
-
|
|
26
|
-
// Set as default profile if it's the first one or explicitly setting default
|
|
27
|
-
if (!store.defaultProfile || profileName === DEFAULT_PROFILE_NAME) {
|
|
28
|
-
store.defaultProfile = profileName;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
await this.saveConfigStore(store);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async getConfig(profile?: string): Promise<Config | null> {
|
|
35
|
-
const store = await this.getConfigStore();
|
|
36
|
-
const profileName = profile || store.defaultProfile || DEFAULT_PROFILE_NAME;
|
|
37
|
-
|
|
38
|
-
return store.profiles[profileName] || null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async listProfiles(): Promise<Profile[]> {
|
|
42
|
-
const store = await this.getConfigStore();
|
|
43
|
-
const currentProfile = store.defaultProfile || DEFAULT_PROFILE_NAME;
|
|
44
|
-
|
|
45
|
-
return Object.entries(store.profiles).map(([name, config]) => ({
|
|
46
|
-
name,
|
|
47
|
-
config,
|
|
48
|
-
isDefault: name === currentProfile,
|
|
49
|
-
}));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async useProfile(profile: string): Promise<void> {
|
|
53
|
-
const store = await this.getConfigStore();
|
|
54
|
-
|
|
55
|
-
if (!store.profiles[profile]) {
|
|
56
|
-
throw new Error(`Profile "${profile}" does not exist`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
store.defaultProfile = profile;
|
|
60
|
-
await this.saveConfigStore(store);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async getCurrentProfile(): Promise<string> {
|
|
64
|
-
const store = await this.getConfigStore();
|
|
65
|
-
return store.defaultProfile || DEFAULT_PROFILE_NAME;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async clearConfig(profile?: string): Promise<void> {
|
|
69
|
-
const store = await this.getConfigStore();
|
|
70
|
-
const profileName = profile || store.defaultProfile || DEFAULT_PROFILE_NAME;
|
|
71
|
-
|
|
72
|
-
delete store.profiles[profileName];
|
|
73
|
-
|
|
74
|
-
// If we deleted the default profile, set a new default
|
|
75
|
-
if (store.defaultProfile === profileName) {
|
|
76
|
-
const remainingProfiles = Object.keys(store.profiles);
|
|
77
|
-
if (remainingProfiles.length > 0) {
|
|
78
|
-
store.defaultProfile = remainingProfiles[0];
|
|
79
|
-
} else {
|
|
80
|
-
// No profiles left, delete the config file
|
|
81
|
-
try {
|
|
82
|
-
await fs.unlink(this.configPath);
|
|
83
|
-
} catch (error: unknown) {
|
|
84
|
-
if (error && typeof error === 'object' && 'code' in error && error.code !== 'ENOENT') {
|
|
85
|
-
throw error;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
await this.saveConfigStore(store);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
maskToken(token: string): string {
|
|
96
|
-
return maskToken(token);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
private async getConfigStore(): Promise<ConfigStore> {
|
|
100
|
-
try {
|
|
101
|
-
const data = await fs.readFile(this.configPath, 'utf-8');
|
|
102
|
-
const parsed = JSON.parse(data);
|
|
103
|
-
|
|
104
|
-
// Handle migration from old format
|
|
105
|
-
if (this.needsMigration(parsed)) {
|
|
106
|
-
return await this.migrateOldConfig(parsed);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return parsed as ConfigStore;
|
|
110
|
-
} catch (error: unknown) {
|
|
111
|
-
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
|
|
112
|
-
return { profiles: {} };
|
|
113
|
-
}
|
|
114
|
-
if (error instanceof SyntaxError) {
|
|
115
|
-
throw new Error(ERROR_MESSAGES.INVALID_CONFIG_FORMAT);
|
|
116
|
-
}
|
|
117
|
-
throw error;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
private needsMigration(data: unknown): boolean {
|
|
122
|
-
const configData = data as Record<string, unknown>;
|
|
123
|
-
return Boolean(configData.token && !configData.profiles);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
private async migrateOldConfig(oldData: unknown): Promise<ConfigStore> {
|
|
127
|
-
const data = oldData as { token: string; updatedAt: string };
|
|
128
|
-
const oldConfig: Config = {
|
|
129
|
-
token: data.token,
|
|
130
|
-
updatedAt: data.updatedAt,
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const newStore: ConfigStore = {
|
|
134
|
-
profiles: { [DEFAULT_PROFILE_NAME]: oldConfig },
|
|
135
|
-
defaultProfile: DEFAULT_PROFILE_NAME,
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
// Save migrated config
|
|
139
|
-
await this.saveConfigStore(newStore);
|
|
140
|
-
return newStore;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
private async saveConfigStore(store: ConfigStore): Promise<void> {
|
|
144
|
-
const configDir = path.dirname(this.configPath);
|
|
145
|
-
await fs.mkdir(configDir, { recursive: true });
|
|
146
|
-
|
|
147
|
-
await fs.writeFile(this.configPath, JSON.stringify(store, null, 2));
|
|
148
|
-
await fs.chmod(this.configPath, FILE_PERMISSIONS.CONFIG_FILE);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export const profileConfig = {
|
|
153
|
-
getCurrentProfile: (): string => {
|
|
154
|
-
return DEFAULT_PROFILE_NAME;
|
|
155
|
-
},
|
|
156
|
-
getToken: (_profile?: string): string | undefined => {
|
|
157
|
-
// This is a simplified version for testing
|
|
158
|
-
// In real usage, it would need to be async
|
|
159
|
-
return undefined;
|
|
160
|
-
},
|
|
161
|
-
};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
export function parseScheduledTimestamp(value: string): number | null {
|
|
2
|
-
const trimmed = value.trim();
|
|
3
|
-
|
|
4
|
-
if (/^\d+$/.test(trimmed)) {
|
|
5
|
-
const timestamp = Number.parseInt(trimmed, 10);
|
|
6
|
-
return Number.isSafeInteger(timestamp) ? timestamp : null;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const parsedMs = Date.parse(trimmed);
|
|
10
|
-
if (Number.isNaN(parsedMs)) {
|
|
11
|
-
return null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return Math.floor(parsedMs / 1000);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function resolvePostAt(
|
|
18
|
-
at: string | undefined,
|
|
19
|
-
afterMinutes: string | undefined,
|
|
20
|
-
nowMs = Date.now()
|
|
21
|
-
): number | null {
|
|
22
|
-
if (at) {
|
|
23
|
-
return parseScheduledTimestamp(at);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (!afterMinutes) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const trimmedAfter = afterMinutes.trim();
|
|
31
|
-
if (!/^\d+$/.test(trimmedAfter)) {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const minutes = Number.parseInt(trimmedAfter, 10);
|
|
36
|
-
if (!Number.isSafeInteger(minutes) || minutes <= 0) {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return Math.floor(nowMs / 1000) + minutes * 60;
|
|
41
|
-
}
|