confluence-cli 1.14.0 → 1.15.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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [1.15.0](https://github.com/pchuri/confluence-cli/compare/v1.14.0...v1.15.0) (2026-02-06)
2
+
3
+
4
+ ### Features
5
+
6
+ * Add CLI flags to confluence init for non-interactive setup ([#30](https://github.com/pchuri/confluence-cli/issues/30)) ([09b6b85](https://github.com/pchuri/confluence-cli/commit/09b6b85a243da5ab86eb61a1a2376a64ce6979c7))
7
+
1
8
  # [1.14.0](https://github.com/pchuri/confluence-cli/compare/v1.13.0...v1.14.0) (2026-02-03)
2
9
 
3
10
 
package/README.md CHANGED
@@ -67,13 +67,45 @@ npx confluence-cli
67
67
  confluence init
68
68
  ```
69
69
 
70
- The wizard now helps you choose the right API endpoint and authentication method. It recommends `/wiki/rest/api` for Atlassian Cloud domains (e.g., `*.atlassian.net`) and `/rest/api` for self-hosted/Data Center instances, then prompts for Basic (email + token) or Bearer authentication.
70
+ The wizard helps you choose the right API endpoint and authentication method. It recommends `/wiki/rest/api` for Atlassian Cloud domains (e.g., `*.atlassian.net`) and `/rest/api` for self-hosted/Data Center instances, then prompts for Basic (email + token) or Bearer authentication.
71
71
 
72
- ### Option 2: Environment Variables
72
+ ### Option 2: Non-interactive Setup (CLI Flags)
73
+
74
+ Provide all required configuration via command-line flags. Perfect for CI/CD pipelines, Docker builds, and AI coding agents.
75
+
76
+ **Complete non-interactive mode** (all required fields provided):
77
+ ```bash
78
+ confluence init \
79
+ --domain "company.atlassian.net" \
80
+ --api-path "/wiki/rest/api" \
81
+ --auth-type "basic" \
82
+ --email "user@example.com" \
83
+ --token "your-api-token"
84
+ ```
85
+
86
+ **Hybrid mode** (some fields provided, rest via prompts):
87
+ ```bash
88
+ # Domain and token provided, will prompt for auth method and email
89
+ confluence init --domain "company.atlassian.net" --token "your-api-token"
90
+
91
+ # Email indicates basic auth, will prompt for domain and token
92
+ confluence init --email "user@example.com" --token "your-api-token"
93
+ ```
94
+
95
+ **Available flags:**
96
+ - `-d, --domain <domain>` - Confluence domain (e.g., `company.atlassian.net`)
97
+ - `-p, --api-path <path>` - REST API path (e.g., `/wiki/rest/api`)
98
+ - `-a, --auth-type <type>` - Authentication type: `basic` or `bearer`
99
+ - `-e, --email <email>` - Email for basic authentication
100
+ - `-t, --token <token>` - API token
101
+
102
+ ⚠️ **Security note:** While flags work, storing tokens in shell history is risky. Prefer environment variables (Option 3) for production environments.
103
+
104
+ ### Option 3: Environment Variables
73
105
  ```bash
74
106
  export CONFLUENCE_DOMAIN="your-domain.atlassian.net"
75
107
  export CONFLUENCE_API_TOKEN="your-api-token"
76
- export CONFLUENCE_EMAIL="your.email@example.com" # required when using Atlassian Cloud
108
+ export CONFLUENCE_EMAIL="your.email@example.com" # required when using basic auth
77
109
  export CONFLUENCE_API_PATH="/wiki/rest/api" # Cloud default; use /rest/api for Server/DC
78
110
  # Optional: set to 'bearer' for self-hosted/Data Center instances
79
111
  export CONFLUENCE_AUTH_TYPE="basic"
package/bin/confluence.js CHANGED
@@ -17,8 +17,13 @@ program
17
17
  program
18
18
  .command('init')
19
19
  .description('Initialize Confluence CLI configuration')
20
- .action(async () => {
21
- await initConfig();
20
+ .option('-d, --domain <domain>', 'Confluence domain')
21
+ .option('-p, --api-path <path>', 'REST API path')
22
+ .option('-a, --auth-type <type>', 'Authentication type (basic or bearer)')
23
+ .option('-e, --email <email>', 'Email for basic auth')
24
+ .option('-t, --token <token>', 'API token')
25
+ .action(async (options) => {
26
+ await initConfig(options);
22
27
  });
23
28
 
24
29
  // Read command
package/lib/config.js CHANGED
@@ -55,22 +55,90 @@ const normalizeApiPath = (rawValue, domain) => {
55
55
  return withoutTrailing || inferApiPath(domain);
56
56
  };
57
57
 
58
- async function initConfig() {
59
- console.log(chalk.blue('🚀 Confluence CLI Configuration'));
60
- console.log('Please provide your Confluence connection details:\n');
58
+ // Helper function to validate CLI-provided options
59
+ const validateCliOptions = (options) => {
60
+ const errors = [];
61
61
 
62
- const answers = await inquirer.prompt([
63
- {
62
+ if (options.domain && !options.domain.trim()) {
63
+ errors.push('--domain cannot be empty');
64
+ }
65
+
66
+ if (options.token && !options.token.trim()) {
67
+ errors.push('--token cannot be empty');
68
+ }
69
+
70
+ if (options.email && !options.email.trim()) {
71
+ errors.push('--email cannot be empty');
72
+ }
73
+
74
+ if (options.apiPath) {
75
+ if (!options.apiPath.startsWith('/')) {
76
+ errors.push('--api-path must start with "/"');
77
+ } else {
78
+ // Validate API path format
79
+ try {
80
+ normalizeApiPath(options.apiPath, options.domain || 'example.com');
81
+ } catch (error) {
82
+ errors.push(`--api-path is invalid: ${error.message}`);
83
+ }
84
+ }
85
+ }
86
+
87
+ if (options.authType && !['basic', 'bearer'].includes(options.authType.toLowerCase())) {
88
+ errors.push('--auth-type must be "basic" or "bearer"');
89
+ }
90
+
91
+ // Check if basic auth is provided with email
92
+ const normAuthType = options.authType ? normalizeAuthType(options.authType, Boolean(options.email)) : null;
93
+ if (normAuthType === 'basic' && !options.email) {
94
+ errors.push('--email is required when using basic authentication');
95
+ }
96
+
97
+ return errors;
98
+ };
99
+
100
+ // Helper function to save configuration with validation
101
+ const saveConfig = (configData) => {
102
+ if (!fs.existsSync(CONFIG_DIR)) {
103
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
104
+ }
105
+
106
+ const config = {
107
+ domain: configData.domain.trim(),
108
+ apiPath: normalizeApiPath(configData.apiPath, configData.domain),
109
+ token: configData.token.trim(),
110
+ authType: configData.authType,
111
+ email: configData.authType === 'basic' && configData.email ? configData.email.trim() : undefined
112
+ };
113
+
114
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
115
+
116
+ console.log(chalk.green('✅ Configuration saved successfully!'));
117
+ console.log(`Config file location: ${chalk.gray(CONFIG_FILE)}`);
118
+ console.log(chalk.yellow('\n💡 Tip: You can regenerate this config anytime by running "confluence init"'));
119
+ };
120
+
121
+ // Helper function to prompt for missing values
122
+ const promptForMissingValues = async (providedValues) => {
123
+ const questions = [];
124
+
125
+ // Domain question
126
+ if (!providedValues.domain) {
127
+ questions.push({
64
128
  type: 'input',
65
129
  name: 'domain',
66
130
  message: 'Confluence domain (e.g., yourcompany.atlassian.net):',
67
131
  validate: requiredInput('Domain')
68
- },
69
- {
132
+ });
133
+ }
134
+
135
+ // API Path question
136
+ if (!providedValues.apiPath) {
137
+ questions.push({
70
138
  type: 'input',
71
139
  name: 'apiPath',
72
140
  message: 'REST API path (Cloud: /wiki/rest/api, Server: /rest/api):',
73
- default: (responses) => inferApiPath(responses.domain),
141
+ default: (responses) => inferApiPath(providedValues.domain || responses.domain),
74
142
  validate: (input, responses) => {
75
143
  const value = (input || '').trim();
76
144
  if (!value) {
@@ -80,52 +148,205 @@ async function initConfig() {
80
148
  return 'API path must start with "/"';
81
149
  }
82
150
  try {
83
- normalizeApiPath(value, responses.domain);
151
+ const domain = providedValues.domain || responses.domain;
152
+ normalizeApiPath(value, domain);
84
153
  return true;
85
154
  } catch (error) {
86
155
  return error.message;
87
156
  }
88
157
  }
89
- },
90
- {
158
+ });
159
+ }
160
+
161
+ // Auth Type question
162
+ const hasEmail = Boolean(providedValues.email);
163
+ if (!providedValues.authType) {
164
+ questions.push({
91
165
  type: 'list',
92
166
  name: 'authType',
93
167
  message: 'Authentication method:',
94
168
  choices: AUTH_CHOICES,
95
- default: 'basic'
96
- },
97
- {
169
+ default: hasEmail ? 'basic' : 'bearer'
170
+ });
171
+ }
172
+
173
+ // Email question (conditional on authType)
174
+ if (!providedValues.email) {
175
+ questions.push({
98
176
  type: 'input',
99
177
  name: 'email',
100
178
  message: 'Confluence email (used with API token):',
101
- when: (responses) => responses.authType === 'basic',
179
+ when: (responses) => {
180
+ const authType = providedValues.authType || responses.authType;
181
+ return authType === 'basic';
182
+ },
102
183
  validate: requiredInput('Email')
103
- },
104
- {
184
+ });
185
+ }
186
+
187
+ // Token question
188
+ if (!providedValues.token) {
189
+ questions.push({
105
190
  type: 'password',
106
191
  name: 'token',
107
192
  message: 'API Token:',
108
193
  validate: requiredInput('API Token')
109
- }
110
- ]);
194
+ });
195
+ }
111
196
 
112
- if (!fs.existsSync(CONFIG_DIR)) {
113
- fs.mkdirSync(CONFIG_DIR, { recursive: true });
197
+ if (questions.length === 0) {
198
+ return providedValues;
114
199
  }
115
200
 
116
- const config = {
117
- domain: answers.domain.trim(),
118
- apiPath: normalizeApiPath(answers.apiPath, answers.domain),
119
- token: answers.token.trim(),
120
- authType: answers.authType,
121
- email: answers.authType === 'basic' ? answers.email.trim() : undefined
201
+ const answers = await inquirer.prompt(questions);
202
+ return { ...providedValues, ...answers };
203
+ };
204
+
205
+ async function initConfig(cliOptions = {}) {
206
+ // Extract provided values from CLI options
207
+ const providedValues = {
208
+ domain: cliOptions.domain,
209
+ apiPath: cliOptions.apiPath,
210
+ authType: cliOptions.authType,
211
+ email: cliOptions.email,
212
+ token: cliOptions.token
122
213
  };
123
214
 
124
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
215
+ // Check if any CLI options were provided
216
+ const hasCliOptions = Object.values(providedValues).some(v => v);
125
217
 
126
- console.log(chalk.green('✅ Configuration saved successfully!'));
127
- console.log(`Config file location: ${chalk.gray(CONFIG_FILE)}`);
128
- console.log(chalk.yellow('\n💡 Tip: You can regenerate this config anytime by running "confluence init"'));
218
+ if (!hasCliOptions) {
219
+ // Interactive mode: no CLI options provided
220
+ console.log(chalk.blue('🚀 Confluence CLI Configuration'));
221
+ console.log('Please provide your Confluence connection details:\n');
222
+
223
+ const answers = await inquirer.prompt([
224
+ {
225
+ type: 'input',
226
+ name: 'domain',
227
+ message: 'Confluence domain (e.g., yourcompany.atlassian.net):',
228
+ validate: requiredInput('Domain')
229
+ },
230
+ {
231
+ type: 'input',
232
+ name: 'apiPath',
233
+ message: 'REST API path (Cloud: /wiki/rest/api, Server: /rest/api):',
234
+ default: (responses) => inferApiPath(responses.domain),
235
+ validate: (input, responses) => {
236
+ const value = (input || '').trim();
237
+ if (!value) {
238
+ return true;
239
+ }
240
+ if (!value.startsWith('/')) {
241
+ return 'API path must start with "/"';
242
+ }
243
+ try {
244
+ normalizeApiPath(value, responses.domain);
245
+ return true;
246
+ } catch (error) {
247
+ return error.message;
248
+ }
249
+ }
250
+ },
251
+ {
252
+ type: 'list',
253
+ name: 'authType',
254
+ message: 'Authentication method:',
255
+ choices: AUTH_CHOICES,
256
+ default: 'basic'
257
+ },
258
+ {
259
+ type: 'input',
260
+ name: 'email',
261
+ message: 'Confluence email (used with API token):',
262
+ when: (responses) => responses.authType === 'basic',
263
+ validate: requiredInput('Email')
264
+ },
265
+ {
266
+ type: 'password',
267
+ name: 'token',
268
+ message: 'API Token:',
269
+ validate: requiredInput('API Token')
270
+ }
271
+ ]);
272
+
273
+ saveConfig(answers);
274
+ return;
275
+ }
276
+
277
+ // Non-interactive or hybrid mode: CLI options provided
278
+ // Validate provided options
279
+ const validationErrors = validateCliOptions(providedValues);
280
+ if (validationErrors.length > 0) {
281
+ console.error(chalk.red('❌ Configuration Error:'));
282
+ validationErrors.forEach(error => {
283
+ console.error(chalk.red(` • ${error}`));
284
+ });
285
+ process.exit(1);
286
+ }
287
+
288
+ // Check if all required values are provided for non-interactive mode
289
+ // Non-interactive requires: domain, token, and either authType or email (for inference)
290
+ const hasRequiredValues = Boolean(
291
+ providedValues.domain &&
292
+ providedValues.token &&
293
+ (providedValues.authType || providedValues.email)
294
+ );
295
+
296
+ if (hasRequiredValues) {
297
+ // Non-interactive mode: all required values provided
298
+ try {
299
+ // Infer authType if not provided
300
+ let inferredAuthType = providedValues.authType;
301
+ if (!inferredAuthType) {
302
+ inferredAuthType = providedValues.email ? 'basic' : 'bearer';
303
+ }
304
+
305
+ const normalizedAuthType = normalizeAuthType(inferredAuthType, Boolean(providedValues.email));
306
+ const normalizedDomain = providedValues.domain.trim();
307
+
308
+ // Verify basic auth has email
309
+ if (normalizedAuthType === 'basic' && !providedValues.email) {
310
+ console.error(chalk.red('❌ Email is required for basic authentication'));
311
+ process.exit(1);
312
+ }
313
+
314
+ // Verify API path format if provided
315
+ if (providedValues.apiPath) {
316
+ normalizeApiPath(providedValues.apiPath, normalizedDomain);
317
+ }
318
+
319
+ const configData = {
320
+ domain: normalizedDomain,
321
+ apiPath: providedValues.apiPath || inferApiPath(normalizedDomain),
322
+ token: providedValues.token,
323
+ authType: normalizedAuthType,
324
+ email: providedValues.email
325
+ };
326
+
327
+ saveConfig(configData);
328
+ } catch (error) {
329
+ console.error(chalk.red(`❌ ${error.message}`));
330
+ process.exit(1);
331
+ }
332
+ return;
333
+ }
334
+
335
+ // Hybrid mode: some values provided, prompt for the rest
336
+ try {
337
+ console.log(chalk.blue('🚀 Confluence CLI Configuration'));
338
+ console.log('Completing configuration with interactive prompts:\n');
339
+
340
+ const mergedValues = await promptForMissingValues(providedValues);
341
+
342
+ // Normalize auth type
343
+ mergedValues.authType = normalizeAuthType(mergedValues.authType, Boolean(mergedValues.email));
344
+
345
+ saveConfig(mergedValues);
346
+ } catch (error) {
347
+ console.error(chalk.red(`❌ ${error.message}`));
348
+ process.exit(1);
349
+ }
129
350
  }
130
351
 
131
352
  function getConfig() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "confluence-cli",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "description": "A command-line interface for Atlassian Confluence with page creation and editing capabilities",
5
5
  "main": "index.js",
6
6
  "bin": {