From 58509092e22875e6dbb690185d94162f0221ffc1 Mon Sep 17 00:00:00 2001 From: "ninan.nn" Date: Thu, 21 May 2026 11:25:56 +0800 Subject: [PATCH 1/4] fix(sdk): use java duration for Kotlin command timeouts --- sdks/sandbox/kotlin/gradle.properties | 2 +- .../alibaba/opensandbox/sandbox/config/ConnectionConfig.kt | 2 +- .../domain/models/execd/executions/RunCommandRequest.kt | 2 +- .../domain/models/execd/executions/RunInSessionRequest.kt | 2 +- .../alibaba/opensandbox/sandbox/domain/services/Commands.kt | 2 +- .../infrastructure/adapters/converter/ExecutionConverter.kt | 2 +- .../infrastructure/adapters/service/CommandsAdapter.kt | 2 +- .../infrastructure/adapters/service/CommandsAdapterTest.kt | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sdks/sandbox/kotlin/gradle.properties b/sdks/sandbox/kotlin/gradle.properties index ad3c8fa62..688c7d3be 100644 --- a/sdks/sandbox/kotlin/gradle.properties +++ b/sdks/sandbox/kotlin/gradle.properties @@ -5,5 +5,5 @@ org.gradle.parallel=true # Project metadata project.group=com.alibaba.opensandbox -project.version=1.0.11 +project.version=1.0.12 project.description=A Kotlin SDK for Open Sandbox API diff --git a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/config/ConnectionConfig.kt b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/config/ConnectionConfig.kt index b9a7e6019..64888e54e 100644 --- a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/config/ConnectionConfig.kt +++ b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/config/ConnectionConfig.kt @@ -71,7 +71,7 @@ class ConnectionConfig private constructor( private const val ENV_API_KEY = "OPEN_SANDBOX_API_KEY" private const val ENV_DOMAIN = "OPEN_SANDBOX_DOMAIN" - private const val DEFAULT_USER_AGENT = "OpenSandbox-Kotlin-SDK/1.0.11" + private const val DEFAULT_USER_AGENT = "OpenSandbox-Kotlin-SDK/1.0.12" private const val API_VERSION = "v1" @JvmStatic diff --git a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequest.kt b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequest.kt index f080486a6..7a679e306 100644 --- a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequest.kt +++ b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequest.kt @@ -16,7 +16,7 @@ package com.alibaba.opensandbox.sandbox.domain.models.execd.executions -import kotlin.time.Duration +import java.time.Duration /** * Parameters for command execution. diff --git a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequest.kt b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequest.kt index fb7dd9ae4..860e258a1 100644 --- a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequest.kt +++ b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequest.kt @@ -16,7 +16,7 @@ package com.alibaba.opensandbox.sandbox.domain.models.execd.executions -import kotlin.time.Duration +import java.time.Duration /** * Request to run a command in an existing bash session. diff --git a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/services/Commands.kt b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/services/Commands.kt index fcf8af170..d0e32824d 100644 --- a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/services/Commands.kt +++ b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/services/Commands.kt @@ -21,7 +21,7 @@ import com.alibaba.opensandbox.sandbox.domain.models.execd.executions.CommandSta import com.alibaba.opensandbox.sandbox.domain.models.execd.executions.Execution import com.alibaba.opensandbox.sandbox.domain.models.execd.executions.RunCommandRequest import com.alibaba.opensandbox.sandbox.domain.models.execd.executions.RunInSessionRequest -import kotlin.time.Duration +import java.time.Duration /** * Command execution operations for sandbox environments. diff --git a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/converter/ExecutionConverter.kt b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/converter/ExecutionConverter.kt index 9e64d11ba..00fe67e40 100644 --- a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/converter/ExecutionConverter.kt +++ b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/converter/ExecutionConverter.kt @@ -27,7 +27,7 @@ object ExecutionConverter { command = command, background = background, cwd = workingDirectory, - timeout = timeout?.inWholeMilliseconds, + timeout = timeout?.toMillis(), uid = uid, gid = gid, envs = envs, diff --git a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapter.kt b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapter.kt index 76d2b249e..0cbb9db3d 100644 --- a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapter.kt +++ b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapter.kt @@ -196,7 +196,7 @@ internal class CommandsAdapter( RunInSessionRequestApi( command = request.command, cwd = request.workingDirectory, - timeout = request.timeout?.inWholeMilliseconds, + timeout = request.timeout?.toMillis(), ) val runUrl = execdBaseUrl diff --git a/sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapterTest.kt b/sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapterTest.kt index 0985dbe3b..a906bd77a 100644 --- a/sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapterTest.kt +++ b/sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapterTest.kt @@ -39,9 +39,9 @@ import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows +import java.time.Duration import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit -import kotlin.time.Duration.Companion.seconds class CommandsAdapterTest { // CommandsAdapter unit tests @@ -340,7 +340,7 @@ data: {"type":"execution_complete","execution_time":100,"timestamp":167253120100 RunInSessionRequest.builder() .command("echo Hello") .workingDirectory("/workspace") - .timeout(5.seconds) + .timeout(Duration.ofSeconds(5)) .handlers(handlers) .build(), ) From e07b9fde000d9a7341cdab7d12e3e3e0d97f05f4 Mon Sep 17 00:00:00 2001 From: "ninan.nn" Date: Thu, 21 May 2026 15:02:30 +0800 Subject: [PATCH 2/4] fix(sdk): add kotlin duration compatibility overloads --- sdks/AGENTS.md | 1 + .../execd/executions/RunCommandRequest.kt | 9 ++++ .../execd/executions/RunInSessionRequest.kt | 9 ++++ .../sandbox/domain/services/Commands.kt | 18 +++++++ .../execd/executions/RunCommandRequestTest.kt | 47 +++++++++++++++++++ .../executions/RunInSessionRequestTest.kt | 47 +++++++++++++++++++ 6 files changed, 131 insertions(+) create mode 100644 sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequestTest.kt create mode 100644 sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequestTest.kt diff --git a/sdks/AGENTS.md b/sdks/AGENTS.md index b0acafe30..b021a68cd 100644 --- a/sdks/AGENTS.md +++ b/sdks/AGENTS.md @@ -120,6 +120,7 @@ Always: - Keep package-local validation fast before widening to multi-language verification. - Match public behavior across languages unless a documented platform constraint prevents it. - Keep wire-format units and public SDK units separate. Public SDK interfaces should expose time durations as language-native duration types where available (`timedelta`, `Duration`) or otherwise as explicitly second-based fields such as `timeoutSeconds`. +- For Kotlin SDK public APIs intended for Java interoperability, do not expose Kotlin value classes such as `kotlin.time.Duration`; they are JVM-name-mangled and can be inaccessible from Java. Prefer `java.time.Duration` or explicit primitive wire units at the public boundary, with deprecated Kotlin-friendly overloads when needed for migration. Ask first: diff --git a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequest.kt b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequest.kt index 7a679e306..0ecf2bec5 100644 --- a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequest.kt +++ b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequest.kt @@ -17,6 +17,7 @@ package com.alibaba.opensandbox.sandbox.domain.models.execd.executions import java.time.Duration +import kotlin.time.toJavaDuration /** * Parameters for command execution. @@ -80,6 +81,14 @@ class RunCommandRequest private constructor( return this } + @Deprecated( + message = "Use java.time.Duration instead.", + replaceWith = ReplaceWith("timeout(timeout?.toJavaDuration())", "kotlin.time.toJavaDuration"), + ) + fun timeout(timeout: kotlin.time.Duration?): Builder { + return timeout(timeout?.toJavaDuration()) + } + fun uid(uid: Int?): Builder { require(uid == null || uid >= 0) { "Uid must be >= 0" } this.uid = uid diff --git a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequest.kt b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequest.kt index 860e258a1..25f0e9575 100644 --- a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequest.kt +++ b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequest.kt @@ -17,6 +17,7 @@ package com.alibaba.opensandbox.sandbox.domain.models.execd.executions import java.time.Duration +import kotlin.time.toJavaDuration /** * Request to run a command in an existing bash session. @@ -59,6 +60,14 @@ class RunInSessionRequest private constructor( return this } + @Deprecated( + message = "Use java.time.Duration instead.", + replaceWith = ReplaceWith("timeout(timeout?.toJavaDuration())", "kotlin.time.toJavaDuration"), + ) + fun timeout(timeout: kotlin.time.Duration?): Builder { + return timeout(timeout?.toJavaDuration()) + } + fun handlers(handlers: ExecutionHandlers?): Builder { this.handlers = handlers return this diff --git a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/services/Commands.kt b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/services/Commands.kt index d0e32824d..65a3c20b0 100644 --- a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/services/Commands.kt +++ b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/services/Commands.kt @@ -22,6 +22,7 @@ import com.alibaba.opensandbox.sandbox.domain.models.execd.executions.Execution import com.alibaba.opensandbox.sandbox.domain.models.execd.executions.RunCommandRequest import com.alibaba.opensandbox.sandbox.domain.models.execd.executions.RunInSessionRequest import java.time.Duration +import kotlin.time.toJavaDuration /** * Command execution operations for sandbox environments. @@ -125,6 +126,23 @@ interface Commands { ) } + @Deprecated( + message = "Use java.time.Duration instead.", + replaceWith = + ReplaceWith( + "runInSession(sessionId, command, workingDirectory, timeout.toJavaDuration())", + "kotlin.time.toJavaDuration", + ), + ) + fun runInSession( + sessionId: String, + command: String, + workingDirectory: String? = null, + timeout: kotlin.time.Duration, + ): Execution { + return runInSession(sessionId, command, workingDirectory, timeout.toJavaDuration()) + } + /** * Deletes a bash session and releases resources. * diff --git a/sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequestTest.kt b/sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequestTest.kt new file mode 100644 index 000000000..c3bfcce05 --- /dev/null +++ b/sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequestTest.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2025 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.opensandbox.sandbox.domain.models.execd.executions + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import java.time.Duration +import kotlin.time.Duration.Companion.seconds + +class RunCommandRequestTest { + @Test + fun `builder accepts java duration for timeout`() { + val request = + RunCommandRequest.builder() + .command("echo hi") + .timeout(Duration.ofSeconds(5)) + .build() + + assertEquals(Duration.ofSeconds(5), request.timeout) + } + + @Suppress("DEPRECATION") + @Test + fun `builder accepts deprecated kotlin duration for timeout`() { + val request = + RunCommandRequest.builder() + .command("echo hi") + .timeout(5.seconds) + .build() + + assertEquals(Duration.ofSeconds(5), request.timeout) + } +} diff --git a/sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequestTest.kt b/sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequestTest.kt new file mode 100644 index 000000000..6dd02a907 --- /dev/null +++ b/sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequestTest.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2025 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.opensandbox.sandbox.domain.models.execd.executions + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import java.time.Duration +import kotlin.time.Duration.Companion.seconds + +class RunInSessionRequestTest { + @Test + fun `builder accepts java duration for timeout`() { + val request = + RunInSessionRequest.builder() + .command("echo hi") + .timeout(Duration.ofSeconds(5)) + .build() + + assertEquals(Duration.ofSeconds(5), request.timeout) + } + + @Suppress("DEPRECATION") + @Test + fun `builder accepts deprecated kotlin duration for timeout`() { + val request = + RunInSessionRequest.builder() + .command("echo hi") + .timeout(5.seconds) + .build() + + assertEquals(Duration.ofSeconds(5), request.timeout) + } +} From 371417055129df58e507ba5b8a948cea87556ef8 Mon Sep 17 00:00:00 2001 From: "ninan.nn" Date: Thu, 21 May 2026 15:19:17 +0800 Subject: [PATCH 3/4] fix(sdk): make command timeout setters non-null --- .../models/execd/executions/RunCommandRequest.kt | 8 ++++---- .../models/execd/executions/RunInSessionRequest.kt | 8 ++++---- .../opensandbox/sandbox/domain/services/Commands.kt | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequest.kt b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequest.kt index 0ecf2bec5..18b9fbdfd 100644 --- a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequest.kt +++ b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunCommandRequest.kt @@ -76,17 +76,17 @@ class RunCommandRequest private constructor( * Maximum execution time; server will terminate the command when reached. * If omitted, the server will not enforce any timeout. */ - fun timeout(timeout: Duration?): Builder { + fun timeout(timeout: Duration): Builder { this.timeout = timeout return this } @Deprecated( message = "Use java.time.Duration instead.", - replaceWith = ReplaceWith("timeout(timeout?.toJavaDuration())", "kotlin.time.toJavaDuration"), + replaceWith = ReplaceWith("timeout(timeout.toJavaDuration())", "kotlin.time.toJavaDuration"), ) - fun timeout(timeout: kotlin.time.Duration?): Builder { - return timeout(timeout?.toJavaDuration()) + fun timeout(timeout: kotlin.time.Duration): Builder { + return timeout(timeout.toJavaDuration()) } fun uid(uid: Int?): Builder { diff --git a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequest.kt b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequest.kt index 25f0e9575..699a0e698 100644 --- a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequest.kt +++ b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/models/execd/executions/RunInSessionRequest.kt @@ -55,17 +55,17 @@ class RunInSessionRequest private constructor( return this } - fun timeout(timeout: Duration?): Builder { + fun timeout(timeout: Duration): Builder { this.timeout = timeout return this } @Deprecated( message = "Use java.time.Duration instead.", - replaceWith = ReplaceWith("timeout(timeout?.toJavaDuration())", "kotlin.time.toJavaDuration"), + replaceWith = ReplaceWith("timeout(timeout.toJavaDuration())", "kotlin.time.toJavaDuration"), ) - fun timeout(timeout: kotlin.time.Duration?): Builder { - return timeout(timeout?.toJavaDuration()) + fun timeout(timeout: kotlin.time.Duration): Builder { + return timeout(timeout.toJavaDuration()) } fun handlers(handlers: ExecutionHandlers?): Builder { diff --git a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/services/Commands.kt b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/services/Commands.kt index 65a3c20b0..db9b8940e 100644 --- a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/services/Commands.kt +++ b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/domain/services/Commands.kt @@ -116,14 +116,14 @@ interface Commands { workingDirectory: String? = null, timeout: Duration? = null, ): Execution { - return runInSession( - sessionId, + val builder = RunInSessionRequest.builder() .command(command) .workingDirectory(workingDirectory) - .timeout(timeout) - .build(), - ) + if (timeout != null) { + builder.timeout(timeout) + } + return runInSession(sessionId, builder.build()) } @Deprecated( From f8d2572041dd25997c5143bf1e9d466cda108198 Mon Sep 17 00:00:00 2001 From: "ninan.nn" Date: Thu, 21 May 2026 15:24:54 +0800 Subject: [PATCH 4/4] fix(sdk): reject oversized command timeouts --- .../adapters/converter/ExecutionConverter.kt | 12 +++++++++++- .../adapters/service/CommandsAdapter.kt | 3 ++- .../adapters/service/CommandsAdapterTest.kt | 10 ++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/converter/ExecutionConverter.kt b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/converter/ExecutionConverter.kt index 00fe67e40..e07126ba3 100644 --- a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/converter/ExecutionConverter.kt +++ b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/converter/ExecutionConverter.kt @@ -18,6 +18,7 @@ package com.alibaba.opensandbox.sandbox.infrastructure.adapters.converter import com.alibaba.opensandbox.sandbox.domain.models.execd.executions.CommandStatus import com.alibaba.opensandbox.sandbox.domain.models.execd.executions.RunCommandRequest +import java.time.Duration import com.alibaba.opensandbox.sandbox.api.models.execd.CommandStatusResponse as ApiCommandStatusResponse import com.alibaba.opensandbox.sandbox.api.models.execd.RunCommandRequest as ApiRunCommandRequest @@ -27,7 +28,7 @@ object ExecutionConverter { command = command, background = background, cwd = workingDirectory, - timeout = timeout?.toMillis(), + timeout = timeout?.toCommandTimeoutMillis(), uid = uid, gid = gid, envs = envs, @@ -46,3 +47,12 @@ object ExecutionConverter { ) } } + +internal fun Duration.toCommandTimeoutMillis(): Long { + require(!isNegative) { "Timeout must be non-negative, got: $this" } + return try { + toMillis() + } catch (e: ArithmeticException) { + throw IllegalArgumentException("Timeout is too large to represent in milliseconds: $this", e) + } +} diff --git a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapter.kt b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapter.kt index 0cbb9db3d..3eb201537 100644 --- a/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapter.kt +++ b/sdks/sandbox/kotlin/sandbox/src/main/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapter.kt @@ -42,6 +42,7 @@ import com.alibaba.opensandbox.sandbox.infrastructure.adapters.converter.Executi import com.alibaba.opensandbox.sandbox.infrastructure.adapters.converter.ExecutionEventDispatcher import com.alibaba.opensandbox.sandbox.infrastructure.adapters.converter.jsonParser import com.alibaba.opensandbox.sandbox.infrastructure.adapters.converter.parseSandboxError +import com.alibaba.opensandbox.sandbox.infrastructure.adapters.converter.toCommandTimeoutMillis import com.alibaba.opensandbox.sandbox.infrastructure.adapters.converter.toSandboxException import okhttp3.Headers.Companion.toHeaders import okhttp3.HttpUrl.Companion.toHttpUrlOrNull @@ -196,7 +197,7 @@ internal class CommandsAdapter( RunInSessionRequestApi( command = request.command, cwd = request.workingDirectory, - timeout = request.timeout?.toMillis(), + timeout = request.timeout?.toCommandTimeoutMillis(), ) val runUrl = execdBaseUrl diff --git a/sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapterTest.kt b/sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapterTest.kt index a906bd77a..87cca06a8 100644 --- a/sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapterTest.kt +++ b/sdks/sandbox/kotlin/sandbox/src/test/kotlin/com/alibaba/opensandbox/sandbox/infrastructure/adapters/service/CommandsAdapterTest.kt @@ -25,6 +25,7 @@ import com.alibaba.opensandbox.sandbox.domain.models.execd.executions.ExecutionH import com.alibaba.opensandbox.sandbox.domain.models.execd.executions.RunCommandRequest import com.alibaba.opensandbox.sandbox.domain.models.execd.executions.RunInSessionRequest import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.SandboxEndpoint +import com.alibaba.opensandbox.sandbox.infrastructure.adapters.converter.toCommandTimeoutMillis import kotlinx.serialization.json.Json import kotlinx.serialization.json.booleanOrNull import kotlinx.serialization.json.intOrNull @@ -359,6 +360,15 @@ data: {"type":"execution_complete","execution_time":100,"timestamp":167253120100 assertEquals(5000L, requestBodyJson["timeout"]?.jsonPrimitive?.content?.toLong()) } + @Test + fun `command timeout conversion should reject durations too large for milliseconds`() { + val exception = + assertThrows(IllegalArgumentException::class.java) { + Duration.ofSeconds(Long.MAX_VALUE).toCommandTimeoutMillis() + } + assertTrue(exception.message!!.contains("too large to represent in milliseconds")) + } + @Test fun `runInSession should infer non-zero exit code from command error event`() { val initEvent = """data: {"type":"init","text":"cmd-123","timestamp":1672531200000}"""