confluencedc-cli 1.0.5 → 1.0.6

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.
Files changed (113) hide show
  1. package/dist/index.js +572 -32
  2. package/package.json +5 -4
  3. package/dist/commands/attachment/delete.d.ts +0 -3
  4. package/dist/commands/attachment/delete.d.ts.map +0 -1
  5. package/dist/commands/attachment/delete.js +0 -14
  6. package/dist/commands/attachment/delete.js.map +0 -1
  7. package/dist/commands/attachment/download-all.d.ts +0 -3
  8. package/dist/commands/attachment/download-all.d.ts.map +0 -1
  9. package/dist/commands/attachment/download-all.js +0 -46
  10. package/dist/commands/attachment/download-all.js.map +0 -1
  11. package/dist/commands/attachment/download.d.ts +0 -3
  12. package/dist/commands/attachment/download.d.ts.map +0 -1
  13. package/dist/commands/attachment/download.js +0 -16
  14. package/dist/commands/attachment/download.js.map +0 -1
  15. package/dist/commands/attachment/index.d.ts +0 -3
  16. package/dist/commands/attachment/index.d.ts.map +0 -1
  17. package/dist/commands/attachment/index.js +0 -24
  18. package/dist/commands/attachment/index.js.map +0 -1
  19. package/dist/commands/attachment/list.d.ts +0 -3
  20. package/dist/commands/attachment/list.d.ts.map +0 -1
  21. package/dist/commands/attachment/list.js +0 -22
  22. package/dist/commands/attachment/list.js.map +0 -1
  23. package/dist/commands/attachment/upload.d.ts +0 -3
  24. package/dist/commands/attachment/upload.d.ts.map +0 -1
  25. package/dist/commands/attachment/upload.js +0 -41
  26. package/dist/commands/attachment/upload.js.map +0 -1
  27. package/dist/commands/comment/add.d.ts +0 -3
  28. package/dist/commands/comment/add.d.ts.map +0 -1
  29. package/dist/commands/comment/add.js +0 -15
  30. package/dist/commands/comment/add.js.map +0 -1
  31. package/dist/commands/comment/delete.d.ts +0 -3
  32. package/dist/commands/comment/delete.d.ts.map +0 -1
  33. package/dist/commands/comment/delete.js +0 -14
  34. package/dist/commands/comment/delete.js.map +0 -1
  35. package/dist/commands/comment/index.d.ts +0 -3
  36. package/dist/commands/comment/index.d.ts.map +0 -1
  37. package/dist/commands/comment/index.js +0 -23
  38. package/dist/commands/comment/index.js.map +0 -1
  39. package/dist/commands/comment/list.d.ts +0 -3
  40. package/dist/commands/comment/list.d.ts.map +0 -1
  41. package/dist/commands/comment/list.js +0 -25
  42. package/dist/commands/comment/list.js.map +0 -1
  43. package/dist/commands/comment/reply.d.ts +0 -3
  44. package/dist/commands/comment/reply.d.ts.map +0 -1
  45. package/dist/commands/comment/reply.js +0 -20
  46. package/dist/commands/comment/reply.js.map +0 -1
  47. package/dist/commands/comment/update.d.ts +0 -3
  48. package/dist/commands/comment/update.d.ts.map +0 -1
  49. package/dist/commands/comment/update.js +0 -17
  50. package/dist/commands/comment/update.js.map +0 -1
  51. package/dist/commands/label/add.d.ts +0 -3
  52. package/dist/commands/label/add.d.ts.map +0 -1
  53. package/dist/commands/label/add.js +0 -15
  54. package/dist/commands/label/add.js.map +0 -1
  55. package/dist/commands/label/index.d.ts +0 -3
  56. package/dist/commands/label/index.d.ts.map +0 -1
  57. package/dist/commands/label/index.js +0 -15
  58. package/dist/commands/label/index.js.map +0 -1
  59. package/dist/commands/label/list.d.ts +0 -3
  60. package/dist/commands/label/list.d.ts.map +0 -1
  61. package/dist/commands/label/list.js +0 -16
  62. package/dist/commands/label/list.js.map +0 -1
  63. package/dist/commands/page/children.d.ts +0 -3
  64. package/dist/commands/page/children.d.ts.map +0 -1
  65. package/dist/commands/page/children.js +0 -22
  66. package/dist/commands/page/children.js.map +0 -1
  67. package/dist/commands/page/create.d.ts +0 -3
  68. package/dist/commands/page/create.d.ts.map +0 -1
  69. package/dist/commands/page/create.js +0 -28
  70. package/dist/commands/page/create.js.map +0 -1
  71. package/dist/commands/page/delete.d.ts +0 -3
  72. package/dist/commands/page/delete.d.ts.map +0 -1
  73. package/dist/commands/page/delete.js +0 -14
  74. package/dist/commands/page/delete.js.map +0 -1
  75. package/dist/commands/page/get.d.ts +0 -3
  76. package/dist/commands/page/get.d.ts.map +0 -1
  77. package/dist/commands/page/get.js +0 -55
  78. package/dist/commands/page/get.js.map +0 -1
  79. package/dist/commands/page/history.d.ts +0 -3
  80. package/dist/commands/page/history.d.ts.map +0 -1
  81. package/dist/commands/page/history.js +0 -16
  82. package/dist/commands/page/history.js.map +0 -1
  83. package/dist/commands/page/index.d.ts +0 -3
  84. package/dist/commands/page/index.d.ts.map +0 -1
  85. package/dist/commands/page/index.js +0 -27
  86. package/dist/commands/page/index.js.map +0 -1
  87. package/dist/commands/page/update.d.ts +0 -3
  88. package/dist/commands/page/update.d.ts.map +0 -1
  89. package/dist/commands/page/update.js +0 -36
  90. package/dist/commands/page/update.js.map +0 -1
  91. package/dist/commands/search/index.d.ts +0 -3
  92. package/dist/commands/search/index.d.ts.map +0 -1
  93. package/dist/commands/search/index.js +0 -5
  94. package/dist/commands/search/index.js.map +0 -1
  95. package/dist/commands/search/search.d.ts +0 -3
  96. package/dist/commands/search/search.d.ts.map +0 -1
  97. package/dist/commands/search/search.js +0 -20
  98. package/dist/commands/search/search.js.map +0 -1
  99. package/dist/index.d.ts +0 -3
  100. package/dist/index.d.ts.map +0 -1
  101. package/dist/index.js.map +0 -1
  102. package/dist/utils/client.d.ts +0 -3
  103. package/dist/utils/client.d.ts.map +0 -1
  104. package/dist/utils/client.js +0 -19
  105. package/dist/utils/client.js.map +0 -1
  106. package/dist/utils/output.d.ts +0 -3
  107. package/dist/utils/output.d.ts.map +0 -1
  108. package/dist/utils/output.js +0 -60
  109. package/dist/utils/output.js.map +0 -1
  110. package/dist/utils/strip-response.d.ts +0 -6
  111. package/dist/utils/strip-response.d.ts.map +0 -1
  112. package/dist/utils/strip-response.js +0 -29
  113. package/dist/utils/strip-response.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,33 +1,574 @@
