indent 0.1.20__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.
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.20'
32
- __version_tuple__ = version_tuple = (0, 1, 20)
31
+ __version__ = version = '0.1.28'
32
+ __version_tuple__ = version_tuple = (0, 1, 28)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -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
  )
@@ -207,11 +207,14 @@ def run_until_complete(coro: Coroutine[Any, Any, Any]) -> Any:
207
207
  try:
208
208
  loop.run_until_complete(task)
209
209
  except KeyboardInterrupt:
210
+ click.echo("\nReceived interrupt signal, shutting down gracefully...")
210
211
  task.cancel()
211
212
  try:
212
213
  loop.run_until_complete(task)
213
214
  except asyncio.CancelledError:
214
215
  pass
216
+ click.secho("Exited due to keyboard interrupt", fg="yellow")
217
+ sys.exit(130) # Standard exit code for SIGINT (128 + 2)
215
218
  except ExponentError as e:
216
219
  try:
217
220
  settings = get_settings()
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
@@ -66,14 +66,28 @@ class FileMetadata(msgspec.Struct):
66
66
  file_mode: str
67
67
 
68
68
 
69
+ class ReadToolArtifactResult(ToolResult, tag=READ_TOOL_ARTIFACT_NAME):
70
+ s3_uri: str
71
+ file_path: str
72
+ media_type: str
73
+
74
+ def to_text(self) -> str:
75
+ return f"[Image artifact uploaded to {self.s3_uri}]"
76
+
77
+
69
78
  class ReadToolResult(ToolResult, tag=READ_TOOL_NAME):
70
- content: str
71
- num_lines: int
72
- start_line: int
73
- total_lines: int
79
+ content: str | None = None
80
+ num_lines: int | None = None
81
+ start_line: int | None = None
82
+ total_lines: int | None = None
74
83
  metadata: FileMetadata | None = None
84
+ artifact: ReadToolArtifactResult | None = None
75
85
 
76
86
  def to_text(self) -> str:
87
+ if self.artifact:
88
+ return self.artifact.to_text()
89
+ assert self.content is not None
90
+ assert self.start_line is not None
77
91
  lines = self.content.splitlines()
