subactor-shell 0.2.2__tar.gz → 0.2.3__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.
- {subactor_shell-0.2.2/src/subactor_shell.egg-info → subactor_shell-0.2.3}/PKG-INFO +78 -8
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/README.md +77 -7
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/pyproject.toml +1 -1
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/__init__.py +1 -1
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/app.py +248 -5
- subactor_shell-0.2.3/src/subactor_shell/auth.py +110 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/catalog.py +79 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/config.py +119 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/connectors.py +20 -4
- subactor_shell-0.2.3/src/subactor_shell/fleet_status.py +221 -0
- subactor_shell-0.2.3/src/subactor_shell/interaction.py +56 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/operations.py +169 -2
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/repl.py +168 -80
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/store.py +28 -0
- subactor_shell-0.2.3/src/subactor_shell/surface.py +175 -0
- subactor_shell-0.2.3/src/subactor_shell/system_config.py +83 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3/src/subactor_shell.egg-info}/PKG-INFO +78 -8
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell.egg-info/SOURCES.txt +11 -0
- subactor_shell-0.2.3/tests/test_auth.py +64 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_canonical_cli.py +37 -0
- subactor_shell-0.2.3/tests/test_fleet_status.py +108 -0
- subactor_shell-0.2.3/tests/test_installer.py +54 -0
- subactor_shell-0.2.3/tests/test_interaction.py +66 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_operations.py +64 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_shell_integration.py +36 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_store.py +19 -0
- subactor_shell-0.2.3/tests/test_surface.py +96 -0
- subactor_shell-0.2.3/tests/test_system_config.py +102 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/LICENSE +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/setup.cfg +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/__main__.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/acp_agent.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/artifacts.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/chat.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/compiler.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/context_builder.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/control.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/control_env.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/intent_ir.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/models.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/orchestration.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/policy.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/providers/__init__.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/providers/anthropic.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/providers/base.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/providers/mock.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/providers/openai_compat.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/providers/subactor_control.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/redaction.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/routing.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/secret_refs.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/terminal.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/token_budget.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell/vault.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell.egg-info/dependency_links.txt +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell.egg-info/entry_points.txt +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell.egg-info/requires.txt +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/src/subactor_shell.egg-info/top_level.txt +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_acp_agent.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_acp_extensions.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_app_errors.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_artifact_selection.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_chat.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_context_budget.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_control.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_control_env.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_orchestration.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_providers.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_redaction.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_router_local_parser.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_structured_output.py +0 -0
- {subactor_shell-0.2.2 → subactor_shell-0.2.3}/tests/test_vault.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: subactor-shell
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Subactor terminal shell with IntentIR orchestration, governed Control integration, persistent sessions and ACP stdio.
|
|
5
5
|
Author: Subactor community
|
|
6
6
|
Author-email: Tom Sapletta <tom@sapletta.com>
|
|
@@ -25,18 +25,16 @@ Dynamic: license-file
|
|
|
25
25
|
|
|
26
26
|
## AI Cost Tracking
|
|
27
27
|
|
|
28
|
-
    
|
|
29
|
+
  
