runspec-confluence 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,52 @@
1
+ """runspec-confluence — a Confluence runnable (pages, search, comments, labels,
2
+ restrictions) over ``atlassian-python-api``, on Cloud and Server/Data Center.
3
+
4
+ The logic lives in the dependency-light ``runspec-confluence-core``; this package
5
+ is the thin ``confluence`` runnable wrapper. The core helpers are re-exported here
6
+ for back-compat so ``from runspec_confluence import get_page`` works without naming
7
+ the core package.
8
+ """
9
+
10
+ from runspec_confluence_core import (
11
+ ConfluenceConfig,
12
+ ConfluenceError,
13
+ add_comment,
14
+ add_label,
15
+ config_from_env,
16
+ create_page,
17
+ delete_page,
18
+ get_comments,
19
+ get_labels,
20
+ get_page,
21
+ get_page_ancestors,
22
+ get_page_children,
23
+ get_page_history,
24
+ get_page_restrictions,
25
+ get_space,
26
+ get_space_pages,
27
+ move_page,
28
+ search,
29
+ update_page,
30
+ )
31
+
32
+ __all__ = [
33
+ "ConfluenceConfig",
34
+ "ConfluenceError",
35
+ "config_from_env",
36
+ "get_page",
37
+ "get_page_children",
38
+ "get_page_ancestors",
39
+ "create_page",
40
+ "update_page",
41
+ "delete_page",
42
+ "move_page",
43
+ "get_space_pages",
44
+ "get_space",
45
+ "search",
46
+ "get_page_history",
47
+ "get_labels",
48
+ "add_label",
49
+ "get_comments",
50
+ "add_comment",
51
+ "get_page_restrictions",
52
+ ]
@@ -0,0 +1,125 @@
1
+ """cli.py — the ``confluence`` entry point.
2
+
3
+ One binary, a verb per Confluence tool. ``rs.parse("confluence")`` resolves the
4
+ subcommand and its args; we build a :class:`ConfluenceConfig` from the environment
5
+ (scoped by the global ``--env``), unpack each ``.value`` at the boundary, and call
6
+ the matching :mod:`runspec_confluence_core` helper.
7
+
8
+ Failures (the core :class:`ConfluenceError` and arg-shape ``ValueError``s) become a
9
+ JSON ``{"error": ...}`` payload + a non-zero exit.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ import sys
16
+ from typing import Any
17
+
18
+ import runspec as rs
19
+ from runspec_confluence_core import (
20
+ ConfluenceConfig,
21
+ ConfluenceError,
22
+ add_comment,
23
+ add_label,
24
+ config_from_env,
25
+ create_page,
26
+ delete_page,
27
+ get_comments,
28
+ get_labels,
29
+ get_page,
30
+ get_page_ancestors,
31
+ get_page_children,
32
+ get_page_history,
33
+ get_page_restrictions,
34
+ get_space,
35
+ get_space_pages,
36
+ move_page,
37
+ search,
38
+ update_page,
39
+ )
40
+
41
+
42
+ def _emit(payload: Any) -> None:
43
+ print(json.dumps(payload, default=str))
44
+
45
+
46
+ def _dispatch(spec: rs.RunSpec, config: ConfluenceConfig) -> Any:
47
+ """Run the active verb against ``config``, unpacking only its declared args."""
48
+ command = spec.runspec_command
49
+
50
+ # ── reads ──
51
+ if command == "search":
52
+ return search(config, spec.cql.value, start=spec.start.value, limit=spec.limit.value, expand=spec.expand.value)
53
+ if command == "get-page":
54
+ return get_page(config, page_id=spec.page_id.value, space=spec.space.value, title=spec.title.value, expand=spec.expand.value)
55
+ if command == "get-page-children":
56
+ return get_page_children(config, spec.page_id.value)
57
+ if command == "get-page-ancestors":
58
+ return get_page_ancestors(config, spec.page_id.value)
59
+ if command == "get-page-history":
60
+ return get_page_history(config, spec.page_id.value)
61
+ if command == "get-labels":
62
+ return get_labels(config, spec.page_id.value)
63
+ if command == "get-comments":
64
+ return get_comments(config, spec.page_id.value, limit=spec.limit.value)
65
+ if command == "get-page-restrictions":
66
+ return get_page_restrictions(config, spec.page_id.value)
67
+ if command == "get-space":
68
+ return get_space(config, spec.space_key.value)
69
+ if command == "get-space-pages":
70
+ return get_space_pages(config, spec.space.value, start=spec.start.value, limit=spec.limit.value)
71
+
72
+ # ── writes ──
73
+ if command == "create-page":
74
+ return create_page(
75
+ config,
76
+ spec.space.value,
77
+ spec.title.value,
78
+ spec.content.value,
79
+ parent_id=spec.parent_id.value,
80
+ representation=spec.content_format.value,
81
+ )
82
+ if command == "update-page":
83
+ return update_page(
84
+ config,
85
+ spec.page_id.value,
86
+ spec.title.value,
87
+ body=spec.content.value,
88
+ parent_id=spec.parent_id.value,
89
+ representation=spec.content_format.value,
90
+ version_comment=spec.version_comment.value,
91
+ minor_edit=spec.minor_edit.value,
92
+ )
93
+ if command == "delete-page":
94
+ return delete_page(config, spec.page_id.value, recursive=spec.recursive.value)
95
+ if command == "move-page":
96
+ return move_page(
97
+ config,
98
+ spec.space.value,
99
+ spec.page_id.value,
100
+ target_id=spec.target_id.value,
101
+ target_title=spec.target_title.value,
102
+ position=spec.position.value,
103
+ )
104
+ if command == "add-label":
105
+ return add_label(config, spec.page_id.value, spec.label.value)
106
+ if command == "add-comment":
107
+ return add_comment(config, spec.page_id.value, spec.text.value)
108
+
109
+ # require-command = true makes a bare invocation unreachable.
110
+ raise SystemExit("no command — see `confluence --help`") # pragma: no cover
111
+
112
+
113
+ def main() -> None:
114
+ spec = rs.parse("confluence")
115
+ try:
116
+ config = config_from_env(env=spec.env.value)
117
+ result = _dispatch(spec, config)
118
+ except (ConfluenceError, ValueError) as exc:
119
+ _emit({"error": str(exc), "kind": type(exc).__name__})
120
+ sys.exit(1)
121
+ _emit(result)
122
+
123
+
124
+ if __name__ == "__main__":
125
+ main()
@@ -0,0 +1,166 @@
1
+ #:schema https://runspec.app/runspec.schema.json
2
+
3
+ # runspec-confluence — a Confluence runnable ported from the Atlassian MCP
4
+ # server (sooperset/mcp-atlassian), onto atlassian-python-api.
5
+ #
6
+ # One runnable, `confluence`, with a verb per tool. `runspec serve` re-expands
7
+ # each verb into its own leaf MCP tool (confluence_get-page, confluence_create-page,
8
+ # …), so a runspec-console agent calls them individually — over SSH on any fleet
9
+ # host, with no second server process.
10
+ #
11
+ # Cloud and Server/Data Center. Auth is env-var driven:
12
+ # CONFLUENCE_URL + (CONFLUENCE_PERSONAL_TOKEN | CONFLUENCE_USERNAME + CONFLUENCE_API_TOKEN)
13
+ # Per-environment overrides use a CONFLUENCE_<ENV>_ prefix, selected with the
14
+ # global --env (dev/uat/prod) arg. Cloud vs Server/DC is set by CONFLUENCE_CLOUD,
15
+ # else inferred from a .atlassian.net host.
16
+ #
17
+ # Reads are autonomous; writes require confirmation. Page bodies are passed in a
18
+ # Confluence representation via --content-format (storage XHTML by default, or
19
+ # wiki/editor). Markdown conversion is not done here.
20
+
21
+ [config]
22
+ autonomy-default = "confirm"
23
+
24
+ [config.logging]
25
+ store = "per-run"
26
+
27
+ [confluence]
28
+ description = "Read and manage Confluence pages, spaces, comments, and labels (Cloud or Server/Data Center)"
29
+ output = "json"
30
+ require-command = true
31
+ examples = [
32
+ {cmd = "confluence get-page --page-id 12345 --expand body.storage", description = "Fetch a page with its storage body"},
33
+ {cmd = "confluence search --cql 'space = DOCS AND text ~ onboarding'", description = "Search content with CQL"},
34
+ {cmd = "confluence create-page --space DOCS --title 'Runbook' --content '<p>Steps</p>'", description = "Create a page"},
35
+ {cmd = "confluence add-comment --page-id 12345 --text 'Reviewed'", description = "Comment on a page"},
36
+ ]
37
+
38
+ # ── global args (inherited by every verb) ──────────────────────────────────
39
+ [confluence.args]
40
+ env = {type = "choice", options = ["dev", "uat", "prod"], short = "-e", required = false, description = "Target environment (selects CONFLUENCE_<ENV>_* vars, falling back to CONFLUENCE_*)"}
41
+
42
+ # ── reads → autonomous ─────────────────────────────────────────────────────
43
+
44
+ [confluence.commands.search]
45
+ description = "Search content with CQL (Confluence Query Language); returns one page of results"
46
+ autonomy = "autonomous"
47
+ [confluence.commands.search.args]
48
+ cql = {type = "str", required = true, description = "CQL query, e.g. 'type = page AND space = DOCS AND text ~ \"onboarding\"'"}
49
+ limit = {type = "int", default = 25, description = "Max results"}
50
+ start = {type = "int", default = 0, description = "Pagination offset"}
51
+ expand = {type = "str", required = false, description = "Comma-separated expand sections"}
52
+
53
+ [confluence.commands.get-page]
54
+ description = "Fetch a page by id, or by space + title"
55
+ autonomy = "autonomous"
56
+ [confluence.commands.get-page.args]
57
+ page-id = {type = "str", required = false, description = "Page id"}
58
+ space = {type = "str", required = false, description = "Space key (with --title; falls back to CONFLUENCE_SPACE)"}
59
+ title = {type = "str", required = false, description = "Page title (with --space)"}
60
+ expand = {type = "str", required = false, description = "Comma-separated expand sections, e.g. body.storage,version"}
61
+
62
+ [confluence.commands.get-page-children]
63
+ description = "List a page's immediate child pages"
64
+ autonomy = "autonomous"
65
+ [confluence.commands.get-page-children.args]
66
+ page-id = {type = "str", required = true, description = "Parent page id"}
67
+
68
+ [confluence.commands.get-page-ancestors]
69
+ description = "List a page's ancestor pages (breadcrumb, root first)"
70
+ autonomy = "autonomous"
71
+ [confluence.commands.get-page-ancestors.args]
72
+ page-id = {type = "str", required = true, description = "Page id"}
73
+
74
+ [confluence.commands.get-page-history]
75
+ description = "Return a page's content history (created/last-updated, versions)"
76
+ autonomy = "autonomous"
77
+ [confluence.commands.get-page-history.args]
78
+ page-id = {type = "str", required = true, description = "Page id"}
79
+
80
+ [confluence.commands.get-labels]
81
+ description = "List a page's labels"
82
+ autonomy = "autonomous"
83
+ [confluence.commands.get-labels.args]
84
+ page-id = {type = "str", required = true, description = "Page id"}
85
+
86
+ [confluence.commands.get-comments]
87
+ description = "List a page's comments (rendered bodies)"
88
+ autonomy = "autonomous"
89
+ [confluence.commands.get-comments.args]
90
+ page-id = {type = "str", required = true, description = "Page id"}
91
+ limit = {type = "int", default = 25, description = "Max comments"}
92
+
93
+ [confluence.commands.get-page-restrictions]
94
+ description = "Return the view/edit restrictions on a page"
95
+ autonomy = "autonomous"
96
+ [confluence.commands.get-page-restrictions.args]
97
+ page-id = {type = "str", required = true, description = "Page id"}
98
+
99
+ [confluence.commands.get-space]
100
+ description = "Fetch a space's metadata"
101
+ autonomy = "autonomous"
102
+ [confluence.commands.get-space.args]
103
+ space-key = {type = "str", required = true, description = "Space key"}
104
+
105
+ [confluence.commands.get-space-pages]
106
+ description = "List pages in a space (one page of results)"
107
+ autonomy = "autonomous"
108
+ [confluence.commands.get-space-pages.args]
109
+ space = {type = "str", required = true, description = "Space key"}
110
+ limit = {type = "int", default = 50, description = "Max results"}
111
+ start = {type = "int", default = 0, description = "Pagination offset"}
112
+
113
+ # ── writes → confirm ───────────────────────────────────────────────────────
114
+
115
+ [confluence.commands.create-page]
116
+ description = "Create a page. --content is in the --content-format representation."
117
+ autonomy = "confirm"
118
+ [confluence.commands.create-page.args]
119
+ space = {type = "str", required = true, description = "Space key"}
120
+ title = {type = "str", required = true, description = "Page title"}
121
+ content = {type = "str", required = true, description = "Page body in the --content-format representation"}
122
+ parent-id = {type = "str", required = false, description = "Parent page id"}
123
+ content-format = {type = "choice", options = ["storage", "wiki", "editor"], default = "storage", description = "Body representation (storage = Confluence XHTML)"}
124
+
125
+ [confluence.commands.update-page]
126
+ description = "Update a page. --title is required by the API even when only the body changes."
127
+ autonomy = "confirm"
128
+ [confluence.commands.update-page.args]
129
+ page-id = {type = "str", required = true, description = "Page id"}
130
+ title = {type = "str", required = true, description = "Page title (pass the current title if unchanged)"}
131
+ content = {type = "str", required = false, description = "New body in the --content-format representation"}
132
+ parent-id = {type = "str", required = false, description = "New parent page id"}
133
+ content-format = {type = "choice", options = ["storage", "wiki", "editor"], default = "storage", description = "Body representation"}
134
+ version-comment = {type = "str", required = false, description = "Version comment for the edit"}
135
+ minor-edit = {type = "flag", default = false, description = "Mark as a minor edit (no notifications)"}
136
+
137
+ [confluence.commands.delete-page]
138
+ description = "Delete a page (and, with --recursive, its descendants)"
139
+ autonomy = "confirm"
140
+ [confluence.commands.delete-page.args]
141
+ page-id = {type = "str", required = true, description = "Page id"}
142
+ recursive = {type = "flag", default = false, description = "Also delete descendant pages"}
143
+
144
+ [confluence.commands.move-page]
145
+ description = "Move a page relative to a target (append under it, or above/below)"
146
+ autonomy = "confirm"
147
+ [confluence.commands.move-page.args]
148
+ space = {type = "str", required = true, description = "Space key of the page"}
149
+ page-id = {type = "str", required = true, description = "Page id to move"}
150
+ target-id = {type = "str", required = false, description = "Target page id"}
151
+ target-title = {type = "str", required = false, description = "Target page title (alternative to --target-id)"}
152
+ position = {type = "choice", options = ["append", "above", "below"], default = "append", description = "Placement relative to the target"}
153
+
154
+ [confluence.commands.add-label]
155
+ description = "Add a label to a page"
156
+ autonomy = "confirm"
157
+ [confluence.commands.add-label.args]
158
+ page-id = {type = "str", required = true, description = "Page id"}
159
+ label = {type = "str", required = true, description = "Label to add"}
160
+
161
+ [confluence.commands.add-comment]
162
+ description = "Add a comment to a page"
163
+ autonomy = "confirm"
164
+ [confluence.commands.add-comment.args]
165
+ page-id = {type = "str", required = true, description = "Page id"}
166
+ text = {type = "str", required = true, description = "Comment text"}
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: runspec-confluence
3
+ Version: 0.1.0
4
+ Summary: Confluence runnable for runspec — pages, search, comments, labels, and restrictions (Cloud + Server/Data Center)
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: runspec-confluence-core>=0.1.0
7
+ Requires-Dist: runspec>=0.44.0
8
+ Provides-Extra: dev
9
+ Requires-Dist: mypy; extra == 'dev'
10
+ Requires-Dist: pytest>=8.0; extra == 'dev'
11
+ Requires-Dist: ruff==0.15.20; extra == 'dev'
@@ -0,0 +1,7 @@
1
+ runspec_confluence/__init__.py,sha256=3ojWN41pnkHUURxWlGHDeP6J_mVPXUQZ1NfqpgwcthI,1234
2
+ runspec_confluence/cli.py,sha256=UmdQibFSVv2wX-BHTKWS8tvYUcNmNVy58Iab57shcKM,4235
3
+ runspec_confluence/runspec.toml,sha256=EArKbgtvENH2l-OhQJtum7P3oBnkOmhmunU14PeNdQo,8707
4
+ runspec_confluence-0.1.0.dist-info/METADATA,sha256=ddwZA_JBrmwUSIKYeAt626YsZ0-Fb_h6ruAkloQI4YQ,431
5
+ runspec_confluence-0.1.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
6
+ runspec_confluence-0.1.0.dist-info/entry_points.txt,sha256=ilX_-nfCAEqUtobdmyrHn7FD81yvrRkT1dr7NqviM_8,59
7
+ runspec_confluence-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.31.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ confluence = runspec_confluence.cli:main