confluence-cli 1.32.0 → 1.33.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 +24 -0
- package/lib/config.js +25 -2
- package/lib/confluence-client.js +1 -0
- package/lib/macro-converter.js +15 -3
- package/npm-shrinkwrap.json +499 -4754
- package/package.json +1 -1
- package/plugins/confluence/skills/confluence/SKILL.md +1 -0
package/README.md
CHANGED
|
@@ -249,6 +249,30 @@ Or add `"forceCloud": true` to your profile in `~/.confluence-cli/config.json`:
|
|
|
249
249
|
}
|
|
250
250
|
```
|
|
251
251
|
|
|
252
|
+
**Link rendering on Cloud (`linkStyle`):**
|
|
253
|
+
|
|
254
|
+
Some Cloud instances — particularly custom-domain Cloud setups — fail to render smart links (`<a data-card-appearance="inline">`) and show "Cannot handle: DefaultLink" errors instead. If you hit this, set `linkStyle` to `plain` to emit simple `<a href>` tags, which render reliably everywhere:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
export CONFLUENCE_LINK_STYLE=plain
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Or per-profile:
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{
|
|
264
|
+
"profiles": {
|
|
265
|
+
"default": {
|
|
266
|
+
"domain": "wiki.example.org",
|
|
267
|
+
"forceCloud": true,
|
|
268
|
+
"linkStyle": "plain"
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Valid values: `smart` (Cloud smart links), `plain` (simple `<a href>`), `wiki` (Server/DC `ac:link`). When unset, the CLI picks `smart` for Cloud and `wiki` for Server/DC — existing behavior is unchanged.
|
|
275
|
+
|
|
252
276
|
**Read-only mode** (recommended for AI agents):
|
|
253
277
|
```bash
|
|
254
278
|
export CONFLUENCE_READ_ONLY=true
|
package/lib/config.js
CHANGED
|
@@ -17,6 +17,23 @@ const AUTH_CHOICES = [
|
|
|
17
17
|
|
|
18
18
|
const AUTH_TYPES = ['basic', 'bearer', 'mtls', 'cookie'];
|
|
19
19
|
|
|
20
|
+
const { VALID_LINK_STYLES } = require('./macro-converter');
|
|
21
|
+
|
|
22
|
+
const normalizeLinkStyle = (rawValue, source) => {
|
|
23
|
+
if (rawValue === undefined || rawValue === null || rawValue === '') {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const value = String(rawValue).trim().toLowerCase();
|
|
27
|
+
if (VALID_LINK_STYLES.includes(value)) {
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
const label = source ? `${source} ` : '';
|
|
31
|
+
console.error(chalk.yellow(
|
|
32
|
+
`⚠ Invalid linkStyle ${label}"${rawValue}"; valid values: ${VALID_LINK_STYLES.join(', ')}. Falling back to auto-detection.`
|
|
33
|
+
));
|
|
34
|
+
return undefined;
|
|
35
|
+
};
|
|
36
|
+
|
|
20
37
|
const isValidProfileName = (name) => /^[a-zA-Z0-9_-]+$/.test(name);
|
|
21
38
|
|
|
22
39
|
const requiredInput = (label) => (input) => {
|
|
@@ -719,6 +736,7 @@ function getConfig(profileName) {
|
|
|
719
736
|
const envProtocol = process.env.CONFLUENCE_PROTOCOL;
|
|
720
737
|
const envReadOnly = process.env.CONFLUENCE_READ_ONLY;
|
|
721
738
|
const envForceCloud = process.env.CONFLUENCE_FORCE_CLOUD;
|
|
739
|
+
const envLinkStyle = normalizeLinkStyle(process.env.CONFLUENCE_LINK_STYLE, 'from CONFLUENCE_LINK_STYLE');
|
|
722
740
|
const envCookie = process.env.CONFLUENCE_COOKIE;
|
|
723
741
|
const envMtls = normalizeMtlsConfig({
|
|
724
742
|
caCert: process.env.CONFLUENCE_TLS_CA_CERT,
|
|
@@ -772,7 +790,8 @@ function getConfig(profileName) {
|
|
|
772
790
|
authType,
|
|
773
791
|
mtls: envMtls,
|
|
774
792
|
readOnly: envReadOnly === 'true',
|
|
775
|
-
forceCloud: envForceCloud === 'true'
|
|
793
|
+
forceCloud: envForceCloud === 'true',
|
|
794
|
+
linkStyle: envLinkStyle
|
|
776
795
|
};
|
|
777
796
|
}
|
|
778
797
|
|
|
@@ -844,6 +863,9 @@ function getConfig(profileName) {
|
|
|
844
863
|
? envForceCloud === 'true'
|
|
845
864
|
: Boolean(storedConfig.forceCloud);
|
|
846
865
|
|
|
866
|
+
const linkStyle = envLinkStyle
|
|
867
|
+
?? normalizeLinkStyle(storedConfig.linkStyle, `in profile "${targetProfile}"`);
|
|
868
|
+
|
|
847
869
|
return {
|
|
848
870
|
domain: trimmedDomain,
|
|
849
871
|
protocol: normalizeProtocol(storedConfig.protocol),
|
|
@@ -854,7 +876,8 @@ function getConfig(profileName) {
|
|
|
854
876
|
authType,
|
|
855
877
|
mtls,
|
|
856
878
|
readOnly,
|
|
857
|
-
forceCloud
|
|
879
|
+
forceCloud,
|
|
880
|
+
linkStyle
|
|
858
881
|
};
|
|
859
882
|
} catch (error) {
|
|
860
883
|
console.error(chalk.red('❌ Error reading configuration file:'), error.message);
|
package/lib/confluence-client.js
CHANGED
package/lib/macro-converter.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
const MarkdownIt = require('markdown-it');
|
|
2
2
|
const { htmlToMarkdown } = require('./html-to-markdown');
|
|
3
3
|
|
|
4
|
+
const VALID_LINK_STYLES = ['smart', 'plain', 'wiki'];
|
|
5
|
+
|
|
4
6
|
class MacroConverter {
|
|
5
|
-
constructor({ isCloud = false, webUrlPrefix = '', buildUrl = null } = {}) {
|
|
7
|
+
constructor({ isCloud = false, webUrlPrefix = '', buildUrl = null, linkStyle = null } = {}) {
|
|
6
8
|
this._isCloud = isCloud;
|
|
7
9
|
this.webUrlPrefix = webUrlPrefix;
|
|
8
10
|
this.buildUrl = buildUrl || ((pathOrUrl) => pathOrUrl);
|
|
11
|
+
this.linkStyle = VALID_LINK_STYLES.includes(linkStyle)
|
|
12
|
+
? linkStyle
|
|
13
|
+
: (isCloud ? 'smart' : 'wiki');
|
|
9
14
|
this.markdown = new MarkdownIt();
|
|
10
15
|
this.setupConfluenceMarkdownExtensions();
|
|
11
16
|
}
|
|
@@ -107,11 +112,17 @@ class MacroConverter {
|
|
|
107
112
|
storage = storage.replace(/<th>(.*?)<\/th>/g, '<th><p>$1</p></th>');
|
|
108
113
|
storage = storage.replace(/<td>(.*?)<\/td>/g, '<td><p>$1</p></td>');
|
|
109
114
|
|
|
110
|
-
|
|
115
|
+
// Convert links based on linkStyle:
|
|
116
|
+
// "smart" — Cloud smart links (<a data-card-appearance="inline">)
|
|
117
|
+
// "plain" — simple <a href>; workaround for "Cannot handle: DefaultLink"
|
|
118
|
+
// errors on custom-domain Cloud instances
|
|
119
|
+
// "wiki" — Server/DC ac:link + ri:url storage format
|
|
120
|
+
if (this.linkStyle === 'smart') {
|
|
111
121
|
storage = storage.replace(/<a href="(.*?)">(.*?)<\/a>/g, '<a href="$1" data-card-appearance="inline">$2</a>');
|
|
112
|
-
} else {
|
|
122
|
+
} else if (this.linkStyle === 'wiki') {
|
|
113
123
|
storage = storage.replace(/<a href="(.*?)">(.*?)<\/a>/g, '<ac:link><ri:url ri:value="$1" /><ac:plain-text-link-body><![CDATA[$2]]></ac:plain-text-link-body></ac:link>');
|
|
114
124
|
}
|
|
125
|
+
// "plain" — leave <a href> tags as-is
|
|
115
126
|
|
|
116
127
|
storage = storage.replace(/<hr\s*\/?>/g, '<hr />');
|
|
117
128
|
|
|
@@ -296,3 +307,4 @@ class MacroConverter {
|
|
|
296
307
|
}
|
|
297
308
|
|
|
298
309
|
module.exports = MacroConverter;
|
|
310
|
+
module.exports.VALID_LINK_STYLES = VALID_LINK_STYLES;
|