confluence-cli 1.25.1 → 1.26.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/bin/confluence.js +108 -27
- package/lib/config.js +163 -20
- package/package.json +1 -1
package/bin/confluence.js
CHANGED
|
@@ -4,7 +4,7 @@ const { program } = require('commander');
|
|
|
4
4
|
const chalk = require('chalk');
|
|
5
5
|
const inquirer = require('inquirer');
|
|
6
6
|
const ConfluenceClient = require('../lib/confluence-client');
|
|
7
|
-
const { getConfig, initConfig } = require('../lib/config');
|
|
7
|
+
const { getConfig, initConfig, listProfiles, setActiveProfile, deleteProfile, isValidProfileName } = require('../lib/config');
|
|
8
8
|
const Analytics = require('../lib/analytics');
|
|
9
9
|
const pkg = require('../package.json');
|
|
10
10
|
|
|
@@ -16,7 +16,13 @@ function buildPageUrl(config, path) {
|
|
|
16
16
|
program
|
|
17
17
|
.name('confluence')
|
|
18
18
|
.description('CLI tool for Atlassian Confluence')
|
|
19
|
-
.version(pkg.version)
|
|
19
|
+
.version(pkg.version)
|
|
20
|
+
.option('--profile <name>', 'Use a specific configuration profile');
|
|
21
|
+
|
|
22
|
+
// Helper: resolve profile name from global --profile flag
|
|
23
|
+
function getProfileName() {
|
|
24
|
+
return program.opts().profile || undefined;
|
|
25
|
+
}
|
|
20
26
|
|
|
21
27
|
// Init command
|
|
22
28
|
program
|
|
@@ -29,7 +35,8 @@ program
|
|
|
29
35
|
.option('-e, --email <email>', 'Email or username for basic auth')
|
|
30
36
|
.option('-t, --token <token>', 'API token')
|
|
31
37
|
.action(async (options) => {
|
|
32
|
-
|
|
38
|
+
const profile = getProfileName();
|
|
39
|
+
await initConfig({ ...options, profile });
|
|
33
40
|
});
|
|
34
41
|
|
|
35
42
|
// Read command
|
|
@@ -40,7 +47,7 @@ program
|
|
|
40
47
|
.action(async (pageId, options) => {
|
|
41
48
|
const analytics = new Analytics();
|
|
42
49
|
try {
|
|
43
|
-
const client = new ConfluenceClient(getConfig());
|
|
50
|
+
const client = new ConfluenceClient(getConfig(getProfileName()));
|
|
44
51
|
const content = await client.readPage(pageId, options.format);
|
|
45
52
|
console.log(content);
|
|
46
53
|
analytics.track('read', true);
|
|
@@ -58,7 +65,7 @@ program
|
|
|
58
65
|
.action(async (pageId) => {
|
|
59
66
|
const analytics = new Analytics();
|
|
60
67
|
try {
|
|
61
|
-
const client = new ConfluenceClient(getConfig());
|
|
68
|
+
const client = new ConfluenceClient(getConfig(getProfileName()));
|
|
62
69
|
const info = await client.getPageInfo(pageId);
|
|
63
70
|
console.log(chalk.blue('Page Information:'));
|
|
64
71
|
console.log(`Title: ${chalk.green(info.title)}`);
|
|
@@ -85,7 +92,7 @@ program
|
|
|
85
92
|
.action(async (query, options) => {
|
|
86
93
|
const analytics = new Analytics();
|
|
87
94
|
try {
|
|
88
|
-
const client = new ConfluenceClient(getConfig());
|
|
95
|
+
const client = new ConfluenceClient(getConfig(getProfileName()));
|
|
89
96
|
const results = await client.search(query, parseInt(options.limit), options.cql);
|
|
90
97
|
|
|
91
98
|
if (results.length === 0) {
|
|
@@ -116,7 +123,7 @@ program
|
|
|
116
123
|
.action(async () => {
|
|
117
124
|
const analytics = new Analytics();
|
|
118
125
|
try {
|
|
119
|
-
const config = getConfig();
|
|
126
|
+
const config = getConfig(getProfileName());
|
|
120
127
|
const client = new ConfluenceClient(config);
|
|
121
128
|
const spaces = await client.getSpaces();
|
|
122
129
|
|
|
@@ -200,7 +207,7 @@ program
|
|
|
200
207
|
.action(async (title, spaceKey, options) => {
|
|
201
208
|
const analytics = new Analytics();
|
|
202
209
|
try {
|
|
203
|
-
const config = getConfig();
|
|
210
|
+
const config = getConfig(getProfileName());
|
|
204
211
|
const client = new ConfluenceClient(config);
|
|
205
212
|
|
|
206
213
|
let content = '';
|
|
@@ -243,7 +250,7 @@ program
|
|
|
243
250
|
.action(async (title, parentId, options) => {
|
|
244
251
|
const analytics = new Analytics();
|
|
245
252
|
try {
|
|
246
|
-
const config = getConfig();
|
|
253
|
+
const config = getConfig(getProfileName());
|
|
247
254
|
const client = new ConfluenceClient(config);
|
|
248
255
|
|
|
249
256
|
// Get parent page info to get space key
|
|
@@ -297,7 +304,7 @@ program
|
|
|
297
304
|
throw new Error('At least one of --title, --file, or --content must be provided.');
|
|
298
305
|
}
|
|
299
306
|
|
|
300
|
-
const config = getConfig();
|
|
307
|
+
const config = getConfig(getProfileName());
|
|
301
308
|
const client = new ConfluenceClient(config);
|
|
302
309
|
|
|
303
310
|
let content = null; // Use null to indicate no content change
|
|
@@ -336,7 +343,7 @@ program
|
|
|
336
343
|
.action(async (pageId, newParentId, options) => {
|
|
337
344
|
const analytics = new Analytics();
|
|
338
345
|
try {
|
|
339
|
-
const config = getConfig();
|
|
346
|
+
const config = getConfig(getProfileName());
|
|
340
347
|
const client = new ConfluenceClient(config);
|
|
341
348
|
const result = await client.movePage(pageId, newParentId, options.title);
|
|
342
349
|
|
|
@@ -363,7 +370,7 @@ program
|
|
|
363
370
|
.action(async (pageIdOrUrl, options) => {
|
|
364
371
|
const analytics = new Analytics();
|
|
365
372
|
try {
|
|
366
|
-
const config = getConfig();
|
|
373
|
+
const config = getConfig(getProfileName());
|
|
367
374
|
const client = new ConfluenceClient(config);
|
|
368
375
|
const pageInfo = await client.getPageInfo(pageIdOrUrl);
|
|
369
376
|
|
|
@@ -406,7 +413,7 @@ program
|
|
|
406
413
|
.action(async (pageId, options) => {
|
|
407
414
|
const analytics = new Analytics();
|
|
408
415
|
try {
|
|
409
|
-
const config = getConfig();
|
|
416
|
+
const config = getConfig(getProfileName());
|
|
410
417
|
const client = new ConfluenceClient(config);
|
|
411
418
|
const pageData = await client.getPageForEdit(pageId);
|
|
412
419
|
|
|
@@ -443,7 +450,7 @@ program
|
|
|
443
450
|
.action(async (title, options) => {
|
|
444
451
|
const analytics = new Analytics();
|
|
445
452
|
try {
|
|
446
|
-
const config = getConfig();
|
|
453
|
+
const config = getConfig(getProfileName());
|
|
447
454
|
const client = new ConfluenceClient(config);
|
|
448
455
|
const pageInfo = await client.findPageByTitle(title, options.space);
|
|
449
456
|
|
|
@@ -473,7 +480,7 @@ program
|
|
|
473
480
|
.action(async (pageId, options) => {
|
|
474
481
|
const analytics = new Analytics();
|
|
475
482
|
try {
|
|
476
|
-
const config = getConfig();
|
|
483
|
+
const config = getConfig(getProfileName());
|
|
477
484
|
const client = new ConfluenceClient(config);
|
|
478
485
|
const maxResults = options.limit ? parseInt(options.limit, 10) : null;
|
|
479
486
|
const pattern = options.pattern ? options.pattern.trim() : null;
|
|
@@ -605,7 +612,7 @@ program
|
|
|
605
612
|
|
|
606
613
|
const fs = require('fs');
|
|
607
614
|
const path = require('path');
|
|
608
|
-
const config = getConfig();
|
|
615
|
+
const config = getConfig(getProfileName());
|
|
609
616
|
const client = new ConfluenceClient(config);
|
|
610
617
|
|
|
611
618
|
const resolvedFiles = files.map((filePath) => ({
|
|
@@ -652,7 +659,7 @@ program
|
|
|
652
659
|
.action(async (pageId, attachmentId, options) => {
|
|
653
660
|
const analytics = new Analytics();
|
|
654
661
|
try {
|
|
655
|
-
const config = getConfig();
|
|
662
|
+
const config = getConfig(getProfileName());
|
|
656
663
|
const client = new ConfluenceClient(config);
|
|
657
664
|
|
|
658
665
|
if (!options.yes) {
|
|
@@ -696,7 +703,7 @@ program
|
|
|
696
703
|
.action(async (pageId, options) => {
|
|
697
704
|
const analytics = new Analytics();
|
|
698
705
|
try {
|
|
699
|
-
const config = getConfig();
|
|
706
|
+
const config = getConfig(getProfileName());
|
|
700
707
|
const client = new ConfluenceClient(config);
|
|
701
708
|
|
|
702
709
|
const format = (options.format || 'text').toLowerCase();
|
|
@@ -766,7 +773,7 @@ program
|
|
|
766
773
|
.action(async (pageId, key, options) => {
|
|
767
774
|
const analytics = new Analytics();
|
|
768
775
|
try {
|
|
769
|
-
const config = getConfig();
|
|
776
|
+
const config = getConfig(getProfileName());
|
|
770
777
|
const client = new ConfluenceClient(config);
|
|
771
778
|
|
|
772
779
|
const format = (options.format || 'text').toLowerCase();
|
|
@@ -802,7 +809,7 @@ program
|
|
|
802
809
|
.action(async (pageId, key, options) => {
|
|
803
810
|
const analytics = new Analytics();
|
|
804
811
|
try {
|
|
805
|
-
const config = getConfig();
|
|
812
|
+
const config = getConfig(getProfileName());
|
|
806
813
|
const client = new ConfluenceClient(config);
|
|
807
814
|
|
|
808
815
|
if (!options.value && !options.file) {
|
|
@@ -858,7 +865,7 @@ program
|
|
|
858
865
|
.action(async (pageId, key, options) => {
|
|
859
866
|
const analytics = new Analytics();
|
|
860
867
|
try {
|
|
861
|
-
const config = getConfig();
|
|
868
|
+
const config = getConfig(getProfileName());
|
|
862
869
|
const client = new ConfluenceClient(config);
|
|
863
870
|
|
|
864
871
|
if (!options.yes) {
|
|
@@ -904,7 +911,7 @@ program
|
|
|
904
911
|
.action(async (pageId, options) => {
|
|
905
912
|
const analytics = new Analytics();
|
|
906
913
|
try {
|
|
907
|
-
const config = getConfig();
|
|
914
|
+
const config = getConfig(getProfileName());
|
|
908
915
|
const client = new ConfluenceClient(config);
|
|
909
916
|
|
|
910
917
|
const format = (options.format || 'text').toLowerCase();
|
|
@@ -1059,7 +1066,7 @@ program
|
|
|
1059
1066
|
const analytics = new Analytics();
|
|
1060
1067
|
let location = null;
|
|
1061
1068
|
try {
|
|
1062
|
-
const config = getConfig();
|
|
1069
|
+
const config = getConfig(getProfileName());
|
|
1063
1070
|
const client = new ConfluenceClient(config);
|
|
1064
1071
|
|
|
1065
1072
|
let content = '';
|
|
@@ -1173,7 +1180,7 @@ program
|
|
|
1173
1180
|
.action(async (commentId, options) => {
|
|
1174
1181
|
const analytics = new Analytics();
|
|
1175
1182
|
try {
|
|
1176
|
-
const config = getConfig();
|
|
1183
|
+
const config = getConfig(getProfileName());
|
|
1177
1184
|
const client = new ConfluenceClient(config);
|
|
1178
1185
|
|
|
1179
1186
|
if (!options.yes) {
|
|
@@ -1225,7 +1232,7 @@ program
|
|
|
1225
1232
|
.action(async (pageId, options) => {
|
|
1226
1233
|
const analytics = new Analytics();
|
|
1227
1234
|
try {
|
|
1228
|
-
const config = getConfig();
|
|
1235
|
+
const config = getConfig(getProfileName());
|
|
1229
1236
|
const client = new ConfluenceClient(config);
|
|
1230
1237
|
const fs = require('fs');
|
|
1231
1238
|
const path = require('path');
|
|
@@ -1591,7 +1598,7 @@ program
|
|
|
1591
1598
|
.action(async (sourcePageId, targetParentId, newTitle, options) => {
|
|
1592
1599
|
const analytics = new Analytics();
|
|
1593
1600
|
try {
|
|
1594
|
-
const config = getConfig();
|
|
1601
|
+
const config = getConfig(getProfileName());
|
|
1595
1602
|
const client = new ConfluenceClient(config);
|
|
1596
1603
|
|
|
1597
1604
|
// Parse numeric flags with safe fallbacks
|
|
@@ -1711,7 +1718,7 @@ program
|
|
|
1711
1718
|
.action(async (pageId, options) => {
|
|
1712
1719
|
const analytics = new Analytics();
|
|
1713
1720
|
try {
|
|
1714
|
-
const config = getConfig();
|
|
1721
|
+
const config = getConfig(getProfileName());
|
|
1715
1722
|
const client = new ConfluenceClient(config);
|
|
1716
1723
|
|
|
1717
1724
|
// Extract page ID from URL if needed
|
|
@@ -1852,6 +1859,80 @@ function printTree(nodes, config, options, depth = 1) {
|
|
|
1852
1859
|
});
|
|
1853
1860
|
}
|
|
1854
1861
|
|
|
1862
|
+
// Profile management commands
|
|
1863
|
+
const profileCmd = program
|
|
1864
|
+
.command('profile')
|
|
1865
|
+
.description('Manage configuration profiles');
|
|
1866
|
+
|
|
1867
|
+
profileCmd
|
|
1868
|
+
.command('list')
|
|
1869
|
+
.description('List all configuration profiles')
|
|
1870
|
+
.action(() => {
|
|
1871
|
+
const { profiles } = listProfiles();
|
|
1872
|
+
if (profiles.length === 0) {
|
|
1873
|
+
console.log(chalk.yellow('No profiles configured. Run "confluence init" to create one.'));
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
console.log(chalk.blue('Configuration profiles:\n'));
|
|
1877
|
+
profiles.forEach(p => {
|
|
1878
|
+
const marker = p.active ? chalk.green(' (active)') : '';
|
|
1879
|
+
console.log(` ${p.active ? chalk.green('*') : ' '} ${chalk.cyan(p.name)}${marker} - ${chalk.gray(p.domain)}`);
|
|
1880
|
+
});
|
|
1881
|
+
});
|
|
1882
|
+
|
|
1883
|
+
profileCmd
|
|
1884
|
+
.command('use <name>')
|
|
1885
|
+
.description('Set the active configuration profile')
|
|
1886
|
+
.action((name) => {
|
|
1887
|
+
try {
|
|
1888
|
+
setActiveProfile(name);
|
|
1889
|
+
console.log(chalk.green(`Switched to profile "${name}"`));
|
|
1890
|
+
} catch (error) {
|
|
1891
|
+
console.error(chalk.red('Error:'), error.message);
|
|
1892
|
+
process.exit(1);
|
|
1893
|
+
}
|
|
1894
|
+
});
|
|
1895
|
+
|
|
1896
|
+
profileCmd
|
|
1897
|
+
.command('add <name>')
|
|
1898
|
+
.description('Add a new configuration profile interactively')
|
|
1899
|
+
.option('-d, --domain <domain>', 'Confluence domain')
|
|
1900
|
+
.option('--protocol <protocol>', 'Protocol (http or https)')
|
|
1901
|
+
.option('-p, --api-path <path>', 'REST API path')
|
|
1902
|
+
.option('-a, --auth-type <type>', 'Authentication type (basic or bearer)')
|
|
1903
|
+
.option('-e, --email <email>', 'Email or username for basic auth')
|
|
1904
|
+
.option('-t, --token <token>', 'API token')
|
|
1905
|
+
.action(async (name, options) => {
|
|
1906
|
+
if (!isValidProfileName(name)) {
|
|
1907
|
+
console.error(chalk.red('Invalid profile name. Use only letters, numbers, hyphens, and underscores.'));
|
|
1908
|
+
process.exit(1);
|
|
1909
|
+
}
|
|
1910
|
+
await initConfig({ ...options, profile: name });
|
|
1911
|
+
});
|
|
1912
|
+
|
|
1913
|
+
profileCmd
|
|
1914
|
+
.command('remove <name>')
|
|
1915
|
+
.description('Remove a configuration profile')
|
|
1916
|
+
.action(async (name) => {
|
|
1917
|
+
try {
|
|
1918
|
+
const { confirmed } = await inquirer.prompt([{
|
|
1919
|
+
type: 'confirm',
|
|
1920
|
+
name: 'confirmed',
|
|
1921
|
+
message: `Delete profile "${name}"?`,
|
|
1922
|
+
default: false
|
|
1923
|
+
}]);
|
|
1924
|
+
if (!confirmed) {
|
|
1925
|
+
console.log(chalk.yellow('Cancelled.'));
|
|
1926
|
+
return;
|
|
1927
|
+
}
|
|
1928
|
+
deleteProfile(name);
|
|
1929
|
+
console.log(chalk.green(`Profile "${name}" removed.`));
|
|
1930
|
+
} catch (error) {
|
|
1931
|
+
console.error(chalk.red('Error:'), error.message);
|
|
1932
|
+
process.exit(1);
|
|
1933
|
+
}
|
|
1934
|
+
});
|
|
1935
|
+
|
|
1855
1936
|
// Exported for testing
|
|
1856
1937
|
module.exports = {
|
|
1857
1938
|
program,
|
package/lib/config.js
CHANGED
|
@@ -6,12 +6,15 @@ const chalk = require('chalk');
|
|
|
6
6
|
|
|
7
7
|
const CONFIG_DIR = path.join(os.homedir(), '.confluence-cli');
|
|
8
8
|
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
9
|
+
const DEFAULT_PROFILE = 'default';
|
|
9
10
|
|
|
10
11
|
const AUTH_CHOICES = [
|
|
11
12
|
{ name: 'Basic (credentials)', value: 'basic' },
|
|
12
13
|
{ name: 'Bearer token', value: 'bearer' }
|
|
13
14
|
];
|
|
14
15
|
|
|
16
|
+
const isValidProfileName = (name) => /^[a-zA-Z0-9_-]+$/.test(name);
|
|
17
|
+
|
|
15
18
|
const requiredInput = (label) => (input) => {
|
|
16
19
|
if (!input || !input.trim()) {
|
|
17
20
|
return `${label} is required`;
|
|
@@ -68,6 +71,47 @@ const normalizeApiPath = (rawValue, domain) => {
|
|
|
68
71
|
return withoutTrailing || inferApiPath(domain);
|
|
69
72
|
};
|
|
70
73
|
|
|
74
|
+
// Read config file with backward compatibility for old flat format
|
|
75
|
+
function readConfigFile() {
|
|
76
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const raw = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
82
|
+
|
|
83
|
+
// Detect old flat format (has domain at top level, no profiles key)
|
|
84
|
+
if (raw.domain && !raw.profiles) {
|
|
85
|
+
const profile = {
|
|
86
|
+
domain: raw.domain,
|
|
87
|
+
protocol: raw.protocol,
|
|
88
|
+
apiPath: raw.apiPath,
|
|
89
|
+
token: raw.token,
|
|
90
|
+
authType: raw.authType
|
|
91
|
+
};
|
|
92
|
+
if (raw.email) {
|
|
93
|
+
profile.email = raw.email;
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
activeProfile: DEFAULT_PROFILE,
|
|
97
|
+
profiles: { [DEFAULT_PROFILE]: profile }
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return raw;
|
|
102
|
+
} catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Write the full multi-profile config structure
|
|
108
|
+
function saveConfigFile(data) {
|
|
109
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
110
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
111
|
+
}
|
|
112
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2));
|
|
113
|
+
}
|
|
114
|
+
|
|
71
115
|
// Helper function to validate CLI-provided options
|
|
72
116
|
const validateCliOptions = (options) => {
|
|
73
117
|
const errors = [];
|
|
@@ -115,23 +159,36 @@ const validateCliOptions = (options) => {
|
|
|
115
159
|
};
|
|
116
160
|
|
|
117
161
|
// Helper function to save configuration with validation
|
|
118
|
-
const saveConfig = (configData) => {
|
|
119
|
-
if (!fs.existsSync(CONFIG_DIR)) {
|
|
120
|
-
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
121
|
-
}
|
|
122
|
-
|
|
162
|
+
const saveConfig = (configData, profileName) => {
|
|
123
163
|
const config = {
|
|
124
164
|
domain: configData.domain.trim(),
|
|
125
165
|
protocol: normalizeProtocol(configData.protocol),
|
|
126
166
|
apiPath: normalizeApiPath(configData.apiPath, configData.domain),
|
|
127
167
|
token: configData.token.trim(),
|
|
128
|
-
authType: configData.authType
|
|
129
|
-
email: configData.authType === 'basic' && configData.email ? configData.email.trim() : undefined
|
|
168
|
+
authType: configData.authType
|
|
130
169
|
};
|
|
131
170
|
|
|
132
|
-
|
|
171
|
+
if (configData.authType === 'basic' && configData.email) {
|
|
172
|
+
config.email = configData.email.trim();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Read existing config file (or create new structure)
|
|
176
|
+
const fileData = readConfigFile() || { activeProfile: DEFAULT_PROFILE, profiles: {} };
|
|
177
|
+
|
|
178
|
+
const targetProfile = profileName || fileData.activeProfile || DEFAULT_PROFILE;
|
|
179
|
+
fileData.profiles[targetProfile] = config;
|
|
180
|
+
|
|
181
|
+
// If this is the first profile, make it active
|
|
182
|
+
if (!fileData.activeProfile || !fileData.profiles[fileData.activeProfile]) {
|
|
183
|
+
fileData.activeProfile = targetProfile;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
saveConfigFile(fileData);
|
|
133
187
|
|
|
134
188
|
console.log(chalk.green('✅ Configuration saved successfully!'));
|
|
189
|
+
if (profileName) {
|
|
190
|
+
console.log(`Profile: ${chalk.cyan(targetProfile)}`);
|
|
191
|
+
}
|
|
135
192
|
console.log(`Config file location: ${chalk.gray(CONFIG_FILE)}`);
|
|
136
193
|
console.log(chalk.yellow('\n💡 Tip: You can regenerate this config anytime by running "confluence init"'));
|
|
137
194
|
};
|
|
@@ -232,6 +289,14 @@ const promptForMissingValues = async (providedValues) => {
|
|
|
232
289
|
};
|
|
233
290
|
|
|
234
291
|
async function initConfig(cliOptions = {}) {
|
|
292
|
+
const profileName = cliOptions.profile;
|
|
293
|
+
|
|
294
|
+
// Validate profile name if provided
|
|
295
|
+
if (profileName && !isValidProfileName(profileName)) {
|
|
296
|
+
console.error(chalk.red('❌ Invalid profile name. Use only letters, numbers, hyphens, and underscores.'));
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
|
|
235
300
|
// Extract provided values from CLI options
|
|
236
301
|
const providedValues = {
|
|
237
302
|
protocol: cliOptions.protocol,
|
|
@@ -248,6 +313,9 @@ async function initConfig(cliOptions = {}) {
|
|
|
248
313
|
if (!hasCliOptions) {
|
|
249
314
|
// Interactive mode: no CLI options provided
|
|
250
315
|
console.log(chalk.blue('🚀 Confluence CLI Configuration'));
|
|
316
|
+
if (profileName) {
|
|
317
|
+
console.log(`Profile: ${chalk.cyan(profileName)}`);
|
|
318
|
+
}
|
|
251
319
|
console.log('Please provide your Confluence connection details:\n');
|
|
252
320
|
|
|
253
321
|
const answers = await inquirer.prompt([
|
|
@@ -307,7 +375,7 @@ async function initConfig(cliOptions = {}) {
|
|
|
307
375
|
}
|
|
308
376
|
]);
|
|
309
377
|
|
|
310
|
-
saveConfig(answers);
|
|
378
|
+
saveConfig(answers, profileName);
|
|
311
379
|
return;
|
|
312
380
|
}
|
|
313
381
|
|
|
@@ -325,8 +393,8 @@ async function initConfig(cliOptions = {}) {
|
|
|
325
393
|
// Check if all required values are provided for non-interactive mode
|
|
326
394
|
// Non-interactive requires: domain, token, and either authType or email (for inference)
|
|
327
395
|
const hasRequiredValues = Boolean(
|
|
328
|
-
providedValues.domain &&
|
|
329
|
-
providedValues.token &&
|
|
396
|
+
providedValues.domain &&
|
|
397
|
+
providedValues.token &&
|
|
330
398
|
(providedValues.authType || providedValues.email)
|
|
331
399
|
);
|
|
332
400
|
|
|
@@ -341,7 +409,7 @@ async function initConfig(cliOptions = {}) {
|
|
|
341
409
|
|
|
342
410
|
const normalizedAuthType = normalizeAuthType(inferredAuthType, Boolean(providedValues.email));
|
|
343
411
|
const normalizedDomain = providedValues.domain.trim();
|
|
344
|
-
|
|
412
|
+
|
|
345
413
|
// Verify basic auth has email
|
|
346
414
|
if (normalizedAuthType === 'basic' && !providedValues.email) {
|
|
347
415
|
console.error(chalk.red('❌ Email is required for basic authentication'));
|
|
@@ -362,7 +430,7 @@ async function initConfig(cliOptions = {}) {
|
|
|
362
430
|
email: providedValues.email
|
|
363
431
|
};
|
|
364
432
|
|
|
365
|
-
saveConfig(configData);
|
|
433
|
+
saveConfig(configData, profileName);
|
|
366
434
|
} catch (error) {
|
|
367
435
|
console.error(chalk.red(`❌ ${error.message}`));
|
|
368
436
|
process.exit(1);
|
|
@@ -373,21 +441,24 @@ async function initConfig(cliOptions = {}) {
|
|
|
373
441
|
// Hybrid mode: some values provided, prompt for the rest
|
|
374
442
|
try {
|
|
375
443
|
console.log(chalk.blue('🚀 Confluence CLI Configuration'));
|
|
444
|
+
if (profileName) {
|
|
445
|
+
console.log(`Profile: ${chalk.cyan(profileName)}`);
|
|
446
|
+
}
|
|
376
447
|
console.log('Completing configuration with interactive prompts:\n');
|
|
377
448
|
|
|
378
449
|
const mergedValues = await promptForMissingValues(providedValues);
|
|
379
|
-
|
|
450
|
+
|
|
380
451
|
// Normalize auth type
|
|
381
452
|
mergedValues.authType = normalizeAuthType(mergedValues.authType, Boolean(mergedValues.email));
|
|
382
|
-
|
|
383
|
-
saveConfig(mergedValues);
|
|
453
|
+
|
|
454
|
+
saveConfig(mergedValues, profileName);
|
|
384
455
|
} catch (error) {
|
|
385
456
|
console.error(chalk.red(`❌ ${error.message}`));
|
|
386
457
|
process.exit(1);
|
|
387
458
|
}
|
|
388
459
|
}
|
|
389
460
|
|
|
390
|
-
function getConfig() {
|
|
461
|
+
function getConfig(profileName) {
|
|
391
462
|
const envDomain = process.env.CONFLUENCE_DOMAIN || process.env.CONFLUENCE_HOST;
|
|
392
463
|
const envToken = process.env.CONFLUENCE_API_TOKEN || process.env.CONFLUENCE_PASSWORD;
|
|
393
464
|
const envEmail = process.env.CONFLUENCE_EMAIL || process.env.CONFLUENCE_USERNAME;
|
|
@@ -422,15 +493,34 @@ function getConfig() {
|
|
|
422
493
|
};
|
|
423
494
|
}
|
|
424
495
|
|
|
425
|
-
|
|
496
|
+
// Resolve profile: explicit param > CONFLUENCE_PROFILE env var > activeProfile > default
|
|
497
|
+
const resolvedProfileName = profileName
|
|
498
|
+
|| process.env.CONFLUENCE_PROFILE
|
|
499
|
+
|| null;
|
|
500
|
+
|
|
501
|
+
const fileData = readConfigFile();
|
|
502
|
+
|
|
503
|
+
if (!fileData) {
|
|
426
504
|
console.error(chalk.red('❌ No configuration found!'));
|
|
427
505
|
console.log(chalk.yellow('Please run "confluence init" to set up your configuration.'));
|
|
428
506
|
console.log(chalk.gray('Or set environment variables: CONFLUENCE_DOMAIN, CONFLUENCE_API_TOKEN (or CONFLUENCE_PASSWORD), CONFLUENCE_EMAIL (or CONFLUENCE_USERNAME), and optionally CONFLUENCE_API_PATH, CONFLUENCE_PROTOCOL.'));
|
|
429
507
|
process.exit(1);
|
|
430
508
|
}
|
|
431
509
|
|
|
510
|
+
const targetProfile = resolvedProfileName || fileData.activeProfile || DEFAULT_PROFILE;
|
|
511
|
+
const storedConfig = fileData.profiles && fileData.profiles[targetProfile];
|
|
512
|
+
|
|
513
|
+
if (!storedConfig) {
|
|
514
|
+
console.error(chalk.red(`❌ Profile "${targetProfile}" not found!`));
|
|
515
|
+
const available = fileData.profiles ? Object.keys(fileData.profiles) : [];
|
|
516
|
+
if (available.length > 0) {
|
|
517
|
+
console.log(chalk.yellow(`Available profiles: ${available.join(', ')}`));
|
|
518
|
+
}
|
|
519
|
+
console.log(chalk.yellow('Run "confluence init --profile <name>" to create it, or "confluence profile list" to see available profiles.'));
|
|
520
|
+
process.exit(1);
|
|
521
|
+
}
|
|
522
|
+
|
|
432
523
|
try {
|
|
433
|
-
const storedConfig = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
434
524
|
const trimmedDomain = (storedConfig.domain || '').trim();
|
|
435
525
|
const trimmedToken = (storedConfig.token || '').trim();
|
|
436
526
|
const trimmedEmail = storedConfig.email ? storedConfig.email.trim() : undefined;
|
|
@@ -472,7 +562,60 @@ function getConfig() {
|
|
|
472
562
|
}
|
|
473
563
|
}
|
|
474
564
|
|
|
565
|
+
function listProfiles() {
|
|
566
|
+
const fileData = readConfigFile();
|
|
567
|
+
if (!fileData || !fileData.profiles || Object.keys(fileData.profiles).length === 0) {
|
|
568
|
+
return { activeProfile: null, profiles: [] };
|
|
569
|
+
}
|
|
570
|
+
return {
|
|
571
|
+
activeProfile: fileData.activeProfile,
|
|
572
|
+
profiles: Object.keys(fileData.profiles).map(name => ({
|
|
573
|
+
name,
|
|
574
|
+
active: name === fileData.activeProfile,
|
|
575
|
+
domain: fileData.profiles[name].domain
|
|
576
|
+
}))
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function setActiveProfile(profileName) {
|
|
581
|
+
const fileData = readConfigFile();
|
|
582
|
+
if (!fileData) {
|
|
583
|
+
throw new Error('No configuration file found. Run "confluence init" first.');
|
|
584
|
+
}
|
|
585
|
+
if (!fileData.profiles || !fileData.profiles[profileName]) {
|
|
586
|
+
const available = fileData.profiles ? Object.keys(fileData.profiles) : [];
|
|
587
|
+
throw new Error(`Profile "${profileName}" not found. Available: ${available.join(', ')}`);
|
|
588
|
+
}
|
|
589
|
+
fileData.activeProfile = profileName;
|
|
590
|
+
saveConfigFile(fileData);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function deleteProfile(profileName) {
|
|
594
|
+
const fileData = readConfigFile();
|
|
595
|
+
if (!fileData) {
|
|
596
|
+
throw new Error('No configuration file found. Run "confluence init" first.');
|
|
597
|
+
}
|
|
598
|
+
if (!fileData.profiles || !fileData.profiles[profileName]) {
|
|
599
|
+
throw new Error(`Profile "${profileName}" not found.`);
|
|
600
|
+
}
|
|
601
|
+
if (Object.keys(fileData.profiles).length === 1) {
|
|
602
|
+
throw new Error('Cannot delete the only remaining profile.');
|
|
603
|
+
}
|
|
604
|
+
delete fileData.profiles[profileName];
|
|
605
|
+
if (fileData.activeProfile === profileName) {
|
|
606
|
+
fileData.activeProfile = Object.keys(fileData.profiles)[0];
|
|
607
|
+
}
|
|
608
|
+
saveConfigFile(fileData);
|
|
609
|
+
}
|
|
610
|
+
|
|
475
611
|
module.exports = {
|
|
476
612
|
initConfig,
|
|
477
|
-
getConfig
|
|
613
|
+
getConfig,
|
|
614
|
+
listProfiles,
|
|
615
|
+
setActiveProfile,
|
|
616
|
+
deleteProfile,
|
|
617
|
+
isValidProfileName,
|
|
618
|
+
CONFIG_DIR,
|
|
619
|
+
CONFIG_FILE,
|
|
620
|
+
DEFAULT_PROFILE
|
|
478
621
|
};
|