indent 0.1.26__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.
Files changed (55) hide show
  1. exponent/__init__.py +34 -0
  2. exponent/cli.py +110 -0
  3. exponent/commands/cloud_commands.py +585 -0
  4. exponent/commands/common.py +411 -0
  5. exponent/commands/config_commands.py +334 -0
  6. exponent/commands/run_commands.py +222 -0
  7. exponent/commands/settings.py +56 -0
  8. exponent/commands/types.py +111 -0
  9. exponent/commands/upgrade.py +29 -0
  10. exponent/commands/utils.py +146 -0
  11. exponent/core/config.py +180 -0
  12. exponent/core/graphql/__init__.py +0 -0
  13. exponent/core/graphql/client.py +61 -0
  14. exponent/core/graphql/get_chats_query.py +47 -0
  15. exponent/core/graphql/mutations.py +160 -0
  16. exponent/core/graphql/queries.py +146 -0
  17. exponent/core/graphql/subscriptions.py +16 -0
  18. exponent/core/remote_execution/checkpoints.py +212 -0
  19. exponent/core/remote_execution/cli_rpc_types.py +499 -0
  20. exponent/core/remote_execution/client.py +999 -0
  21. exponent/core/remote_execution/code_execution.py +77 -0
  22. exponent/core/remote_execution/default_env.py +31 -0
  23. exponent/core/remote_execution/error_info.py +45 -0
  24. exponent/core/remote_execution/exceptions.py +10 -0
  25. exponent/core/remote_execution/file_write.py +35 -0
  26. exponent/core/remote_execution/files.py +330 -0
  27. exponent/core/remote_execution/git.py +268 -0
  28. exponent/core/remote_execution/http_fetch.py +94 -0
  29. exponent/core/remote_execution/languages/python_execution.py +239 -0
  30. exponent/core/remote_execution/languages/shell_streaming.py +226 -0
  31. exponent/core/remote_execution/languages/types.py +20 -0
  32. exponent/core/remote_execution/port_utils.py +73 -0
  33. exponent/core/remote_execution/session.py +128 -0
  34. exponent/core/remote_execution/system_context.py +26 -0
  35. exponent/core/remote_execution/terminal_session.py +375 -0
  36. exponent/core/remote_execution/terminal_types.py +29 -0
  37. exponent/core/remote_execution/tool_execution.py +595 -0
  38. exponent/core/remote_execution/tool_type_utils.py +39 -0
  39. exponent/core/remote_execution/truncation.py +296 -0
  40. exponent/core/remote_execution/types.py +635 -0
  41. exponent/core/remote_execution/utils.py +477 -0
  42. exponent/core/types/__init__.py +0 -0
  43. exponent/core/types/command_data.py +206 -0
  44. exponent/core/types/event_types.py +89 -0
  45. exponent/core/types/generated/__init__.py +0 -0
  46. exponent/core/types/generated/strategy_info.py +213 -0
  47. exponent/migration-docs/login.md +112 -0
  48. exponent/py.typed +4 -0
  49. exponent/utils/__init__.py +0 -0
  50. exponent/utils/colors.py +92 -0
  51. exponent/utils/version.py +289 -0
  52. indent-0.1.26.dist-info/METADATA +38 -0
  53. indent-0.1.26.dist-info/RECORD +55 -0
  54. indent-0.1.26.dist-info/WHEEL +4 -0
  55. indent-0.1.26.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,160 @@
