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:
@@ -80,7 +80,7 @@ curl http://localhost:8080/health
|
|||||||
|
|
||||||
- Запускаемые:
|
- Запускаемые:
|
||||||
- [`:standalone`](standalone/README.md) — single-jar HTTP-сервер.
|
- [`:standalone`](standalone/README.md) — single-jar HTTP-сервер.
|
||||||
- [`:agentik-cli`](agentik-cli/README.md) — one-shot CLI-клиент (kotlinx.cli).
|
- [`:agentik-cli`](agentik-cli/README.md) — one-shot CLI-клиент (kotlinx.cli), JVM + 4 native.
|
||||||
- Библиотеки (контракты и реализации):
|
- Библиотеки (контракты и реализации):
|
||||||
- [`:proto`](proto/README.md) — stateful KMP-протокол.
|
- [`:proto`](proto/README.md) — stateful KMP-протокол.
|
||||||
- [`:server`](server/README.md) — HTTP/SSE фасад `:proto`.
|
- [`:server`](server/README.md) — HTTP/SSE фасад `:proto`.
|
||||||
|
|||||||
+65
-28
@@ -1,14 +1,26 @@
|
|||||||
# `:agentik-cli` — JVM CLI клиент к `/agentik`
|
# `:agentik-cli` — one-shot CLI клиент к `/agentik`
|
||||||
|
|
||||||
## Что это
|
## Что это
|
||||||
|
|
||||||
JVM-only **one-shot subcommand CLI** к серверу `:standalone` через
|
**One-shot subcommand CLI** (Kotlin Multiplatform) к серверу
|
||||||
`:client` по HTTP+SSE. Один вызов — одна команда: стримы ответа
|
`:standalone` через `:client` по HTTP+SSE. Один вызов — одна команда:
|
||||||
`send` идёт в stdout построчно, никакого embedded-REPL.
|
стрим ответа `send` идёт в stdout построчно, никакого embedded-REPL.
|
||||||
|
|
||||||
Решает: быстрый способ дёрнуть агента из shell-скрипта или руками,
|
Решает: быстрый способ дёрнуть агента из shell-скрипта или руками,
|
||||||
не поднимая отдельную TUI-сессию.
|
не поднимая отдельную 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
|
### JVM (fatjar)
|
||||||
|
|
||||||
```bash
|
|
||||||
java --enable-native-access=ALL-UNNAMED \
|
|
||||||
-jar agentik-cli-0.1.0-SNAPSHOT-all.jar --help
|
|
||||||
```
|
|
||||||
|
|
||||||
### Через Gradle
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./gradlew :agentik-cli:shadowJar
|
./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
|
```bash
|
||||||
@@ -76,6 +99,9 @@ agentik-cli interrupt --server http://localhost:8080/agentik "$ID"
|
|||||||
|
|
||||||
# Удалить
|
# Удалить
|
||||||
agentik-cli conv-delete --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`
|
## Формат вывода `send`
|
||||||
@@ -94,28 +120,39 @@ event End
|
|||||||
Терминальные события (`End`, `Interrupted`, `Error`) тоже
|
Терминальные события (`End`, `Interrupted`, `Error`) тоже
|
||||||
печатаются; CLI выходит сразу после `End`.
|
печатаются; CLI выходит сразу после `End`.
|
||||||
|
|
||||||
## Почему JVM-only (а не KMP)
|
## Почему kotlinx.cli (а не clikt)
|
||||||
|
|
||||||
Аргумент-парсер: `kotlinx.cli` 0.3.6 (JetBrains, KMP) — единственный
|
- **kotlinx.cli 0.3.6** (JetBrains, KMP) — единственный зрелый
|
||||||
зрелый вариант, который линкуется нативно под `linuxX64`/`linuxArm64`
|
arg-parser, который стабильно линкуется под `linux_x64` +
|
||||||
без upstream-багов. Альтернативы:
|
`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` отложены до стабилизации
|
- **entryPoint на K/N** — это FQN функции **без** суффикса `Kt`
|
||||||
arg-parser-ситуации.
|
(т.е. `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
|
```bash
|
||||||
./gradlew :agentik-cli:jvmTest
|
./gradlew :agentik-cli:jvmTest # 0/0 — пока пусто
|
||||||
```
|
```
|
||||||
|
|
||||||
## Версии
|
## Версии
|
||||||
|
|||||||
@@ -11,10 +11,23 @@ plugins {
|
|||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(21)
|
jvmToolchain(21)
|
||||||
|
|
||||||
// Одноразовый CLI. Пока только JVM. Native-бинари добавим позже —
|
// Native-таргеты, которые покрывает kotlinx.cli 0.3.6 (см. его .module
|
||||||
// kotlinx.cli 0.3.6 не имеет linuxArm64, а clikt имеет upstream-баг
|
// в Maven Central): linux_x64, macos_x64, macos_arm64, mingw_x64.
|
||||||
// с duplicate symbol selfAndAncestors между clikt и clikt-mordant.
|
// 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()
|
jvm()
|
||||||
|
listOf(
|
||||||
|
linuxX64(),
|
||||||
|
macosX64(),
|
||||||
|
macosArm64(),
|
||||||
|
mingwX64(),
|
||||||
|
)
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain.dependencies {
|
commonMain.dependencies {
|
||||||
@@ -29,8 +42,8 @@ kotlin {
|
|||||||
|
|
||||||
implementation(libs.kotlinx.coroutines.core)
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
}
|
}
|
||||||
// :agentik-cli — JVM-only one-shot CLI. Нет jvmMain / nativeMain.
|
// :agentik-cli — commonMain-only (нет jvmMain/nativeMain разделения):
|
||||||
// Всё (включая platformEnv) лежит в commonMain.
|
// весь код, включая platformEnv, лежит в commonMain.
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalKotlinGradlePluginApi::class)
|
@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.
|
// Fatjar — аналог :standalone.
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package pw.binom.agentik.cli
|
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