confluence-cli 1.26.0 → 1.27.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.
@@ -25,6 +25,18 @@ confluence --version # verify install
25
25
  | `CONFLUENCE_AUTH_TYPE` | `basic` or `bearer` | `basic` |
26
26
  | `CONFLUENCE_EMAIL` | Email address (basic auth only) | `user@company.com` |
27
27
  | `CONFLUENCE_API_TOKEN` | API token or personal access token | `ATATT3x...` |
28
+ | `CONFLUENCE_PROFILE` | Named profile to use (optional) | `staging` |
29
+ | `CONFLUENCE_READ_ONLY` | Block all write operations when `true` | `true` |
30
+
31
+ **Global `--profile` flag (use a named profile for any command):**
32
+
33
+ ```sh
34
+ confluence --profile <name> <command>
35
+ ```
36
+
37
+ Config resolution works in two stages:
38
+ - **Direct env config:** If both `CONFLUENCE_DOMAIN` and `CONFLUENCE_API_TOKEN` are set, they are used directly and the config file / profiles are not consulted.
39
+ - **Profile-based config:** Otherwise, a profile is selected in this order: `--profile` flag > `CONFLUENCE_PROFILE` env > `activeProfile` in config > `default`.
28
40
 
29
41
  **Non-interactive init (good for CI/CD scripts):**
30
42
 
@@ -52,6 +64,27 @@ export CONFLUENCE_EMAIL="user@company.com"
52
64
  export CONFLUENCE_API_TOKEN="your-scoped-token"
53
65
  ```
54
66
 
67
+ **Read-only mode (recommended for AI agents):**
68
+
69
+ Prevents all write operations (create, update, delete, move, etc.) at the profile level. Useful when giving an AI agent access to Confluence for reading only.
70
+
71
+ ```sh
72
+ # Via profile flag
73
+ confluence profile add agent --domain "company.atlassian.net" --token "xxx" --read-only
74
+
75
+ # Via environment variable (overrides config file)
76
+ export CONFLUENCE_READ_ONLY=true
77
+ ```
78
+
79
+ When read-only mode is active, any write command exits with an error:
80
+ ```
81
+ Error: This profile is in read-only mode. Write operations are not allowed.
82
+ ```
83
+
84
+ `profile list` shows read-only profiles with a `[read-only]` badge.
85
+
86
+ ---
87
+
55
88
  ## Page ID Resolution
56
89
 
57
90
  Most commands accept `<pageId>` — a numeric ID or any of the supported URL formats below.
@@ -91,10 +124,14 @@ confluence read "https://company.atlassian.net/wiki/spaces/MYSPACE/pages/1234567
91
124
  Initialize configuration. Saves credentials to `~/.confluence-cli/config.json`.
92
125
 
93
126
  ```sh