|
|
30
30
|
|
|
31
|
-
- 🤖 **LLM usage:** $0.
|
|
32
|
-
- 👤 **Human dev:** ~$
|
|
31
|
+
- 🤖 **LLM usage:** $0.3788 (7 commits)
|
|
32
|
+
- 👤 **Human dev:** ~$2968 (29.7h @ $100/h, 30min dedup)
|
|
33
33
|
|
|
34
|
-
Generated on 2026-
|
|
34
|
+
Generated on 2026-09-03 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
38
|
Subactor Shell jest trwałą warstwą rozmowy i orkiestracji dla terminala oraz klientów ACP. Wersja 0.2.2 integruje pytania operacyjne z zarządzanym CLI Subactora, poprawia obsługę kończenia sesji i nie przejmuje nazwy Founder Chat.
|
|
41
39
|
|
|
42
40
|
```text
|
|
@@ -70,6 +68,40 @@ Pełny transcript nadal jest zapisywany w SQLite, ale provider rozmowy dostaje t
|
|
|
70
68
|
|
|
71
69
|
## Instalacja
|
|
72
70
|
|
|
71
|
+
### Jednolinijkowy instalator (zalecane)
|
|
72
|
+
|
|
73
|
+
Mac / Linux:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
curl -fsSL https://subactor.github.io/shell/install.sh | sh
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Windows:
|
|
80
|
+
|
|
81
|
+
```powershell
|
|
82
|
+
powershell -ExecutionPolicy ByPass -c "irm https://subactor.github.io/shell/install.ps1 | iex"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Instalator domyślnie pobiera metadane z
|
|
86
|
+
[`subactor.github.io/shell`](https://subactor.github.io/shell/) i w razie
|
|
87
|
+
problemu przechodzi na
|
|
88
|
+
[GitHub Releases](https://github.com/subactor/shell/releases). Aby wymusić
|
|
89
|
+
GitHub Releases:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
curl -fsSL https://subactor.github.io/shell/install.sh | SUBACTOR_SHELL_INSTALLER_USE_GITHUB_IO=false sh
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```powershell
|
|
96
|
+
$env:SUBACTOR_SHELL_INSTALLER_USE_GITHUB_IO='false'; irm https://subactor.github.io/shell/install.ps1 | iex
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Wymagania: Python 3.11+. Instalator tworzy izolowane środowisko w
|
|
100
|
+
`~/.local/share/subactor-shell/venv` i linkuje `subactor-shell` do
|
|
101
|
+
`~/.local/bin`.
|
|
102
|
+
|
|
103
|
+
### Z wheel lub źródeł
|
|
104
|
+
|
|
73
105
|
Pakiet instaluje wyłącznie polecenie `subactor-shell`. Nazwa `subactor` jest
|
|
74
106
|
zarezerwowana dla Founder Chat dostarczanego przez Platformę, dzięki czemu
|
|
75
107
|
`subactor chat` zachowuje swój interfejs, pełną diagnostykę i kontrakt sesji.
|
|
@@ -131,6 +163,44 @@ subactor-shell one 'pokaż sesje'
|
|
|
131
163
|
|
|
132
164
|
Przy znanym intencie read-only wynik może zostać wykonany lokalnie bez wywołania providera rozmowy.
|
|
133
165
|
|
|
166
|
+
## Scope node, konto GitHub i źródła konfiguracji
|
|
167
|
+
|
|
168
|
+
Shell pobiera wspólną, pozbawioną sekretów projekcję z usługi
|
|
169
|
+
`subactor/config`. Po uruchomieniu jej lokalnego endpointu można sprawdzić
|
|
170
|
+
bieżący komputer, osobno scope kodu i danych, ustawienia konta oraz federacyjny
|
|
171
|
+
katalog właścicieli konfiguracji:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
subactor-shell scope --node current
|
|
175
|
+
subactor-shell scope --node current --kind code --json
|
|
176
|
+
subactor-shell scope --node current --kind data
|
|
177
|
+
subactor-shell account github
|
|
178
|
+
subactor-shell account github --json
|
|
179
|
+
subactor-shell source list
|
|
180
|
+
subactor-shell source list --provides configuration.propose --json
|
|
181
|
+
subactor-shell source show credential-vault
|
|
182
|
+
subactor-shell resolve credential.metadata
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Endpoint jest jawnie konfigurowany w `~/.config/subactor-shell/config.toml`:
|
|
186
|
+
|
|
187
|
+
```toml
|
|
188
|
+
[system_config]
|
|
189
|
+
base_url = "http://127.0.0.1:8198"
|
|
190
|
+
timeout_seconds = 5.0
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Port `8198` jest kanonicznym bindingiem `subactor/config`; `8098` pozostaje
|
|
194
|
+
zajęty przez lokalny LLM Gateway.
|
|
195
|
+
|
|
196
|
+
Wynik konta pokazuje tylko skonfigurowany podmiot, znormalizowany stan
|
|
197
|
+
uwierzytelnienia oraz bezpieczne lokalizacje ustawień. Token ani surowy wynik
|
|
198
|
+
`gh auth status` nie są zwracane.
|
|
199
|
+
|
|
200
|
+
Resolver zwraca źródła, ich właścicieli i dostępność na bieżącym node. Wynik
|
|
201
|
+
ma `authority=evidence-only`: pełny zakres decyzji Supervisora nie zastępuje
|
|
202
|
+
zewnętrznego grantu wymaganego do mutacji.
|
|
203
|
+
|
|
134
204
|
## Routing modeli
|
|
135
205
|
|
|
136
206
|
Minimalna konfiguracja lokalnego parsera OpenAI-compatible:
|
|
@@ -3,18 +3,16 @@
|
|
|
3
3
|
|
|
4
4
|
## AI Cost Tracking
|
|
5
5
|
|
|
6
|
-
    
|
|
7
|
+
  
|
|
8
8
|
|
|
9
|
-
- 🤖 **LLM usage:** $0.
|
|
10
|
-
- 👤 **Human dev:** ~$
|
|
9
|
+
- 🤖 **LLM usage:** $0.3788 (7 commits)
|
|
10
|
+
- 👤 **Human dev:** ~$2968 (29.7h @ $100/h, 30min dedup)
|
|
11
11
|
|
|
12
|
-
Generated on 2026-
|
|
12
|
+
Generated on 2026-09-03 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
16
|
Subactor Shell jest trwałą warstwą rozmowy i orkiestracji dla terminala oraz klientów ACP. Wersja 0.2.2 integruje pytania operacyjne z zarządzanym CLI Subactora, poprawia obsługę kończenia sesji i nie przejmuje nazwy Founder Chat.
|
|
19
17
|
|
|
20
18
|
```text
|
|
@@ -48,6 +46,40 @@ Pełny transcript nadal jest zapisywany w SQLite, ale provider rozmowy dostaje t
|
|
|
48
46
|
|
|
49
47
|
## Instalacja
|
|
50
48
|
|
|
49
|
+
### Jednolinijkowy instalator (zalecane)
|
|
50
|
+
|
|
51
|
+
Mac / Linux:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
curl -fsSL https://subactor.github.io/shell/install.sh | sh
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Windows:
|
|
58
|
+
|
|
59
|
+
```powershell
|
|
60
|
+
powershell -ExecutionPolicy ByPass -c "irm https://subactor.github.io/shell/install.ps1 | iex"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Instalator domyślnie pobiera metadane z
|
|
64
|
+
[`subactor.github.io/shell`](https://subactor.github.io/shell/) i w razie
|
|
65
|
+
problemu przechodzi na
|
|
66
|
+
[GitHub Releases](https://github.com/subactor/shell/releases). Aby wymusić
|
|
67
|
+
GitHub Releases:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
curl -fsSL https://subactor.github.io/shell/install.sh | SUBACTOR_SHELL_INSTALLER_USE_GITHUB_IO=false sh
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```powershell
|
|
74
|
+
$env:SUBACTOR_SHELL_INSTALLER_USE_GITHUB_IO='false'; irm https://subactor.github.io/shell/install.ps1 | iex
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Wymagania: Python 3.11+. Instalator tworzy izolowane środowisko w
|
|
78
|
+
`~/.local/share/subactor-shell/venv` i linkuje `subactor-shell` do
|
|
79
|
+
`~/.local/bin`.
|
|
80
|
+
|
|
81
|
+
### Z wheel lub źródeł
|
|
82
|
+
|
|
51
83
|
Pakiet instaluje wyłącznie polecenie `subactor-shell`. Nazwa `subactor` jest
|
|
52
84
|
zarezerwowana dla Founder Chat dostarczanego przez Platformę, dzięki czemu
|
|
53
85
|
`subactor chat` zachowuje swój interfejs, pełną diagnostykę i kontrakt sesji.
|
|
@@ -109,6 +141,44 @@ subactor-shell one 'pokaż sesje'
|
|
|
109
141
|
|
|
110
142
|
Przy znanym intencie read-only wynik może zostać wykonany lokalnie bez wywołania providera rozmowy.
|
|
111
143
|
|
|
144
|
+
## Scope node, konto GitHub i źródła konfiguracji
|
|
145
|
+
|
|
146
|
+
Shell pobiera wspólną, pozbawioną sekretów projekcję z usługi
|
|
147
|
+
`subactor/config`. Po uruchomieniu jej lokalnego endpointu można sprawdzić
|
|
148
|
+
bieżący komputer, osobno scope kodu i danych, ustawienia konta oraz federacyjny
|
|
149
|
+
katalog właścicieli konfiguracji:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
subactor-shell scope --node current
|
|
153
|
+
subactor-shell scope --node current --kind code --json
|
|
154
|
+
subactor-shell scope --node current --kind data
|
|
155
|
+
subactor-shell account github
|
|
156
|
+
subactor-shell account github --json
|
|
157
|
+
subactor-shell source list
|
|
158
|
+
subactor-shell source list --provides configuration.propose --json
|
|
159
|
+
subactor-shell source show credential-vault
|
|
160
|
+
subactor-shell resolve credential.metadata
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Endpoint jest jawnie konfigurowany w `~/.config/subactor-shell/config.toml`:
|
|
164
|
+
|
|
165
|
+
```toml
|
|
166
|
+
[system_config]
|
|
167
|
+
base_url = "http://127.0.0.1:8198"
|
|
168
|
+
timeout_seconds = 5.0
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Port `8198` jest kanonicznym bindingiem `subactor/config`; `8098` pozostaje
|
|
172
|
+
zajęty przez lokalny LLM Gateway.
|
|
173
|
+
|
|
174
|
+
Wynik konta pokazuje tylko skonfigurowany podmiot, znormalizowany stan
|
|
175
|
+
uwierzytelnienia oraz bezpieczne lokalizacje ustawień. Token ani surowy wynik
|
|
176
|
+
`gh auth status` nie są zwracane.
|
|
177
|
+
|
|
178
|
+
Resolver zwraca źródła, ich właścicieli i dostępność na bieżącym node. Wynik
|
|
179
|
+
ma `authority=evidence-only`: pełny zakres decyzji Supervisora nie zastępuje
|
|
180
|
+
zewnętrznego grantu wymaganego do mutacji.
|
|
181
|
+
|
|
112
182
|
## Routing modeli
|
|
113
183
|
|
|
114
184
|
Minimalna konfiguracja lokalnego parsera OpenAI-compatible:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "subactor-shell"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.3"
|
|
8
8
|
description = "Subactor terminal shell with IntentIR orchestration, governed Control integration, persistent sessions and ACP stdio."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -4,6 +4,7 @@ import argparse
|
|
|
4
4
|
import asyncio
|
|
5
5
|
import getpass
|
|
6
6
|
import json
|
|
7
|
+
import os
|
|
7
8
|
import stat
|
|
8
9
|
import sys
|
|
9
10
|
from pathlib import Path
|
|
@@ -18,9 +19,12 @@ from .compiler import ExecutionPlan
|
|
|
18
19
|
from .config import initialize_layout, load_config
|
|
19
20
|
from .control import SubactorControlClient
|
|
20
21
|
from .control_env import apply_control_environment
|
|
22
|
+
from .fleet_status import fetch_fleet_overview, render_fleet_startup_banner
|
|
21
23
|
from .operations import OperationSettings, OperationsClient, run_operational_command
|
|
22
24
|
from .repl import ShellRepl
|
|
23
25
|
from .store import Store
|
|
26
|
+
from .system_config import SystemConfigClient
|
|
27
|
+
from .terminal import terminal_hyperlinks_enabled
|
|
24
28
|
|
|
25
29
|
|
|
26
30
|
def build_parser() -> argparse.ArgumentParser:
|
|
@@ -48,6 +52,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
48
52
|
one.add_argument("--model", help="model dla nowej sesji")
|
|
49
53
|
one.add_argument("--attach", action="append", type=Path, default=[])
|
|
50
54
|
one.add_argument("--grant", action="append", default=[], help="jednorazowy grant aliasu sekretu")
|
|
55
|
+
one.add_argument("--json", action="store_true", help="emituj ustrukturyzowaną odpowiedź w formacie JSON")
|
|
51
56
|
|
|
52
57
|
data = sub.add_parser("data", help="zapisuj jawne dane tekstowe i pliki")
|
|
53
58
|
data_sub = data.add_subparsers(dest="data_command", required=True)
|
|
@@ -77,8 +82,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
77
82
|
vault_wrap.add_argument("alias")
|
|
78
83
|
vault_wrap.add_argument("--ttl", default="5m")
|
|
79
84
|
|
|
80
|
-
sessions = sub.add_parser("sessions", help="lista
|
|
81
|
-
sessions.add_argument("--json", action="store_true")
|
|
85
|
+
sessions = sub.add_parser("sessions", help="zarządzanie sesjami (lista, usuwanie, czyszczenie)")
|
|
86
|
+
sessions.add_argument("--json", action="store_true", help="zwróć listę sesji jako JSON")
|
|
87
|
+
sessions.add_argument("--delete", metavar="SESSION_ID", help="usuń wskazaną sesję")
|
|
88
|
+
sessions.add_argument("--prune", action="store_true", help="usuń nieaktywne lub puste sesje")
|
|
89
|
+
sessions.add_argument("--older-than-days", type=int, default=30, help="próg wieku dla --prune (domyślnie 30 dni)")
|
|
90
|
+
sessions.add_argument("--empty-only", action="store_true", help="usuń tylko puste sesje bez wiadomości")
|
|
82
91
|
|
|
83
92
|
export = sub.add_parser("export", help="eksport sesji do JSON")
|
|
84
93
|
export.add_argument("session")
|
|
@@ -108,6 +117,14 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
108
117
|
tickets.add_argument("--project", default="")
|
|
109
118
|
tickets.add_argument("--text", default="")
|
|
110
119
|
tickets.add_argument("--json", action="store_true")
|
|
120
|
+
orgs = sub.add_parser("orgs", help="rejestry organizacji (dashboard lub lista zasobu)")
|
|
121
|
+
orgs.add_argument("resource", nargs="?", default="", help="np. organizations, contacts")
|
|
122
|
+
orgs.add_argument("--json", action="store_true")
|
|
123
|
+
projects = sub.add_parser("projects", help="portfolio projektów lub reconciliation")
|
|
124
|
+
projects.add_argument("--recon", action="store_true", help="widok /api/projects/reconciliation")
|
|
125
|
+
projects.add_argument("--json", action="store_true")
|
|
126
|
+
performance = sub.add_parser("performance", aliases=["perf"], help="ranking kosztu i ROI URI Process")
|
|
127
|
+
performance.add_argument("--json", action="store_true")
|
|
111
128
|
sub.add_parser("health", help="publiczny health Subactor Control")
|
|
112
129
|
dispatch = sub.add_parser("dispatch", help="rozdysponuj pracę przez Control")
|
|
113
130
|
dispatch.add_argument("--confirm", default="")
|
|
@@ -128,6 +145,25 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
128
145
|
post.add_argument("--confirm", default="")
|
|
129
146
|
sub.add_parser("endpoints", help="katalog operacyjnego API")
|
|
130
147
|
|
|
148
|
+
scope = sub.add_parser("scope", help="scope code i data na wskazanym node")
|
|
149
|
+
scope.add_argument("--node", default="current")
|
|
150
|
+
scope.add_argument("--kind", choices=["all", "code", "data"], default="all")
|
|
151
|
+
scope.add_argument("--json", action="store_true")
|
|
152
|
+
account = sub.add_parser("account", help="scope i ustawienia konta providera")
|
|
153
|
+
account.add_argument("provider", choices=["github"])
|
|
154
|
+
account.add_argument("--json", action="store_true")
|
|
155
|
+
source = sub.add_parser("source", help="federacyjny katalog źródeł konfiguracji")
|
|
156
|
+
source_sub = source.add_subparsers(dest="source_command", required=True)
|
|
157
|
+
source_list = source_sub.add_parser("list", help="lista źródeł konfiguracji")
|
|
158
|
+
source_list.add_argument("--provides", default=None, help="filtr dokładnej zdolności")
|
|
159
|
+
source_list.add_argument("--json", action="store_true")
|
|
160
|
+
source_show = source_sub.add_parser("show", help="szczegóły źródła konfiguracji")
|
|
161
|
+
source_show.add_argument("source_id")
|
|
162
|
+
source_show.add_argument("--json", action="store_true")
|
|
163
|
+
resolve = sub.add_parser("resolve", help="znajdź źródła dla wymaganej zdolności")
|
|
164
|
+
resolve.add_argument("need")
|
|
165
|
+
resolve.add_argument("--json", action="store_true")
|
|
166
|
+
|
|
131
167
|
receipts = sub.add_parser("receipts", help="przeglądaj krótkie receipts wykonania")
|
|
132
168
|
receipts_sub = receipts.add_subparsers(dest="receipts_command", required=True)
|
|
133
169
|
receipts_list = receipts_sub.add_parser("list")
|
|
@@ -146,7 +182,27 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
146
182
|
connectors = sub.add_parser("connectors", help="allowlista nazwanych connectorów")
|
|
147
183
|
connectors.add_argument("--json", action="store_true")
|
|
148
184
|
|
|
185
|
+
login = sub.add_parser("login", help="uwierzytelnienie sesji CLI (magic link + PKCE)")
|
|
186
|
+
login.add_argument("email", help="adres e-mail konta SaaS / Subactor")
|
|
187
|
+
|
|
188
|
+
auth = sub.add_parser("auth", help="stan uwierzytelnienia i adres Process Control (:8091)")
|
|
189
|
+
auth.add_argument("--json", action="store_true")
|
|
190
|
+
|
|
191
|
+
backup = sub.add_parser("backup", help="zarządzanie kopiami zapasowymi konta i baz SQLite")
|
|
192
|
+
backup_sub = backup.add_subparsers(dest="backup_command", required=True)
|
|
193
|
+
backup_create = backup_sub.add_parser("create", help="utwórz nowy snapshot baz i manifest SHA-256")
|
|
194
|
+
backup_create.add_argument("--tenant", default="default-tenant", help="identyfikator konta organizacji")
|
|
195
|
+
backup_create.add_argument("--json", action="store_true")
|
|
196
|
+
backup_list = backup_sub.add_parser("list", help="lista dostępnych kopii zapasowych")
|
|
197
|
+
backup_list.add_argument("--json", action="store_true")
|
|
198
|
+
backup_restore = backup_sub.add_parser("restore", help="przywróć bazę z archiwum kopii zapasowej")
|
|
199
|
+
backup_restore.add_argument("archive", help="ścieżka do pliku archiwum .tar.gz")
|
|
200
|
+
backup_restore.add_argument("--target-dir", default=None, help="docelowy katalog przywrócenia")
|
|
201
|
+
backup_restore.add_argument("--dry-run", action="store_true")
|
|
202
|
+
|
|
149
203
|
sub.add_parser("doctor", help="diagnostyka bez ujawniania sekretów")
|
|
204
|
+
sub.add_parser("fleet", help="stan Pull Requestów, ticketów i floty ekosystemu")
|
|
205
|
+
sub.add_parser("prs", help="otwarte Pull Requesty (subactor, if-uri)")
|
|
150
206
|
sub.add_parser("acp-agent", help="uruchom agenta ACP v1 po stdio")
|
|
151
207
|
return parser
|
|
152
208
|
|
|
@@ -169,11 +225,30 @@ async def _one(chat: ChatService, args: argparse.Namespace, console: Console) ->
|
|
|
169
225
|
session = chat.get_or_create_session(args.session, provider=args.provider, model=args.model)
|
|
170
226
|
for alias in args.grant:
|
|
171
227
|
chat.grant_secret(alias)
|
|
228
|
+
is_json = bool(getattr(args, "json", False))
|
|
229
|
+
chunks: list[str] = []
|
|
172
230
|
async for chunk in chat.stream_message(session.id, args.message, attachment_paths=args.attach):
|
|
173
|
-
|
|
231
|
+
chunks.append(chunk)
|
|
232
|
+
if not is_json:
|
|
233
|
+
console.print(chunk, end="", markup=False, soft_wrap=True)
|
|
234
|
+
|
|
235
|
+
route = chat.store.last_routing_decision(session.id)
|
|
236
|
+
if is_json:
|
|
237
|
+
receipt = None
|
|
238
|
+
receipts = chat.store.list_execution_receipts(session.id)
|
|
239
|
+
if receipts:
|
|
240
|
+
receipt = receipts[0]
|
|
241
|
+
payload = {
|
|
242
|
+
"session_id": session.id,
|
|
243
|
+
"text": "".join(chunks),
|
|
244
|
+
"route": route,
|
|
245
|
+
"receipt": receipt,
|
|
246
|
+
}
|
|
247
|
+
console.print_json(json.dumps(payload, ensure_ascii=False))
|
|
248
|
+
return 0
|
|
249
|
+
|
|
174
250
|
console.print()
|
|
175
251
|
console.print(f"[dim]session={session.id}[/dim]")
|
|
176
|
-
route = chat.store.last_routing_decision(session.id)
|
|
177
252
|
if route and bool(chat.config.orchestration.get("show_route", False)):
|
|
178
253
|
console.print(
|
|
179
254
|
f"[dim]route={route['route']} intent={route['intent_id']} confidence={route['confidence']:.3f}[/dim]"
|
|
@@ -181,6 +256,7 @@ async def _one(chat: ChatService, args: argparse.Namespace, console: Console) ->
|
|
|
181
256
|
return 0
|
|
182
257
|
|
|
183
258
|
|
|
259
|
+
|
|
184
260
|
def _doctor(config, store: Store, chat: ChatService, console: Console) -> int:
|
|
185
261
|
table = Table("Test", "Wynik", "Szczegóły")
|
|
186
262
|
failures = 0
|
|
@@ -363,6 +439,77 @@ def _receipts_command(store: Store, args: argparse.Namespace, console: Console)
|
|
|
363
439
|
return 0
|
|
364
440
|
|
|
365
441
|
|
|
442
|
+
def _system_config_command(client: SystemConfigClient, args: argparse.Namespace, console: Console) -> int:
|
|
443
|
+
if args.command == "scope":
|
|
444
|
+
payload = client.node_scope(args.node)
|
|
445
|
+
if args.json:
|
|
446
|
+
console.print_json(json.dumps(payload, ensure_ascii=False))
|
|
447
|
+
return 0
|
|
448
|
+
console.print(f"Node: [cyan]{payload['node']['id']}[/cyan] ({payload['node']['hostname']})")
|
|
449
|
+
if args.kind != "data":
|
|
450
|
+
table = Table("Repo", "Branch", "URI", "Dirty")
|
|
451
|
+
for item in payload["scope"]["code"]["repositories"]:
|
|
452
|
+
table.add_row(str(item["id"]), str(item["branch"]), str(item["uri"]), "tak" if item["dirty"] else "nie")
|
|
453
|
+
console.print(table)
|
|
454
|
+
if args.kind != "code":
|
|
455
|
+
table = Table("Dane", "Rodzaj", "Dostęp", "URI")
|
|
456
|
+
for item in payload["scope"]["data"]["resources"]:
|
|
457
|
+
table.add_row(str(item["id"]), str(item["kind"]), str(item["access"]), str(item["uri"]))
|
|
458
|
+
console.print(table)
|
|
459
|
+
return 0
|
|
460
|
+
|
|
461
|
+
if args.command == "account":
|
|
462
|
+
payload = client.account(args.provider)
|
|
463
|
+
console.print_json(json.dumps(payload, ensure_ascii=False)) if args.json else None
|
|
464
|
+
if args.json:
|
|
465
|
+
return 0
|
|
466
|
+
account = payload["account"]
|
|
467
|
+
authentication = payload["authentication"]
|
|
468
|
+
console.print(f"Konto: [cyan]{account['provider']}:{account['configuredSubject']}[/cyan]")
|
|
469
|
+
console.print(f"Uwierzytelnienie: {authentication['state']} · effective scope: {authentication['effectiveScope']}")
|
|
470
|
+
table = Table("Ustawienie", "Status", "Lokalizacja")
|
|
471
|
+
for item in payload["settingsLocations"]:
|
|
472
|
+
table.add_row(str(item["kind"]), str(item["status"]), str(item["uri"]))
|
|
473
|
+
console.print(table)
|
|
474
|
+
console.print(f"Declared scope: code={len(payload['scope']['code'])}, data={len(payload['scope']['data'])}")
|
|
475
|
+
return 0
|
|
476
|
+
|
|
477
|
+
if args.command == "source":
|
|
478
|
+
if args.source_command == "list":
|
|
479
|
+
payload = client.sources(provides=args.provides)
|
|
480
|
+
if args.json:
|
|
481
|
+
console.print_json(json.dumps(payload, ensure_ascii=False))
|
|
482
|
+
return 0
|
|
483
|
+
table = Table("Źródło", "Rodzaj", "Dostępność", "Zdolności")
|
|
484
|
+
for item in payload:
|
|
485
|
+
table.add_row(str(item["id"]), str(item["kind"]), str(item["availability"]["state"]), ", ".join(item["provides"]))
|
|
486
|
+
console.print(table)
|
|
487
|
+
return 0
|
|
488
|
+
payload = client.source(args.source_id)
|
|
489
|
+
if args.json:
|
|
490
|
+
console.print_json(json.dumps(payload, ensure_ascii=False))
|
|
491
|
+
return 0
|
|
492
|
+
console.print(f"Źródło: [cyan]{payload['id']}[/cyan] · {payload['availability']['state']}")
|
|
493
|
+
console.print(f"Właściciele: {', '.join(payload['owners'])}")
|
|
494
|
+
console.print(f"Zdolności: {', '.join(payload['provides'])}")
|
|
495
|
+
console.print(f"Authority: {payload['authority']}")
|
|
496
|
+
return 0
|
|
497
|
+
|
|
498
|
+
if args.command == "resolve":
|
|
499
|
+
payload = client.resolve(args.need)
|
|
500
|
+
if args.json:
|
|
501
|
+
console.print_json(json.dumps(payload, ensure_ascii=False))
|
|
502
|
+
else:
|
|
503
|
+
table = Table("Potrzeba", "Źródło", "Dostępność", "Authority")
|
|
504
|
+
for item in payload["matchedSources"]:
|
|
505
|
+
table.add_row(payload["need"], str(item["id"]), str(item["availability"]["state"]), payload["authority"])
|
|
506
|
+
console.print(table)
|
|
507
|
+
console.print(f"Mutation mode: {payload['control']['mutationMode']}")
|
|
508
|
+
return 0 if payload["matchedSources"] else 2
|
|
509
|
+
|
|
510
|
+
raise RuntimeError("Nieobsługiwana komenda konfiguracji")
|
|
511
|
+
|
|
512
|
+
|
|
366
513
|
def main(argv: list[str] | None = None) -> None:
|
|
367
514
|
parser = build_parser()
|
|
368
515
|
args = parser.parse_args(argv)
|
|
@@ -382,7 +529,11 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
382
529
|
|
|
383
530
|
apply_control_environment()
|
|
384
531
|
|
|
385
|
-
|
|
532
|
+
if command in {"scope", "account", "source", "resolve"}:
|
|
533
|
+
config = load_config(args.config, args.data_dir, create=False)
|
|
534
|
+
raise SystemExit(_system_config_command(SystemConfigClient(config.system_config), args, console))
|
|
535
|
+
|
|
536
|
+
operational = {"status", "tickets", "orgs", "projects", "performance", "perf", "health", "dispatch", "uri", "api", "get", "post", "endpoints"}
|
|
386
537
|
if command in operational or (command == "plans" and args.plans_command == "remote"):
|
|
387
538
|
client = OperationsClient(OperationSettings.from_environment())
|
|
388
539
|
raise SystemExit(run_operational_command(args, console, client))
|
|
@@ -400,6 +551,17 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
400
551
|
elif command == "vault":
|
|
401
552
|
raise SystemExit(_vault_command(chat, args, console))
|
|
402
553
|
elif command == "sessions":
|
|
554
|
+
if getattr(args, "delete", None):
|
|
555
|
+
removed = store.delete_session(args.delete)
|
|
556
|
+
console.print(f"Usunięto sesję [cyan]{args.delete}[/cyan]." if removed else f"Nie znaleziono sesji {args.delete}.")
|
|
557
|
+
raise SystemExit(0 if removed else 1)
|
|
558
|
+
if getattr(args, "prune", False):
|
|
559
|
+
count = store.prune_sessions(
|
|
560
|
+
older_than_days=args.older_than_days,
|
|
561
|
+
empty_only=args.empty_only,
|
|
562
|
+
)
|
|
563
|
+
console.print(f"Wyczyszczono [cyan]{count}[/cyan] sesji.")
|
|
564
|
+
raise SystemExit(0)
|
|
403
565
|
sessions = store.list_sessions()
|
|
404
566
|
if args.json:
|
|
405
567
|
console.print_json(
|
|
@@ -423,6 +585,7 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
423
585
|
for item in sessions:
|
|
424
586
|
table.add_row(item.id, item.name, item.provider, item.model, item.updated_at)
|
|
425
587
|
console.print(table)
|
|
588
|
+
|
|
426
589
|
elif command == "export":
|
|
427
590
|
payload = store.export_session(args.session)
|
|
428
591
|
output = args.output.expanduser()
|
|
@@ -464,8 +627,88 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
464
627
|
for item in chat.orchestration.registry.list():
|
|
465
628
|
table.add_row(item.name, item.kind, item.effect, ", ".join(item.allowed_operations))
|
|
466
629
|
console.print(table)
|
|
630
|
+
elif command == "login":
|
|
631
|
+
from .auth import login_with_email, mask_email
|
|
632
|
+
control_url = getattr(config, "control_url", "http://192.168.188.212:8091")
|
|
633
|
+
try:
|
|
634
|
+
res = login_with_email(args.email, control_url=control_url)
|
|
635
|
+
console.print(f"[green]✓[/green] Wysłano link zatwierdzający na adres [cyan]{res.get('masked_email', args.email)}[/cyan].")
|
|
636
|
+
console.print("[dim]Kliknij link w wiadomości e-mail, aby aktywować sesję CLI.[/dim]")
|
|
637
|
+
return
|
|
638
|
+
except Exception as err:
|
|
639
|
+
Console(stderr=True).print(f"[red]Błąd logowania:[/red] {err}")
|
|
640
|
+
raise SystemExit(1)
|
|
641
|
+
elif command == "auth":
|
|
642
|
+
from .auth import probe_auth_session, default_session_path
|
|
643
|
+
control_url = getattr(config, "control_url", "http://192.168.188.212:8091")
|
|
644
|
+
bearer_token = os.environ.get("SUBACTOR_ADMIN_TOKEN")
|
|
645
|
+
probe = probe_auth_session(control_url, bearer_token)
|
|
646
|
+
if getattr(args, "json", False):
|
|
647
|
+
console.print_json(json.dumps(probe, ensure_ascii=False))
|
|
648
|
+
return
|
|
649
|
+
console.print(f"[bold] Uwierzytelnianie Process Control / SaaS[/bold]")
|
|
650
|
+
console.print(f" [dim]Adres usługi:[/dim] [cyan]{control_url}[/cyan]")
|
|
651
|
+
console.print(f" [dim]Status sesji:[/dim] {'[green]Uwierzytelniony[/green]' if probe.get('authenticated') else '[yellow]Sesja anonimowa / lokalna[/yellow]'}")
|
|
652
|
+
if probe.get("identity"):
|
|
653
|
+
console.print(f" [dim]Tożsamość:[/dim] [bold]{probe['identity']}[/bold]")
|
|
654
|
+
console.print(f" [dim]Plik sesji:[/dim] {default_session_path()}")
|
|
655
|
+
return
|
|
656
|
+
elif command == "backup":
|
|
657
|
+
import httpx
|
|
658
|
+
control_url = getattr(config, "control_url", "http://192.168.188.212:8091")
|
|
659
|
+
bearer_token = os.environ.get("SUBACTOR_ADMIN_TOKEN")
|
|
660
|
+
headers = {"Authorization": f"Bearer {bearer_token}"} if bearer_token else {}
|
|
661
|
+
|
|
662
|
+
if args.backup_command == "create":
|
|
663
|
+
with httpx.Client(timeout=30.0) as client:
|
|
664
|
+
resp = client.post(f"{control_url.rstrip('/')}/api/account/backup/export", json={"tenant_id": args.tenant}, headers=headers)
|
|
665
|
+
if resp.status_code != 200:
|
|
666
|
+
raise RuntimeError(f"Błąd tworzenia backupu: {resp.text}")
|
|
667
|
+
data = resp.json()
|
|
668
|
+
if getattr(args, "json", False):
|
|
669
|
+
console.print_json(json.dumps(data, ensure_ascii=False))
|
|
670
|
+
return
|
|
671
|
+
console.print(f"[green]✓[/green] Utworzono kopię zapasową konta [bold]{data.get('tenant_id')}[/bold]")
|
|
672
|
+
console.print(f" [dim]Archiwum:[/dim] [cyan]{data.get('archive_path')}[/cyan]")
|
|
673
|
+
console.print(f" [dim]Rozmiar:[/dim] {data.get('size_bytes')} bajtów ({data.get('files_count')} plików)")
|
|
674
|
+
console.print(f" [dim]SHA-256:[/dim] {data.get('sha256')[:16]}...")
|
|
675
|
+
return
|
|
676
|
+
|
|
677
|
+
if args.backup_command == "list":
|
|
678
|
+
with httpx.Client(timeout=10.0) as client:
|
|
679
|
+
resp = client.get(f"{control_url.rstrip('/')}/api/account/backup/list", headers=headers)
|
|
680
|
+
if resp.status_code != 200:
|
|
681
|
+
raise RuntimeError(f"Błąd pobierania listy backupów: {resp.text}")
|
|
682
|
+
data = resp.json()
|
|
683
|
+
if getattr(args, "json", False):
|
|
684
|
+
console.print_json(json.dumps(data, ensure_ascii=False))
|
|
685
|
+
return
|
|
686
|
+
table = Table("Plik kopii zapasowej", "Rozmiar", "Data utworzenia")
|
|
687
|
+
for b in data.get("backups", []):
|
|
688
|
+
table.add_row(b.get("file_name", ""), f"{b.get('size_bytes', 0)} B", b.get("created_at", ""))
|
|
689
|
+
console.print(table)
|
|
690
|
+
return
|
|
691
|
+
|
|
692
|
+
if args.backup_command == "restore":
|
|
693
|
+
with httpx.Client(timeout=30.0) as client:
|
|
694
|
+
resp = client.post(
|
|
695
|
+
f"{control_url.rstrip('/')}/api/account/backup/restore",
|
|
696
|
+
json={"archive_path": args.archive, "target_dir": args.target_dir, "dry_run": args.dry_run},
|
|
697
|
+
headers=headers,
|
|
698
|
+
)
|
|
699
|
+
if resp.status_code != 200:
|
|
700
|
+
raise RuntimeError(f"Błąd przywracania backupu: {resp.text}")
|
|
701
|
+
data = resp.json()
|
|
702
|
+
console.print(f"[green]✓[/green] Przywrócono dane z kopii [bold]{data.get('backup_id')}[/bold]")
|
|
703
|
+
console.print(f" [dim]Zweryfikowano plików:[/dim] {len(data.get('verified_files', []))}")
|
|
704
|
+
return
|
|
467
705
|
elif command == "doctor":
|
|
468
706
|
raise SystemExit(_doctor(config, store, chat, console))
|
|
707
|
+
elif command in {"fleet", "prs"}:
|
|
708
|
+
hyperlinks = terminal_hyperlinks_enabled(is_terminal=console.is_terminal)
|
|
709
|
+
overview = fetch_fleet_overview()
|
|
710
|
+
render_fleet_startup_banner(console, overview, hyperlinks=hyperlinks)
|
|
711
|
+
return
|
|
469
712
|
elif command == "acp-agent":
|
|
470
713
|
asyncio.run(AcpAgent(chat).run_stdio())
|
|
471
714
|
else:
|