indent 0.1.13__py3-none-any.whl → 0.1.28__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 (31) hide show
  1. exponent/__init__.py +2 -2
  2. exponent/cli.py +0 -2
  3. exponent/commands/cloud_commands.py +2 -87
  4. exponent/commands/common.py +25 -40
  5. exponent/commands/config_commands.py +0 -87
  6. exponent/commands/run_commands.py +5 -2
  7. exponent/core/config.py +1 -1
  8. exponent/core/container_build/__init__.py +0 -0
  9. exponent/core/container_build/types.py +25 -0
  10. exponent/core/graphql/mutations.py +2 -31
  11. exponent/core/graphql/queries.py +0 -3
  12. exponent/core/remote_execution/cli_rpc_types.py +201 -5
  13. exponent/core/remote_execution/client.py +355 -92
  14. exponent/core/remote_execution/code_execution.py +26 -7
  15. exponent/core/remote_execution/default_env.py +31 -0
  16. exponent/core/remote_execution/languages/shell_streaming.py +11 -6
  17. exponent/core/remote_execution/port_utils.py +73 -0
  18. exponent/core/remote_execution/system_context.py +2 -0
  19. exponent/core/remote_execution/terminal_session.py +517 -0
  20. exponent/core/remote_execution/terminal_types.py +29 -0
  21. exponent/core/remote_execution/tool_execution.py +228 -18
  22. exponent/core/remote_execution/tool_type_utils.py +39 -0
  23. exponent/core/remote_execution/truncation.py +9 -1
  24. exponent/core/remote_execution/types.py +71 -19
  25. exponent/utils/version.py +8 -7
  26. {indent-0.1.13.dist-info → indent-0.1.28.dist-info}/METADATA +5 -2
  27. {indent-0.1.13.dist-info → indent-0.1.28.dist-info}/RECORD +29 -24
  28. exponent/commands/workflow_commands.py +0 -111
  29. exponent/core/graphql/github_config_queries.py +0 -56
  30. {indent-0.1.13.dist-info → indent-0.1.28.dist-info}/WHEEL +0 -0
  31. {indent-0.1.13.dist-info → indent-0.1.28.dist-info}/entry_points.txt +0 -0
exponent/__init__.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.1.13'
32
- __version_tuple__ = version_tuple = (0, 1, 13)
31
+ __version__ = version = '0.1.28'
32
+ __version_tuple__ = version_tuple = (0, 1, 28)
33
33
 
34
34
  __commit_id__ = commit_id = None
exponent/cli.py CHANGED
@@ -7,7 +7,6 @@ from exponent.commands.config_commands import config_cli
7
7
  from exponent.commands.run_commands import run, run_cli
8
8
  from exponent.commands.types import ExponentGroup, exponent_cli_group
9
9
  from exponent.commands.upgrade import upgrade_cli
10
- from exponent.commands.workflow_commands import workflow_cli
11
10
  from exponent.utils.version import (
12
11
  get_installed_version,
13
12
  )
@@ -34,7 +33,6 @@ sources: list[ExponentGroup] = [
34
33
  run_cli, # Run AI chat commands
35
34
  upgrade_cli, # Upgrade-related commands
36
35
  cloud_cli, # Cloud commands
37
- workflow_cli, # Workflow commands
38
36
  ]
39
37
 
40
38
  for source in sources:
@@ -23,7 +23,6 @@ from exponent.core.graphql.client import GraphQLClient
23
23
  from exponent.core.graphql.mutations import (
24
24
  CREATE_CLOUD_CHAT_FROM_REPOSITORY_MUTATION,
25
25
  ENABLE_CLOUD_REPOSITORY_MUTATION,
26
- INCREMENTAL_BUILD_CLOUD_REPOSITORY_MUTATION,
27
26
  REBUILD_CLOUD_REPOSITORY_MUTATION,
28
27
  START_CHAT_TURN_MUTATION,
29
28
  )