94
- confluence init [--domain <domain>] [--api-path <path>] [--auth-type basic|bearer] [--email <email>] [--token <token>]
127
+ confluence init [--domain <domain>] [--api-path <path>] [--auth-type basic|bearer] [--email <email>] [--token <token>] [--read-only]
95
128
  ```
96
129
 
97
- All flags are optional; omitting any flag triggers an interactive prompt for that field. Provide all flags to run fully non-interactive.
130
+ All flags are optional; omitting any flag triggers an interactive prompt for that field. Provide all flags to run fully non-interactive. Use the global `--profile` flag to save to a named profile:
131
+
132
+ ```sh
133
+ confluence --profile staging init --domain "staging.example.com" --auth-type bearer --token "your-token"
134
+ ```
98
135
 
99
136
  ---
100
137
 
@@ -520,6 +557,60 @@ confluence copy-tree 123456789 987654321 --exclude "Draft*,Archive*"
520
557
 
521
558
  ---
522
559
 
560
+ ### `profile list`
561
+
562
+ List all configuration profiles with the active profile marked.
563
+
564
+ ```sh
565
+ confluence profile list
566
+ ```
567
+
568
+ ---
569
+
570
+ ### `profile use <name>`
571
+
572
+ Switch the active configuration profile.
573
+
574
+ ```sh
575
+ confluence profile use <name>
576
+ ```
577
+
578
+ ```sh
579
+ confluence profile use staging
580
+ ```
581
+
582
+ ---
583
+
584
+ ### `profile add <name>`
585
+
586
+ Add a new configuration profile. Supports the same options as `init` (interactive, non-interactive, or hybrid).
587
+
588
+ ```sh
589
+ confluence profile add <name> [--domain <domain>] [--api-path <path>] [--auth-type basic|bearer] [--email <email>] [--token <token>] [--protocol http|https] [--read-only]
590
+ ```
591
+
592
+ Profile names may contain letters, numbers, hyphens, and underscores only.
593
+
594
+ ```sh
595
+ confluence profile add staging --domain "staging.example.com" --auth-type bearer --token "xyz"
596
+ ```
597
+
598
+ ---
599
+
600
+ ### `profile remove <name>`
601
+
602
+ Remove a configuration profile (prompts for confirmation). Cannot remove the only remaining profile.
603
+
604
+ ```sh
605
+ confluence profile remove <name>
606
+ ```
607
+
608
+ ```sh
609
+ confluence profile remove staging
610
+ ```
611
+
612
+ ---
613
+
523
614
  ### `stats`
524
615
 
525
616
  Show local usage statistics.
@@ -614,6 +705,8 @@ confluence search "release notes" --limit 20
614
705
  - **ANSI color codes**: stdout may contain ANSI escape sequences. Pipe through `| cat` or use `NO_COLOR=1` if your downstream tool doesn't handle them.
615
706
  - **Page ID vs URL**: when you have a Confluence URL, extract `?pageId=<number>` and pass the number. Do not pass pretty/display URLs — they are not supported.
616
707
  - **Cross-space moves**: `confluence move` only works within the same space. Moving across spaces is not supported.
708
+ - **Multiple instances**: Use `--profile <name>` or `CONFLUENCE_PROFILE` env var to target different Confluence instances without reconfiguring.
709
+ - **Read-only mode**: Set `CONFLUENCE_READ_ONLY=true` or use `--read-only` when creating profiles to prevent accidental writes. This is enforced at the CLI level — all write commands will be blocked.
617
710
 
618
711
  ## Error Patterns
619
712
 
@@ -624,3 +717,6 @@ confluence search "release notes" --limit 20
624
717
  | 400 on inline comment creation | Editor metadata required | Use `--location footer` or reply to existing inline comment with `--parent` |
625
718
  | `File not found: <path>` | `--file` path doesn't exist | Check the path before calling the command |
626
719
  | `At least one of --title, --file, or --content must be provided` | `update` called with no content options | Provide at least one of the required options |
720
+ | `Profile "<name>" not found!` | Specified profile doesn't exist | Run `confluence profile list` to see available profiles |
721
+ | `Cannot delete the only remaining profile.` | Tried to remove the last profile | Add another profile before removing |
722
+ | `This profile is in read-only mode` | Write command used with a read-only profile | Use a writable profile or remove `readOnly` from config |
package/README.md CHANGED
@@ -16,6 +16,8 @@ A powerful command-line interface for Atlassian Confluence that allows you to re
16
16
  - 💬 **Comments** - List, create, and delete page comments (footer or inline)
17
17
  - 📦 **Export** - Save a page and its attachments to a local folder
18
18
  - 🛠️ **Edit workflow** - Export page content for editing and re-import
19
+ - 🔀 **Profiles** - Manage multiple Confluence instances with named configuration profiles
20
+ - 🔒 **Read-only mode** - Profile-level write protection for safe AI agent usage
19
21
  - 🔧 **Easy setup** - Simple configuration with environment variables or interactive setup
20
22
 
21
23
  ## Installation
@@ -115,6 +117,15 @@ confluence init \
115
117
  --token "your-scoped-token"
116
118
  ```
117
119
 
120
+ **Named profile** (save to a specific profile):
121
+ ```bash
122
+ confluence --profile staging init \
123
+ --domain "staging.example.com" \
124
+ --api-path "/rest/api" \
125
+ --auth-type "bearer" \
126
+ --token "your-personal-access-token"
127
+ ```
128
+
118
129
  **Hybrid mode** (some fields provided, rest via prompts):
