kiwi-code 0.0.4__tar.gz → 0.0.6__tar.gz
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.
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/.gitignore +1 -1
- kiwi_code-0.0.6/CLAUDE.md +79 -0
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/Makefile +6 -1
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/PKG-INFO +7 -7
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/README.md +4 -4
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/pyproject.toml +5 -5
- kiwi_code-0.0.6/src/kiwi_cli/__init__.py +3 -0
- {kiwi_code-0.0.4/src/kiwi_tui → kiwi_code-0.0.6/src/kiwi_cli}/auth.py +2 -2
- {kiwi_code-0.0.4/src/kiwi_tui → kiwi_code-0.0.6/src/kiwi_cli}/cli.py +1 -1
- {kiwi_code-0.0.4/src/kiwi_tui → kiwi_code-0.0.6/src/kiwi_cli}/client.py +80 -9
- {kiwi_code-0.0.4/src/kiwi_tui → kiwi_code-0.0.6/src/kiwi_cli}/commands.py +20 -0
- {kiwi_code-0.0.4/src/kiwi_tui → kiwi_code-0.0.6/src/kiwi_cli}/config.py +2 -2
- {kiwi_code-0.0.4/src/kiwi_tui → kiwi_code-0.0.6/src/kiwi_cli}/logger.py +1 -1
- {kiwi_code-0.0.4/src/kiwi_tui → kiwi_code-0.0.6/src/kiwi_cli}/runtime_manager.py +48 -2
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/src/kiwi_runtime/main.py +204 -22
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/src/kiwi_tui/main.py +116 -11
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/src/kiwi_tui/screens/actions.py +2 -2
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/src/kiwi_tui/screens/autobots.py +2 -2
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/src/kiwi_tui/screens/dashboard.py +531 -11
- kiwi_code-0.0.6/src/kiwi_tui/screens/file_browser.py +182 -0
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/src/kiwi_tui/screens/login.py +3 -2
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/uv.lock +567 -568
- kiwi_code-0.0.4/poetry.lock +0 -1570
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/.github/workflows/publish.yml +0 -0
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/.python-version +0 -0
- {kiwi_code-0.0.4/src/kiwi_tui → kiwi_code-0.0.6/src/kiwi_cli}/models.py +0 -0
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/src/kiwi_runtime/__init__.py +0 -0
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/src/kiwi_runtime/__main__.py +0 -0
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/src/kiwi_tui/__init__.py +0 -0
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/src/kiwi_tui/screens/__init__.py +0 -0
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/src/kiwi_tui/screens/runtime_logs.py +0 -0
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/src/kiwi_tui/widgets.py +0 -0
- {kiwi_code-0.0.4 → kiwi_code-0.0.6}/test_hello.py +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
Kiwi Code is a TUI + CLI client for the Kiwi AI platform. It provides a chat interface (Textual) and CLI tools (Typer) for managing AI actions, action graphs, and their runs. A WebSocket-based runtime agent executes shell commands on behalf of the LLM.
|
|
8
|
+
|
|
9
|
+
## Build & Run Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Package manager: uv | Build backend: hatchling | Python: 3.13+
|
|
13
|
+
uv sync # Install/sync dependencies
|
|
14
|
+
uv build # Build package
|
|
15
|
+
|
|
16
|
+
# Run
|
|
17
|
+
make run # Launch TUI (kiwi)
|
|
18
|
+
make dev # TUI with live reload
|
|
19
|
+
make serve # Serve TUI in browser (port 8566)
|
|
20
|
+
make cli # Run CLI (kiwicli)
|
|
21
|
+
make runtime ARGS="..." # Run runtime agent with args
|
|
22
|
+
|
|
23
|
+
# Test
|
|
24
|
+
make test-hello # Test backend API connection
|
|
25
|
+
uv run python test_hello.py # Same, without make
|
|
26
|
+
|
|
27
|
+
# Clean
|
|
28
|
+
make clean # Remove caches and build artifacts
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
There are three entry points defined in pyproject.toml:
|
|
32
|
+
- `kiwi` → `kiwi_tui.main:main` (TUI)
|
|
33
|
+
- `kiwicli` → `kiwi_cli.cli:cli` (CLI)
|
|
34
|
+
- `kiwi-runtime` → `kiwi_runtime.main:main` (WebSocket agent)
|
|
35
|
+
|
|
36
|
+
## Architecture
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
┌─────────────────────────────────┐
|
|
40
|
+
│ Shared Libraries │
|
|
41
|
+
│ commands.py · client.py │
|
|
42
|
+
│ auth.py · models.py · config.py│
|
|
43
|
+
└──────────┬──────────┬───────────┘
|
|
44
|
+
│ │
|
|
45
|
+
┌──────▼──────┐ └──────────────┐
|
|
46
|
+
│ kiwi (TUI) │ kiwicli (CLI) │
|
|
47
|
+
│ Textual app │ Typer CLI │
|
|
48
|
+
└──────┬──────┘ └──────────────┘
|
|
49
|
+
│ auto-starts
|
|
50
|
+
┌──────▼──────────────────┐
|
|
51
|
+
│ kiwi-runtime │
|
|
52
|
+
│ WebSocket agent │
|
|
53
|
+
│ One per working dir │
|
|
54
|
+
│ Survives TUI restarts │
|
|
55
|
+
└─────────────────────────┘
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Key design decisions:**
|
|
59
|
+
- Shared infrastructure lives in `kiwi_cli`; TUI imports from it via absolute imports (e.g., `from kiwi_cli.auth import TokenManager`)
|
|
60
|
+
- One runtime agent per working directory, managed via `runtime_manager.py`
|
|
61
|
+
- Runtime state stored in `~/.kiwi/runtimes/<key>/` (pid, log, cwd, tokens)
|
|
62
|
+
- Auth tokens stored in `~/.kiwi/tokens.json` with 0600 permissions
|
|
63
|
+
- Config stored in `~/.kiwi/config.json`
|
|
64
|
+
|
|
65
|
+
## Three Source Packages
|
|
66
|
+
|
|
67
|
+
- **`src/kiwi_cli/`** — Typer CLI app and shared infrastructure: auth, client, config, commands, models, runtime_manager, logger. Both `kiwi_tui` and the CLI entry point depend on this package.
|
|
68
|
+
- **`src/kiwi_tui/`** — Textual TUI app, screens, and widgets. Imports shared modules from `kiwi_cli`.
|
|
69
|
+
- **`src/kiwi_runtime/`** — Standalone WebSocket agent (~43KB `main.py`) that connects to Kiwi server and executes shell commands, sandboxed to CWD by default
|
|
70
|
+
|
|
71
|
+
## Server Presets (runtime)
|
|
72
|
+
|
|
73
|
+
- `app` → `https://api.meetkiwi.ai` (production)
|
|
74
|
+
- `dev` → `https://dev.api.myautobots.com` (default for TUI)
|
|
75
|
+
- `local` → `http://localhost:8000`
|
|
76
|
+
|
|
77
|
+
## CI/CD
|
|
78
|
+
|
|
79
|
+
GitHub Actions (`publish.yml`): triggered by manual dispatch or `v*.*.*` tags. Runs smoke tests (`kiwicli --help`, `kiwi-runtime --help`, `kiwicli runtime status`), builds, and publishes to PyPI.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.PHONY: run dev cli runtime test-hello install sync clean help
|
|
1
|
+
.PHONY: run dev serve cli runtime test-hello install sync clean help
|
|
2
2
|
|
|
3
3
|
# Run the Kiwi TUI application
|
|
4
4
|
run:
|
|
@@ -8,6 +8,10 @@ run:
|
|
|
8
8
|
dev:
|
|
9
9
|
uv run --dev kiwi
|
|
10
10
|
|
|
11
|
+
# Serve the Kiwi TUI in the browser via textual serve (e.g. make serve PORT=8566)
|
|
12
|
+
serve:
|
|
13
|
+
uv run textual serve --port $(or $(PORT),8566) -c "python -m kiwi_tui.main"
|
|
14
|
+
|
|
11
15
|
# Run the Kiwi CLI
|
|
12
16
|
cli:
|
|
13
17
|
uv run kiwicli
|
|
@@ -53,6 +57,7 @@ help:
|
|
|
53
57
|
@echo "Commands:"
|
|
54
58
|
@echo " run - Run the Kiwi TUI application (kiwi)"
|
|
55
59
|
@echo " dev - Run TUI in development mode (with live reload)"
|
|
60
|
+
@echo " serve - Serve the TUI in the browser (PORT=8566 make serve)"
|
|
56
61
|
@echo " cli - Run the Kiwi CLI (kiwicli)"
|
|
57
62
|
@echo " runtime - Run the Kiwi Runtime agent (kiwi-runtime)"
|
|
58
63
|
@echo " e.g. make runtime ARGS=\"connect --server app\""
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kiwi-code
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.6
|
|
4
4
|
Summary: A textual-based terminal user interface application
|
|
5
5
|
Project-URL: Homepage, https://meetkiwi.ai
|
|
6
6
|
Project-URL: Repository, https://github.com/jetoslabs/kiwi-code
|
|
7
7
|
Author-email: Anurag Jha <anurag@meetkiwi.co>
|
|
8
|
-
License:
|
|
8
|
+
License: Proprietary
|
|
9
9
|
Keywords: cli,terminal,textual,tui
|
|
10
10
|
Classifier: Development Status :: 3 - Alpha
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License ::
|
|
12
|
+
Classifier: License :: Other/Proprietary License
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
15
|
Requires-Python: <4.0,>=3.13
|
|
@@ -77,7 +77,7 @@ Kiwi Code has three entry points, but they are **not three services** — they a
|
|
|
77
77
|
- **`kiwi` automatically spawns `kiwi-runtime`** when you log in. The runtime runs as an independent process — quitting or restarting the TUI does **not** kill it.
|
|
78
78
|
- **One runtime per directory.** Each working directory gets its own runtime, scoped to that directory. Opening the TUI in two different project folders runs two independent runtimes.
|
|
79
79
|
- **`kiwicli` is for headless/scripting use.** It provides the same query commands (actions, runs, graphs) as the TUI, but as standalone CLI commands.
|
|
80
|
-
- **Authentication is shared.** Both use `TokenManager` to read/write tokens from `~/.
|
|
80
|
+
- **Authentication is shared.** Both use `TokenManager` to read/write tokens from `~/.kiwi/tokens.json`, so logging in via one works for the other.
|
|
81
81
|
- **Runtime lifecycle is independent.** The runtime persists across TUI restarts. Use `kiwicli runtime stop` to explicitly stop it.
|
|
82
82
|
|
|
83
83
|
## Installation
|
|
@@ -207,7 +207,7 @@ In **restricted mode** (default), commands are sandboxed to the current working
|
|
|
207
207
|
|
|
208
208
|
## Configuration
|
|
209
209
|
|
|
210
|
-
Stored at `~/.
|
|
210
|
+
Stored at `~/.kiwi/config.json`:
|
|
211
211
|
|
|
212
212
|
```json
|
|
213
213
|
{
|
|
@@ -218,7 +218,7 @@ Stored at `~/.autobots-tui/config.json`:
|
|
|
218
218
|
}
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
-
Logs are written to `~/.
|
|
221
|
+
Logs are written to `~/.kiwi/kiwi_tui.log`.
|
|
222
222
|
|
|
223
223
|
## Development
|
|
224
224
|
|
|
@@ -231,4 +231,4 @@ uv run kiwi
|
|
|
231
231
|
|
|
232
232
|
## License
|
|
233
233
|
|
|
234
|
-
|
|
234
|
+
Proprietary. All rights reserved.
|
|
@@ -51,7 +51,7 @@ Kiwi Code has three entry points, but they are **not three services** — they a
|
|
|
51
51
|
- **`kiwi` automatically spawns `kiwi-runtime`** when you log in. The runtime runs as an independent process — quitting or restarting the TUI does **not** kill it.
|
|
52
52
|
- **One runtime per directory.** Each working directory gets its own runtime, scoped to that directory. Opening the TUI in two different project folders runs two independent runtimes.
|
|
53
53
|
- **`kiwicli` is for headless/scripting use.** It provides the same query commands (actions, runs, graphs) as the TUI, but as standalone CLI commands.
|
|
54
|
-
- **Authentication is shared.** Both use `TokenManager` to read/write tokens from `~/.
|
|
54
|
+
- **Authentication is shared.** Both use `TokenManager` to read/write tokens from `~/.kiwi/tokens.json`, so logging in via one works for the other.
|
|
55
55
|
- **Runtime lifecycle is independent.** The runtime persists across TUI restarts. Use `kiwicli runtime stop` to explicitly stop it.
|
|
56
56
|
|
|
57
57
|
## Installation
|
|
@@ -181,7 +181,7 @@ In **restricted mode** (default), commands are sandboxed to the current working
|
|
|
181
181
|
|
|
182
182
|
## Configuration
|
|
183
183
|
|
|
184
|
-
Stored at `~/.
|
|
184
|
+
Stored at `~/.kiwi/config.json`:
|
|
185
185
|
|
|
186
186
|
```json
|
|
187
187
|
{
|
|
@@ -192,7 +192,7 @@ Stored at `~/.autobots-tui/config.json`:
|
|
|
192
192
|
}
|
|
193
193
|
```
|
|
194
194
|
|
|
195
|
-
Logs are written to `~/.
|
|
195
|
+
Logs are written to `~/.kiwi/kiwi_tui.log`.
|
|
196
196
|
|
|
197
197
|
## Development
|
|
198
198
|
|
|
@@ -205,4 +205,4 @@ uv run kiwi
|
|
|
205
205
|
|
|
206
206
|
## License
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
Proprietary. All rights reserved.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "kiwi-code"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.6"
|
|
4
4
|
description = "A textual-based terminal user interface application"
|
|
5
5
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
6
6
|
requires-python = ">=3.13,<4.0"
|
|
@@ -18,12 +18,12 @@ dependencies = [
|
|
|
18
18
|
authors = [
|
|
19
19
|
{ name = "Anurag Jha", email = "anurag@meetkiwi.co" }
|
|
20
20
|
]
|
|
21
|
-
license = { text = "
|
|
21
|
+
license = { text = "Proprietary" }
|
|
22
22
|
keywords = ["tui", "textual", "terminal", "cli"]
|
|
23
23
|
classifiers = [
|
|
24
24
|
"Development Status :: 3 - Alpha",
|
|
25
25
|
"Intended Audience :: Developers",
|
|
26
|
-
"License ::
|
|
26
|
+
"License :: Other/Proprietary License",
|
|
27
27
|
"Programming Language :: Python :: 3",
|
|
28
28
|
"Programming Language :: Python :: 3.13",
|
|
29
29
|
]
|
|
@@ -34,11 +34,11 @@ Repository = "https://github.com/jetoslabs/kiwi-code"
|
|
|
34
34
|
|
|
35
35
|
[project.scripts]
|
|
36
36
|
kiwi = "kiwi_tui.main:main"
|
|
37
|
-
kiwicli = "
|
|
37
|
+
kiwicli = "kiwi_cli.cli:cli"
|
|
38
38
|
kiwi-runtime = "kiwi_runtime.main:main"
|
|
39
39
|
|
|
40
40
|
[tool.hatch.build.targets.wheel]
|
|
41
|
-
packages = ["src/kiwi_tui", "src/kiwi_runtime"]
|
|
41
|
+
packages = ["src/kiwi_cli", "src/kiwi_tui", "src/kiwi_runtime"]
|
|
42
42
|
|
|
43
43
|
[build-system]
|
|
44
44
|
requires = ["hatchling"]
|
|
@@ -15,10 +15,10 @@ class TokenManager:
|
|
|
15
15
|
"""Initialize token manager.
|
|
16
16
|
|
|
17
17
|
Args:
|
|
18
|
-
token_path: Path to token file, defaults to ~/.
|
|
18
|
+
token_path: Path to token file, defaults to ~/.kiwi/tokens.json
|
|
19
19
|
"""
|
|
20
20
|
if token_path is None:
|
|
21
|
-
token_path = Path.home() / ".
|
|
21
|
+
token_path = Path.home() / ".kiwi" / "tokens.json"
|
|
22
22
|
|
|
23
23
|
self.token_path = token_path
|
|
24
24
|
self._tokens: Optional[AuthTokens] = None
|
|
@@ -229,39 +229,110 @@ class AutobotsClientWrapper:
|
|
|
229
229
|
logger.error(error_msg)
|
|
230
230
|
return False, None, error_msg
|
|
231
231
|
|
|
232
|
-
def
|
|
232
|
+
def upload_files(self, file_paths: list[str]) -> tuple[bool, list[str], str]:
|
|
233
|
+
"""Upload files via /v1/files/ API and return their URLs.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
file_paths: List of local file paths to upload
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
Tuple of (success, urls, message)
|
|
240
|
+
"""
|
|
241
|
+
try:
|
|
242
|
+
if not isinstance(self.client, AuthenticatedClient):
|
|
243
|
+
return False, [], "Authentication required"
|
|
244
|
+
|
|
245
|
+
import mimetypes
|
|
246
|
+
from pathlib import Path
|
|
247
|
+
from autobots_client.api.files import upload_files_v1_files_post
|
|
248
|
+
from autobots_client.models.body_upload_files_v1_files_post import BodyUploadFilesV1FilesPost
|
|
249
|
+
|
|
250
|
+
# Validate all files exist first
|
|
251
|
+
paths = []
|
|
252
|
+
for fp in file_paths:
|
|
253
|
+
p = Path(fp).expanduser().resolve()
|
|
254
|
+
if not p.exists():
|
|
255
|
+
return False, [], f"File not found: {fp}"
|
|
256
|
+
paths.append(p)
|
|
257
|
+
|
|
258
|
+
# The SDK's BodyUploadFilesV1FilesPost.to_multipart() sends files as
|
|
259
|
+
# text/plain strings, which doesn't work for binary uploads.
|
|
260
|
+
# Use the SDK's endpoint URL but build the multipart manually.
|
|
261
|
+
multipart_files = []
|
|
262
|
+
file_handles = []
|
|
263
|
+
for p in paths:
|
|
264
|
+
mime = mimetypes.guess_type(str(p))[0] or "application/octet-stream"
|
|
265
|
+
fh = open(p, "rb")
|
|
266
|
+
file_handles.append(fh)
|
|
267
|
+
multipart_files.append(("files", (p.name, fh, mime)))
|
|
268
|
+
|
|
269
|
+
http_client = self.client.get_httpx_client()
|
|
270
|
+
response = http_client.request(
|
|
271
|
+
method="POST",
|
|
272
|
+
url="/v1/files/public/",
|
|
273
|
+
files=multipart_files,
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
# Close opened file handles
|
|
277
|
+
for fh in file_handles:
|
|
278
|
+
fh.close()
|
|
279
|
+
|
|
280
|
+
if response.status_code == 200:
|
|
281
|
+
result = response.json()
|
|
282
|
+
urls = [item["url"] for item in result if "url" in item]
|
|
283
|
+
names = [item.get("name", "?") for item in result]
|
|
284
|
+
logger.info(f"Uploaded {len(urls)} file(s): {names}")
|
|
285
|
+
return True, urls, f"Uploaded {len(urls)} file(s)"
|
|
286
|
+
else:
|
|
287
|
+
error_msg = f"Upload failed: HTTP {response.status_code}"
|
|
288
|
+
logger.error(error_msg)
|
|
289
|
+
return False, [], error_msg
|
|
290
|
+
|
|
291
|
+
except Exception as e:
|
|
292
|
+
error_msg = f"Upload error: {e}"
|
|
293
|
+
logger.error(error_msg)
|
|
294
|
+
return False, [], error_msg
|
|
295
|
+
|
|
296
|
+
def run_action_async(self, action_id: str, user_input: str, action_result_id: Optional[str] = None, urls: Optional[list[str]] = None, metadata: Optional[dict] = None) -> tuple[bool, Optional[str], str]:
|
|
233
297
|
"""Run an action asynchronously and return the run ID.
|
|
234
298
|
|
|
235
299
|
Args:
|
|
236
300
|
action_id: ID of the action to run
|
|
237
301
|
user_input: User input text for the action
|
|
238
302
|
action_result_id: Optional ID to continue an existing conversation
|
|
303
|
+
urls: Optional list of file URLs to attach
|
|
304
|
+
metadata: Optional metadata dict to merge into the DataBlock
|
|
239
305
|
|
|
240
306
|
Returns:
|
|
241
307
|
Tuple of (success, run_id, message)
|
|
242
308
|
"""
|
|
243
309
|
try:
|
|
244
310
|
if action_result_id:
|
|
245
|
-
logger.info(f"Running action {action_id} with input: {user_input}, continuing run_id: {action_result_id}")
|
|
311
|
+
logger.info(f"Running action {action_id} with input: {user_input}, urls: {urls}, continuing run_id: {action_result_id}")
|
|
246
312
|
else:
|
|
247
|
-
logger.info(f"Running action {action_id} with input: {user_input}")
|
|
313
|
+
logger.info(f"Running action {action_id} with input: {user_input}, urls: {urls}")
|
|
248
314
|
|
|
249
315
|
if not isinstance(self.client, AuthenticatedClient):
|
|
250
316
|
return False, None, "Authentication required"
|
|
251
317
|
|
|
252
318
|
# Create input body - DataBlock structure
|
|
319
|
+
default_metadata = {
|
|
320
|
+
"process_url_in_text": False,
|
|
321
|
+
"process_attachment_url": True,
|
|
322
|
+
"attachment_requested": 0
|
|
323
|
+
}
|
|
324
|
+
if metadata:
|
|
325
|
+
default_metadata.update(metadata)
|
|
326
|
+
|
|
253
327
|
input_data = BodyAsyncRunActionV1ActionsIdAsyncRunPostInput()
|
|
254
328
|
input_data.additional_properties = {
|
|
255
329
|
"text": user_input,
|
|
256
|
-
"urls": [],
|
|
257
|
-
"metadata":
|
|
258
|
-
"process_url_in_text": False,
|
|
259
|
-
"process_attachment_url": True,
|
|
260
|
-
"attachment_requested": 0
|
|
261
|
-
}
|
|
330
|
+
"urls": urls or [],
|
|
331
|
+
"metadata": default_metadata
|
|
262
332
|
}
|
|
263
333
|
|
|
264
334
|
body = BodyAsyncRunActionV1ActionsIdAsyncRunPost(input_=input_data)
|
|
335
|
+
logger.info(f"Request body: {body.to_dict()}")
|
|
265
336
|
|
|
266
337
|
# Import UNSET for optional parameter
|
|
267
338
|
from autobots_client.types import UNSET
|
|
@@ -296,6 +296,26 @@ Session:
|
|
|
296
296
|
/continue <run_id> Continue an existing run
|
|
297
297
|
/new Start new conversation
|
|
298
298
|
/status Show current action & run
|
|
299
|
+
/cancel Cancel active request
|
|
300
|
+
|
|
301
|
+
Files:
|
|
302
|
+
/upload <path> [...] Upload file(s), attach to next message
|
|
303
|
+
/files Show pending file uploads
|
|
304
|
+
/clear-files Clear pending file uploads
|
|
305
|
+
|
|
306
|
+
Metadata (persistent across runs):
|
|
307
|
+
/metadata Show current metadata
|
|
308
|
+
/metadata set <k> <v> Set a metadata field
|
|
309
|
+
/metadata remove <k> Remove a metadata field
|
|
310
|
+
/metadata clear Clear all metadata
|
|
311
|
+
|
|
312
|
+
Runtime:
|
|
313
|
+
/runtime status Show runtime status
|
|
314
|
+
/runtime start Start runtime
|
|
315
|
+
/runtime stop Stop runtime
|
|
316
|
+
/runtime restart Restart runtime
|
|
317
|
+
/runtime list List all runtimes
|
|
318
|
+
/runtime logs Show runtime logs
|
|
299
319
|
|
|
300
320
|
Query:
|
|
301
321
|
/actions list [--name NAME] [--limit N]
|
|
@@ -14,10 +14,10 @@ class ConfigManager:
|
|
|
14
14
|
"""Initialize config manager.
|
|
15
15
|
|
|
16
16
|
Args:
|
|
17
|
-
config_path: Path to config file, defaults to ~/.
|
|
17
|
+
config_path: Path to config file, defaults to ~/.kiwi/config.json
|
|
18
18
|
"""
|
|
19
19
|
if config_path is None:
|
|
20
|
-
config_path = Path.home() / ".
|
|
20
|
+
config_path = Path.home() / ".kiwi" / "config.json"
|
|
21
21
|
|
|
22
22
|
self.config_path = config_path
|
|
23
23
|
self._config: Optional[AppConfig] = None
|
|
@@ -16,7 +16,7 @@ def setup_logging(log_level: str = "INFO", log_file: str = "kiwi_tui.log") -> No
|
|
|
16
16
|
logger.remove()
|
|
17
17
|
|
|
18
18
|
# Only log to file, not to console (to avoid interference with Textual UI)
|
|
19
|
-
log_path = Path.home() / ".
|
|
19
|
+
log_path = Path.home() / ".kiwi" / log_file
|
|
20
20
|
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
21
21
|
|
|
22
22
|
logger.add(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Shared helpers for managing per-directory kiwi-runtime processes.
|
|
2
2
|
|
|
3
3
|
Each working directory gets its own runtime, identified by a hash of the
|
|
4
|
-
absolute path. State is stored under ~/.
|
|
4
|
+
absolute path. State is stored under ~/.kiwi/runtimes/<key>/:
|
|
5
5
|
pid — PID of the running process
|
|
6
6
|
log — stdout/stderr of the runtime
|
|
7
7
|
cwd — the working directory this runtime is scoped to
|
|
@@ -13,7 +13,7 @@ import signal
|
|
|
13
13
|
import time
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
|
-
RUNTIMES_DIR = Path.home() / ".
|
|
16
|
+
RUNTIMES_DIR = Path.home() / ".kiwi" / "runtimes"
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def runtime_key(cwd: str | None = None) -> str:
|
|
@@ -58,6 +58,52 @@ def log_path(cwd: str | None = None) -> Path:
|
|
|
58
58
|
return runtime_dir(cwd) / "log"
|
|
59
59
|
|
|
60
60
|
|
|
61
|
+
def token_path(cwd: str | None = None) -> Path:
|
|
62
|
+
"""Return the shared token file path for a directory's runtime."""
|
|
63
|
+
return runtime_dir(cwd) / "token"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def refresh_token_path(cwd: str | None = None) -> Path:
|
|
67
|
+
"""Return the shared refresh token file path for a directory's runtime."""
|
|
68
|
+
return runtime_dir(cwd) / "refresh_token"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def save_token(token: str, cwd: str | None = None) -> None:
|
|
72
|
+
"""Write a refreshed access token so the runtime can pick it up."""
|
|
73
|
+
tp = token_path(cwd)
|
|
74
|
+
tp.write_text(token)
|
|
75
|
+
tp.chmod(0o600)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def save_refresh_token(token: str, cwd: str | None = None) -> None:
|
|
79
|
+
"""Write the refresh token so the runtime can refresh on its own."""
|
|
80
|
+
tp = refresh_token_path(cwd)
|
|
81
|
+
tp.write_text(token)
|
|
82
|
+
tp.chmod(0o600)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def read_token(cwd: str | None = None) -> str | None:
|
|
86
|
+
"""Read the shared access token (returns None if missing)."""
|
|
87
|
+
tp = token_path(cwd)
|
|
88
|
+
if not tp.exists():
|
|
89
|
+
return None
|
|
90
|
+
try:
|
|
91
|
+
return tp.read_text().strip() or None
|
|
92
|
+
except OSError:
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def read_refresh_token(cwd: str | None = None) -> str | None:
|
|
97
|
+
"""Read the shared refresh token (returns None if missing)."""
|
|
98
|
+
tp = refresh_token_path(cwd)
|
|
99
|
+
if not tp.exists():
|
|
100
|
+
return None
|
|
101
|
+
try:
|
|
102
|
+
return tp.read_text().strip() or None
|
|
103
|
+
except OSError:
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
|
|
61
107
|
def stop_runtime(cwd: str | None = None) -> bool:
|
|
62
108
|
"""Stop the runtime for a directory. Returns True if a process was stopped."""
|
|
63
109
|
pid = get_running_pid(cwd)
|