1
+ HALT_CHAT_STREAM_MUTATION: str = """
2
+ mutation HaltChatStream($chatUuid: UUID!) {
3
+ haltChatStream(chatUuid: $chatUuid) {
4
+ __typename
5
+ }
6
+ }
7
+ """
8
+
9
+
10
+ SET_LOGIN_COMPLETE_MUTATION: str = """
11
+ mutation SetLoginComplete {
12
+ setLoginComplete {
13
+ __typename
14
+ ... on User {
15
+ userApiKey
16
+ }
17
+ ... on UnauthenticatedError {
18
+ message
19
+ }
20
+ }
21
+ }
22
+ """
23
+
24
+
25
+ REFRESH_API_KEY_MUTATION = """
26
+ mutation RefreshApiKey {
27
+ refreshApiKey {
28
+ ... on User {
29
+ userApiKey
30
+ }
31
+ ... on UnauthenticatedError {
32
+ message
33
+ }
34
+ }
35
+ }
36
+ """
37
+
38
+ START_CHAT_TURN_MUTATION = """
39
+ mutation StartChatTurnMutation($chatInput: ChatInput!, $parentUuid: String, $chatConfig: ChatConfig!) {
40
+ startChatReply(
41
+ chatInput: $chatInput,
42
+ parentUuid: $parentUuid,
43
+ chatConfig: $chatConfig
44
+ ) {
45
+ __typename
46
+ ... on UnauthenticatedError {
47
+ message
48
+ }
49
+ ... on ChatNotFoundError {
50
+ message
51
+ }
52
+ ... on Chat {
53
+ chatUuid
54
+ }
55
+ }
56
+ }
57
+ """
58
+
59
+
60
+ CREATE_CLOUD_CHAT_MUTATION = """
61
+ mutation CreateCloudChat($configId: String!) {
62
+ createCloudChat(cloudConfigUuid: $configId) {
63
+ __typename
64
+ ...on Chat {
65
+ chatUuid
66
+ }
67
+ ...on CloudSessionError {
68
+ message
69
+ }
70
+ ...on UnauthenticatedError {
71
+ message
72
+ }
73
+ }
74
+ }
75
+ """
76
+
77
+
78
+ CREATE_CLOUD_CHAT_FROM_REPOSITORY_MUTATION = """
79
+ mutation CreateCloudChatFromRepository($repositoryId: String!, $provider: String) {
80
+ createCloudChat(repositoryId: $repositoryId, provider: $provider) {
81
+ __typename
82
+ ...on Chat {
83
+ chatUuid
84
+ }
85
+ ...on UnauthenticatedError {
86
+ message
87
+ }
88
+ ...on ChatNotFoundError {
89
+ message
90
+ }
91
+ ...on CloudConfigNotFoundError {
92
+ message
93
+ }
94
+ ...on GithubConfigNotFoundError {
95
+ message
96
+ }
97
+ ...on CloudSessionError {
98
+ message
99
+ }
100
+ }
101
+ }
102
+ """
103
+
104
+
105
+ ENABLE_CLOUD_REPOSITORY_MUTATION = """
106
+ mutation EnableCloudRepository($orgName: String!, $repoName: String!) {
107
+ enableCloudRepository(orgName: $orgName, repoName: $repoName) {
108
+ __typename
109
+ ...on ContainerImage {
110
+ buildRef
111
+ createdAt
112
+ updatedAt
113
+ }
114
+ ...on UnauthenticatedError {
115
+ message
116
+ }
117
+ ...on CloudConfigNotFoundError {
118
+ message
119
+ }
120
+ ...on GithubConfigNotFoundError {
121
+ message
122
+ }
123
+ ...on CloudSessionError {
124
+ message
125
+ }
126
+ ...on Error {
127
+ message
128
+ }
129
+ }
130
+ }
131
+ """
132
+
133
+
134
+ REBUILD_CLOUD_REPOSITORY_MUTATION = """
135
+ mutation RebuildCloudRepository($orgName: String!, $repoName: String!) {
136
+ rebuildCloudRepository(orgName: $orgName, repoName: $repoName) {
137
+ __typename
138
+ ...on ContainerImage {
139
+ buildRef
140
+ createdAt
141
+ updatedAt
142
+ }
143
+ ...on UnauthenticatedError {
144
+ message
145
+ }
146
+ ...on CloudConfigNotFoundError {
147
+ message
148
+ }
149
+ ...on GithubConfigNotFoundError {
150
+ message
151
+ }
152
+ ...on CloudSessionError {
153
+ message
154
+ }
155
+ ...on Error {
156
+ message
157
+ }
158
+ }
159
+ }
160
+ """
@@ -0,0 +1,146 @@
1
+ GITHUB_REPOSITORIES_QUERY: str = """
2
+ query GithubRepositories {
3
+ githubRepositories {
4
+ __typename
5
+ ... on GithubRepositories {
6
+ repositories {
7
+ id
8
+ githubOrgName
9
+ githubRepoName
10
+ baseHost
11
+ containerImageId
12
+ createdAt
13
+ updatedAt
14
+ }
15
+ }
16
+ ... on Error {
17
+ message
18
+ }
19
+ }
20
+ }
21
+ """
22
+
23
+
24
+ EVENTS_FOR_CHAT_QUERY: str = """query EventsForChat($chatUuid: UUID!) {
25
+ eventsForChat(chatUuid: $chatUuid) {
26
+ ... on EventHistory {
27
+ events {
28
+ ... on UserEvent {
29
+ uuid
30
+ parentUuid
31
+ chatId
32
+ isSidechain
33
+ version
34
+ createdAt
35
+ sidechainRootUuid
36
+ synthetic
37
+ messageData: message {
38
+ ... on TextMessage {
39
+ text
40
+ }
41
+ ... on ToolCallMessage {
42
+ messageId
43
+ toolUseId
44
+ toolName
45
+ toolInput {
46
+ ... on BashToolInput {
47
+ command
48
+ }
49
+ ... on ReadToolInput {
50
+ filePath
51
+ }
52
+ }
53
+ }
54
+ ... on ToolResultMessage {
55
+ messageId
56
+ toolUseId
57
+ text
58
+ resultData {
59
+ ... on BashToolResult {
60
+ shellOutput
61
+ exitCode
62
+ }
63
+ ... on ReadToolResult {
64
+ content
65
+ }
66
+ }
67
+ }
68
+ }
69
+ }
70
+ ... on AssistantEvent {
71
+ uuid
72
+ parentUuid
73
+ chatId
74
+ isSidechain
75
+ version
76
+ createdAt
77
+ sidechainRootUuid
78
+ synthetic
79
+ messageData: message {
80
+ ... on TextMessage {
81
+ text
82
+ }
83
+ ... on ToolCallMessage {
84
+ messageId
85
+ toolUseId
86
+ toolName
87
+ toolInput {
88
+ ... on BashToolInput {
89
+ command
90
+ }
91
+ ... on ReadToolInput {
92
+ filePath
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
98
+ ... on SystemEvent {
99
+ uuid
100
+ parentUuid
101
+ chatId
102
+ isSidechain
103
+ version
104
+ createdAt
105
+ sidechainRootUuid
106
+ messageData: message {
107
+ ... on ToolCallMessage {
108
+ messageId
109
+ toolUseId
110
+ toolName
111
+ toolInput {
112
+ ... on BashToolInput {
113
+ command
114
+ }
115
+ ... on ReadToolInput {
116
+ filePath
117
+ }
118
+ }
119
+ }
120
+ ... on ToolResultMessage {
121
+ messageId
122
+ toolUseId
123
+ text
124
+ resultData {
125
+ ... on BashToolResult {
126
+ shellOutput
127
+ exitCode
128
+ }
129
+ ... on ReadToolResult {
130
+ content
131
+ }
132
+ }
133
+ }
134
+ ... on ToolExecutionStatusMessage {
135
+ executionStatus: status
136
+ }
137
+ ... on ToolPermissionStatusMessage {
138
+ permissionStatus: status
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ """
@@ -0,0 +1,16 @@
1
+ AUTHENTICATED_USER_SUBSCRIPTION = """
2
+ subscription {
3
+ testAuthenticatedUser {
4
+ __typename
5
+ ... on UnauthenticatedError {
6
+ message
7
+ }
8
+ ...on Error {
9
+ message
10
+ }
11
+ ... on User {
12
+ userUuid
13
+ }
14
+ }
15
+ }
16
+ """
@@ -0,0 +1,212 @@
1
+ import os
2
+ import subprocess
3
+ import tempfile
4
+
5
+ from pygit2.repository import Repository
6
+
7
+ from exponent.core.remote_execution.types import (
8
+ CreateCheckpointRequest,
9
+ CreateCheckpointResponse,
10
+ GitCommitMetadata,
11
+ GitDiff,
12
+ GitFileChange,
13
+ RollbackToCheckpointResponse,
14
+ )
15
+
16
+
17
+ async def create_checkpoint(
18
+ request: CreateCheckpointRequest,
19
+ ) -> CreateCheckpointResponse:
20
+ repo = Repository(".")
21
+ head_commit = str(repo.head.target)
22
+ uncommitted_changes_commit = None
23
+ diff_versus_last_checkpoint = None
24
+
25
+ # Get metadata for head commit - fetch each field separately
26
+ author_name = (
27
+ subprocess.run(
28
+ ["git", "log", "--format=%aN", "-1", head_commit],
29
+ capture_output=True,
30
+ text=True,
31
+ check=True,
32
+ ).stdout.strip()
33
+ or "unknown"
34
+ )
35
+
36
+ author_email = (
37
+ subprocess.run(
38
+ ["git", "log", "--format=%aE", "-1", head_commit],
39
+ capture_output=True,
40
+ text=True,
41
+ check=True,
42
+ ).stdout.strip()
43
+ or "unknown@unknown"
44
+ )
45
+
46
+ author_date = subprocess.run(
47
+ ["git", "log", "--format=%ai", "-1", head_commit],
48
+ capture_output=True,
49
+ text=True,
50
+ check=True,
51
+ ).stdout.strip()
52
+
53
+ commit_date = subprocess.run(
54
+ ["git", "log", "--format=%ci", "-1", head_commit],
55
+ capture_output=True,
56
+ text=True,
57
+ check=True,
58
+ ).stdout.strip()
59
+
60
+ commit_message = subprocess.run(
61
+ ["git", "log", "--format=%B", "-1", head_commit],
62
+ capture_output=True,
63
+ text=True,
64
+ check=True,
65
+ ).stdout.strip()
66
+
67
+ # Get current branch
68
+ branch_result = subprocess.run(
69
+ ["git", "rev-parse", "--abbrev-ref", "HEAD"],
70
+ capture_output=True,
71
+ text=True,
72
+ check=True,
73
+ )
74
+ branch = branch_result.stdout.strip()
75
+
76
+ head_metadata = GitCommitMetadata(
77
+ author_name=author_name,
78
+ author_email=author_email,
79
+ author_date=author_date,
80
+ commit_date=commit_date,
81
+ branch=branch,
82
+ commit_message=commit_message,
83
+ )
84
+
85
+ if repo.status(): # working dir is dirty
86
+ with tempfile.NamedTemporaryFile(prefix="git_index_") as tmp:
87
+ tmp_index_path = tmp.name
88
+
89
+ # Set up environment with temporary index
90
+ env = os.environ.copy()
91
+ env["GIT_INDEX_FILE"] = tmp_index_path
92
+
93
+ # Initialize temporary index from HEAD
94
+ subprocess.run(["git", "read-tree", head_commit], env=env, check=True)
95
+
96
+ # Add all files (including untracked) to temporary index
97
+ subprocess.run(["git", "add", "-A"], env=env, check=True)
98
+
99
+ # Write tree object from our temporary index
100
+ result = subprocess.run(
101
+ ["git", "write-tree"],
102
+ env=env,
103
+ capture_output=True,
104
+ text=True,
105
+ check=True,
106
+ )
107
+ tree_hash = result.stdout.strip()
108
+
109
+ if not tree_hash:
110
+ raise ValueError("Failed to create tree object")
111
+
112
+ # Create commit object from the tree with HEAD as parent
113
+ result = subprocess.run(
114
+ [
115
+ "git",
116
+ "commit-tree",
117
+ tree_hash,
118
+ "-p",
119
+ str(head_commit),
120
+ "-m",
121
+ "Checkpoint commit",
122
+ ],
123
+ capture_output=True,
124
+ text=True,
125
+ check=True,
126
+ )
127
+ uncommitted_changes_commit = result.stdout.strip()
128
+
129
+ if not uncommitted_changes_commit:
130
+ raise ValueError("Failed to create checkpoint commit")
131
+
132
+ if request.last_checkpoint_head_commit:
133
+ last_checkpoint_commit = (
134
+ request.last_checkpoint_uncommitted_changes_commit
135
+ or request.last_checkpoint_head_commit
136
+ )
137
+ current_commit = uncommitted_changes_commit or head_commit
138
+
139
+ diff_versus_last_checkpoint = _parse_git_diff_tree(
140
+ last_checkpoint_commit,
141
+ current_commit,
142
+ )
143
+
144
+ return CreateCheckpointResponse(
145
+ correlation_id=request.correlation_id,
146
+ head_commit_hash=head_commit,
147
+ head_commit_metadata=head_metadata,
148
+ uncommitted_changes_commit_hash=uncommitted_changes_commit,
149
+ diff_versus_last_checkpoint=diff_versus_last_checkpoint,
150
+ )
151
+
152
+
153
+ async def rollback_to_checkpoint(
154
+ correlation_id: str,
155
+ head_commit: str,
156
+ checkpoint_commit: str | None,
157
+ ) -> RollbackToCheckpointResponse:
158
+ # Clean working directory (including untracked files) before any operations
159
+ subprocess.run(
160
+ ["git", "clean", "-fd"], check=True
161
+ ) # Remove untracked files and directories
162
+ subprocess.run(
163
+ ["git", "reset", "--hard"], check=True
164
+ ) # Remove staged/unstaged changes
165
+
166
+ # Now reset HEAD to the original commit state
167
+ subprocess.run(["git", "reset", "--hard", head_commit], check=True)
168
+
169
+ if checkpoint_commit:
170
+ # Cherry-pick the checkpoint commit to restore all changes
171
+ subprocess.run(
172
+ ["git", "cherry-pick", "--no-commit", checkpoint_commit], check=True
173
+ )
174
+ subprocess.run(["git", "reset"], check=True)
175
+
176
+ return RollbackToCheckpointResponse(
177
+ correlation_id=correlation_id,
178
+ )
179
+
180
+
181
+ def _parse_git_diff_tree(
182
+ from_commit: str, to_commit: str, max_files: int = 50
183
+ ) -> GitDiff:
184
+ """Parse git diff-tree output into a GitDiff object.
185
+
186
+ Args:
187
+ from_commit: Starting commit hash
188
+ to_commit: Ending commit hash
189
+ max_files: Maximum number of files to include in the diff
190
+ """
191
+ result = subprocess.run(
192
+ ["git", "diff-tree", "--numstat", from_commit, to_commit],
193
+ capture_output=True,
194
+ text=True,
195
+ check=True,
196
+ )
197
+
198
+ files = []
199
+ for line in result.stdout.splitlines():
200
+ if not line.strip():
201
+ continue
202
+ added, deleted, path = line.split("\t")
203
+ files.append(
204
+ GitFileChange(path=path, lines_added=int(added), lines_deleted=int(deleted))
205
+ )
206
+
207
+ total_files = len(files)
208
+ truncated = total_files > max_files
209
+ if truncated:
210
+ files = files[:max_files]
211
+
212
+ return GitDiff(files=files, truncated=truncated, total_files=total_files)