119
130
  ```bash
120
131
  # Domain and token provided, will prompt for auth method and email
@@ -130,6 +141,7 @@ confluence init --email "user@example.com" --token "your-api-token"
130
141
  - `-a, --auth-type <type>` - Authentication type: `basic` or `bearer`
131
142
  - `-e, --email <email>` - Email or username for basic authentication
132
143
  - `-t, --token <token>` - API token or password
144
+ - `--read-only` - Enable read-only mode (blocks all write operations)
133
145
 
134
146
  ⚠️ **Security note:** While flags work, storing tokens in shell history is risky. Prefer environment variables (Option 3) for production environments.
135
147
 
@@ -141,6 +153,8 @@ export CONFLUENCE_EMAIL="your.email@example.com" # required for basic auth (ali
141
153
  export CONFLUENCE_API_PATH="/wiki/rest/api" # Cloud default; use /rest/api for Server/DC
142
154
  # Optional: set to 'bearer' for self-hosted/Data Center instances
143
155
  export CONFLUENCE_AUTH_TYPE="basic"
156
+ # Optional: select a named profile (overridden by --profile flag)
157
+ export CONFLUENCE_PROFILE="default"
144
158
  ```
145
159
 
146
160
  **Scoped API token** (recommended for agents):
@@ -154,6 +168,12 @@ export CONFLUENCE_API_TOKEN="your-scoped-token"
154
168
 
155
169
  `CONFLUENCE_API_PATH` defaults to `/wiki/rest/api` for Atlassian Cloud domains and `/rest/api` otherwise. Override it when your site lives under a custom reverse proxy or on-premises path. `CONFLUENCE_AUTH_TYPE` defaults to `basic` when an email is present and falls back to `bearer` otherwise.
156
170
 
171
+ **Read-only mode** (recommended for AI agents):
172
+ ```bash
173
+ export CONFLUENCE_READ_ONLY=true
174
+ ```
175
+ When set, all write operations (`create`, `update`, `delete`, etc.) are blocked at the CLI level. The environment variable overrides the profile's `readOnly` setting.
176
+
157
177
  ### Getting Your API Token
158
178
 
159
179
  **Atlassian Cloud:**
@@ -434,6 +454,52 @@ vim ./page-to-edit.xml
434
454
  confluence update 123456789 --file ./page-to-edit.xml --format storage
435
455
  ```
436
456
 
457
+ ### Profile Management
458
+ ```bash
459
+ # List all profiles and see which is active
460
+ confluence profile list
461
+
462
+ # Switch the active profile
463
+ confluence profile use staging
464
+
465
+ # Add a new profile interactively
466
+ confluence profile add staging
467
+
468
+ # Add a new profile non-interactively
469
+ confluence profile add staging --domain "staging.example.com" --auth-type bearer --token "xyz"
470
+
471
+ # Add a read-only profile (blocks all write operations)
472
+ confluence profile add agent --domain "company.atlassian.net" --auth-type basic --email "bot@example.com" --token "xyz" --read-only
473
+
474
+ # Remove a profile
475
+ confluence profile remove staging
476
+
477
+ # Use a specific profile for a single command
478
+ confluence --profile staging spaces
479
+ ```
480
+
481
+ ### Read-Only Mode
482
+
483
+ Read-only mode blocks all write operations at the CLI level, making it safe to hand the tool to AI agents (Claude Code, Copilot, etc.) without risking accidental edits.
484
+
485
+ **Enable via profile:**
486
+ ```bash
487
+ # During init
488
+ confluence init --read-only
489
+
490
+ # When adding a profile
491
+ confluence profile add agent --domain "company.atlassian.net" --token "xyz" --read-only
492
+ ```
493
+
494
+ **Enable via environment variable:**
495
+ ```bash
496
+ export CONFLUENCE_READ_ONLY=true # overrides profile setting
497
+ ```
498
+
499
+ When read-only mode is active, any write command (`create`, `create-child`, `update`, `delete`, `move`, `edit`, `comment`, `attachment-upload`, `attachment-delete`, `property-set`, `property-delete`, `comment-delete`, `copy-tree`) exits with code 1 and prints an error message.
500
+
501
+ `confluence profile list` shows a `[read-only]` badge next to protected profiles.
502
+
437
503
  ### View Usage Statistics
438
504
  ```bash
439
505
  confluence stats
@@ -443,7 +509,7 @@ confluence stats
443
509
 
444
510
  | Command | Description | Options |
445
511
  |---|---|---|
446
- | `init` | Initialize CLI configuration | |
512
+ | `init` | Initialize CLI configuration | `--read-only` |
447
513
  | `read <pageId_or_url>` | Read page content | `--format <html\|text\|markdown>` |
448
514
  | `info <pageId_or_url>` | Get page information | |
449
515
  | `search <query>` | Search for pages | `--limit <number>` |
