@cloudglab/confluence-cli 0.0.1

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 (100) hide show
  1. package/AGENTS.md +34 -0
  2. package/CHANGELOG.md +26 -0
  3. package/README.md +147 -0
  4. package/assets/readme/confluence-cli-hero.png +0 -0
  5. package/assets/readme/confluence-cli-hero.svg +7 -0
  6. package/assets/readme/prompts/01-cover-confluence-cli.md +61 -0
  7. package/dist/api/endpoints.d.ts +404 -0
  8. package/dist/api/endpoints.js +85 -0
  9. package/dist/api/index.d.ts +148 -0
  10. package/dist/api/index.js +143 -0
  11. package/dist/bin/confluence-reader.d.ts +2 -0
  12. package/dist/bin/confluence-reader.js +8 -0
  13. package/dist/bin/confluence-writer.d.ts +2 -0
  14. package/dist/bin/confluence-writer.js +8 -0
  15. package/dist/bin/confluence.d.ts +2 -0
  16. package/dist/bin/confluence.js +11 -0
  17. package/dist/cli.d.ts +1 -0
  18. package/dist/cli.js +154 -0
  19. package/dist/core/api-provider.d.ts +3 -0
  20. package/dist/core/api-provider.js +13 -0
  21. package/dist/core/changelog.d.ts +7 -0
  22. package/dist/core/changelog.js +42 -0
  23. package/dist/core/cli-output.d.ts +16 -0
  24. package/dist/core/cli-output.js +318 -0
  25. package/dist/core/cli-registry.d.ts +20 -0
  26. package/dist/core/cli-registry.js +148 -0
  27. package/dist/core/command-groups.generated.d.ts +2 -0
  28. package/dist/core/command-groups.generated.js +88 -0
  29. package/dist/core/config.d.ts +5 -0
  30. package/dist/core/config.js +108 -0
  31. package/dist/core/http-error.d.ts +2 -0
  32. package/dist/core/http-error.js +4 -0
  33. package/dist/core/http.d.ts +28 -0
  34. package/dist/core/http.js +124 -0
  35. package/dist/core/inline-comment.d.ts +23 -0
  36. package/dist/core/inline-comment.js +27 -0
  37. package/dist/core/list-result.d.ts +14 -0
  38. package/dist/core/list-result.js +81 -0
  39. package/dist/core/manifest.d.ts +11 -0
  40. package/dist/core/manifest.js +42 -0
  41. package/dist/core/pagination.d.ts +26 -0
  42. package/dist/core/pagination.js +45 -0
  43. package/dist/core/roles.d.ts +4 -0
  44. package/dist/core/roles.js +12 -0
  45. package/dist/core/tool-registry.d.ts +9 -0
  46. package/dist/core/tool-registry.js +60 -0
  47. package/dist/core/validation.d.ts +2 -0
  48. package/dist/core/validation.js +10 -0
  49. package/dist/core/value.d.ts +2 -0
  50. package/dist/core/value.js +19 -0
  51. package/dist/core/write-guard.d.ts +25 -0
  52. package/dist/core/write-guard.js +49 -0
  53. package/dist/index.d.ts +3 -0
  54. package/dist/index.js +3 -0
  55. package/dist/install.d.ts +3 -0
  56. package/dist/install.js +407 -0
  57. package/dist/manifest.json +122 -0
  58. package/dist/tools/attachments.d.ts +2 -0
  59. package/dist/tools/attachments.js +46 -0
  60. package/dist/tools/content.d.ts +2 -0
  61. package/dist/tools/content.js +45 -0
  62. package/dist/tools/convert.d.ts +2 -0
  63. package/dist/tools/convert.js +63 -0
  64. package/dist/tools/init.d.ts +2 -0
  65. package/dist/tools/init.js +24 -0
  66. package/dist/tools/install.d.ts +2 -0
  67. package/dist/tools/install.js +52 -0
  68. package/dist/tools/labels.d.ts +2 -0
  69. package/dist/tools/labels.js +22 -0
  70. package/dist/tools/metadata.d.ts +2 -0
  71. package/dist/tools/metadata.js +26 -0
  72. package/dist/tools/rest.d.ts +2 -0
  73. package/dist/tools/rest.js +52 -0
  74. package/dist/tools/spaces.d.ts +2 -0
  75. package/dist/tools/spaces.js +18 -0
  76. package/dist/tools/transfer.d.ts +2 -0
  77. package/dist/tools/transfer.js +407 -0
  78. package/dist/types/common.d.ts +17 -0
  79. package/dist/types/common.js +2 -0
  80. package/dist/update-probe.d.ts +2 -0
  81. package/dist/update-probe.js +142 -0
  82. package/dist/utils/mark-metadata.d.ts +9 -0
  83. package/dist/utils/mark-metadata.js +16 -0
  84. package/dist/utils/markdown.d.ts +9 -0
  85. package/dist/utils/markdown.js +220 -0
  86. package/dist/utils/result.d.ts +3 -0
  87. package/dist/utils/result.js +7 -0
  88. package/dist/version.d.ts +1 -0
  89. package/dist/version.js +2 -0
  90. package/docs/confluence-7.13.7-api.md +183 -0
  91. package/docs/index.html +608 -0
  92. package/docs/release.md +41 -0
  93. package/package.json +63 -0
  94. package/skills/confluence-cli/SKILL.md +63 -0
  95. package/skills/confluence-cli/reference/cli.md +36 -0
  96. package/skills/confluence-cli/reference/commands.md +41 -0
  97. package/skills/confluence-cli/reference/content.md +23 -0
  98. package/skills/confluence-cli/reference/overview.md +23 -0
  99. package/skills/confluence-cli/reference/rest.md +19 -0
  100. package/skills/confluence-cli/reference/transfer.md +27 -0