1
1
  #!/usr/bin/env node
2
- import { styleText } from 'node:util';
3
- import { Command } from 'commander';
4
- import { registerAttachmentCommands } from './commands/attachment/index.js';
5
- import { registerCommentCommands } from './commands/comment/index.js';
6
- import { registerLabelCommands } from './commands/label/index.js';
7
- import { registerPageCommands } from './commands/page/index.js';
8
- import { registerSearchCommands } from './commands/search/index.js';
9
- import { handleError } from './utils/output.js';
10
- const DIM = '\x1b[2m';
11
- const RESET = '\x1b[0m';
12
- const program = new Command();
13
- program
14
- .name('confluencedc')
15
- .description('Confluence Data Center CLI')
16
- .version('1.0.0')
17
- .configureHelp({
18
- styleTitle: (str) => styleText('bold', str),
19
- styleUsage: (str) => styleText('dim', str),
20
- styleCommandDescription: (str) => styleText('dim', str),
21
- styleOptionDescription: (str) => styleText('dim', str),
22
- styleSubcommandDescription: (str) => styleText('dim', str),
23
- })
24
- .addHelpText('beforeAll', `\n${styleText('bold', 'confluencedc')} ${DIM}— Confluence Data Center CLI${RESET}\n`)
25
- .addHelpText('after', `
26
- ${styleText('bold', 'Environment:')}
2
+
3
+ // src/index.ts
4
+ import { styleText } from "util";
5
+ import { Command } from "commander";
6
+
7
+ // src/utils/client.ts
8
+ import { ConfluenceClient } from "confluence-data-center-client";
9
+ function getClient() {
10
+ const baseUrl = process.env.CONFLUENCE_URL;
11
+ const token = process.env.CONFLUENCE_TOKEN;
12
+ if (!baseUrl || !token) {
13
+ const missing = [...!baseUrl ? ["CONFLUENCE_URL"] : [], ...!token ? ["CONFLUENCE_TOKEN"] : []];
14
+ process.stderr.write(
15
+ `${JSON.stringify({
16
+ error: `Missing required environment variables: ${missing.join(", ")}`,
17
+ setup: {
18
+ CONFLUENCE_URL: "Your Confluence Server base URL (e.g., https://confluence.example.com)",
19
+ CONFLUENCE_TOKEN: "Personal Access Token \u2014 generate in Confluence > Profile > Personal Access Tokens"
20
+ },
21
+ hint: "Export these in your shell profile (e.g., ~/.zshrc)."
22
+ })}
23
+ `
24
+ );
25
+ process.exit(1);
26
+ }
27
+ return new ConfluenceClient({ baseUrl, token });
28
+ }
29
+
30
+ // src/utils/strip-response.ts
31
+ function stripResponse(obj) {
32
+ if (Array.isArray(obj)) {
33
+ return obj.map(stripResponse);
34
+ }
35
+ if (obj === null || typeof obj !== "object") {
36
+ return obj;
37
+ }
38
+ const record = obj;
39
+ const result = {};
40
+ for (const [key, value] of Object.entries(record)) {
41
+ if (key === "self" || key === "_links" || key === "_expandable" || key === "expand" || key === "avatarUrls" || key === "avatarId" || key === "iconUrl") {
42
+ continue;
43
+ }
44
+ result[key] = stripResponse(value);
45
+ }
46
+ return result;
47
+ }
48
+
49
+ // src/utils/output.ts
50
+ function output(data) {
51
+ process.stdout.write(`${JSON.stringify(stripResponse(data), null, 2)}
52
+ `);
53
+ }
54
+ function handleError(err) {
55
+ const message = err instanceof Error ? err.message : String(err);
56
+ const axiosStatus = err?.response?.status;
57
+ if (axiosStatus === 400) {
58
+ const responseData = err?.response?.data;
59
+ const apiErrors = responseData && typeof responseData === "object" ? responseData : void 0;
60
+ process.stderr.write(
61
+ `${JSON.stringify({
62
+ error: "Bad request (HTTP 400)",
63
+ detail: apiErrors ?? message,
64
+ hint: "Check that all parameters are valid (CQL syntax, space keys, page IDs, etc.)."
65
+ })}
66
+ `
67
+ );
68
+ } else if (axiosStatus === 401) {
69
+ process.stderr.write(
70
+ `${JSON.stringify({
71
+ error: "Authentication failed (HTTP 401)",
72
+ hint: "Verify that CONFLUENCE_URL and CONFLUENCE_TOKEN are set correctly. The token may be expired or invalid."
73
+ })}
74
+ `
75
+ );
76
+ } else if (axiosStatus === 403) {
77
+ const responseData = err?.response?.data;
78
+ process.stderr.write(
79
+ `${JSON.stringify({
80
+ error: "Forbidden (HTTP 403)",
81
+ detail: responseData && typeof responseData === "object" ? responseData : message,
82
+ hint: "Your account does not have permission for this operation. Check space permissions and token scope."
83
+ })}
84
+ `
85
+ );
86
+ } else if (axiosStatus === 404) {
87
+ const responseData = err?.response?.data;
88
+ const authFlag = responseData && typeof responseData === "object" ? responseData.data : void 0;
89
+ const isAuthIssue = authFlag && typeof authFlag === "object" && authFlag.authorized === false;
90
+ process.stderr.write(
91
+ `${JSON.stringify({
92
+ error: "Not found (HTTP 404)",
93
+ detail: responseData && typeof responseData === "object" ? responseData : message,
94
+ hint: isAuthIssue ? "Access denied. Confluence returns 404 for unauthorized access \u2014 verify that CONFLUENCE_TOKEN is valid and has access to this space." : "Resource not found. Check that the space key, page ID, or content ID exists. Confluence also returns 404 for unauthorized access \u2014 if the resource exists, verify your token."
95
+ })}
96
+ `
97
+ );
98
+ } else if (message.includes("ENOTFOUND") || message.includes("ECONNREFUSED") || message.includes("ECONNRESET")) {
99
+ process.stderr.write(
100
+ `${JSON.stringify({
101
+ error: `Cannot connect to Confluence server: ${message}`,
102
+ hint: "Verify that CONFLUENCE_URL is correct and the server is reachable."
103
+ })}
104
+ `
105
+ );
106
+ } else if (axiosStatus === 500) {
107
+ process.stderr.write(
108
+ `${JSON.stringify({
109
+ error: `Server error (HTTP 500): ${message}`,
110
+ hint: "The server returned an internal error. Check that all parameters are valid (space keys, page IDs, CQL query syntax)."
111
+ })}
112
+ `
113
+ );
114
+ } else {
115
+ process.stderr.write(`${JSON.stringify({ error: message })}
116
+ `);
117
+ }
118
+ process.exit(1);
119
+ }
120
+
121
+ // src/commands/attachment/delete.ts
122
+ function deleteAttachment(parent) {
123
+ parent.command("delete <attachmentId>").description("Delete an attachment").addHelpText("after", "\nExamples:\n confluencedc attachment delete att12345").action(async (attachmentId) => {
124
+ const client = getClient();
125
+ await client.attachments.delete({ attachmentId });
126
+ output({ deleted: true, attachmentId });
127
+ });
128
+ }
129
+
130
+ // src/commands/attachment/download-all.ts
131
+ import { mkdirSync } from "fs";
132
+ import { join } from "path";
133
+ function downloadAll(parent) {
134
+ parent.command("download-all <pageId>").description("Download all attachments from a page").requiredOption("--output <dir>", "Local directory to save attachments into").addHelpText("after", "\nExamples:\n confluencedc attachment download-all 12345 --output ./downloads").action(async (pageId, opts) => {
135
+ const client = getClient();
136
+ mkdirSync(opts.output, { recursive: true });
137
+ const attachments = await client.attachments.get({ pageId });
138
+ const items = attachments.results ?? [];
139
+ if (items.length === 0) {
140
+ output({ pageId, downloaded: 0, files: [] });
141
+ return;
142
+ }
143
+ const results = [];
144
+ const failed = [];
145
+ for (const att of items) {
146
+ const downloadUrl = att._links?.download;
147
+ if (!downloadUrl) {
148
+ failed.push({ filename: att.title, error: "No download URL" });
149
+ continue;
150
+ }
151
+ const destPath = join(opts.output, att.title);
152
+ try {
153
+ await client.attachments.download({ downloadUrl, destinationPath: destPath });
154
+ results.push({ filename: att.title, size: att.fileSize ?? 0, path: destPath });
155
+ } catch (err) {
156
+ failed.push({ filename: att.title, error: String(err) });
157
+ }
158
+ }
159
+ output({
160
+ pageId,
161
+ downloaded: results.length,
162
+ total: items.length,
163
+ files: results,
164
+ ...failed.length > 0 && { failed }
165
+ });
166
+ });
167
+ }
168
+
169
+ // src/commands/attachment/download.ts
170
+ function download(parent) {
171
+ parent.command("download").description("Download a single attachment by URL").requiredOption("--url <downloadUrl>", "Download URL from attachment _links.download").requiredOption("--output <path>", "Local file path to save to").addHelpText(
172
+ "after",
173
+ '\nExamples:\n confluencedc attachment download --url "/download/attachments/12345/report.pdf" --output ./report.pdf'
174
+ ).action(async (opts) => {
175
+ const client = getClient();
176
+ await client.attachments.download({ downloadUrl: opts.url, destinationPath: opts.output });
177
+ output({ downloaded: true, path: opts.output });
178
+ });
179
+ }
180
+
181
+ // src/commands/attachment/list.ts
182
+ function list(parent) {
183
+ parent.command("list <pageId>").description("Get attachments on a page").option("--start <number>", "Starting index", parseInt).option("--limit <number>", "Maximum results", parseInt).option("--filename <name>", "Filter by filename").addHelpText(
184
+ "after",
185
+ "\nExamples:\n confluencedc attachment list 12345\n confluencedc attachment list 12345 --filename report.pdf"
186
+ ).action(async (pageId, opts) => {
187
+ const client = getClient();
188
+ const result = await client.attachments.get({
189
+ pageId,
190
+ start: opts.start,
191
+ limit: opts.limit,
192
+ filename: opts.filename
193
+ });
194
+ output(result);
195
+ });
196
+ }
197
+
198
+ // src/commands/attachment/upload.ts
199
+ function upload(parent) {
200
+ parent.command("upload <pageId>").description("Upload attachments to a page").requiredOption("--files <paths>", "Comma-separated local file paths").option("--comment <text>", "Attachment comment").option("--no-minor-edit", "Mark as regular edit (default: minor edit)").addHelpText(
201
+ "after",
202
+ '\nExamples:\n confluencedc attachment upload 12345 --files report.pdf\n confluencedc attachment upload 12345 --files file1.txt,file2.pdf --comment "Updated docs"'
203
+ ).action(async (pageId, opts) => {
204
+ const client = getClient();
205
+ const filePaths = opts.files.split(",").map((p) => p.trim()).filter(Boolean);
206
+ const results = [];
207
+ const failed = [];
208
+ for (const filePath of filePaths) {
209
+ try {
210
+ const result = await client.attachments.upload({
211
+ pageId,
212
+ filePath,
213
+ comment: opts.comment,
214
+ minorEdit: opts.minorEdit
215
+ });
216
+ results.push({ filename: result.title, id: result.id });
217
+ } catch (err) {
218
+ failed.push({ filePath, error: err instanceof Error ? err.message : String(err) });
219
+ }
220
+ }
221
+ output({
222
+ pageId,
223
+ uploaded: results.length,
224
+ files: results,
225
+ ...failed.length > 0 && { failed }
226
+ });
227
+ });
228
+ }
229
+
230
+ // src/commands/attachment/index.ts
231
+ function registerAttachmentCommands(program2) {
232
+ const attachment = program2.command("attachment").description("Attachment operations").addHelpText(
233
+ "after",
234
+ `
235
+ Examples:
236
+ $ confluencedc attachment list 12345
237
+ $ confluencedc attachment download --url "/download/attachments/12345/file.pdf" --output ./file.pdf
238
+ $ confluencedc attachment download-all 12345 --output ./downloads
239
+ $ confluencedc attachment upload 12345 --files report.pdf,notes.txt
240
+ $ confluencedc attachment delete att12345
241
+ `
242
+ );
243
+ list(attachment);
244
+ download(attachment);
245
+ downloadAll(attachment);
246
+ upload(attachment);
247
+ deleteAttachment(attachment);
248
+ }
249
+
250
+ // src/commands/comment/add.ts
251
+ function add(parent) {
252
+ parent.command("add <pageId>").description("Add a comment to a page").requiredOption("--body <text>", "Comment body in wiki markup").addHelpText("after", '\nExamples:\n confluencedc comment add 12345 --body "Looks good, approved."').action(async (pageId, opts) => {
253
+ const client = getClient();
254
+ const result = await client.comments.add({ pageId, body: opts.body });
255
+ output(result);
256
+ });
257
+ }
258
+
259
+ // src/commands/comment/delete.ts
260
+ function deleteComment(parent) {
261
+ parent.command("delete <commentId>").description("Delete a comment").addHelpText("after", "\nExamples:\n confluencedc comment delete 67890").action(async (commentId) => {
262
+ const client = getClient();
263
+ await client.comments.delete({ commentId });
264
+ output({ deleted: true, commentId });
265
+ });
266
+ }
267
+
268
+ // src/commands/comment/list.ts
269
+ function list2(parent) {
270
+ parent.command("list <pageId>").description("Get comments on a page").option("--start <number>", "Starting index", parseInt).option("--limit <number>", "Maximum results", parseInt).option("--depth <depth>", 'Comment depth (e.g., "all" for all threads)').option("--expand <expand>", 'Expand options (e.g., "children.comment,body.storage")').addHelpText(
271
+ "after",
272
+ "\nExamples:\n confluencedc comment list 12345\n confluencedc comment list 12345 --depth all --limit 50\n confluencedc comment list 12345 --expand children.comment,body.storage"
273
+ ).action(async (pageId, opts) => {
274
+ const client = getClient();
275
+ const expand = opts.expand ? `body.storage,${opts.expand}` : void 0;
276
+ const result = await client.comments.get({
277
+ pageId,
278
+ start: opts.start,
279
+ limit: opts.limit,
280
+ depth: opts.depth,
281
+ expand
282
+ });
283
+ output(result);
284
+ });
285
+ }
286
+
287
+ // src/commands/comment/reply.ts
288
+ function reply(parent) {
289
+ parent.command("reply <commentId>").description("Reply to a comment").requiredOption("--page <pageId>", "Page ID the comment belongs to").requiredOption("--body <text>", "Reply body in wiki markup").addHelpText("after", '\nExamples:\n confluencedc comment reply 67890 --page 12345 --body "Good point, agreed."').action(async (commentId, opts) => {
290
+ const client = getClient();
291
+ const result = await client.comments.reply({
292
+ pageId: opts.page,
293
+ parentCommentId: commentId,
294
+ body: opts.body
295
+ });
296
+ output(result);
297
+ });
298
+ }
299
+
300
+ // src/commands/comment/update.ts
301
+ function update(parent) {
302
+ parent.command("update <commentId>").description("Update a comment").requiredOption("--body <text>", "New comment body in wiki markup").addHelpText("after", '\nExamples:\n confluencedc comment update 67890 --body "Updated text"').action(async (commentId, opts) => {
303
+ const client = getClient();
304
+ const current = await client.comments.getById(commentId);
305
+ const version = current.version?.number ?? 1;
306
+ const result = await client.comments.update({ commentId, body: opts.body, version });
307
+ output(result);
308
+ });
309
+ }
310
+
311
+ // src/commands/comment/index.ts
312
+ function registerCommentCommands(program2) {
313
+ const comment = program2.command("comment").description("Comment operations").addHelpText(
314
+ "after",
315
+ `
316
+ Examples:
317
+ $ confluencedc comment list 12345
318
+ $ confluencedc comment add 12345 --body "Great work!"
319
+ $ confluencedc comment update 67890 --body "Updated"
320
+ $ confluencedc comment delete 67890
321
+ `
322
+ );
323
+ list2(comment);
324
+ add(comment);
325
+ update(comment);
326
+ deleteComment(comment);
327
+ reply(comment);
328
+ }
329
+
330
+ // src/commands/label/add.ts
331
+ function add2(parent) {
332
+ parent.command("add <pageId>").description("Add a label to a page").requiredOption("--label <name>", "Label name").addHelpText("after", "\nExamples:\n confluencedc label add 12345 --label important").action(async (pageId, opts) => {
333
+ const client = getClient();
334
+ const result = await client.labels.add({ pageId, label: opts.label });
335
+ output(result);
336
+ });
337
+ }
338
+
339
+ // src/commands/label/list.ts
340
+ function list3(parent) {
341
+ parent.command("list <pageId>").description("Get labels on a page").option("--start <number>", "Starting index", parseInt).option("--limit <number>", "Maximum results", parseInt).addHelpText("after", "\nExamples:\n confluencedc label list 12345").action(async (pageId, opts) => {
342
+ const client = getClient();
343
+ const result = await client.labels.get({ pageId, start: opts.start, limit: opts.limit });
344
+ output(result);
345
+ });
346
+ }
347
+
348
+ // src/commands/label/index.ts
349
+ function registerLabelCommands(program2) {
350
+ const label = program2.command("label").description("Label operations").addHelpText(
351
+ "after",
352
+ `
353
+ Examples:
354
+ $ confluencedc label list 12345
355
+ $ confluencedc label add 12345 --label important
356
+ `
357
+ );
358
+ list3(label);
359
+ add2(label);
360
+ }
361
+
362
+ // src/commands/page/children.ts
363
+ function children(parent) {
364
+ parent.command("children <pageId>").description("Get child pages of a page").option("--start <number>", "Starting index", parseInt).option("--limit <number>", "Maximum results", parseInt).option("--expand <expand>", "Expand options").addHelpText(
365
+ "after",
366
+ "\nExamples:\n confluencedc page children 12345\n confluencedc page children 12345 --limit 10"
367
+ ).action(async (pageId, opts) => {
368
+ const client = getClient();
369
+ const result = await client.pages.getChildren({
370
+ pageId,
371
+ start: opts.start,
372
+ limit: opts.limit,
373
+ expand: opts.expand
374
+ });
375
+ output(result);
376
+ });
377
+ }
378
+
379
+ // src/commands/page/create.ts
380
+ function create(parent) {
381
+ parent.command("create").description("Create a new page").requiredOption("--space <key>", "Space key").requiredOption("--title <title>", "Page title").requiredOption("--body <content>", "Page content (XHTML storage format by default)").option("--parent <pageId>", "Parent page ID (creates as child page)").option("--format <format>", "Content format: storage (default) or wiki", "storage").addHelpText(
382
+ "after",
383
+ `
384
+ Examples:
385
+ confluencedc page create --space DEV --title "New Page" --body "<p>Hello world</p>"
386
+ confluencedc page create --space DEV --title "Child Page" --body "<p>Content</p>" --parent 12345
387
+ confluencedc page create --space DEV --title "Wiki Page" --body "h1. Title" --format wiki`
388
+ ).action(async (opts) => {
389
+ const client = getClient();
390
+ const result = await client.pages.create({
391
+ spaceKey: opts.space,
392
+ title: opts.title,
393
+ body: opts.body,
394
+ parentId: opts.parent,
395
+ contentFormat: opts.format
396
+ });
397
+ output(result);
398
+ });
399
+ }
400
+
401
+ // src/commands/page/delete.ts
402
+ function deletePage(parent) {
403
+ parent.command("delete <pageId>").description("Delete a page").addHelpText("after", "\nExamples:\n confluencedc page delete 12345").action(async (pageId) => {
404
+ const client = getClient();
405
+ await client.pages.delete({ pageId });
406
+ output({ deleted: true, pageId });
407
+ });
408
+ }
409
+
410
+ // src/commands/page/get.ts
411
+ function get(parent) {
412
+ parent.command("get [pageId]").description("Get a page by ID, or by title + space key").option("--title <title>", "Page title (requires --space)").option("--space <key>", "Space key (requires --title)").option("--page-version <number>", "Version number for historical content", parseInt).option("--expand <expand>", 'Expand options (default: "body.storage,version,space")').addHelpText(
413
+ "after",
414
+ `
415
+ Examples:
416
+ confluencedc page get 12345
417
+ confluencedc page get 12345 --page-version 3
418
+ confluencedc page get --title "Meeting Notes" --space DEV
419
+ confluencedc page get 12345 --expand body.wiki,version`
420
+ ).action(
421
+ async (pageId, opts) => {
422
+ const client = getClient();
423
+ let resolvedPageId = pageId;
424
+ if (!resolvedPageId) {
425
+ if (!opts.title || !opts.space) {
426
+ process.stderr.write(`${JSON.stringify({ error: "Provide <pageId> or both --title and --space" })}
427
+ `);
428
+ process.exit(1);
429
+ }
430
+ const searchResult = await client.search.query({
431
+ cql: `title = "${opts.title}" AND space = "${opts.space}" AND type = page`,
432
+ limit: 1
433
+ });
434
+ const results = searchResult.results ?? [];
435
+ if (results.length === 0) {
436
+ process.stderr.write(
437
+ `${JSON.stringify({ error: `No page found with title "${opts.title}" in space "${opts.space}"` })}
438
+ `
439
+ );
440
+ process.exit(1);
441
+ }
442
+ const content = results[0].content ?? results[0];
443
+ const id = content.id;
444
+ if (!id) {
445
+ process.stderr.write(
446
+ `${JSON.stringify({
447
+ error: `Page found but missing ID: title="${opts.title}" in space "${opts.space}"`
448
+ })}
449
+ `
450
+ );
451
+ process.exit(1);
452
+ }
453
+ resolvedPageId = String(id);
454
+ }
455
+ const params = {
456
+ pageId: resolvedPageId,
457
+ expand: opts.expand
458
+ };
459
+ if (opts.pageVersion !== void 0) {
460
+ params.version = opts.pageVersion;
461
+ params.status = "historical";
462
+ }
463
+ const result = await client.pages.get(params);
464
+ output(result);
465
+ }
466
+ );
467
+ }
468
+
469
+ // src/commands/page/history.ts
470
+ function history(parent) {
471
+ parent.command("history <pageId>").description("Get version history of a page").option("--start <number>", "Starting index", parseInt).option("--limit <number>", "Maximum results", parseInt).addHelpText("after", "\nExamples:\n confluencedc page history 12345\n confluencedc page history 12345 --limit 5").action(async (pageId, opts) => {
472
+ const client = getClient();
473
+ const result = await client.pages.getHistory({ pageId, start: opts.start, limit: opts.limit });
474
+ output(result);
475
+ });
476
+ }
477
+
478
+ // src/commands/page/update.ts
479
+ function update2(parent) {
480
+ parent.command("update <pageId>").description("Update a page. Version is auto-fetched if not provided.").requiredOption("--title <title>", "Page title").requiredOption("--body <content>", "Page content (XHTML storage format by default)").option("--version <number>", "Current version number (auto-fetched if omitted)", parseInt).option("--minor-edit", "Mark as minor edit (default: false)").option("--message <text>", "Version comment").option("--format <format>", "Content format: storage (default) or wiki", "storage").addHelpText(
481
+ "after",
482
+ `
483
+ Examples:
484
+ confluencedc page update 12345 --title "Updated Title" --body "<p>New content</p>"
485
+ confluencedc page update 12345 --title "Title" --body "<p>Fix typo</p>" --minor-edit --message "Fixed typo"
486
+ confluencedc page update 12345 --title "Title" --body "<p>Content</p>" --version 5`
487
+ ).action(
488
+ async (pageId, opts) => {
489
+ const client = getClient();
490
+ let version = opts.version;
491
+ if (version === void 0) {
492
+ const current = await client.pages.get({ pageId, expand: "version" });
493
+ version = current.version?.number ?? 1;
494
+ }
495
+ const result = await client.pages.update({
496
+ pageId,
497
+ title: opts.title,
498
+ body: opts.body,
499
+ version,
500
+ minorEdit: opts.minorEdit,
501
+ versionMessage: opts.message,
502
+ contentFormat: opts.format
503
+ });
504
+ output(result);
505
+ }
506
+ );
507
+ }
508
+
509
+ // src/commands/page/index.ts
510
+ function registerPageCommands(program2) {
511
+ const page = program2.command("page").description("Page operations").addHelpText(
512
+ "after",
513
+ `
514
+ Examples:
515
+ $ confluencedc page get 12345
516
+ $ confluencedc page get --title "Meeting Notes" --space DEV
517
+ $ confluencedc page children 12345
518
+ $ confluencedc page create --space DEV --title "New Page" --body "<p>Content</p>"
519
+ $ confluencedc page update 12345 --title "Title" --body "<p>Updated</p>"
520
+ $ confluencedc page delete 12345
521
+ `
522
+ );
523
+ get(page);
524
+ children(page);
525
+ history(page);
526
+ create(page);
527
+ update2(page);
528
+ deletePage(page);
529
+ }
530
+
531
+ // src/commands/search/search.ts
532
+ function search(parent) {
533
+ parent.command("search <cql>").description("Search Confluence using CQL (Confluence Query Language)").option("--start <number>", "Starting index for pagination (default: 0)", parseInt).option("--limit <number>", "Maximum results per page (default: 25)", parseInt).option("--expand <expand>", 'Expand options (e.g., "body.wiki,version")').addHelpText(
534
+ "after",
535
+ `
536
+ Examples:
537
+ confluencedc search "type = page AND space = DEV"
538
+ confluencedc search "title ~ \\"meeting notes\\"" --limit 10
539
+ confluencedc search "label = important AND lastModified > now('-7d')" --expand body.wiki`
540
+ ).action(async (cql, opts) => {
541
+ const client = getClient();
542
+ const result = await client.search.query({ cql, start: opts.start, limit: opts.limit, expand: opts.expand });
543
+ output(result);
544
+ });
545
+ }
546
+
547
+ // src/commands/search/index.ts
548
+ function registerSearchCommands(program2) {
549
+ search(program2);
550
+ }
551
+
552
+ // src/index.ts
553
+ var DIM = "\x1B[2m";
554
+ var RESET = "\x1B[0m";
555
+ var program = new Command();
556
+ program.name("confluencedc").description("Confluence Data Center CLI").version("1.0.0").configureHelp({
557
+ styleTitle: (str) => styleText("bold", str),
558
+ styleUsage: (str) => styleText("dim", str),
559
+ styleCommandDescription: (str) => styleText("dim", str),
560
+ styleOptionDescription: (str) => styleText("dim", str),
561
+ styleSubcommandDescription: (str) => styleText("dim", str)
562
+ }).addHelpText("beforeAll", `
563
+ ${styleText("bold", "confluencedc")} ${DIM}\u2014 Confluence Data Center CLI${RESET}
564
+ `).addHelpText(
565
+ "after",
566
+ `
567
+ ${styleText("bold", "Environment:")}
27
568
  CONFLUENCE_URL Confluence Server base URL ${DIM}(e.g., https://confluence.example.com)${RESET}
28
569
  CONFLUENCE_TOKEN Personal Access Token ${DIM}(generate in Confluence > Profile > Personal Access Tokens)${RESET}
29
570
 
30
- ${styleText('bold', 'Examples:')}
571
+ ${styleText("bold", "Examples:")}
31
572
  ${DIM}$${RESET} confluencedc search "type = page AND space = DEV" --limit 10
32
573
  ${DIM}$${RESET} confluencedc page get 12345
33
574
  ${DIM}$${RESET} confluencedc page get --title "Meeting Notes" --space DEV
@@ -36,16 +577,15 @@ ${styleText('bold', 'Examples:')}
36
577
  ${DIM}$${RESET} confluencedc comment add 12345 --body "Looks good"
37
578
  ${DIM}$${RESET} confluencedc label add 12345 --label important
38
579
  ${DIM}$${RESET} confluencedc attachment list 12345
39
- `);
580
+ `
581
+ );
40
582
  registerSearchCommands(program);
41
583
  registerPageCommands(program);
42
584
  registerCommentCommands(program);
43
585
  registerLabelCommands(program);
44
586
  registerAttachmentCommands(program);
45
587
  try {
46
- await program.parseAsync();
47
- }
48
- catch (err) {
49
- handleError(err);
588
+ await program.parseAsync();
589
+ } catch (err) {
590
+ handleError(err);
50
591
  }
51
- //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "confluencedc-cli",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "publish": true,
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -18,17 +18,18 @@
18
18
  "@types/node": "24.10.4",
19
19
  "dotenv": "17.2.3",
20
20
  "eslint": "^9.39.0",
21
+ "tsup": "^8.5.1",
21
22
  "tsx": "^4.19.2",
22
23
  "typescript": "^5.7.2",
23
24
  "vitest": "^4.0.16",
24
- "config-eslint": "0.0.0",
25
- "config-typescript": "0.0.0"
25
+ "config-typescript": "0.0.0",
26
+ "config-eslint": "0.0.0"
26
27
  },