@@ -468,8 +534,14 @@ confluence stats
468
534
  | `property-set <pageId_or_url> <key>` | Set a content property (create or update) | `--value <json>`, `--file <path>`, `--format <text\|json>` |
469
535
  | `property-delete <pageId_or_url> <key>` | Delete a content property by key | `--yes` |
470
536
  | `export <pageId_or_url>` | Export a page to a directory with its attachments | `--format <html\|text\|markdown>`, `--dest <directory>`, `--file <filename>`, `--attachments-dir <name>`, `--pattern <glob>`, `--referenced-only`, `--skip-attachments` |
537
+ | `profile list` | List all configuration profiles | |
538
+ | `profile use <name>` | Set the active configuration profile | |
539
+ | `profile add <name>` | Add a new configuration profile | `-d, --domain`, `-p, --api-path`, `-a, --auth-type`, `-e, --email`, `-t, --token`, `--protocol`, `--read-only` |
540
+ | `profile remove <name>` | Remove a configuration profile | |
471
541
  | `stats` | View your usage statistics | |
472
542
 
543
+ **Global option:** `--profile <name>` — Use a specific profile for any command (overrides `CONFLUENCE_PROFILE` env var and active profile).
544
+
473
545
  ## Examples
474
546
 
475
547
  ```bash
@@ -503,6 +575,11 @@ confluence attachment-delete 123456789 998877 --yes
503
575
 
504
576
  # View usage statistics
505
577
  confluence stats
578
+
579
+ # Profile management
580
+ confluence profile list
581
+ confluence profile use staging
582
+ confluence --profile staging spaces
506
583
  ```
507
584
 
508
585
  ## Development
package/bin/confluence.js CHANGED
@@ -13,6 +13,14 @@ function buildPageUrl(config, path) {
13
13
  return `${protocol}://${config.domain}${path}`;
14
14
  }
15
15
 
16
+ function assertWritable(config) {
17
+ if (config.readOnly) {
18
+ console.error(chalk.red('Error: This profile is in read-only mode. Write operations are not allowed.'));
19
+ console.error(chalk.yellow('Tip: Use "confluence profile add <name>" without --read-only, or set readOnly to false in config.'));
20
+ process.exit(1);
21
+ }
22
+ }
23
+
16
24
  program
17
25
  .name('confluence')
18
26
  .description('CLI tool for Atlassian Confluence')
@@ -34,6 +42,7 @@ program
34
42
  .option('-a, --auth-type <type>', 'Authentication type (basic or bearer)')
35
43
  .option('-e, --email <email>', 'Email or username for basic auth')
36
44
  .option('-t, --token <token>', 'API token')