@@ -62,32 +61,6 @@ async def enable_cloud_repository(
62
61
  return cast(dict[str, Any], result["enableCloudRepository"])
63
62
 
64
63
 
65
- async def incremental_build_cloud_repository(
66
- api_key: str,
67
- base_api_url: str,
68
- base_ws_url: str,
69
- org_name: str,
70
- repo_name: str,
71
- ) -> dict[str, Any]:
72
- graphql_client = GraphQLClient(
73
- api_key=api_key, base_api_url=base_api_url, base_ws_url=base_ws_url
74
- )
75
-
76
- variables = {
77
- "orgName": org_name,
78
- "repoName": repo_name,
79
- }
80
-
81
- result = await graphql_client.execute(
82
- INCREMENTAL_BUILD_CLOUD_REPOSITORY_MUTATION,
83
- variables,
84
- "IncrementalBuildCloudRepository",
85
- timeout=120,
86
- )
87
-
88
- return cast(dict[str, Any], result["incrementalBuildCloudRepository"])
89
-
90
-
91
64
  async def rebuild_cloud_repository(
92
65
  api_key: str,
93
66
  base_api_url: str,
@@ -138,6 +111,7 @@ async def create_cloud_chat_from_repository(
138
111
  base_api_url: str,
139
112
  base_ws_url: str,
140
113
  repository_id: str,
114
+ provider: str | None = None,
141
115
  ) -> dict[str, Any]:
142
116
  graphql_client = GraphQLClient(
143
117
  api_key=api_key, base_api_url=base_api_url, base_ws_url=base_ws_url
@@ -145,6 +119,7 @@ async def create_cloud_chat_from_repository(
145
119
 
146
120
  variables = {
147
121
  "repositoryId": repository_id,
122
+ "provider": provider,
148
123
  }
149
124
 
150
125
  result = await graphql_client.execute(
@@ -243,63 +218,6 @@ def enable_repo(
243
218
  sys.exit(1)
244
219
 
245
220
 
246
- @cloud_cli.command(hidden=True)
247
- @click.option(
248
- "--org-name",
249
- help="GitHub organization name",
250
- required=True,
251
- )
252
- @click.option(
253
- "--repo-name",
254
- help="GitHub repository name",
255
- required=True,
256
- )
257
- @use_settings
258
- def incremental_build(
259
- settings: Settings,
260
- org_name: str,
261
- repo_name: str,
262
- ) -> None:
263
- """Test utility for incremental build of cloud repository."""
264
- check_exponent_version_and_upgrade(settings)
265
-
266
- if not settings.api_key:
267
- redirect_to_login(settings)
268
- return
269
-
270
- loop = asyncio.get_event_loop()
271
-
272
- api_key = settings.api_key
273
- base_api_url = settings.get_base_api_url()
274
- base_ws_url = settings.get_base_ws_url()
275
-
276
- try:
277
- result = loop.run_until_complete(
278
- incremental_build_cloud_repository(
279
- api_key, base_api_url, base_ws_url, org_name, repo_name
280
- )
281
- )
282
-
283
- if result["__typename"] == "ContainerImage":
284
- click.secho(
285
- f"✓ Successfully triggered incremental build for {org_name}/{repo_name}",
286
- fg="green",
287
- )
288
- click.echo(f" Build ref: {result.get('buildRef', 'N/A')}")
289
- click.echo(f" Created at: {result.get('createdAt', 'N/A')}")
290
- click.echo(f" Updated at: {result.get('updatedAt', 'N/A')}")
291
- else:
292
- click.secho(
293
- f"✗ Failed to trigger incremental build: {result.get('message', 'Unknown error')}",
294
- fg="red",
295
- )
296
- click.echo(f" Error type: {result['__typename']}")
297
-
298
- except Exception as e:
299
- click.secho(f"✗ Error triggering incremental build: {e!s}", fg="red")
300
- sys.exit(1)
301
-
302
-
303
221
  @cloud_cli.command(hidden=True)
304
222
  @click.option(
305
223
  "--org-name",
@@ -654,9 +572,6 @@ def cloud(
654
572
  create_cloud_chat(api_key, base_api_url, base_ws_url, cloud_config_id)
655
573
  )
656
574
 
657
- if chat_uuid is None:
658
- sys.exit(1)
659
-
660
575
  loop.run_until_complete(
661
576
  start_chat_turn(api_key, base_api_url, base_ws_url, chat_uuid, prompt)
662
577
  )
@@ -37,7 +37,8 @@ from exponent.core.remote_execution.exceptions import (
37
37
  )
38
38
  from exponent.core.remote_execution.files import FileCache
39
39
  from exponent.core.remote_execution.git import get_git_info
40
- from exponent.core.remote_execution.types import ChatSource, GitInfo
40
+ from exponent.core.remote_execution.session import send_exception_log
41
+ from exponent.core.remote_execution.types import ChatSource
41
42
 
42
43
  load_dotenv()
43
44
 
@@ -132,13 +133,13 @@ async def check_inside_git_repo(settings: Settings) -> None:
132
133
  )
133
134
  )
134
135
  click.echo(
135
- "This is a check to make sure you are running Exponent from the root of your project."
136
+ "This is a check to make sure you are running Indent from the root of your project."
136
137
  )
137
138
 
138
139
  click.echo(f"\nCurrent directory: {click.style(os.getcwd(), fg='cyan')}")
139
140
 
140
141
  click.echo("\nRecommendation:")
141
- click.echo(" Run Exponent from the root directory of your codebase.")
142
+ click.echo(" Run Indent from the root directory of your codebase.")
142
143
  click.echo("\nExample:")
143
144
  click.echo(
144
145
  f" If your project is in {click.style('~/my-project', fg='cyan')}, run:"
@@ -147,7 +148,7 @@ async def check_inside_git_repo(settings: Settings) -> None:
147
148
 
148
149
  # Tell the user they can run exponent config --no-git-warning to disable this check
149
150
  click.echo(
150
- f"\nYou can run {click.style('exponent config --set-git-warning-disabled', fg='green')} to disable this check."
151
+ f"\nYou can run {click.style('indent config --set-git-warning-disabled', fg='green')} to disable this check."
151
152
  )
152
153
 
153
154
  if not click.confirm(
@@ -165,26 +166,26 @@ def check_running_from_home_directory(require_confirmation: bool = True) -> bool
165
166
  if os.path.expanduser("~") == os.getcwd():
166
167
  click.echo(
167
168
  click.style(
168
- "\nWarning: Running Exponent from Home Directory",
169
+ "\nWarning: Running Indent from Home Directory",
169
170
  fg="yellow",
170
171
  bold=True,
171
172
  )
172
173
  )
173
174
  click.echo(
174
- "Running Exponent from your home directory can cause unexpected issues."
175
+ "Running Indent from your home directory can cause unexpected issues."
175
176
  )
176
177
  click.echo("\nRecommendation:")
177
- click.echo(" Run Exponent from the root directory of your codebase.")
178
+ click.echo(" Run Indent from the root directory of your codebase.")
178
179
  click.echo("\nExample:")
179
180
  click.echo(
180
181
  f" If your project is in {click.style('~/my-project', fg='cyan')}, run:"
181
182
  )
182
- click.echo(f" {click.style('cd ~/my-project && exponent run', fg='green')}")
183
+ click.echo(f" {click.style('cd ~/my-project && indent run', fg='green')}")
183
184
 
184
185
  if require_confirmation:
185
186
  if not click.confirm(
186
187
  click.style(
187
- f"\nDo you want to continue running Exponent from {os.getcwd()}?",
188
+ f"\nDo you want to continue running indent from {os.getcwd()}?",
188
189
  fg="yellow",
189
190
  ),
190
191
  default=True,
@@ -206,15 +207,25 @@ def run_until_complete(coro: Coroutine[Any, Any, Any]) -> Any:
206
207
  try:
207
208
  loop.run_until_complete(task)
208
209
  except KeyboardInterrupt:
210
+ click.echo("\nReceived interrupt signal, shutting down gracefully...")
209
211
  task.cancel()
210
212
  try:
211
213
  loop.run_until_complete(task)
212
214
  except asyncio.CancelledError:
213
215
  pass
216
+ click.secho("Exited due to keyboard interrupt", fg="yellow")
217
+ sys.exit(130) # Standard exit code for SIGINT (128 + 2)
214
218
  except ExponentError as e:
219
+ try:
220
+ settings = get_settings()
221
+ loop.run_until_complete(
222
+ send_exception_log(e, session=None, settings=settings)
223
+ )
224
+ except Exception:
225
+ pass
215
226
  click.secho(f"Encountered error: {e}", fg="red")
216
227
  click.secho(
217
- "The Exponent team has been notified, "
228
+ "The Indent team has been notified, "
218
229
  "please try again and reach out if the problem persists.",
219
230
  fg="yellow",
220
231
  )
@@ -321,7 +332,9 @@ async def start_client(
321
332
  if prompt:
322
333
  # If given a prompt, we also need to send a request
323
334
  # to kick off the initial turn loop for the chat
324
- raise NotImplementedError("Kicking off with initial prompt not implemented")
335
+ aux_coros.append(
336
+ start_chat_turn(api_key, base_api_url, base_ws_url, chat_uuid, prompt)
337
+ )
325
338
  elif workflow_id:
326
339
  # Similarly, if given a workflow ID, we need to send
327
340
  # a request to kick off the workflow
@@ -346,34 +359,6 @@ async def create_chat(
346
359
  return None
347
360
 
348
361
 
349
- async def get_gh_app_installation_token(
350
- api_key: str, base_api_url: str, base_ws_url: str, git_info: GitInfo
351
- ) -> dict[str, Any] | None:
352
- try:
353
- async with RemoteExecutionClient.session(
354
- api_key, base_api_url, base_ws_url, os.getcwd()
355
- ) as client:
356
- return await client.get_gh_installation_token(git_info)
357
- except (httpx.ConnectError, ExponentError) as e:
358
- click.secho(f"Error: {e}", fg="red")
359
- return None
360
-
361
-
362
- async def verify_gh_app_installation(
363
- api_key: str, base_api_url: str, base_ws_url: str, git_info: GitInfo
364
- ) -> bool:
365
- try:
366
- async with RemoteExecutionClient.session(
367
- api_key, base_api_url, base_ws_url, os.getcwd()
368
- ) as client:
369
- res = await client.get_gh_installation_token(git_info)
370
- if "token" in res:
371
- return True
372
- except (httpx.ConnectError, ExponentError) as e:
373
- click.secho(f"Error: {e}", fg="red")
374
- return False
375
-
376
-
377
362
  async def set_login_complete(api_key: str, base_api_url: str, base_ws_url: str) -> None:
378
363
  graphql_client = GraphQLClient(
379
364
  api_key=api_key, base_api_url=base_api_url, base_ws_url=base_ws_url
@@ -392,7 +377,7 @@ async def set_login_complete(api_key: str, base_api_url: str, base_ws_url: str)
392
377
  # than the one used in the user's request...
393
378
  # This should never happen
394
379
  raise HandledExponentError(
395
- "Invalid API key, login to https://exponent.run to find your API key."
380
+ "Invalid API key, login to https://indent.com to find your API key."
396
381
  )
397
382
 
398
383
 
@@ -14,11 +14,6 @@ from exponent.commands.types import exponent_cli_group
14
14
  from exponent.core.config import Settings
15
15
  from exponent.core.graphql.client import GraphQLClient
16
16
  from exponent.core.graphql.get_chats_query import GET_CHATS_QUERY
17
- from exponent.core.graphql.github_config_queries import (
18
- CHECK_GITHUB_CONFIG_VALIDITY_QUERY,
19
- CREATE_GITHUB_CONFIG_MUTATION,
20
- REPOS_FOR_GITHUB_CONFIG_QUERY,
21
- )
22
17
  from exponent.core.graphql.subscriptions import AUTHENTICATED_USER_SUBSCRIPTION
23
18
  from exponent.utils.version import (
24
19
  get_installed_metadata,
@@ -56,74 +51,6 @@ def debug(
56
51
  click.echo(get_installed_metadata())
57
52
 
58
53
 
59
- @config_cli.command(hidden=True)
60
- @use_settings
61
- def check_github_config_validity(
62
- settings: Settings,
63
- ) -> None:
64
- if not settings.api_key:
65
- redirect_to_login(settings)
66
- return
67
-
68
- run_until_complete(
69
- check_github_config_validity_task(
70
- api_key=settings.api_key,
71
- base_api_url=settings.get_base_api_url(),
72
- base_ws_url=settings.get_base_ws_url(),
73
- )
74
- )
75
-
76
-
77
- async def check_github_config_validity_task(
78
- api_key: str,
79
- base_api_url: str,
80
- base_ws_url: str,
81
- ) -> None:
82
- graphql_client = GraphQLClient(api_key, base_api_url, base_ws_url)
83
- result = await graphql_client.execute(CHECK_GITHUB_CONFIG_VALIDITY_QUERY)
84
- click.echo(result)
85
-
86
-
87
- @config_cli.command(hidden=True)
88
- @use_settings
89
- def repos_for_github_config(
90
- settings: Settings,
91
- ) -> None:
92
- if not settings.api_key:
93
- redirect_to_login(settings)
94
- return
95
-
96
- run_until_complete(
97
- repos_for_github_config_task(
98
- api_key=settings.api_key,
99
- base_api_url=settings.get_base_api_url(),
100
- base_ws_url=settings.get_base_ws_url(),
101
- )
102
- )
103
-
104
-
105
- async def repos_for_github_config_task(
106
- api_key: str,
107
- base_api_url: str,
108
- base_ws_url: str,
109
- ) -> None:
110
- graphql_client = GraphQLClient(api_key, base_api_url, base_ws_url)
111
- try:
112
- click.echo("Sending request to fetch repos...")
113
- result = await graphql_client.execute(
114
- REPOS_FOR_GITHUB_CONFIG_QUERY, timeout=120
115
- ) # 120 seconds timeout
116
- click.echo("Request completed. Result:")
117
- click.echo(result)
118
- except Exception as e:
119
- click.echo(f"An error occurred while fetching repos: {e!s}")
120
- click.echo(f"Error type: {type(e).__name__}")
121
- # Add more detailed error information if available
122
- if hasattr(e, "response"):
123
- click.echo(f"Response status: {e.response.status_code}")
124
- click.echo(f"Response content: {e.response.text}")
125
-
126
-
127
54
  @config_cli.command(hidden=True)
128
55
  @click.option(
129
56
  "--set-git-warning-disabled",
@@ -389,20 +316,6 @@ async def get_authenticated_user_task(
389
316
  click.echo(it)
390
317
 
391
318
 
392
- async def create_github_config_task(
393
- api_key: str,
394
- base_api_url: str,
395
- base_ws_url: str,
396
- github_pat: str,
397
- ) -> None:
398
- graphql_client = GraphQLClient(api_key, base_api_url, base_ws_url)
399
- variables = {
400
- "githubPat": github_pat,
401
- }
402
- result = await graphql_client.execute(CREATE_GITHUB_CONFIG_MUTATION, variables)
403
- click.echo(result)
404
-
405
-
406
319
  @config_cli.command(hidden=True)
407
320
  @use_settings
408
321
  def refresh_key(settings: Settings) -> None:
@@ -97,12 +97,15 @@ def run(
97
97
  create_chat(api_key, base_api_url, base_ws_url, ChatSource.CLI_RUN)
98
98
  )
99
99
 
100
+ if isinstance(timeout_seconds, int) and timeout_seconds <= 0:
101
+ click.secho("Error: --timeout-seconds must be a positive integer", fg="red")
102
+ sys.exit(1)
103
+
100
104
  if chat_uuid is None:
101
105
  sys.exit(1)
102
106
 
103
107
  if (
104
- not prompt
105
- and (not inside_ssh_session())
108
+ (not inside_ssh_session())
106
109
  and (not workflow_id)
107
110
  # If the user specified a chat ID, they probably don't want to re-launch the chat
108
111
  and (not chat_id)
exponent/core/config.py CHANGED
@@ -162,7 +162,7 @@ def get_settings(use_prod: bool = False, use_staging: bool = False) -> Settings:
162
162
  base_ws_url = "ws://localhost:8000"
163
163
  environment = Environment.development
164
164
  elif use_staging:
165
- base_url = "https://staging.indent.com"
165
+ base_url = "https://app.staging.indent.com"
166
166
  base_api_url = "https://staging-api.indent.com"
167
167
  base_ws_url = "wss://ws-staging-api.indent.com"
168
168
  environment = Environment.staging
File without changes
@@ -0,0 +1,25 @@
1
+ """Types for container build log streaming."""
2
+
3
+ from typing import Literal
4
+
5
+ import msgspec
6
+
7
+
8
+ class BuildLogOutput(msgspec.Struct, tag="container_build_log_output"):
9
+ """A single log line from a container build."""
10
+
11
+ container_image_uuid: str
12
+ data: str
13
+ timestamp: float
14
+ level: Literal["info", "error", "warning"] = "info"
15
+
16
+
17
+ class BuildLogStatus(msgspec.Struct, tag="container_build_log_status"):
18
+ """Status update for a container build."""
19
+
20
+ container_image_uuid: str
21
+ status: Literal["started", "completed", "failed"]
22
+ message: str | None = None
23
+
24
+
25
+ BuildLogMessage = BuildLogOutput | BuildLogStatus
@@ -76,8 +76,8 @@ mutation CreateCloudChat($configId: String!) {
76
76
 
77
77
 
78
78
  CREATE_CLOUD_CHAT_FROM_REPOSITORY_MUTATION = """
79
- mutation CreateCloudChatFromRepository($repositoryId: String!) {
80
- createCloudChat(repositoryId: $repositoryId) {
79
+ mutation CreateCloudChatFromRepository($repositoryId: String!, $provider: SandboxProvider) {
80
+ createCloudChat(repositoryId: $repositoryId, provider: $provider) {
81
81
  __typename
82
82
  ...on Chat {
83
83
  chatUuid
@@ -131,35 +131,6 @@ mutation EnableCloudRepository($orgName: String!, $repoName: String!) {
131
131
  """
132
132
 
133
133
 
134
- INCREMENTAL_BUILD_CLOUD_REPOSITORY_MUTATION = """
135
- mutation IncrementalBuildCloudRepository($orgName: String!, $repoName: String!) {
136
- incrementalBuildCloudRepository(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
- """
161
-
162
-
163
134
  REBUILD_CLOUD_REPOSITORY_MUTATION = """
164
135
  mutation RebuildCloudRepository($orgName: String!, $repoName: String!) {
165
136
  rebuildCloudRepository(orgName: $orgName, repoName: $repoName) {
@@ -28,7 +28,6 @@ EVENTS_FOR_CHAT_QUERY: str = """query EventsForChat($chatUuid: UUID!) {
28
28
  ... on UserEvent {
29
29
  uuid
30
30
  parentUuid
31
- chatId
32
31
  isSidechain
33
32
  version
34
33
  createdAt
@@ -70,7 +69,6 @@ EVENTS_FOR_CHAT_QUERY: str = """query EventsForChat($chatUuid: UUID!) {
70
69
  ... on AssistantEvent {
71
70
  uuid
72
71
  parentUuid
73
- chatId
74
72
  isSidechain
75
73
  version
76
74
  createdAt
@@ -98,7 +96,6 @@ EVENTS_FOR_CHAT_QUERY: str = """query EventsForChat($chatUuid: UUID!) {
98
96
  ... on SystemEvent {
99
97
  uuid
100
98
  parentUuid
101
- chatId
102
99
  isSidechain
103
100
  version
104
101
  createdAt