gitd-client 0.14.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.
gitd/contract.py ADDED
@@ -0,0 +1,50 @@
1
+ """gitd's contract, read at run time from contract.json, which `make generate` builds from
2
+ proto/gitd/v1/gitd.proto. Only protobuf's public API is used: no code is generated."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from importlib import resources
7
+
8
+ from google.protobuf import descriptor_pb2, descriptor_pool, json_format, message_factory
9
+ from google.protobuf.descriptor import EnumDescriptor
10
+ from google.protobuf.message import Message
11
+
12
+ PACKAGE = "gitd.v1"
13
+ CONTRACT_FILE = "contract.json"
14
+
15
+
16
+ class Enumeration:
17
+ """An enumeration of the contract, by the names and numbers of its values."""
18
+
19
+ def __init__(self, descriptor: EnumDescriptor) -> None:
20
+ self.descriptor = descriptor
21
+
22
+ def number(self, name: str) -> int:
23
+ return self.descriptor.values_by_name[name].number
24
+
25
+ def name(self, number: int) -> str:
26
+ """The value's name, or an empty one for a value newer than this client."""
27
+ value = self.descriptor.values_by_number.get(number)
28
+ return value.name if value else ""
29
+
30
+
31
+ class Contract:
32
+ """The messages and enumerations of gitd's API."""
33
+
34
+ def __init__(self, pool: descriptor_pool.DescriptorPool) -> None:
35
+ self.pool = pool
36
+
37
+ @classmethod
38
+ def load(cls) -> Contract:
39
+ text = resources.files(__package__).joinpath(CONTRACT_FILE).read_text(encoding="utf-8")
40
+ files = json_format.Parse(text, descriptor_pb2.FileDescriptorSet())
41
+ pool = descriptor_pool.DescriptorPool()
42
+ for file in files.file:
43
+ pool.Add(file)
44
+ return cls(pool)
45
+
46
+ def message(self, name: str) -> type[Message]:
47
+ return message_factory.GetMessageClass(self.pool.FindMessageTypeByName(f"{PACKAGE}.{name}"))
48
+
49
+ def enumeration(self, name: str) -> Enumeration:
50
+ return Enumeration(self.pool.FindEnumTypeByName(f"{PACKAGE}.{name}"))
@@ -0,0 +1,69 @@
1
+ """The calls that manage the credentials gitd keeps: for a platform's own pages, never for
2
+ an agent."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from . import messages, methods
7
+ from .connection import Connection
8
+ from .kept_credentials import AppToCreate, Connected, KeptCredential, SignIn
9
+
10
+
11
+ class Credentials:
12
+ """The credentials gitd keeps, as gitd.credentials.
13
+
14
+ app = gitd.credentials.begin_github_app("acme-agents", "https://platform.example.com/github/created",
15
+ organization="acme")
16
+ # the browser posts app.manifest to app.form_action; GitHub sends it back with a code
17
+ connected = gitd.credentials.complete_github_app(code, app.state)
18
+ """
19
+
20
+ def __init__(self, connection: Connection) -> None:
21
+ self.connection = connection
22
+
23
+ def list(self) -> list[KeptCredential]:
24
+ """Every credential gitd keeps, gitd.toml's included, never a secret."""
25
+ response = self.connection.call(methods.LIST_CONNECTIONS, messages.ListConnectionsRequest())
26
+ return [KeptCredential.from_message(connection) for connection in response.connections]
27
+
28
+ def remove(self, credential_id: str) -> None:
29
+ """Forgets a credential kept through the API."""
30
+ self.connection.call(methods.REMOVE_CONNECTION, messages.RemoveConnectionRequest(id=credential_id))
31
+
32
+ def add_token(self, url_prefix: str, token: str, *, user: str = "") -> KeptCredential:
33
+ """Keeps a token for the repositories under url_prefix, which names the host."""
34
+ request = messages.AddTokenRequest(url_prefix=url_prefix, token=token, user=user)
35
+ return KeptCredential.from_message(self.connection.call(methods.ADD_TOKEN, request).connection)
36
+
37
+ def add_github_app(
38
+ self, app_id: int, private_key: str, *, host: str = "", url_prefix: str = ""
39
+ ) -> KeptCredential:
40
+ """Keeps a GitHub App a platform already has, once GitHub takes the key as its own."""
41
+ request = messages.AddGitHubAppRequest(
42
+ host=host, app_id=app_id, private_key=private_key, url_prefix=url_prefix
43
+ )
44
+ return KeptCredential.from_message(self.connection.call(methods.ADD_GITHUB_APP, request).connection)
45
+
46
+ def begin_github_app(
47
+ self, name: str, redirect_url: str, *, organization: str = "", host: str = ""
48
+ ) -> AppToCreate:
49
+ """Starts creating the organisation's own GitHub App, named name; GitHub sends the
50
+ person back to redirect_url with a code and the state."""
51
+ request = messages.BeginGitHubAppRequest(
52
+ host=host, organization=organization, name=name, redirect_url=redirect_url
53
+ )
54
+ return AppToCreate.from_message(self.connection.call(methods.BEGIN_GITHUB_APP, request))
55
+
56
+ def complete_github_app(self, code: str, state: str) -> Connected:
57
+ """Finishes creating the App with the code and state GitHub sent back."""
58
+ request = messages.CompleteGitHubAppRequest(code=code, state=state)
59
+ return Connected.from_message(self.connection.call(methods.COMPLETE_GITHUB_APP, request))
60
+
61
+ def start_github_login(self, *, host: str = "") -> SignIn:
62
+ """Asks GitHub for the code a person enters to sign in through gitd's public App."""
63
+ request = messages.StartGitHubLoginRequest(host=host)
64
+ return SignIn.from_message(self.connection.call(methods.START_GITHUB_LOGIN, request))
65
+
66
+ def finish_github_login(self, login_id: str) -> Connected:
67
+ """Waits until the person has entered the code, and keeps the sign-in."""
68
+ request = messages.FinishGitHubLoginRequest(login_id=login_id)
69
+ return Connected.from_message(self.connection.call(methods.FINISH_GITHUB_LOGIN, request))
gitd/endpoint_calls.py ADDED
@@ -0,0 +1,52 @@
1
+ """The calls of gitd's endpoint: tokens for sandboxes elsewhere. What a sandbox pushes is
2
+ published with Gitd.publish and its sandbox."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from datetime import timedelta
7
+
8
+ from . import messages, methods
9
+ from .connection import Connection
10
+ from .endpoint_tokens import IssuedToken, TokenInfo
11
+
12
+
13
+ class Endpoint:
14
+ """gitd's endpoint, as gitd.endpoint.
15
+
16
+ issued = gitd.endpoint.issue("https://github.com/acme/web", lifetime=timedelta(hours=2))
17
+ # in the sandbox: git clone issued.authenticated_url() …; git push origin HEAD:agents/fix
18
+ gitd.publish("", "agents/fix", sandbox=issued.info.sandbox)
19
+ """
20
+
21
+ def __init__(self, connection: Connection) -> None:
22
+ self.connection = connection
23
+
24
+ def issue(
25
+ self,
26
+ url: str,
27
+ *,
28
+ branch: str = "",
29
+ lifetime: timedelta | None = None,
30
+ read_only: bool = False,
31
+ sandbox: str = "",
32
+ ) -> IssuedToken:
33
+ """A token for a sandbox: it reads url and pushes to its own sandbox repository (an
34
+ earlier one when sandbox names it), for lifetime (an hour by default, a day at most)."""
35
+ request = messages.IssueTokenRequest(
36
+ url=url,
37
+ branch=branch,
38
+ lifetime_seconds=int(lifetime.total_seconds()) if lifetime else 0,
39
+ read_only=read_only,
40
+ sandbox=sandbox,
41
+ )
42
+ return IssuedToken.from_message(self.connection.call(methods.ISSUE_TOKEN, request))
43
+
44
+ def revoke(self, token_id: str, *, remove_sandbox: bool = False) -> None:
45
+ """Ends a token now, and removes its sandbox repository when asked."""
46
+ request = messages.RevokeTokenRequest(id=token_id, remove_sandbox=remove_sandbox)
47
+ self.connection.call(methods.REVOKE_TOKEN, request)
48
+
49
+ def tokens(self) -> list[TokenInfo]:
50
+ """The tokens alive, never their secrets."""
51
+ response = self.connection.call(methods.LIST_TOKENS, messages.ListTokensRequest())
52
+ return [TokenInfo.from_message(info) for info in response.tokens]
@@ -0,0 +1,55 @@
1
+ """Tokens of gitd's endpoint, for sandboxes that clone over HTTP instead of mounting a
2
+ workspace: a microVM, a remote sandbox, a CI runner elsewhere."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from dataclasses import dataclass
7
+ from datetime import datetime
8
+ from typing import Any
9
+
10
+ from .timestamps import Timestamp
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class TokenInfo:
15
+ """A token as gitd shows it, never its secret: what it reads, the sandbox repository it
16
+ pushes to when writable, and until when."""
17
+
18
+ id: str
19
+ url: str
20
+ sandbox: str
21
+ writable: bool
22
+ issued_at: datetime
23
+ expires_at: datetime
24
+
25
+ @classmethod
26
+ def from_message(cls, info: Any) -> TokenInfo:
27
+ return cls(
28
+ id=info.id,
29
+ url=info.url,
30
+ sandbox=info.sandbox,
31
+ writable=info.writable,
32
+ issued_at=Timestamp.to_datetime(info.issued_at),
33
+ expires_at=Timestamp.to_datetime(info.expires_at),
34
+ )
35
+
36
+
37
+ @dataclass(frozen=True)
38
+ class IssuedToken:
39
+ """A token just issued: its secret, shown this once, and where the sandbox clones from —
40
+ git clone https://x:<secret>@… — never the git host's credential."""
41
+
42
+ secret: str
43
+ clone_url: str
44
+ info: TokenInfo
45
+
46
+ @classmethod
47
+ def from_message(cls, response: Any) -> IssuedToken:
48
+ return cls(
49
+ secret=response.token, clone_url=response.clone_url, info=TokenInfo.from_message(response.info)
50
+ )
51
+
52
+ def authenticated_url(self) -> str:
53
+ """The clone URL with the secret in it, for the sandbox's git."""
54
+ scheme, rest = self.clone_url.split("://", 1)
55
+ return f"{scheme}://x:{self.secret}@{rest}"
gitd/errors.py ADDED
@@ -0,0 +1,36 @@
1
+ """A refusal from gitd."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import grpc
8
+ from grpc_status import rpc_status
9
+
10
+ from . import messages
11
+ from .models import Refusal
12
+
13
+
14
+ class GitdError(Exception):
15
+ """A refusal from gitd: its reason (AUTH_REQUIRED, NOT_FOUND, PATH_REFUSED, BUSY, …) and
16
+ what git said. Tell users what happened from the reason, never by parsing the message."""
17
+
18
+ def __init__(self, reason: str, said: str, code: grpc.StatusCode | None = None) -> None:
19
+ super().__init__(f"{reason}: {said}" if reason else said)
20
+ self.reason = reason
21
+ self.said = said
22
+ self.code = code
23
+
24
+ @classmethod
25
+ def from_rpc_error(cls, error: grpc.RpcError) -> GitdError:
26
+ """Reads the GitFailure gitd attaches to a refused call, if there is one."""
27
+ if not isinstance(error, grpc.Call):
28
+ return cls("", str(error))
29
+ status = rpc_status.from_call(error)
30
+ details = list(status.details) if status else []
31
+ for detail in details:
32
+ if detail.Is(messages.GitFailure.DESCRIPTOR):
33
+ failure: Any = messages.GitFailure()
34
+ detail.Unpack(failure)
35
+ return cls(Refusal.reason_of(failure), failure.git_said, error.code())
36
+ return cls("", error.details() or "", error.code())
@@ -0,0 +1,107 @@
1
+ """The credentials gitd keeps, and the steps of connecting GitHub, as plain dataclasses
2
+ built from the messages gitd answers. None of them ever holds a secret."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from dataclasses import dataclass
7
+ from datetime import datetime
8
+ from enum import StrEnum
9
+ from typing import Any
10
+
11
+ from . import messages
12
+ from .timestamps import Timestamp
13
+
14
+ KIND_PREFIX = "KIND_"
15
+
16
+
17
+ class CredentialKind(StrEnum):
18
+ """What a credential is: a token, an SSH key, a GitHub App, or a person signed in."""
19
+
20
+ TOKEN = "token"
21
+ SSH_KEY = "ssh_key"
22
+ GITHUB_APP = "github_app"
23
+ GITHUB_LOGIN = "github_login"
24
+
25
+ @classmethod
26
+ def from_message(cls, number: int) -> CredentialKind:
27
+ name = messages.CONNECTION_KIND.name(number).removeprefix(KIND_PREFIX).lower()
28
+ return cls(name) if name in {member.value for member in cls} else cls.TOKEN
29
+
30
+
31
+ @dataclass(frozen=True)
32
+ class KeptCredential:
33
+ """One credential gitd keeps: its id to remove it by, what it is, the repositories it
34
+ opens, whom it acts as (an App's bot or a person), an App's id and where it is managed,
35
+ when a sign-in stops renewing, and whether gitd.toml declares it."""
36
+
37
+ id: str
38
+ kind: CredentialKind
39
+ url_prefix: str
40
+ acts_as: str
41
+ app_id: int
42
+ manage_url: str
43
+ expires: datetime | None
44
+ from_file: bool
45
+ created: datetime | None
46
+
47
+ @classmethod
48
+ def from_message(cls, connection: Any) -> KeptCredential:
49
+ return cls(
50
+ id=connection.id,
51
+ kind=CredentialKind.from_message(connection.kind),
52
+ url_prefix=connection.url_prefix,
53
+ acts_as=connection.acts_as,
54
+ app_id=connection.app_id,
55
+ manage_url=connection.manage_url,
56
+ expires=Timestamp.to_datetime(connection.expires) if connection.HasField("expires") else None,
57
+ from_file=connection.from_file,
58
+ created=Timestamp.to_datetime(connection.created) if connection.HasField("created") else None,
59
+ )
60
+
61
+
62
+ @dataclass(frozen=True)
63
+ class Connected:
64
+ """A GitHub App or sign-in gitd now keeps, and where it is installed on repositories."""
65
+
66
+ credential: KeptCredential
67
+ install_url: str
68
+
69
+ @classmethod
70
+ def from_message(cls, response: Any) -> Connected:
71
+ return cls(
72
+ credential=KeptCredential.from_message(response.connection), install_url=response.install_url
73
+ )
74
+
75
+
76
+ @dataclass(frozen=True)
77
+ class AppToCreate:
78
+ """What a browser posts to GitHub to create an App: where (form_action), the manifest,
79
+ as a form field named manifest, and the state GitHub sends back with the code."""
80
+
81
+ form_action: str
82
+ manifest: str
83
+ state: str
84
+
85
+ @classmethod
86
+ def from_message(cls, response: Any) -> AppToCreate:
87
+ return cls(form_action=response.form_action, manifest=response.manifest, state=response.state)
88
+
89
+
90
+ @dataclass(frozen=True)
91
+ class SignIn:
92
+ """A sign-in waiting for a person: the code they enter at verification_url, for how
93
+ long, and the id to finish it by."""
94
+
95
+ user_code: str
96
+ verification_url: str
97
+ login_id: str
98
+ expires_in_seconds: int
99
+
100
+ @classmethod
101
+ def from_message(cls, response: Any) -> SignIn:
102
+ return cls(
103
+ user_code=response.user_code,
104
+ verification_url=response.verification_url,
105
+ login_id=response.login_id,
106
+ expires_in_seconds=response.expires_in_seconds,
107
+ )
gitd/messages.py ADDED
@@ -0,0 +1,92 @@
1
+ """The messages and enumerations of gitd's contract that this client uses."""
2
+
3
+ from .contract import Contract
4
+
5
+ contract = Contract.load()
6
+
7
+ InfoRequest = contract.message("InfoRequest")
8
+ InfoResponse = contract.message("InfoResponse")
9
+ ProbeRequest = contract.message("ProbeRequest")
10
+ ProbeResponse = contract.message("ProbeResponse")
11
+ Revision = contract.message("Revision")
12
+ PrepareRequest = contract.message("PrepareRequest")
13
+ PrepareResponse = contract.message("PrepareResponse")
14
+ UpdateRequest = contract.message("UpdateRequest")
15
+ UpdateResponse = contract.message("UpdateResponse")
16
+ CheckoutRequest = contract.message("CheckoutRequest")
17
+ CheckoutResponse = contract.message("CheckoutResponse")
18
+ DescribeRequest = contract.message("DescribeRequest")
19
+ DescribeResponse = contract.message("DescribeResponse")
20
+ PublishRequest = contract.message("PublishRequest")
21
+ PublishResponse = contract.message("PublishResponse")
22
+ RemoveRequest = contract.message("RemoveRequest")
23
+ RemoveResponse = contract.message("RemoveResponse")
24
+ AuditRequest = contract.message("AuditRequest")
25
+ AuditResponse = contract.message("AuditResponse")
26
+ GitFailure = contract.message("GitFailure")
27
+ PullRequestOptions = contract.message("PullRequestOptions")
28
+ Provenance = contract.message("Provenance")
29
+ ChangesRequest = contract.message("ChangesRequest")
30
+ ChangesResponse = contract.message("ChangesResponse")
31
+ DiffRequest = contract.message("DiffRequest")
32
+ DiffResponse = contract.message("DiffResponse")
33
+ Side = contract.message("Side")
34
+ CompareRequest = contract.message("CompareRequest")
35
+ CompareResponse = contract.message("CompareResponse")
36
+ ConflictsRequest = contract.message("ConflictsRequest")
37
+ ConflictsResponse = contract.message("ConflictsResponse")
38
+ WatchRequest = contract.message("WatchRequest")
39
+ WatchResponse = contract.message("WatchResponse")
40
+ SealRequest = contract.message("SealRequest")
41
+ SealResponse = contract.message("SealResponse")
42
+ ListWarmRequest = contract.message("ListWarmRequest")
43
+ ListWarmResponse = contract.message("ListWarmResponse")
44
+ IssueTokenRequest = contract.message("IssueTokenRequest")
45
+ IssueTokenResponse = contract.message("IssueTokenResponse")
46
+ RevokeTokenRequest = contract.message("RevokeTokenRequest")
47
+ RevokeTokenResponse = contract.message("RevokeTokenResponse")
48
+ ListTokensRequest = contract.message("ListTokensRequest")
49
+ ListTokensResponse = contract.message("ListTokensResponse")
50
+
51
+ ListConnectionsRequest = contract.message("ListConnectionsRequest")
52
+ ListConnectionsResponse = contract.message("ListConnectionsResponse")
53
+ RemoveConnectionRequest = contract.message("RemoveConnectionRequest")
54
+ RemoveConnectionResponse = contract.message("RemoveConnectionResponse")
55
+ AddTokenRequest = contract.message("AddTokenRequest")
56
+ AddTokenResponse = contract.message("AddTokenResponse")
57
+ AddGitHubAppRequest = contract.message("AddGitHubAppRequest")
58
+ AddGitHubAppResponse = contract.message("AddGitHubAppResponse")
59
+ BeginGitHubAppRequest = contract.message("BeginGitHubAppRequest")
60
+ BeginGitHubAppResponse = contract.message("BeginGitHubAppResponse")
61
+ CompleteGitHubAppRequest = contract.message("CompleteGitHubAppRequest")
62
+ CompleteGitHubAppResponse = contract.message("CompleteGitHubAppResponse")
63
+ StartGitHubLoginRequest = contract.message("StartGitHubLoginRequest")
64
+ StartGitHubLoginResponse = contract.message("StartGitHubLoginResponse")
65
+ FinishGitHubLoginRequest = contract.message("FinishGitHubLoginRequest")
66
+ FinishGitHubLoginResponse = contract.message("FinishGitHubLoginResponse")
67
+
68
+ PublishSettings = contract.message("PublishSettings")
69
+ PullRequestSettings = contract.message("PullRequestSettings")
70
+ GetSettingsRequest = contract.message("GetSettingsRequest")
71
+ GetSettingsResponse = contract.message("GetSettingsResponse")
72
+ SetSettingsRequest = contract.message("SetSettingsRequest")
73
+ SetSettingsResponse = contract.message("SetSettingsResponse")
74
+ RemoveSettingsRequest = contract.message("RemoveSettingsRequest")
75
+ RemoveSettingsResponse = contract.message("RemoveSettingsResponse")
76
+ Preset = contract.message("Preset")
77
+ ListPresetsRequest = contract.message("ListPresetsRequest")
78
+ ListPresetsResponse = contract.message("ListPresetsResponse")
79
+ SetPresetRequest = contract.message("SetPresetRequest")
80
+ SetPresetResponse = contract.message("SetPresetResponse")
81
+ RemovePresetRequest = contract.message("RemovePresetRequest")
82
+ RemovePresetResponse = contract.message("RemovePresetResponse")
83
+
84
+ STRATEGY = contract.enumeration("Strategy")
85
+ ACCESS = contract.enumeration("ProbeResponse.Access")
86
+ REASON = contract.enumeration("GitFailure.Reason")
87
+ CONNECTION_KIND = contract.enumeration("Connection.Kind")
88
+ CHANGE_ACTION = contract.enumeration("ConfigurationChange.Action")
89
+ OPENING = contract.enumeration("PullRequestSettings.Open")
90
+ PRESET_ORIGIN = contract.enumeration("Preset.Origin")
91
+ CHANGE_KIND = contract.enumeration("ChangedFile.Kind")
92
+ EVENT_KIND = contract.enumeration("WatchResponse.Kind")
gitd/methods.py ADDED
@@ -0,0 +1,58 @@
1
+ """The gitd methods this client calls, by their gRPC path and the message each answers with."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from google.protobuf.message import Message
8
+
9
+ from . import messages
10
+ from .contract import PACKAGE
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class Method:
15
+ service: str
16
+ name: str
17
+ response: type[Message]
18
+
19
+ @property
20
+ def path(self) -> str:
21
+ return f"/{PACKAGE}.{self.service}/{self.name}"
22
+
23
+
24
+ INFO = Method("ServerService", "Info", messages.InfoResponse)
25
+ AUDIT = Method("ServerService", "Audit", messages.AuditResponse)
26
+ PROBE = Method("RepositoryService", "Probe", messages.ProbeResponse)
27
+ PREPARE = Method("WorkspaceService", "Prepare", messages.PrepareResponse)
28
+ UPDATE = Method("WorkspaceService", "Update", messages.UpdateResponse)
29
+ CHECKOUT = Method("WorkspaceService", "Checkout", messages.CheckoutResponse)
30
+ DESCRIBE = Method("WorkspaceService", "Describe", messages.DescribeResponse)
31
+ PUBLISH = Method("WorkspaceService", "Publish", messages.PublishResponse)
32
+ REMOVE = Method("WorkspaceService", "Remove", messages.RemoveResponse)
33
+ CHANGES = Method("WorkspaceService", "Changes", messages.ChangesResponse)
34
+ DIFF = Method("WorkspaceService", "Diff", messages.DiffResponse)
35
+ COMPARE = Method("WorkspaceService", "Compare", messages.CompareResponse)
36
+ CONFLICTS = Method("WorkspaceService", "Conflicts", messages.ConflictsResponse)
37
+ WATCH = Method("WorkspaceService", "Watch", messages.WatchResponse)
38
+ SEAL = Method("WorkspaceService", "Seal", messages.SealResponse)
39
+ LIST_WARM = Method("WorkspaceService", "ListWarm", messages.ListWarmResponse)
40
+ ISSUE_TOKEN = Method("EndpointService", "IssueToken", messages.IssueTokenResponse)
41
+ REVOKE_TOKEN = Method("EndpointService", "RevokeToken", messages.RevokeTokenResponse)
42
+ LIST_TOKENS = Method("EndpointService", "ListTokens", messages.ListTokensResponse)
43
+
44
+ LIST_CONNECTIONS = Method("ConnectionService", "ListConnections", messages.ListConnectionsResponse)
45
+ REMOVE_CONNECTION = Method("ConnectionService", "RemoveConnection", messages.RemoveConnectionResponse)
46
+ ADD_TOKEN = Method("ConnectionService", "AddToken", messages.AddTokenResponse)
47
+ ADD_GITHUB_APP = Method("ConnectionService", "AddGitHubApp", messages.AddGitHubAppResponse)
48
+ BEGIN_GITHUB_APP = Method("ConnectionService", "BeginGitHubApp", messages.BeginGitHubAppResponse)
49
+ COMPLETE_GITHUB_APP = Method("ConnectionService", "CompleteGitHubApp", messages.CompleteGitHubAppResponse)
50
+ START_GITHUB_LOGIN = Method("ConnectionService", "StartGitHubLogin", messages.StartGitHubLoginResponse)
51
+ FINISH_GITHUB_LOGIN = Method("ConnectionService", "FinishGitHubLogin", messages.FinishGitHubLoginResponse)
52
+
53
+ GET_SETTINGS = Method("PublishingService", "GetSettings", messages.GetSettingsResponse)
54
+ SET_SETTINGS = Method("PublishingService", "SetSettings", messages.SetSettingsResponse)
55
+ REMOVE_SETTINGS = Method("PublishingService", "RemoveSettings", messages.RemoveSettingsResponse)
56
+ LIST_PRESETS = Method("PublishingService", "ListPresets", messages.ListPresetsResponse)
57
+ SET_PRESET = Method("PublishingService", "SetPreset", messages.SetPresetResponse)
58
+ REMOVE_PRESET = Method("PublishingService", "RemovePreset", messages.RemovePresetResponse)