@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.
- package/AGENTS.md +34 -0
- package/CHANGELOG.md +26 -0
- package/README.md +147 -0
- package/assets/readme/confluence-cli-hero.png +0 -0
- package/assets/readme/confluence-cli-hero.svg +7 -0
- package/assets/readme/prompts/01-cover-confluence-cli.md +61 -0
- package/dist/api/endpoints.d.ts +404 -0
- package/dist/api/endpoints.js +85 -0
- package/dist/api/index.d.ts +148 -0
- package/dist/api/index.js +143 -0
- package/dist/bin/confluence-reader.d.ts +2 -0
- package/dist/bin/confluence-reader.js +8 -0
- package/dist/bin/confluence-writer.d.ts +2 -0
- package/dist/bin/confluence-writer.js +8 -0
- package/dist/bin/confluence.d.ts +2 -0
- package/dist/bin/confluence.js +11 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +154 -0
- package/dist/core/api-provider.d.ts +3 -0
- package/dist/core/api-provider.js +13 -0
- package/dist/core/changelog.d.ts +7 -0
- package/dist/core/changelog.js +42 -0
- package/dist/core/cli-output.d.ts +16 -0
- package/dist/core/cli-output.js +318 -0
- package/dist/core/cli-registry.d.ts +20 -0
- package/dist/core/cli-registry.js +148 -0
- package/dist/core/command-groups.generated.d.ts +2 -0
- package/dist/core/command-groups.generated.js +88 -0
- package/dist/core/config.d.ts +5 -0
- package/dist/core/config.js +108 -0
- package/dist/core/http-error.d.ts +2 -0
- package/dist/core/http-error.js +4 -0
- package/dist/core/http.d.ts +28 -0
- package/dist/core/http.js +124 -0
- package/dist/core/inline-comment.d.ts +23 -0
- package/dist/core/inline-comment.js +27 -0
- package/dist/core/list-result.d.ts +14 -0
- package/dist/core/list-result.js +81 -0
- package/dist/core/manifest.d.ts +11 -0
- package/dist/core/manifest.js +42 -0
- package/dist/core/pagination.d.ts +26 -0
- package/dist/core/pagination.js +45 -0
- package/dist/core/roles.d.ts +4 -0
- package/dist/core/roles.js +12 -0
- package/dist/core/tool-registry.d.ts +9 -0
- package/dist/core/tool-registry.js +60 -0
- package/dist/core/validation.d.ts +2 -0
- package/dist/core/validation.js +10 -0
- package/dist/core/value.d.ts +2 -0
- package/dist/core/value.js +19 -0
- package/dist/core/write-guard.d.ts +25 -0
- package/dist/core/write-guard.js +49 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/install.d.ts +3 -0
- package/dist/install.js +407 -0
- package/dist/manifest.json +122 -0
- package/dist/tools/attachments.d.ts +2 -0
- package/dist/tools/attachments.js +46 -0
- package/dist/tools/content.d.ts +2 -0
- package/dist/tools/content.js +45 -0
- package/dist/tools/convert.d.ts +2 -0
- package/dist/tools/convert.js +63 -0
- package/dist/tools/init.d.ts +2 -0
- package/dist/tools/init.js +24 -0
- package/dist/tools/install.d.ts +2 -0
- package/dist/tools/install.js +52 -0
- package/dist/tools/labels.d.ts +2 -0
- package/dist/tools/labels.js +22 -0
- package/dist/tools/metadata.d.ts +2 -0
- package/dist/tools/metadata.js +26 -0
- package/dist/tools/rest.d.ts +2 -0
- package/dist/tools/rest.js +52 -0
- package/dist/tools/spaces.d.ts +2 -0
- package/dist/tools/spaces.js +18 -0
- package/dist/tools/transfer.d.ts +2 -0
- package/dist/tools/transfer.js +407 -0
- package/dist/types/common.d.ts +17 -0
- package/dist/types/common.js +2 -0
- package/dist/update-probe.d.ts +2 -0
- package/dist/update-probe.js +142 -0
- package/dist/utils/mark-metadata.d.ts +9 -0
- package/dist/utils/mark-metadata.js +16 -0
- package/dist/utils/markdown.d.ts +9 -0
- package/dist/utils/markdown.js +220 -0
- package/dist/utils/result.d.ts +3 -0
- package/dist/utils/result.js +7 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +2 -0
- package/docs/confluence-7.13.7-api.md +183 -0
- package/docs/index.html +608 -0
- package/docs/release.md +41 -0
- package/package.json +63 -0
- package/skills/confluence-cli/SKILL.md +63 -0
- package/skills/confluence-cli/reference/cli.md +36 -0
- package/skills/confluence-cli/reference/commands.md +41 -0
- package/skills/confluence-cli/reference/content.md +23 -0
- package/skills/confluence-cli/reference/overview.md +23 -0
- package/skills/confluence-cli/reference/rest.md +19 -0
- 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,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,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,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,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,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
|