78
92
  lines = [
79
93
  f"{str(i).rjust(6)}→{line}"
@@ -82,15 +96,6 @@ class ReadToolResult(ToolResult, tag=READ_TOOL_NAME):
82
96
  return "\n".join(lines)
83
97
 
84
98
 
85
- class ReadToolArtifactResult(ToolResult, tag=READ_TOOL_ARTIFACT_NAME):
86
- s3_uri: str
87
- file_path: str
88
- media_type: str
89
-
90
- def to_text(self) -> str:
91
- return f"[Image artifact uploaded to {self.s3_uri}]"
92
-
93
-
94
99
  LIST_TOOL_NAME = "ls"
95
100
 
96
101
 
@@ -200,6 +205,42 @@ class BashToolResult(ToolResult, tag=BASH_TOOL_NAME):
200
205
  stopped_by_user: bool
201
206
 
202
207
 
208
+ DOWNLOAD_ARTIFACT_TOOL_NAME = "download_artifact"
209
+
210
+
211
+ class DownloadArtifactToolInput(ToolInput, tag=DOWNLOAD_ARTIFACT_TOOL_NAME):
212
+ presigned_url: str
213
+ file_path: str
214
+ artifact_id: str
215
+ overwrite: bool = True
216
+
217
+
218
+ class DownloadArtifactToolResult(ToolResult, tag=DOWNLOAD_ARTIFACT_TOOL_NAME):
219
+ file_path: str
220
+ artifact_id: str
221
+ file_size_bytes: int
222
+ content_preview: str | None = None
223
+ num_lines: int | None = None
224
+ total_lines: int | None = None
225
+ truncated: bool = False
226
+
227
+
228
+ UPLOAD_ARTIFACT_TOOL_NAME = "upload_artifact"
229
+
230
+
231
+ class UploadArtifactToolInput(ToolInput, tag=UPLOAD_ARTIFACT_TOOL_NAME):
232
+ presigned_url: str
233
+ file_path: str
234
+ artifact_id: str
235
+ content_type: str
236
+
237
+
238
+ class UploadArtifactToolResult(ToolResult, tag=UPLOAD_ARTIFACT_TOOL_NAME):
239
+ artifact_id: str
240
+ file_size_bytes: int
241
+ content_type: str
242
+
243
+
203
244
  class HttpRequest(msgspec.Struct, tag="http_fetch_cli"):
204
245
  url: str
205
246
  method: str = "GET"
@@ -223,6 +264,8 @@ ToolInputType = (
223
264
  | GrepToolInput
224
265
  | EditToolInput
225
266
  | BashToolInput
267
+ | DownloadArtifactToolInput
268
+ | UploadArtifactToolInput
226
269
  )
227
270
  PartialToolResultType = PartialBashToolResult
228
271
 
@@ -235,6 +278,8 @@ ToolResultType = (
235
278
  | GrepToolResult
236
279
  | EditToolResult
237
280
  | BashToolResult
281
+ | DownloadArtifactToolResult
282
+ | UploadArtifactToolResult
238
283
  | ErrorToolResult
239
284
  )
240
285
 
@@ -273,6 +318,10 @@ class TerminateResponse(msgspec.Struct, tag="terminate"):
273
318
  pass
274
319
 
275
320
 
321
+ class TimeoutResponse(msgspec.Struct, tag="timeout"):
322
+ pass
323
+
324
+
276
325
  class BatchToolExecutionResponse(msgspec.Struct, tag="batch_tool_execution"):
277
326
  tool_results: list[ToolResultType]
278
327
 
@@ -295,6 +344,80 @@ class GenerateUploadUrlResponse(msgspec.Struct, tag="generate_upload_url"):
295
344
  s3_uri: str
296
345
 
297
346
 
347
+ # Terminal session management
348
+ class StartTerminalRequest(msgspec.Struct, tag="start_terminal"):
349
+ """Start a new terminal session with PTY"""
350
+
351
+ session_id: str
352
+ command: list[str] | None = None # None = default shell, or specific command
353
+ cols: int = 80
354
+ rows: int = 24
355
+ env: dict[str, str] | None = None # Additional environment variables
356
+
357
+
358
+ class StartTerminalResponse(msgspec.Struct, tag="start_terminal"):
359
+ """Response after starting terminal"""
360
+
361
+ session_id: str
362
+ success: bool
363
+ error_message: str | None = None
364
+
365
+
366
+ # Terminal input (user typing)
367
+ class TerminalInputRequest(msgspec.Struct, tag="terminal_input"):
368
+ """Send user input to terminal"""
369
+
370
+ session_id: str
371
+ data: str # Raw input data from user
372
+
373
+
374
+ class TerminalInputResponse(msgspec.Struct, tag="terminal_input"):
375
+ """Acknowledge input received"""
376
+
377
+ session_id: str
378
+ success: bool
379
+ error_message: str | None = None
380
+
381
+
382
+ # Terminal resize
383
+ class TerminalResizeRequest(msgspec.Struct, tag="terminal_resize"):
384
+ """Resize terminal dimensions"""
385
+
386
+ session_id: str
387
+ cols: int
388
+ rows: int
389
+
390
+
391
+ class TerminalResizeResponse(msgspec.Struct, tag="terminal_resize"):
392
+ """Acknowledge resize"""
393
+
394
+ session_id: str
395
+ success: bool
396
+ error_message: str | None = None
397
+
398
+
399
+ # Terminal stop
400
+ class StopTerminalRequest(msgspec.Struct, tag="stop_terminal"):
401
+ """Stop a terminal session"""
402
+
403
+ session_id: str
404
+
405
+
406
+ class StopTerminalResponse(msgspec.Struct, tag="stop_terminal"):
407
+ """Acknowledge terminal stopped"""
408
+
409
+ session_id: str
410
+ success: bool
411
+ error_message: str | None = None
412
+
413
+
414
+ class StreamingCodeExecutionRequest(msgspec.Struct, tag="streaming_code_execution"):
415
+ correlation_id: str
416
+ language: str # "python" or "shell"
417
+ content: str
418
+ timeout: int
419
+
420
+
298
421
  class CliRpcRequest(msgspec.Struct):
299
422
  request_id: str
300
423
  request: (
@@ -306,6 +429,11 @@ class CliRpcRequest(msgspec.Struct):
306
429
  | SwitchCLIChatRequest
307
430
  | KeepAliveCliChatRequest
308
431
  | GenerateUploadUrlRequest
432
+ | StartTerminalRequest
433
+ | TerminalInputRequest
434
+ | TerminalResizeRequest
435
+ | StopTerminalRequest
436
+ | StreamingCodeExecutionRequest
309
437
  )
310
438
 
311
439
 
@@ -317,6 +445,40 @@ class ErrorResponse(msgspec.Struct, tag="error"):
317
445
  error_message: str
318
446
 
319
447
 
448
+ class StreamingCodeExecutionResponseChunk(
449
+ msgspec.Struct, tag="streaming_code_execution_chunk"
450
+ ):
451
+ correlation_id: str
452
+ content: str
453
+ truncated: bool = False
454
+
455
+ def add(
456
+ self, new_chunk: "StreamingCodeExecutionResponseChunk"
457
+ ) -> "StreamingCodeExecutionResponseChunk":
458
+ """Aggregates content of this and a new chunk."""
459
+ assert self.correlation_id == new_chunk.correlation_id
460
+ return StreamingCodeExecutionResponseChunk(
461
+ correlation_id=self.correlation_id,
462
+ content=self.content + new_chunk.content,
463
+ truncated=self.truncated or new_chunk.truncated,
464
+ )
465
+
466
+
467
+ class StreamingCodeExecutionResponse(msgspec.Struct, tag="streaming_code_execution"):
468
+ correlation_id: str
469
+ content: str
470
+ truncated: bool = False
471
+ # Only present for shell code execution
472
+ cancelled_for_timeout: bool = False
473
+ exit_code: int | None = None
474
+ halted: bool = False
475
+
476
+
477
+ class StreamingErrorResponse(msgspec.Struct, tag="streaming_error"):
478
+ correlation_id: str
479
+ error_message: str
480
+
481
+
320
482
  class CliRpcResponse(msgspec.Struct):
321
483
  request_id: str
322
484
  response: (
@@ -324,9 +486,17 @@ class CliRpcResponse(msgspec.Struct):
324
486
  | GetAllFilesResponse
325
487
  | ErrorResponse
326
488
  | TerminateResponse
489
+ | TimeoutResponse
327
490
  | BatchToolExecutionResponse
328
491
  | HttpResponse
329
492
  | SwitchCLIChatResponse
330
493
  | KeepAliveCliChatResponse
331
494
  | GenerateUploadUrlResponse
495
+ | StartTerminalResponse
496
+ | TerminalInputResponse
497
+ | TerminalResizeResponse
498
+ | StopTerminalResponse
499
+ | StreamingCodeExecutionResponseChunk
500
+ | StreamingCodeExecutionResponse
501
+ | StreamingErrorResponse
332
502
  )