27
28
  "engines": {
28
29
  "node": ">=22.0.0"
29
30
  },
30
31
  "scripts": {
31
- "build": "tsc",
32
+ "build": "tsup",
32
33
  "dev": "tsx src/index.ts",
33
34
  "lint": "eslint src",
34
35
  "lint:fix": "eslint src --fix",
@@ -1,3 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function deleteAttachment(parent: Command): void;
3
- //# sourceMappingURL=delete.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/commands/attachment/delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAUtD"}
@@ -1,14 +0,0 @@
1
- import { getClient } from '../../utils/client.js';
2
- import { output } from '../../utils/output.js';
3
- export function deleteAttachment(parent) {
4
- parent
5
- .command('delete <attachmentId>')
6
- .description('Delete an attachment')
7
- .addHelpText('after', '\nExamples:\n confluencedc attachment delete att12345')
8
- .action(async (attachmentId) => {
9
- const client = getClient();
10
- await client.attachments.delete({ attachmentId });
11
- output({ deleted: true, attachmentId });
12
- });
13
- }
14
- //# sourceMappingURL=delete.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"delete.js","sourceRoot":"","sources":["../../../src/commands/attachment/delete.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE/C,MAAM,UAAU,gBAAgB,CAAC,MAAe;IAC9C,MAAM;SACH,OAAO,CAAC,uBAAuB,CAAC;SAChC,WAAW,CAAC,sBAAsB,CAAC;SACnC,WAAW,CAAC,OAAO,EAAE,wDAAwD,CAAC;SAC9E,MAAM,CAAC,KAAK,EAAE,YAAoB,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC;QAClD,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACP,CAAC"}