@@ -0,0 +1,85 @@
1
+ export const CONFLUENCE_7_13_7_ENDPOINTS = [
2
+ { method: "GET", path: "/accessmode", group: "accessmode", write: false },
3
+ { method: "GET", path: "/audit", group: "audit", write: false },
4
+ { method: "POST", path: "/audit", group: "audit", write: true },
5
+ { method: "GET", path: "/audit/export", group: "audit", write: false },
6
+ { method: "GET", path: "/audit/retention", group: "audit", write: false },
7
+ { method: "PUT", path: "/audit/retention", group: "audit", write: true },
8
+ { method: "GET", path: "/audit/since", group: "audit", write: false },
9
+ { method: "GET", path: "/content", group: "content", write: false },
10
+ { method: "POST", path: "/content", group: "content", write: true },
11
+ { method: "GET", path: "/content/search", group: "content", write: false },
12
+ { method: "POST", path: "/content/blueprint/instance/{draftId}", group: "content", write: true },
13
+ { method: "PUT", path: "/content/blueprint/instance/{draftId}", group: "content", write: true },
14
+ { method: "PUT", path: "/content/{contentId}", group: "content", write: true },
15
+ { method: "GET", path: "/content/{id}", group: "content", write: false },
16
+ { method: "DELETE", path: "/content/{id}", group: "content", write: true },
17
+ { method: "GET", path: "/content/{id}/child", group: "content-child", write: false },
18
+ { method: "GET", path: "/content/{id}/child/attachment", group: "attachment", write: false },
19
+ { method: "POST", path: "/content/{id}/child/attachment", group: "attachment", write: true },
20
+ { method: "PUT", path: "/content/{id}/child/attachment/{attachmentId}", group: "attachment", write: true },
21
+ { method: "POST", path: "/content/{id}/child/attachment/{attachmentId}/data", group: "attachment", write: true },
22
+ { method: "GET", path: "/content/{id}/child/comment", group: "content-child", write: false },
23
+ { method: "GET", path: "/content/{id}/child/{type}", group: "content-child", write: false },
24
+ { method: "GET", path: "/content/{id}/descendant", group: "content-descendant", write: false },
25
+ { method: "GET", path: "/content/{id}/descendant/{type}", group: "content-descendant", write: false },
26
+ { method: "GET", path: "/content/{id}/history", group: "content-history", write: false },
27
+ { method: "GET", path: "/content/{id}/history/{version}/macro/hash/{hash}", group: "content-macro", write: false },
28
+ { method: "GET", path: "/content/{id}/history/{version}/macro/id/{macroId}", group: "content-macro", write: false },
29
+ { method: "GET", path: "/content/{id}/label", group: "label", write: false },
30
+ { method: "POST", path: "/content/{id}/label", group: "label", write: true },
31
+ { method: "DELETE", path: "/content/{id}/label", group: "label", write: true },
32
+ { method: "DELETE", path: "/content/{id}/label/{label}", group: "label", write: true },
33
+ { method: "GET", path: "/content/{id}/property", group: "content-property", write: false },
34
+ { method: "POST", path: "/content/{id}/property", group: "content-property", write: true },
35
+ { method: "POST", path: "/content/{id}/property/{key}", group: "content-property", write: true },
36
+ { method: "GET", path: "/content/{id}/property/{key}", group: "content-property", write: false },
37
+ { method: "PUT", path: "/content/{id}/property/{key}", group: "content-property", write: true },
38
+ { method: "DELETE", path: "/content/{id}/property/{key}", group: "content-property", write: true },
39
+ { method: "GET", path: "/content/{id}/restriction/byOperation", group: "restriction", write: false },
40
+ { method: "GET", path: "/content/{id}/restriction/byOperation/{operationKey}", group: "restriction", write: false },
41
+ { method: "POST", path: "/contentbody/convert/{to}", group: "contentbody", write: true },
42
+ { method: "GET", path: "/group", group: "group", write: false },
43
+ { method: "GET", path: "/group/{groupName}", group: "group", write: false },
44
+ { method: "GET", path: "/group/{groupName}/member", group: "group", write: false },
45
+ { method: "GET", path: "/longtask", group: "longtask", write: false },
46
+ { method: "GET", path: "/longtask/{id}", group: "longtask", write: false },
47
+ { method: "GET", path: "/search", group: "search", write: false },
48
+ { method: "GET", path: "/space", group: "space", write: false },
49
+ { method: "POST", path: "/space", group: "space", write: true },
50
+ { method: "POST", path: "/space/_private", group: "space", write: true },
51
+ { method: "GET", path: "/space/{spaceKey}", group: "space", write: false },
52
+ { method: "PUT", path: "/space/{spaceKey}", group: "space", write: true },
53
+ { method: "DELETE", path: "/space/{spaceKey}", group: "space", write: true },
54
+ { method: "GET", path: "/space/{spaceKey}/content", group: "space-content", write: false },
55
+ { method: "GET", path: "/space/{spaceKey}/content/{type}", group: "space-content", write: false },
56
+ { method: "GET", path: "/space/{spaceKey}/property", group: "space-property", write: false },
57
+ { method: "POST", path: "/space/{spaceKey}/property", group: "space-property", write: true },
58
+ { method: "POST", path: "/space/{spaceKey}/property/{key}", group: "space-property", write: true },
59
+ { method: "GET", path: "/space/{spaceKey}/property/{key}", group: "space-property", write: false },
60
+ { method: "PUT", path: "/space/{spaceKey}/property/{key}", group: "space-property", write: true },
61
+ { method: "DELETE", path: "/space/{spaceKey}/property/{key}", group: "space-property", write: true },
62
+ { method: "GET", path: "/user", group: "user", write: false },
63
+ { method: "GET", path: "/user/anonymous", group: "user", write: false },
64
+ { method: "GET", path: "/user/current", group: "user", write: false },
65
+ { method: "GET", path: "/user/memberof", group: "user", write: false },
66
+ { method: "POST", path: "/user/watch/content/{contentId}", group: "watch", write: true },
67
+ { method: "GET", path: "/user/watch/content/{contentId}", group: "watch", write: false },
68
+ { method: "DELETE", path: "/user/watch/content/{contentId}", group: "watch", write: true },
69
+ { method: "POST", path: "/user/watch/space/{spaceKey}", group: "watch", write: true },
70
+ { method: "GET", path: "/user/watch/space/{spaceKey}", group: "watch", write: false },
71
+ { method: "DELETE", path: "/user/watch/space/{spaceKey}", group: "watch", write: true },
72
+ { method: "GET", path: "/webhooks", group: "webhooks", write: false },
73
+ { method: "POST", path: "/webhooks", group: "webhooks", write: true },
74
+ { method: "GET", path: "/webhooks/{webhookId}", group: "webhooks", write: false },
75
+ { method: "PUT", path: "/webhooks/{webhookId}", group: "webhooks", write: true },
76
+ { method: "DELETE", path: "/webhooks/{webhookId}", group: "webhooks", write: true },
77
+ { method: "GET", path: "/webhooks/{webhookId}/latest", group: "webhooks", write: false },
78
+ { method: "GET", path: "/webhooks/{webhookId}/statistics", group: "webhooks", write: false },
79
+ { method: "GET", path: "/webhooks/{webhookId}/statistics/summary", group: "webhooks", write: false },
80
+ { method: "POST", path: "/webhooks/test", group: "webhooks", write: true },
81
+ ];
82
+ export function findEndpoint(method, path) {
83
+ return CONFLUENCE_7_13_7_ENDPOINTS.find((endpoint) => endpoint.method === method && endpoint.path === path);
84
+ }
85
+ //# sourceMappingURL=endpoints.js.map
@@ -0,0 +1,148 @@
1
+ import type { ConfluenceConfig } from "../types/common.js";
2
+ import type { RestMethod } from "./endpoints.js";
3
+ export interface ContentBody {
4
+ storage?: {
5
+ value: string;
6
+ representation: "storage";
7
+ };
8
+ view?: {
9
+ value: string;
10
+ representation: "view";
11
+ };
12
+ wiki?: {
13
+ value: string;
14
+ representation: "wiki";
15
+ };
16
+ }
17
+ export interface ConfluenceContent {
18
+ id: string;
19
+ type: string;
20
+ title: string;
21
+ space?: {
22
+ key: string;
23
+ name?: string;
24
+ };
25
+ version?: {
26
+ number: number;
27
+ by?: {
28
+ displayName?: string;
29
+ };
30
+ when?: string;
31
+ };
32
+ ancestors?: Array<{
33
+ id: string;
34
+ title: string;
35
+ }>;
36
+ body?: ContentBody;
37
+ _links?: Record<string, string>;
38
+ }
39
+ export interface ConfluencePage<T> {
40
+ results: T[];
41
+ size: number;
42
+ limit?: number;
43
+ start?: number;
44
+ _links?: Record<string, string>;
45
+ }
46
+ export interface ConfluenceLabel {
47
+ prefix?: string;
48
+ name: string;
49
+ }
50
+ export interface ConfluenceAttachment {
51
+ id: string;
52
+ type: "attachment";
53
+ title: string;
54
+ version?: {
55
+ number: number;
56
+ };
57
+ metadata?: {
58
+ mediaType?: string;
59
+ };
60
+ _links?: Record<string, string>;
61
+ }
62
+ export declare class ConfluenceApi {
63
+ private readonly http;
64
+ constructor(config: ConfluenceConfig);
65
+ search(cql: string, limit?: number): Promise<{
66
+ results: ConfluenceContent[];
67
+ size: number;
68
+ }>;
69
+ getContent(id: string, expand?: string): Promise<ConfluenceContent>;
70
+ findContent(input: {
71
+ space?: string;
72
+ title?: string;
73
+ type?: string;
74
+ limit?: number;
75
+ expand?: string;
76
+ }): Promise<ConfluencePage<ConfluenceContent>>;
77
+ createContent(input: {
78
+ space: string;
79
+ title: string;
80
+ body: string;
81
+ representation: "wiki" | "storage";
82
+ parentId?: string;
83
+ }): Promise<ConfluenceContent>;
84
+ updateContent(input: {
85
+ id: string;
86
+ title: string;
87
+ body: string;
88
+ representation: "wiki" | "storage";
89
+ version: number;
90
+ parentId?: string;
91
+ }): Promise<ConfluenceContent>;
92
+ deleteContent(id: string): Promise<unknown>;
93
+ getChildren(id: string, type?: string, expand?: string, limit?: number): Promise<ConfluencePage<ConfluenceContent>>;
94
+ getComments(id: string, expand?: string, limit?: number): Promise<ConfluencePage<ConfluenceContent>>;
95
+ addComment(input: {
96
+ pageId: string;
97
+ body: string;
98
+ representation: "wiki" | "storage";
99
+ location?: "inline" | "footer";
100
+ }): Promise<ConfluenceContent>;
101
+ addInlineComment(input: {
102
+ pageId: string;
103
+ selection: string;
104
+ body: string;
105
+ representation: "wiki" | "storage";
106
+ }): Promise<ConfluenceContent>;
107
+ setContentProperty(contentId: string, key: string, value: unknown): Promise<unknown>;
108
+ setContentProperties(contentId: string, properties: Array<{
109
+ key: string;
110
+ value: unknown;
111
+ }>): Promise<void>;
112
+ getLabels(id: string, limit?: number): Promise<ConfluencePage<ConfluenceLabel>>;
113
+ addLabels(id: string, labels: string[]): Promise<ConfluencePage<ConfluenceLabel>>;
114
+ deleteLabel(id: string, label: string): Promise<unknown>;
115
+ listAttachments(id: string, limit?: number): Promise<ConfluencePage<ConfluenceAttachment>>;
116
+ uploadAttachment(input: {
117
+ pageId: string;
118
+ filename: string;
119
+ data: Buffer;
120
+ comment?: string;
121
+ minorEdit?: boolean;
122
+ contentType?: string;
123
+ }): Promise<ConfluencePage<ConfluenceAttachment>>;
124
+ updateAttachmentData(input: {
125
+ pageId: string;
126
+ attachmentId: string;
127
+ filename: string;
128
+ data: Buffer;
129
+ comment?: string;
130
+ minorEdit?: boolean;
131
+ contentType?: string;
132
+ }): Promise<ConfluencePage<ConfluenceAttachment>>;
133
+ downloadAttachment(downloadPath: string): Promise<{
134
+ data: Buffer;
135
+ headers: Record<string, unknown>;
136
+ }>;
137
+ listSpaces(limit?: number): Promise<ConfluencePage<{
138
+ key: string;
139
+ name: string;
140
+ }>>;
141
+ getSpace(spaceKey: string): Promise<{
142
+ key: string;
143
+ name: string;
144
+ }>;
145
+ getCurrentUser(): Promise<unknown>;
146
+ convertBody(to: "storage" | "view" | "export_view" | "styled_view", body: unknown): Promise<unknown>;
147
+ request<T>(method: RestMethod, path: string, query?: Record<string, unknown>, body?: unknown): Promise<T>;
148
+ }
@@ -0,0 +1,143 @@
1
+ import { ConfluenceHttpClient } from "../core/http.js";
2
+ import { findAndAnnotateSelection } from "../core/inline-comment.js";
3
+ export class ConfluenceApi {
4
+ http;
5
+ constructor(config) {
6
+ this.http = new ConfluenceHttpClient(config);
7
+ }
8
+ search(cql, limit = 25) {
9
+ return this.http.get("/content/search", { cql, limit, expand: "space,version" });
10
+ }
11
+ getContent(id, expand = "body.storage,version,space,ancestors,metadata.labels") {
12
+ return this.http.get(`/content/${encodeURIComponent(id)}`, { expand });
13
+ }
14
+ findContent(input) {
15
+ return this.http.get("/content", {
16
+ spaceKey: input.space,
17
+ title: input.title,
18
+ type: input.type ?? "page",
19
+ limit: input.limit ?? 25,
20
+ expand: input.expand ?? "space,version",
21
+ });
22
+ }
23
+ async createContent(input) {
24
+ return this.http.post("/content", {
25
+ type: "page",
26
+ title: input.title,
27
+ space: { key: input.space },
28
+ ancestors: input.parentId ? [{ id: input.parentId }] : undefined,
29
+ body: { [input.representation]: { value: input.body, representation: input.representation } },
30
+ });
31
+ }
32
+ async updateContent(input) {
33
+ return this.http.put(`/content/${encodeURIComponent(input.id)}`, {
34
+ id: input.id,
35
+ type: "page",
36
+ title: input.title,
37
+ ancestors: input.parentId ? [{ id: input.parentId }] : undefined,
38
+ version: { number: input.version },
39
+ body: { [input.representation]: { value: input.body, representation: input.representation } },
40
+ });
41
+ }
42
+ deleteContent(id) {
43
+ return this.http.delete(`/content/${encodeURIComponent(id)}`);
44
+ }
45
+ getChildren(id, type, expand = "space,version", limit = 25) {
46
+ const suffix = type ? `/${encodeURIComponent(type)}` : "";
47
+ return this.http.get(`/content/${encodeURIComponent(id)}/child${suffix}`, { expand, limit });
48
+ }
49
+ getComments(id, expand = "body.storage,version", limit = 25) {
50
+ return this.http.get(`/content/${encodeURIComponent(id)}/child/comment`, { expand, limit });
51
+ }
52
+ addComment(input) {
53
+ return this.http.post("/content", {
54
+ type: "comment",
55
+ container: { id: input.pageId, type: "page" },
56
+ body: { [input.representation]: { value: input.body, representation: input.representation } },
57
+ extensions: input.location ? { location: input.location } : undefined,
58
+ });
59
+ }
60
+ async addInlineComment(input) {
61
+ // 1. 先创建评论,拿到 comment ID(Server 版 marker.ac:ref = commentId)
62
+ const comment = await this.addComment({
63
+ pageId: input.pageId,
64
+ body: input.body,
65
+ representation: input.representation,
66
+ location: "inline",
67
+ });
68
+ // 2. 拉取当前页面 storage body + version
69
+ const page = await this.getContent(input.pageId, "body.storage,version,title");
70
+ const storageBody = page.body?.storage?.value;
71
+ if (!storageBody)
72
+ throw new Error("Page has no storage-format body.");
73
+ // 3. 在 storage 中定位选中文字并注入 marker(ref = commentId)
74
+ const result = findAndAnnotateSelection(storageBody, input.selection, comment.id);
75
+ if (!result) {
76
+ // 回滚:删除刚创建的无关联评论
77
+ await this.deleteContent(comment.id).catch(() => { });
78
+ const preview = input.selection.slice(0, 80);
79
+ throw new Error(`Selected text "${preview}${input.selection.length > 80 ? "..." : ""}" not found in page body. The text may span XML boundary, exist inside a macro, or differ from the storage format.`);
80
+ }
81
+ // 4. 更新页面 body(版本 +1),注入 marker
82
+ const version = (page.version?.number ?? 0) + 1;
83
+ await this.updateContent({
84
+ id: input.pageId,
85
+ title: page.title,
86
+ body: result.annotatedBody,
87
+ representation: "storage",
88
+ version,
89
+ });
90
+ // 5. 设置评论的 Content Properties(标记为 inline comment)
91
+ await this.setContentProperties(comment.id, [
92
+ { key: "inline-comment", value: "true" },
93
+ { key: "inline-marker-ref", value: comment.id },
94
+ { key: "inline-original-selection", value: input.selection },
95
+ ]);
96
+ return comment;
97
+ }
98
+ async setContentProperty(contentId, key, value) {
99
+ return this.http.post(`/content/${encodeURIComponent(contentId)}/property`, { key, value });
100
+ }
101
+ async setContentProperties(contentId, properties) {
102
+ for (const prop of properties) {
103
+ await this.setContentProperty(contentId, prop.key, prop.value);
104
+ }
105
+ }
106
+ getLabels(id, limit = 100) {
107
+ return this.http.get(`/content/${encodeURIComponent(id)}/label`, { limit });
108
+ }
109
+ addLabels(id, labels) {
110
+ return this.http.post(`/content/${encodeURIComponent(id)}/label`, labels.map((name) => ({ prefix: "global", name })));
111
+ }
112
+ deleteLabel(id, label) {
113
+ return this.http.delete(`/content/${encodeURIComponent(id)}/label/${encodeURIComponent(label)}`);
114
+ }
115
+ listAttachments(id, limit = 100) {
116
+ return this.http.get(`/content/${encodeURIComponent(id)}/child/attachment`, { limit, expand: "version,metadata" });
117
+ }
118
+ uploadAttachment(input) {
119
+ return this.http.postMultipart(`/content/${encodeURIComponent(input.pageId)}/child/attachment`, { comment: input.comment, minorEdit: input.minorEdit }, [{ fieldName: "file", filename: input.filename, data: input.data, contentType: input.contentType }]);
120
+ }
121
+ updateAttachmentData(input) {
122
+ return this.http.postMultipart(`/content/${encodeURIComponent(input.pageId)}/child/attachment/${encodeURIComponent(input.attachmentId)}/data`, { comment: input.comment, minorEdit: input.minorEdit }, [{ fieldName: "file", filename: input.filename, data: input.data, contentType: input.contentType }]);
123
+ }
124
+ downloadAttachment(downloadPath) {
125
+ return this.http.getBuffer(downloadPath);
126
+ }
127
+ listSpaces(limit = 25) {
128
+ return this.http.get("/space", { limit });
129
+ }
130
+ getSpace(spaceKey) {
131
+ return this.http.get(`/space/${encodeURIComponent(spaceKey)}`);
132
+ }
133
+ getCurrentUser() {
134
+ return this.http.get("/user/current");
135
+ }
136
+ convertBody(to, body) {
137
+ return this.http.post(`/contentbody/convert/${encodeURIComponent(to)}`, body);
138
+ }
139
+ request(method, path, query, body) {
140
+ return this.http.request(method, path, query, body);
141
+ }
142
+ }
143
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from "../cli.js";
3
+ await runCli(["--role", "reader", ...process.argv.slice(2)]).catch((error) => {
4
+ const message = error instanceof Error ? error.message : String(error);
5
+ process.stderr.write(`${message}\n`);
6
+ process.exit(1);
7
+ });
8
+ //# sourceMappingURL=confluence-reader.js.map
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from "../cli.js";
3
+ await runCli(["--role", "writer", ...process.argv.slice(2)]).catch((error) => {
4
+ const message = error instanceof Error ? error.message : String(error);
5
+ process.stderr.write(`${message}\n`);
6
+ process.exit(1);
7
+ });
8
+ //# sourceMappingURL=confluence-writer.js.map
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from "../cli.js";
3
+ try {
4
+ await runCli(process.argv.slice(2));
5
+ }
6
+ catch (error) {
7
+ const message = error instanceof Error ? error.message : String(error);
8
+ console.error(message);
9
+ process.exit(1);
10
+ }
11
+ //# sourceMappingURL=confluence.js.map
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function runCli(argv: string[]): Promise<void>;
package/dist/cli.js ADDED
@@ -0,0 +1,154 @@
1
+ import { parseCommandInput } from "./core/cli-registry.js";
2
+ import { BUILTIN_COMMAND_NAMES, getBuiltinCommandHelp, printCommandHelp, printCommandList, printHelp, renderChangelog, } from "./core/cli-output.js";
3
+ import { buildRegistryForCommand, getAvailableCommandNames } from "./core/manifest.js";
4
+ import { runInstallCommand, runUninstallCommand, runUpdateCommand } from "./install.js";
5
+ import { runDailyUpdateProbe } from "./update-probe.js";
6
+ import { VERSION } from "./version.js";
7
+ export async function runCli(argv) {
8
+ const { command, commandArgs, role } = parseCli(argv);
9
+ const registeredCommandNames = await getAvailableCommandNames(role);
10
+ const commandNames = [...new Set([...BUILTIN_COMMAND_NAMES, ...registeredCommandNames])]
11
+ .sort((left, right) => left.localeCompare(right));
12
+ await runDailyUpdateProbe(command);
13
+ if (command === "help" && commandArgs[0] && !commandArgs[0].startsWith("--")) {
14
+ const builtinHelp = getBuiltinCommandHelp(commandArgs[0]);
15
+ if (builtinHelp) {
16
+ process.stdout.write(`${builtinHelp}\n`);
17
+ return;
18
+ }
19
+ const targetRegistry = await buildRegistryForCommand(role, commandArgs[0]);
20
+ printCommandHelp(targetRegistry, commandArgs[0]);
21
+ return;
22
+ }
23
+ const helpIndex = commandArgs.indexOf("--help");
24
+ if (command && helpIndex >= 0) {
25
+ const builtinHelp = getBuiltinCommandHelp(command);
26
+ if (builtinHelp) {
27
+ process.stdout.write(`${builtinHelp}\n`);
28
+ return;
29
+ }
30
+ const targetRegistry = await buildRegistryForCommand(role, command);
31
+ printCommandHelp(targetRegistry, command);
32
+ return;
33
+ }
34
+ if (!command || command === "help") {
35
+ printHelp(role, registeredCommandNames);
36
+ return;
37
+ }
38
+ if (command === "list") {
39
+ const listOptions = parseListOptions(commandArgs);
40
+ if (listOptions.raw) {
41
+ process.stdout.write(`${commandNames.join("\n")}\n`);
42
+ return;
43
+ }
44
+ printCommandList(role, registeredCommandNames);
45
+ return;
46
+ }
47
+ if (command === "version") {
48
+ ensureNoUnexpectedArgs("version", commandArgs);
49
+ process.stdout.write(`${VERSION}\n`);
50
+ return;
51
+ }
52
+ if (command === "changelog") {
53
+ const options = parseChangelogOptions(commandArgs);
54
+ process.stdout.write(`${await renderChangelog(options)}\n`);
55
+ return;
56
+ }
57
+ if (command === "install") {
58
+ await runInstallCommand(commandArgs);
59
+ return;
60
+ }
61
+ if (command === "update" || command === "upgrade") {
62
+ await runUpdateCommand(commandArgs);
63
+ return;
64
+ }
65
+ if (command === "uninstall" || command === "remove") {
66
+ await runUninstallCommand(commandArgs);
67
+ return;
68
+ }
69
+ const commandRegistry = await buildRegistryForCommand(role, command);
70
+ const selected = commandRegistry.get(command);
71
+ if (!selected) {
72
+ throw new Error(`Unknown command: ${command}`);
73
+ }
74
+ const input = parseCommandInput(selected.schema, commandArgs);
75
+ const result = await selected.handler(input);
76
+ process.stdout.write(`${result.content[0]?.text ?? ""}\n`);
77
+ }
78
+ function parseListOptions(args) {
79
+ const unexpectedArgs = args.filter((arg) => arg !== "--raw");
80
+ if (unexpectedArgs.length > 0) {
81
+ throw new Error(`list 不支持额外参数: ${unexpectedArgs.join(" ")}`);
82
+ }
83
+ return { raw: args.includes("--raw") };
84
+ }
85
+ function ensureNoUnexpectedArgs(commandName, args) {
86
+ if (args.length > 0) {
87
+ throw new Error(`${commandName} 不支持额外参数: ${args.join(" ")}`);
88
+ }
89
+ }
90
+ function parseChangelogOptions(args) {
91
+ const options = { limit: 5, raw: false };
92
+ for (let index = 0; index < args.length; index += 1) {
93
+ const arg = args[index];
94
+ if (arg === "--raw") {
95
+ options.raw = true;
96
+ continue;
97
+ }
98
+ if (arg === "--limit" || arg.startsWith("--limit=")) {
99
+ const value = arg.startsWith("--limit=") ? arg.slice("--limit=".length) : args[++index];
100
+ if (value === undefined)
101
+ throw new Error("changelog --limit 需要一个值");
102
+ if (value === "all") {
103
+ options.limit = "all";
104
+ continue;
105
+ }
106
+ const parsed = Number(value);
107
+ if (!Number.isInteger(parsed) || parsed <= 0) {
108
+ throw new Error(`changelog --limit 必须是正整数或 all,收到: ${value}`);
109
+ }
110
+ options.limit = parsed;
111
+ continue;
112
+ }
113
+ if (arg === "--version" || arg.startsWith("--version=")) {
114
+ const value = arg.startsWith("--version=") ? arg.slice("--version=".length) : args[++index];
115
+ if (!value)
116
+ throw new Error("changelog --version 需要一个值");
117
+ options.version = value;
118
+ continue;
119
+ }
120
+ if (arg === "--since" || arg.startsWith("--since=")) {
121
+ const value = arg.startsWith("--since=") ? arg.slice("--since=".length) : args[++index];
122
+ if (!value)
123
+ throw new Error("changelog --since 需要一个值");
124
+ options.since = value;
125
+ continue;
126
+ }
127
+ throw new Error(`changelog 不支持参数: ${arg}`);
128
+ }
129
+ return options;
130
+ }
131
+ function parseCli(argv) {
132
+ const args = [...argv];
133
+ let role = "full";
134
+ const inlineRoleIndex = args.findIndex((arg) => arg.startsWith("--role=") || arg.startsWith("-r="));
135
+ if (inlineRoleIndex >= 0) {
136
+ const value = args[inlineRoleIndex].split("=", 2)[1];
137
+ if (value !== "full" && value !== "reader" && value !== "writer") {
138
+ throw new Error(`无效 role: ${value}`);
139
+ }
140
+ role = value;
141
+ args.splice(inlineRoleIndex, 1);
142
+ }
143
+ const roleIndex = args.indexOf("--role");
144
+ if (roleIndex >= 0) {
145
+ const value = args[roleIndex + 1];
146
+ if (value !== "full" && value !== "reader" && value !== "writer") {
147
+ throw new Error("--role must be full, reader, or writer");
148
+ }
149
+ role = value;
150
+ args.splice(roleIndex, 2);
151
+ }
152
+ return { command: args[0], commandArgs: args.slice(1), role };
153
+ }
154
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1,3 @@
1
+ import { ConfluenceApi } from "../api/index.js";
2
+ export declare function setApi(nextApi: ConfluenceApi): void;
3
+ export declare function getApi(): ConfluenceApi;
@@ -0,0 +1,13 @@
1
+ import { ConfluenceApi } from "../api/index.js";
2
+ import { loadConfluenceConfig } from "./config.js";
3
+ let api = null;
4
+ export function setApi(nextApi) {
5
+ api = nextApi;
6
+ }
7
+ export function getApi() {
8
+ if (api)
9
+ return api;
10
+ api = new ConfluenceApi(loadConfluenceConfig());
11
+ return api;
12
+ }
13
+ //# sourceMappingURL=api-provider.js.map
@@ -0,0 +1,7 @@
1
+ export interface ChangelogSection {
2
+ version: string;
3
+ date: string;
4
+ content: string;
5
+ }
6
+ export declare function loadChangelogRaw(): Promise<string>;
7
+ export declare function loadChangelogSections(): Promise<ChangelogSection[]>;
@@ -0,0 +1,42 @@
1
+ import { readFile } from "node:fs/promises";
2
+ const VERSION_HEADING_REGEX = /^##\s+(\d+\.\d+\.\d+)\s*-\s*(\d{4}-\d{2}-\d{2})\s*$/;
3
+ export async function loadChangelogRaw() {
4
+ const changelogUrl = new URL("../../CHANGELOG.md", import.meta.url);
5
+ try {
6
+ return await readFile(changelogUrl, "utf8");
7
+ }
8
+ catch {
9
+ throw new Error("未找到 CHANGELOG.md");
10
+ }
11
+ }
12
+ export async function loadChangelogSections() {
13
+ const text = await loadChangelogRaw();
14
+ const sections = [];
15
+ const lines = text.split("\n");
16
+ let current = null;
17
+ for (const line of lines) {
18
+ const match = line.match(VERSION_HEADING_REGEX);
19
+ if (match) {
20
+ if (current) {
21
+ sections.push({
22
+ version: current.version,
23
+ date: current.date,
24
+ content: current.lines.join("\n").trimEnd(),
25
+ });
26
+ }
27
+ current = { version: match[1], date: match[2], lines: [line] };
28
+ }
29
+ else if (current) {
30
+ current.lines.push(line);
31
+ }
32
+ }
33
+ if (current) {
34
+ sections.push({
35
+ version: current.version,
36
+ date: current.date,
37
+ content: current.lines.join("\n").trimEnd(),
38
+ });
39
+ }
40
+ return sections;
41
+ }
42
+ //# sourceMappingURL=changelog.js.map