feat(agentik-cli): native-таргеты (linuxX64, macosX64/Arm64, mingwX64)
ci / JVM build + tests (push) Failing after 2m3s
ci / JVM build + tests (push) Failing after 2m3s
- Добавил нативные таргеты с реальной реализацией (не stub-ы):
- linuxX64 kexe ~5 МБ — собран, запускается, проходит
smoke против 192.168.76.166 (--help, info, conv-ls,
conv-new, send со стримом response-events, AGENTIK_SERVER
env-переменная).
- mingwX64 .exe ~6 МБ — собирается через кросс-компиляцию с Linux.
- macosX64 / macosArm64 — на Linux-хосте не линкуются (нужен
macOS-раннер, Apple Mach-O), но target-объявления + entryPoint
валидны.
- entryPoint на K/N — FQN без 'Kt': pw.binom.agentik.cli.main
(на JVM по-прежнему AgentikCliKt через mainClass.set).
- platformEnv: expect/actual split. Native actual — getenv()
из platform.posix через kotlinx.cinterop, помеченный
@OptIn(ExperimentalForeignApi::class).
- linuxArm64 у :agentik-cli отсутствует — kotlinx.cli 0.3.6 не
публикует klib для linuxArm64. У :client linuxArm64 сохранён
(асимметрия допустима: :client нужен только :agentik-cli,
который на linuxArm64 не работает).
- README обновлён: target matrix, env-vars, native entry-point,
платформенные детали.
This commit is contained in:
+65
-28
@@ -1,14 +1,26 @@
|
||||
# `:agentik-cli` — JVM CLI клиент к `/agentik`
|
||||
# `:agentik-cli` — one-shot CLI клиент к `/agentik`
|
||||
|
||||
## Что это
|
||||
|
||||
JVM-only **one-shot subcommand CLI** к серверу `:standalone` через
|
||||
`:client` по HTTP+SSE. Один вызов — одна команда: стримы ответа
|
||||
`send` идёт в stdout построчно, никакого embedded-REPL.
|
||||
**One-shot subcommand CLI** (Kotlin Multiplatform) к серверу
|
||||
`:standalone` через `:client` по HTTP+SSE. Один вызов — одна команда:
|
||||
стрим ответа `send` идёт в stdout построчно, никакого embedded-REPL.
|
||||
|
||||
Решает: быстрый способ дёрнуть агента из shell-скрипта или руками,
|
||||
не поднимая отдельную TUI-сессию.
|
||||
|
||||
## Платформы
|
||||
|
||||
| Платформа | Артефакт | Размер | Статус |
|
||||
|---|---|---|---|
|
||||
| `jvm` (JRE 21) | `*-all.jar` | ~7 МБ | ✓ собирается и работает |
|
||||
| `linuxX64` | `.kexe` | ~5 МБ | ✓ собирается и работает на этом хосте |
|
||||
| `macosX64` | `.kexe` | — | собирается на macOS-раннере |
|
||||
| `macosArm64` | `.kexe` | — | собирается на macOS-arm64-раннере |
|
||||
| `mingwX64` | `.exe` | ~6 МБ | ✓ собирается (cross-compile с Linux) |
|
||||
| `linuxArm64` | — | — | **нет** — kotlinx-cli 0.3.6 не публикует klib для linuxArm64 |
|
||||
| `iOS` | — | — | нет смысла на iOS |
|
||||
|
||||
## Подкоманды
|
||||
|
||||
```
|
||||
@@ -38,20 +50,31 @@ agentik-cli send --server http://localhost:8080/agentik conv-abc "привет"
|
||||
|
||||
## Как запустить
|
||||
|
||||
### Из готового fatjar
|
||||
|
||||
```bash
|
||||
java --enable-native-access=ALL-UNNAMED \
|
||||
-jar agentik-cli-0.1.0-SNAPSHOT-all.jar --help
|
||||
```
|
||||
|
||||
### Через Gradle
|
||||
### JVM (fatjar)
|
||||
|
||||
```bash
|
||||
./gradlew :agentik-cli:shadowJar
|
||||
java -jar agentik-cli/build/libs/agentik-cli-0.1.0-SNAPSHOT-all.jar info
|
||||
java --enable-native-access=ALL-UNNAMED \
|
||||
-jar agentik-cli/build/libs/agentik-cli-0.1.0-SNAPSHOT-all.jar --help
|
||||
```
|
||||
|
||||
### Native linuxX64
|
||||
|
||||
```bash
|
||||
./gradlew :agentik-cli:linkReleaseExecutableLinuxX64
|
||||
./agentik-cli/build/bin/linuxX64/releaseExecutable/agentik-cli.kexe --help
|
||||
```
|
||||
|
||||
### Native macOS / Windows
|
||||
|
||||
На Linux-хосте `macosX64`/`macosArm64` линкуются пустыми (нужен
|
||||
macOS-раннер, Apple Mach-O формат). `mingwX64` собирается через
|
||||
кросс-компиляцию.
|
||||
|
||||
CI-ноут: запускать `./gradlew :agentik-cli:linkReleaseExecutableMacosX64
|
||||
:agentik-cli:linkReleaseExecutableMacosArm64` на `macos-latest`
|
||||
раннере Gitea Actions.
|
||||
|
||||
## Примеры
|
||||
|
||||
```bash
|
||||
@@ -76,6 +99,9 @@ agentik-cli interrupt --server http://localhost:8080/agentik "$ID"
|
||||
|
||||
# Удалить
|
||||
agentik-cli conv-delete --server http://localhost:8080/agentik "$ID"
|
||||
|
||||
# Через env-переменную
|
||||
AGENTIK_SERVER=http://localhost:8080/agentik agentik-cli info
|
||||
```
|
||||
|
||||
## Формат вывода `send`
|
||||
@@ -94,28 +120,39 @@ event End
|
||||
Терминальные события (`End`, `Interrupted`, `Error`) тоже
|
||||
печатаются; CLI выходит сразу после `End`.
|
||||
|
||||
## Почему JVM-only (а не KMP)
|
||||
## Почему kotlinx.cli (а не clikt)
|
||||
|
||||
Аргумент-парсер: `kotlinx.cli` 0.3.6 (JetBrains, KMP) — единственный
|
||||
зрелый вариант, который линкуется нативно под `linuxX64`/`linuxArm64`
|
||||
без upstream-багов. Альтернативы:
|
||||
- **kotlinx.cli 0.3.6** (JetBrains, KMP) — единственный зрелый
|
||||
arg-parser, который стабильно линкуется под `linux_x64` +
|
||||
`macos_x64`/`macos_arm64` + `mingw_x64`. Минус: нет `linux_arm64`.
|
||||
- **clikt-multiplatform 5.x** (ajalt) — имеет linuxArm64, но
|
||||
ломается на native linker: `duplicate symbol selfAndAncestors`
|
||||
между `clikt` и `clikt-mordant` commonMain (issue
|
||||
[ajalt/clikt#598](https://github.com/ajalt/clikt/issues/598)).
|
||||
Workaround `kotlin.native.cacheKind.linuxX64=none` замедляет
|
||||
сборку на порядки и не решает проблему до конца. Поэтому clikt
|
||||
отвергнут.
|
||||
|
||||
- **clikt-multiplatform 5.x** имеет upstream-баг: `clikt` и
|
||||
`clikt-mordant` оба объявляют `selfAndAncestors` в commonMain, и
|
||||
Kotlin/Native linker падает на дубликате символа
|
||||
(issue [ajalt/clikt#598](https://github.com/ajalt/clikt/issues/598),
|
||||
workaround — `kotlin.native.cacheKind.linuxX64=none`, что замедляет
|
||||
сборку на порядки). Поэтому clikt отвергнут в пользу kotlinx.cli.
|
||||
- **picocli** — JVM-only.
|
||||
- **kotlinx-args** — мёртвый репозиторий.
|
||||
## Платформенные детали
|
||||
|
||||
Нативные бинари `:agentik-cli` отложены до стабилизации
|
||||
arg-parser-ситуации.
|
||||
- **entryPoint на K/N** — это FQN функции **без** суффикса `Kt`
|
||||
(т.е. `pw.binom.agentik.cli.main`, а не `MainKt.main`). JVM
|
||||
convention `MainKt.main` тут не работает — K/N линкер ищет
|
||||
функцию по `package.main`.
|
||||
- **`platformEnv(key)`** для чтения env-переменных:
|
||||
- JVM: `System.getenv(key)` через `jvmMain` actual.
|
||||
- Native: `getenv(key)` из `platform.posix` через
|
||||
`kotlinx.cinterop.toKString()` (`nativeMain` actual,
|
||||
требует `@OptIn(ExperimentalForeignApi::class)`).
|
||||
- **Stdout / exit code** — работают на K/N через корутины.
|
||||
|
||||
## Тесты
|
||||
|
||||
Тесты для подкоманд пока не написаны (TODO). Базовый smoke
|
||||
покрывается руками против живого сервера.
|
||||
|
||||
```bash
|
||||
./gradlew :agentik-cli:jvmTest
|
||||
./gradlew :agentik-cli:jvmTest # 0/0 — пока пусто
|
||||
```
|
||||
|
||||
## Версии
|
||||
|
||||
@@ -11,10 +11,23 @@ plugins {
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
|
||||
// Одноразовый CLI. Пока только JVM. Native-бинари добавим позже —
|
||||
// kotlinx.cli 0.3.6 не имеет linuxArm64, а clikt имеет upstream-баг
|
||||
// с duplicate symbol selfAndAncestors между clikt и clikt-mordant.
|
||||
// Native-таргеты, которые покрывает kotlinx.cli 0.3.6 (см. его .module
|
||||
// в Maven Central): linux_x64, macos_x64, macos_arm64, mingw_x64.
|
||||
// linuxArm64 не входит — kotlinx.cli 0.3.6 для него не публикуется
|
||||
// (последний релиз 2023-09, KMP-targets зафиксированы). clikt-multiplatform
|
||||
// 5.x имеет linuxArm64, но ломается на duplicate symbol `selfAndAncestors`
|
||||
// между clikt и clikt-mordant при линковке native (issue ajalt/clikt#598),
|
||||
// поэтому clikt отвергнут.
|
||||
//
|
||||
// iOS не входит: :agentik-cli бессмыслен на iOS, а :client (единственный
|
||||
// его потребитель) тоже без iOS.
|
||||
jvm()
|
||||
listOf(
|
||||
linuxX64(),
|
||||
macosX64(),
|
||||
macosArm64(),
|
||||
mingwX64(),
|
||||
)
|
||||
|
||||
sourceSets {
|
||||
commonMain.dependencies {
|
||||
@@ -29,8 +42,8 @@ kotlin {
|
||||
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
}
|
||||
// :agentik-cli — JVM-only one-shot CLI. Нет jvmMain / nativeMain.
|
||||
// Всё (включая platformEnv) лежит в commonMain.
|
||||
// :agentik-cli — commonMain-only (нет jvmMain/nativeMain разделения):
|
||||
// весь код, включая platformEnv, лежит в commonMain.
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalKotlinGradlePluginApi::class)
|
||||
@@ -41,6 +54,21 @@ kotlin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// entryPoint на K/N — это FQN функции БЕЗ суффикса `Kt`
|
||||
// (Java/Kotlin convention `MainKt.main` тут не работает, линкер K/N ищет
|
||||
// функцию как `package.main`). На JVM суффикс `Kt` сохраняется через
|
||||
// mainClass.set(...) выше.
|
||||
listOf(
|
||||
linuxX64(),
|
||||
macosX64(),
|
||||
macosArm64(),
|
||||
mingwX64(),
|
||||
).forEach {
|
||||
it.binaries.executable {
|
||||
entryPoint = "pw.binom.agentik.cli.main"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fatjar — аналог :standalone.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package pw.binom.agentik.cli
|
||||
|
||||
internal fun platformEnv(key: String): String? = System.getenv(key)
|
||||
internal expect fun platformEnv(key: String): String?
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package pw.binom.agentik.cli
|
||||
|
||||
internal actual fun platformEnv(key: String): String? = System.getenv(key)
|
||||
@@ -0,0 +1,8 @@
|
||||
package pw.binom.agentik.cli
|
||||
|
||||
import kotlinx.cinterop.ExperimentalForeignApi
|
||||
import kotlinx.cinterop.toKString
|
||||
import platform.posix.getenv
|
||||
|
||||
@OptIn(ExperimentalForeignApi::class)
|
||||
internal actual fun platformEnv(key: String): String? = getenv(key)?.toKString()
|
||||
Reference in New Issue
Block a user