45
+ .option('--read-only', 'Set profile to read-only mode (blocks write operations)')
37
46
  .action(async (options) => {
38
47
  const profile = getProfileName();
39
48
  await initConfig({ ...options, profile });
@@ -208,8 +217,9 @@ program
208
217
  const analytics = new Analytics();
209
218
  try {
210
219
  const config = getConfig(getProfileName());
220
+ assertWritable(config);
211
221
  const client = new ConfluenceClient(config);
212
-
222
+
213
223
  let content = '';
214
224
 
215
225
  if (options.file) {
@@ -251,8 +261,9 @@ program
251
261
  const analytics = new Analytics();
252
262
  try {
253
263
  const config = getConfig(getProfileName());
264
+ assertWritable(config);
254
265
  const client = new ConfluenceClient(config);
255
-
266
+
256
267
  // Get parent page info to get space key
257
268
  const parentInfo = await client.getPageInfo(parentId);
258
269
  const spaceKey = parentInfo.space.key;
@@ -305,8 +316,9 @@ program
305
316
  }
306
317
 
307
318
  const config = getConfig(getProfileName());
319
+ assertWritable(config);
308
320
  const client = new ConfluenceClient(config);
309
-
321
+
310
322
  let content = null; // Use null to indicate no content change
311
323
 
312
324
  if (options.file) {
@@ -344,6 +356,7 @@ program
344
356
  const analytics = new Analytics();
345
357
  try {
346
358
  const config = getConfig(getProfileName());
359
+ assertWritable(config);
347
360
  const client = new ConfluenceClient(config);
348
361
  const result = await client.movePage(pageId, newParentId, options.title);
349
362
 
@@ -371,6 +384,7 @@ program
371
384
  const analytics = new Analytics();
372
385
  try {
373
386
  const config = getConfig(getProfileName());
387
+ assertWritable(config);
374
388
  const client = new ConfluenceClient(config);
375
389
  const pageInfo = await client.getPageInfo(pageIdOrUrl);
376
390
 
@@ -414,6 +428,7 @@ program
414
428
  const analytics = new Analytics();
415
429
  try {
416
430
  const config = getConfig(getProfileName());
431
+ assertWritable(config);
417
432
  const client = new ConfluenceClient(config);
418
433
  const pageData = await client.getPageForEdit(pageId);
419
434
 
@@ -613,6 +628,7 @@ program
613
628
  const fs = require('fs');
614
629
  const path = require('path');
615
630
  const config = getConfig(getProfileName());
631
+ assertWritable(config);
616
632
  const client = new ConfluenceClient(config);
617
633
 
618
634
  const resolvedFiles = files.map((filePath) => ({
@@ -660,6 +676,7 @@ program
660
676
  const analytics = new Analytics();
661
677
  try {
662
678
  const config = getConfig(getProfileName());
679
+ assertWritable(config);
663
680
  const client = new ConfluenceClient(config);
664
681
 
665
682
  if (!options.yes) {
@@ -810,6 +827,7 @@ program
810
827
  const analytics = new Analytics();
811
828
  try {
812
829
  const config = getConfig(getProfileName());
830
+ assertWritable(config);
813
831
  const client = new ConfluenceClient(config);
814
832
 
815
833
  if (!options.value && !options.file) {
@@ -866,6 +884,7 @@ program
866
884
  const analytics = new Analytics();
867
885
  try {
868
886
  const config = getConfig(getProfileName());
887
+ assertWritable(config);
869
888
  const client = new ConfluenceClient(config);
870
889
 
871
890
  if (!options.yes) {
@@ -1067,6 +1086,7 @@ program
1067
1086
  let location = null;
1068
1087
  try {
1069
1088
  const config = getConfig(getProfileName());
1089
+ assertWritable(config);
1070
1090
  const client = new ConfluenceClient(config);
1071
1091
 
1072
1092
  let content = '';
@@ -1181,6 +1201,7 @@ program
1181
1201
  const analytics = new Analytics();
1182
1202
  try {
1183
1203
  const config = getConfig(getProfileName());
1204
+ assertWritable(config);
1184
1205
  const client = new ConfluenceClient(config);
1185
1206
 
1186
1207
  if (!options.yes) {
@@ -1599,8 +1620,9 @@ program
1599
1620
  const analytics = new Analytics();
1600
1621
  try {
1601
1622
  const config = getConfig(getProfileName());
1623
+ assertWritable(config);
1602
1624
  const client = new ConfluenceClient(config);
1603
-
1625
+
1604
1626
  // Parse numeric flags with safe fallbacks
1605
1627
  const parsedDepth = parseInt(options.maxDepth, 10);
1606
1628
  const maxDepth = Number.isNaN(parsedDepth) ? 10 : parsedDepth;
@@ -1876,7 +1898,8 @@ profileCmd
1876
1898
  console.log(chalk.blue('Configuration profiles:\n'));
1877
1899
  profiles.forEach(p => {
1878
1900
  const marker = p.active ? chalk.green(' (active)') : '';
1879
- console.log(` ${p.active ? chalk.green('*') : ' '} ${chalk.cyan(p.name)}${marker} - ${chalk.gray(p.domain)}`);
1901
+ const readOnlyBadge = p.readOnly ? chalk.red(' [read-only]') : '';
1902
+ console.log(` ${p.active ? chalk.green('*') : ' '} ${chalk.cyan(p.name)}${marker}${readOnlyBadge} - ${chalk.gray(p.domain)}`);
1880
1903
  });
1881
1904
  });
1882
1905
 
@@ -1902,6 +1925,7 @@ profileCmd
1902
1925
  .option('-a, --auth-type <type>', 'Authentication type (basic or bearer)')
1903
1926
  .option('-e, --email <email>', 'Email or username for basic auth')
1904
1927
  .option('-t, --token <token>', 'API token')
1928
+ .option('--read-only', 'Set profile to read-only mode (blocks write operations)')
1905
1929
  .action(async (name, options) => {
1906
1930
  if (!isValidProfileName(name)) {
1907
1931
  console.error(chalk.red('Invalid profile name. Use only letters, numbers, hyphens, and underscores.'));
@@ -1943,6 +1967,7 @@ module.exports = {
1943
1967
  uniquePathFor,
1944
1968
  exportRecursive,
1945
1969
  sanitizeTitle,
1970
+ assertWritable,
1946
1971
  },
1947
1972
  };
1948
1973
 
package/lib/config.js CHANGED
@@ -172,6 +172,10 @@ const saveConfig = (configData, profileName) => {
172
172
  config.email = configData.email.trim();
173
173
  }
174
174
 
175
+ if (configData.readOnly) {
176
+ config.readOnly = true;
177
+ }
178
+
175
179
  // Read existing config file (or create new structure)
176
180
  const fileData = readConfigFile() || { activeProfile: DEFAULT_PROFILE, profiles: {} };
177
181
 
@@ -297,6 +301,8 @@ async function initConfig(cliOptions = {}) {
297
301
  process.exit(1);
298
302
  }
299
303
 
304
+ const readOnly = cliOptions.readOnly || false;
305
+
300
306
  // Extract provided values from CLI options
301
307
  const providedValues = {
302
308
  protocol: cliOptions.protocol,
@@ -375,7 +381,7 @@ async function initConfig(cliOptions = {}) {
375
381
  }
376
382
  ]);
377
383
 
378
- saveConfig(answers, profileName);
384
+ saveConfig({ ...answers, readOnly }, profileName);
379
385
  return;
380
386
  }
381
387
 
@@ -427,7 +433,8 @@ async function initConfig(cliOptions = {}) {
427
433
  apiPath: providedValues.apiPath || inferApiPath(normalizedDomain),
428
434
  token: providedValues.token,
429
435
  authType: normalizedAuthType,
430
- email: providedValues.email
436
+ email: providedValues.email,
437
+ readOnly
431
438
  };
432
439
 
433
440
  saveConfig(configData, profileName);
@@ -451,7 +458,7 @@ async function initConfig(cliOptions = {}) {
451
458
  // Normalize auth type
452
459
  mergedValues.authType = normalizeAuthType(mergedValues.authType, Boolean(mergedValues.email));
453
460
 
454
- saveConfig(mergedValues, profileName);
461
+ saveConfig({ ...mergedValues, readOnly }, profileName);
455
462
  } catch (error) {
456
463
  console.error(chalk.red(`❌ ${error.message}`));
457
464
  process.exit(1);
@@ -465,6 +472,7 @@ function getConfig(profileName) {
465
472
  const envAuthType = process.env.CONFLUENCE_AUTH_TYPE;
466
473
  const envApiPath = process.env.CONFLUENCE_API_PATH;
467
474
  const envProtocol = process.env.CONFLUENCE_PROTOCOL;
475
+ const envReadOnly = process.env.CONFLUENCE_READ_ONLY;
468
476
 
469
477
  if (envDomain && envToken) {
470
478
  const authType = normalizeAuthType(envAuthType, Boolean(envEmail));
@@ -489,7 +497,8 @@ function getConfig(profileName) {
489
497
  apiPath,
490
498
  token: envToken.trim(),
491
499
  email: envEmail ? envEmail.trim() : undefined,
492
- authType
500
+ authType,
501
+ readOnly: envReadOnly === 'true'
493
502
  };
494
503
  }
495
504
 
@@ -547,13 +556,18 @@ function getConfig(profileName) {
547
556
  process.exit(1);
548
557
  }
549
558
 
559
+ const readOnly = envReadOnly !== undefined
560
+ ? envReadOnly === 'true'
561
+ : Boolean(storedConfig.readOnly);
562
+
550
563
  return {
551
564
  domain: trimmedDomain,
552
565
  protocol: normalizeProtocol(storedConfig.protocol),
553
566
  apiPath,
554
567
  token: trimmedToken,
555
568
  email: trimmedEmail,
556
- authType
569
+ authType,
570
+ readOnly
557
571
  };
558
572
  } catch (error) {
559
573
  console.error(chalk.red('❌ Error reading configuration file:'), error.message);
@@ -572,7 +586,8 @@ function listProfiles() {
572
586
  profiles: Object.keys(fileData.profiles).map(name => ({
573
587
  name,
574
588
  active: name === fileData.activeProfile,
575
- domain: fileData.profiles[name].domain
589
+ domain: fileData.profiles[name].domain,
590
+ readOnly: Boolean(fileData.profiles[name].readOnly)
576
591
  }))
577
592
  };
578
593
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "confluence-cli",
3
- "version": "1.26.0",
3
+ "version": "1.27.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": {