arkclaw-webchat-cli 0.2.0__tar.gz → 0.4.0__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.
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/PKG-INFO +30 -17
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/README.md +29 -16
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/pyproject.toml +1 -1
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/cli.py +14 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/config.py +39 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/flows.py +113 -82
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/.gitignore +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/__init__.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/attachments.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/control.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/core.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/doctor.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/errors.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/identity.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/oauth.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/output.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/policy.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/providers.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/secrets_store.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/sts.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/transport/__init__.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/transport/a2a.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/transport/base.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/transport/openclaw.py +0 -0
- {arkclaw_webchat_cli-0.2.0 → arkclaw_webchat_cli-0.4.0}/src/ee_claw/update.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arkclaw-webchat-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: CLI to chat with an ArkClaw EE space's Claw over enterprise SSO — zero permanent AK/SK.
|
|
5
5
|
Author: ArkClaw Team
|
|
6
6
|
Keywords: arkclaw,cli,ee,openclaw,sso,sts
|
|
@@ -29,9 +29,10 @@ own SSO identity, no passwords, no permanent keys.** Works the same for a human
|
|
|
29
29
|
at a prompt and for another agent/script (`--json` + exit codes).
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
arkclaw
|
|
33
|
-
arkclaw
|
|
34
|
-
arkclaw
|
|
32
|
+
arkclaw init # one-time interactive setup (asks only what it can't auto-detect)
|
|
33
|
+
arkclaw login # browser SSO
|
|
34
|
+
arkclaw agents # list the claws you can chat
|
|
35
|
+
arkclaw chat ci-xxxxxxxx
|
|
35
36
|
```
|
|
36
37
|
|
|
37
38
|
---
|
|
@@ -39,7 +40,7 @@ arkclaw chat ci-xxxxxxxx # talk to
|
|
|
39
40
|
## What it can do
|
|
40
41
|
|
|
41
42
|
- **Log in as you** — browser SSO (PKCE), short-lived token, auto-refresh. No AK/SK, no client secret, nothing permanent on disk but a token in your OS keychain.
|
|
42
|
-
- **List your agents** — `arkclaw agents` shows the claws you
|
|
43
|
+
- **List your agents** — `arkclaw agents` shows the claws you've chatted with (kept locally; add one with `arkclaw chat ci-...`).
|
|
43
44
|
- **Chat** — interactive REPL or one-shot `-m`, streaming token-by-token, with a live "what's it doing" spinner and tool-event trace.
|
|
44
45
|
- **Talk to a specific claw** — by id (`chat ci-...`) or by name (`chat 答疑助手`).
|
|
45
46
|
- **Manage a claw's files** — read/write its managed workspace files (`AGENTS.md`, `SOUL.md`, `MEMORY.md`, …) with `ls` / `pull` / `push`.
|
|
@@ -68,10 +69,15 @@ uv venv && uv pip install -e .
|
|
|
68
69
|
## Quick start
|
|
69
70
|
|
|
70
71
|
```bash
|
|
71
|
-
#
|
|
72
|
-
|
|
72
|
+
# 0. one-time setup — interactive. Asks for your space address and (only if the
|
|
73
|
+
# space doesn't already publish them) the CLI client + STS role. Saved to
|
|
74
|
+
# ~/.arkclaw/defaults.json so you never type them again.
|
|
75
|
+
arkclaw init
|
|
76
|
+
|
|
77
|
+
# 1. log in (a browser opens; sign in with SSO)
|
|
78
|
+
arkclaw login
|
|
73
79
|
|
|
74
|
-
# 2.
|
|
80
|
+
# 2. list the claws you've used (empty at first — you add them by chatting)
|
|
75
81
|
arkclaw agents
|
|
76
82
|
|
|
77
83
|
# 3. chat — interactive…
|
|
@@ -80,9 +86,10 @@ arkclaw chat ci-xxxxxxxx
|
|
|
80
86
|
arkclaw chat ci-xxxxxxxx -m "用一句话介绍你自己"
|
|
81
87
|
```
|
|
82
88
|
|
|
83
|
-
`
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
`arkclaw init` resolves everything it can from the address (identity pool,
|
|
90
|
+
region) and only prompts for what's missing. Once your space publishes
|
|
91
|
+
auto-discovery, `init` asks for nothing but the address — and you can even skip
|
|
92
|
+
straight to `arkclaw login https://your-space...`. See **[Configuration](#configuration)**.
|
|
86
93
|
|
|
87
94
|
---
|
|
88
95
|
|
|
@@ -90,8 +97,9 @@ STS role once — see **[Configuration](#configuration)**.
|
|
|
90
97
|
|
|
91
98
|
| Command | What it does |
|
|
92
99
|
|---|---|
|
|
93
|
-
| `arkclaw
|
|
94
|
-
| `arkclaw
|
|
100
|
+
| `arkclaw init [address]` | One-time interactive setup. Saves the address + (only what isn't auto-discovered) the CLI client and STS role to `~/.arkclaw/defaults.json`, so later commands need no env/flags. |
|
|
101
|
+
| `arkclaw login [space-url]` | Browser SSO login. Uses `init` defaults if you omit the URL. `--transport a2a --endpoint <url>` for an agent endpoint. `--clawid ci-...` sets a default claw. Bare `arkclaw login` re-logs into the previous space. |
|
|
102
|
+
| `arkclaw agents` | List the claws you've used from this machine (local history, newest first) + your default claw. Add a claw by chatting it once (`arkclaw chat ci-...`). Not a space-wide directory. |
|
|
95
103
|
| `arkclaw chat [TARGET] [MSG]` | Chat. `TARGET` = a claw id (`ci-...`), an agent name (from `agents`), or a profile. With `MSG` → one-shot; without → interactive REPL. `-f` attach files, `-o` write the reply, `--session NAME` name the conversation, `--approve-all` auto-approve tool runs. |
|
|
96
104
|
| `arkclaw <name>` | Shortcut: `arkclaw 答疑助手` ≡ `arkclaw chat 答疑助手`. |
|
|
97
105
|
| `arkclaw ls` | List the claw's managed workspace files. |
|
|
@@ -161,9 +169,14 @@ arkclaw ls --clawid ci-other # a different claw
|
|
|
161
169
|
## Configuration
|
|
162
170
|
|
|
163
171
|
`login` resolves config in this order: **explicit flag → `ARKCLAW_*` env →
|
|
164
|
-
the space's published discovery →
|
|
165
|
-
|
|
166
|
-
|
|
172
|
+
the space's published discovery → `arkclaw init` defaults → derived from the
|
|
173
|
+
address**. You normally only type the address.
|
|
174
|
+
|
|
175
|
+
**The easy way — `arkclaw init`** asks once (interactively) for whatever the
|
|
176
|
+
space doesn't yet auto-publish, and saves it. After that, just `arkclaw login`.
|
|
177
|
+
|
|
178
|
+
**The env way** (scripts/CI, or instead of `init`) — supply the same non-secret
|
|
179
|
+
values via env:
|
|
167
180
|
|
|
168
181
|
| Env | Purpose |
|
|
169
182
|
|---|---|
|
|
@@ -171,7 +184,7 @@ supply these (non-secret) via env:
|
|
|
171
184
|
| `ARKCLAW_ROLE_TRN` | STS role that mints the ChatToken (openclaw). |
|
|
172
185
|
| `ARKCLAW_REGION` / `ARKCLAW_SPACE_ID` | Override region / space (usually auto). |
|
|
173
186
|
|
|
174
|
-
Other config: `~/.arkclaw/session.json` (routing, 0600), `~/.arkclaw/cmdpolicy.json` (tool-approval allow/deny), OS keychain (tokens).
|
|
187
|
+
Other config: `~/.arkclaw/defaults.json` (`init` answers, 0600), `~/.arkclaw/session.json` (routing, 0600), `~/.arkclaw/cmdpolicy.json` (tool-approval allow/deny), OS keychain (tokens).
|
|
175
188
|
|
|
176
189
|
> **Escape hatch (admin/test):** `arkclaw login <url> --transport openclaw --static-creds` uses `VOLCENGINE_ACCESS_KEY`/`SECRET_KEY` from the env instead of SSO — convenient for CI, but it's account-level keys, not identity-through.
|
|
177
190
|
|
|
@@ -5,9 +5,10 @@ own SSO identity, no passwords, no permanent keys.** Works the same for a human
|
|
|
5
5
|
at a prompt and for another agent/script (`--json` + exit codes).
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
arkclaw
|
|
9
|
-
arkclaw
|
|
10
|
-
arkclaw
|
|
8
|
+
arkclaw init # one-time interactive setup (asks only what it can't auto-detect)
|
|
9
|
+
arkclaw login # browser SSO
|
|
10
|
+
arkclaw agents # list the claws you can chat
|
|
11
|
+
arkclaw chat ci-xxxxxxxx
|
|
11
12
|
```
|
|
12
13
|
|
|
13
14
|
---
|
|
@@ -15,7 +16,7 @@ arkclaw chat ci-xxxxxxxx # talk to
|
|
|
15
16
|
## What it can do
|
|
16
17
|
|
|
17
18
|
- **Log in as you** — browser SSO (PKCE), short-lived token, auto-refresh. No AK/SK, no client secret, nothing permanent on disk but a token in your OS keychain.
|
|
18
|
-
- **List your agents** — `arkclaw agents` shows the claws you
|
|
19
|
+
- **List your agents** — `arkclaw agents` shows the claws you've chatted with (kept locally; add one with `arkclaw chat ci-...`).
|
|
19
20
|
- **Chat** — interactive REPL or one-shot `-m`, streaming token-by-token, with a live "what's it doing" spinner and tool-event trace.
|
|
20
21
|
- **Talk to a specific claw** — by id (`chat ci-...`) or by name (`chat 答疑助手`).
|
|
21
22
|
- **Manage a claw's files** — read/write its managed workspace files (`AGENTS.md`, `SOUL.md`, `MEMORY.md`, …) with `ls` / `pull` / `push`.
|
|
@@ -44,10 +45,15 @@ uv venv && uv pip install -e .
|
|
|
44
45
|
## Quick start
|
|
45
46
|
|
|
46
47
|
```bash
|
|
47
|
-
#
|
|
48
|
-
|
|
48
|
+
# 0. one-time setup — interactive. Asks for your space address and (only if the
|
|
49
|
+
# space doesn't already publish them) the CLI client + STS role. Saved to
|
|
50
|
+
# ~/.arkclaw/defaults.json so you never type them again.
|
|
51
|
+
arkclaw init
|
|
52
|
+
|
|
53
|
+
# 1. log in (a browser opens; sign in with SSO)
|
|
54
|
+
arkclaw login
|
|
49
55
|
|
|
50
|
-
# 2.
|
|
56
|
+
# 2. list the claws you've used (empty at first — you add them by chatting)
|
|
51
57
|
arkclaw agents
|
|
52
58
|
|
|
53
59
|
# 3. chat — interactive…
|
|
@@ -56,9 +62,10 @@ arkclaw chat ci-xxxxxxxx
|
|
|
56
62
|
arkclaw chat ci-xxxxxxxx -m "用一句话介绍你自己"
|
|
57
63
|
```
|
|
58
64
|
|
|
59
|
-
`
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
`arkclaw init` resolves everything it can from the address (identity pool,
|
|
66
|
+
region) and only prompts for what's missing. Once your space publishes
|
|
67
|
+
auto-discovery, `init` asks for nothing but the address — and you can even skip
|
|
68
|
+
straight to `arkclaw login https://your-space...`. See **[Configuration](#configuration)**.
|
|
62
69
|
|
|
63
70
|
---
|
|
64
71
|
|
|
@@ -66,8 +73,9 @@ STS role once — see **[Configuration](#configuration)**.
|
|
|
66
73
|
|
|
67
74
|
| Command | What it does |
|
|
68
75
|
|---|---|
|
|
69
|
-
| `arkclaw
|
|
70
|
-
| `arkclaw
|
|
76
|
+
| `arkclaw init [address]` | One-time interactive setup. Saves the address + (only what isn't auto-discovered) the CLI client and STS role to `~/.arkclaw/defaults.json`, so later commands need no env/flags. |
|
|
77
|
+
| `arkclaw login [space-url]` | Browser SSO login. Uses `init` defaults if you omit the URL. `--transport a2a --endpoint <url>` for an agent endpoint. `--clawid ci-...` sets a default claw. Bare `arkclaw login` re-logs into the previous space. |
|
|
78
|
+
| `arkclaw agents` | List the claws you've used from this machine (local history, newest first) + your default claw. Add a claw by chatting it once (`arkclaw chat ci-...`). Not a space-wide directory. |
|
|
71
79
|
| `arkclaw chat [TARGET] [MSG]` | Chat. `TARGET` = a claw id (`ci-...`), an agent name (from `agents`), or a profile. With `MSG` → one-shot; without → interactive REPL. `-f` attach files, `-o` write the reply, `--session NAME` name the conversation, `--approve-all` auto-approve tool runs. |
|
|
72
80
|
| `arkclaw <name>` | Shortcut: `arkclaw 答疑助手` ≡ `arkclaw chat 答疑助手`. |
|
|
73
81
|
| `arkclaw ls` | List the claw's managed workspace files. |
|
|
@@ -137,9 +145,14 @@ arkclaw ls --clawid ci-other # a different claw
|
|
|
137
145
|
## Configuration
|
|
138
146
|
|
|
139
147
|
`login` resolves config in this order: **explicit flag → `ARKCLAW_*` env →
|
|
140
|
-
the space's published discovery →
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
the space's published discovery → `arkclaw init` defaults → derived from the
|
|
149
|
+
address**. You normally only type the address.
|
|
150
|
+
|
|
151
|
+
**The easy way — `arkclaw init`** asks once (interactively) for whatever the
|
|
152
|
+
space doesn't yet auto-publish, and saves it. After that, just `arkclaw login`.
|
|
153
|
+
|
|
154
|
+
**The env way** (scripts/CI, or instead of `init`) — supply the same non-secret
|
|
155
|
+
values via env:
|
|
143
156
|
|
|
144
157
|
| Env | Purpose |
|
|
145
158
|
|---|---|
|
|
@@ -147,7 +160,7 @@ supply these (non-secret) via env:
|
|
|
147
160
|
| `ARKCLAW_ROLE_TRN` | STS role that mints the ChatToken (openclaw). |
|
|
148
161
|
| `ARKCLAW_REGION` / `ARKCLAW_SPACE_ID` | Override region / space (usually auto). |
|
|
149
162
|
|
|
150
|
-
Other config: `~/.arkclaw/session.json` (routing, 0600), `~/.arkclaw/cmdpolicy.json` (tool-approval allow/deny), OS keychain (tokens).
|
|
163
|
+
Other config: `~/.arkclaw/defaults.json` (`init` answers, 0600), `~/.arkclaw/session.json` (routing, 0600), `~/.arkclaw/cmdpolicy.json` (tool-approval allow/deny), OS keychain (tokens).
|
|
151
164
|
|
|
152
165
|
> **Escape hatch (admin/test):** `arkclaw login <url> --transport openclaw --static-creds` uses `VOLCENGINE_ACCESS_KEY`/`SECRET_KEY` from the env instead of SSO — convenient for CI, but it's account-level keys, not identity-through.
|
|
153
166
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "arkclaw-webchat-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "CLI to chat with an ArkClaw EE space's Claw over enterprise SSO — zero permanent AK/SK."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -47,6 +47,20 @@ def _run(emitter: Emitter, fn: Callable[[], Any]) -> None:
|
|
|
47
47
|
raise typer.Exit(emitter.ok(data))
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
@app.command()
|
|
51
|
+
def init(
|
|
52
|
+
address: str | None = typer.Argument(
|
|
53
|
+
None, metavar="[ADDRESS]", help="Space login URL; omit to be prompted."
|
|
54
|
+
),
|
|
55
|
+
json_mode: bool = _json_opt(),
|
|
56
|
+
) -> None:
|
|
57
|
+
"""One-time interactive setup: capture the login address + (only what isn't
|
|
58
|
+
auto-discovered) the CLI client and STS role, so later `arkclaw login`
|
|
59
|
+
needs no env vars or flags. Run this once, then `arkclaw login`."""
|
|
60
|
+
emitter = Emitter(json_mode=json_mode)
|
|
61
|
+
_run(emitter, lambda: flows.do_init(emitter, address))
|
|
62
|
+
|
|
63
|
+
|
|
50
64
|
@app.command()
|
|
51
65
|
def login(
|
|
52
66
|
address: str | None = typer.Argument(
|
|
@@ -176,6 +176,45 @@ def list_agent_names() -> list[str]:
|
|
|
176
176
|
return sorted(_load_agents())
|
|
177
177
|
|
|
178
178
|
|
|
179
|
+
# --- init defaults ------------------------------------------------------------
|
|
180
|
+
# What `arkclaw init` captures once (interactively) so later `arkclaw login`
|
|
181
|
+
# needs no env/flags: the address + the non-secret bits a platform-published
|
|
182
|
+
# /.well-known/arkclaw-cli would otherwise supply. Explicit, user-set — NOT the
|
|
183
|
+
# auto-learned cache. A live well-known still overrides these at login time.
|
|
184
|
+
|
|
185
|
+
_DEFAULTS_FILE = "defaults.json"
|
|
186
|
+
_DEFAULT_KEYS = (
|
|
187
|
+
"address", "issuer", "client_id", "transport", "region",
|
|
188
|
+
"role_trn", "provider_trn", "endpoint", "space_id", "clawid",
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _defaults_path() -> pathlib.Path:
|
|
193
|
+
return pathlib.Path.home() / ".arkclaw" / _DEFAULTS_FILE
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def load_defaults() -> dict[str, object]:
|
|
197
|
+
"""`arkclaw init` config (empty dict if none). Non-secret only."""
|
|
198
|
+
path = _defaults_path()
|
|
199
|
+
if not path.exists():
|
|
200
|
+
return {}
|
|
201
|
+
try:
|
|
202
|
+
data = json.loads(path.read_text())
|
|
203
|
+
except ValueError:
|
|
204
|
+
return {}
|
|
205
|
+
return {k: v for k, v in data.items() if k in _DEFAULT_KEYS and v} if isinstance(data, dict) else {}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def save_defaults(values: dict[str, object]) -> None:
|
|
209
|
+
"""Persist `arkclaw init` answers (whitelisted, non-None, non-secret)."""
|
|
210
|
+
keep = {k: v for k, v in values.items() if k in _DEFAULT_KEYS and v}
|
|
211
|
+
_dir()
|
|
212
|
+
fd = os.open(_defaults_path(), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
|
|
213
|
+
with os.fdopen(fd, "w") as f:
|
|
214
|
+
f.write(json.dumps(keep, ensure_ascii=False))
|
|
215
|
+
os.chmod(_defaults_path(), 0o600)
|
|
216
|
+
|
|
217
|
+
|
|
179
218
|
# --- Profiles (multi-pool / multi-claw) ---------------------------------------
|
|
180
219
|
# A profile is a named snapshot of the non-secret session config. Tokens are
|
|
181
220
|
# keyed by space host in the secret store, so switching profiles reattaches to
|
|
@@ -11,7 +11,6 @@ and every error is a typed :class:`~ee_claw.errors.ArkclawError`.
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
13
|
import asyncio
|
|
14
|
-
import dataclasses
|
|
15
14
|
import json
|
|
16
15
|
import os
|
|
17
16
|
import pathlib
|
|
@@ -105,6 +104,63 @@ def _is_userpool_address(host: str) -> bool:
|
|
|
105
104
|
return ".userpool.auth.id." in host or host.startswith("userpool-")
|
|
106
105
|
|
|
107
106
|
|
|
107
|
+
def do_init(emitter: Emitter, address: str | None = None) -> dict[str, Any]:
|
|
108
|
+
"""``arkclaw init``: one-time interactive setup. Captures the login address
|
|
109
|
+
plus the bits not yet auto-discoverable (the CLI's OAuth client, the STS
|
|
110
|
+
role) so that afterwards ``arkclaw login`` needs no env vars or flags.
|
|
111
|
+
Anything the space already self-describes (issuer, region, and — once the
|
|
112
|
+
platform publishes it — client_id/role) is auto-filled and not asked."""
|
|
113
|
+
if emitter.json or not sys.stdin.isatty():
|
|
114
|
+
raise ValidationError(
|
|
115
|
+
"arkclaw init 是交互式的,需要终端。",
|
|
116
|
+
hint="在终端直接运行 `arkclaw init`;脚本里改用 env(ARKCLAW_CLIENT_ID/ROLE_TRN)+ flags。",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def ask(label: str, default: str | None = None, *, required: bool = False) -> str | None:
|
|
120
|
+
suffix = f" [{default}]" if default else ""
|
|
121
|
+
while True:
|
|
122
|
+
ans = input(f" {label}{suffix}: ").strip() or (default or "")
|
|
123
|
+
if ans or not required:
|
|
124
|
+
return ans or None
|
|
125
|
+
emitter.line(" (必填)")
|
|
126
|
+
|
|
127
|
+
emitter.line("arkclaw init —— 一次性配置(能自动发现的不会问你)\n")
|
|
128
|
+
address = address or ask("空间登录地址 (https://…)", required=True)
|
|
129
|
+
base, host = _normalize(str(address))
|
|
130
|
+
disc = discover_cli_config(base) or {}
|
|
131
|
+
issuer = disc.get("issuer") or (f"https://{host}" if _is_userpool_address(host) else None)
|
|
132
|
+
region = disc.get("region") or derive_region(base)
|
|
133
|
+
if issuer:
|
|
134
|
+
emitter.line(" ✓ 自动发现身份池 issuer")
|
|
135
|
+
if region:
|
|
136
|
+
emitter.line(f" ✓ 区域: {region}")
|
|
137
|
+
|
|
138
|
+
# User-facing name is "webchat" (the ChatToken/wss path); internally it's
|
|
139
|
+
# the "openclaw" transport. "a2a" is unchanged.
|
|
140
|
+
disc_t = str(disc.get("transport") or "")
|
|
141
|
+
transport_in = ask("传输方式 (webchat/a2a)", default=("a2a" if disc_t == "a2a" else "webchat")) or "webchat"
|
|
142
|
+
transport = "a2a" if transport_in.lower() == "a2a" else "openclaw"
|
|
143
|
+
client_id = disc.get("client_id") or ask(
|
|
144
|
+
"CLI client_id(public OAuth 客户端,问管理员;平台发布发现后免此项)", required=True
|
|
145
|
+
)
|
|
146
|
+
role_trn: object = None
|
|
147
|
+
endpoint: object = None
|
|
148
|
+
if transport == "openclaw":
|
|
149
|
+
role_trn = disc.get("role_trn") or ask("STS role TRN (trn:iam::<账号>:role/<名字>)", required=True)
|
|
150
|
+
else:
|
|
151
|
+
endpoint = disc.get("endpoint") or ask("A2A endpoint (https://…)", required=True)
|
|
152
|
+
clawid = disc.get("clawid") or ask("默认 claw id (ci-…,可留空,登录后用 arkclaw agents 选)")
|
|
153
|
+
|
|
154
|
+
saved = {
|
|
155
|
+
"address": base, "issuer": issuer, "client_id": client_id, "transport": transport,
|
|
156
|
+
"region": region, "role_trn": role_trn, "provider_trn": disc.get("provider_trn"),
|
|
157
|
+
"endpoint": endpoint, "space_id": disc.get("space_id"), "clawid": clawid,
|
|
158
|
+
}
|
|
159
|
+
config_mod.save_defaults(saved)
|
|
160
|
+
emitter.line("\n✓ 配置已存到 ~/.arkclaw/defaults.json。现在运行: arkclaw login")
|
|
161
|
+
return {k: v for k, v in saved.items() if v}
|
|
162
|
+
|
|
163
|
+
|
|
108
164
|
def do_login(
|
|
109
165
|
url: str | None,
|
|
110
166
|
emitter: Emitter,
|
|
@@ -138,31 +194,34 @@ def do_login(
|
|
|
138
194
|
A bare ``arkclaw login`` (no URL) re-logs into the previous space,
|
|
139
195
|
inheriting every non-secret setting from the last login.
|
|
140
196
|
"""
|
|
197
|
+
defaults = config_mod.load_defaults() # what `arkclaw init` captured, if any
|
|
141
198
|
if not url:
|
|
142
199
|
prev = config_mod.load_config()
|
|
143
|
-
if
|
|
200
|
+
if prev and (prev.url or prev.space):
|
|
201
|
+
url = prev.url or f"https://{prev.space}"
|
|
202
|
+
emitter.line(f"↻ 重新登录上次的空间: {url}({prev.transport})")
|
|
203
|
+
transport = transport or prev.transport
|
|
204
|
+
endpoint = endpoint or prev.endpoint
|
|
205
|
+
region = region or prev.region
|
|
206
|
+
role_trn = role_trn or prev.role_trn
|
|
207
|
+
provider_trn = provider_trn or prev.provider_trn
|
|
208
|
+
issuer = issuer or prev.issuer
|
|
209
|
+
client_id = client_id or prev.client_id
|
|
210
|
+
clawid = clawid or prev.claw
|
|
211
|
+
elif defaults.get("address"):
|
|
212
|
+
url = str(defaults["address"])
|
|
213
|
+
emitter.line(f"↻ 用 arkclaw init 的配置登录: {url}")
|
|
214
|
+
else:
|
|
144
215
|
raise ValidationError(
|
|
145
216
|
"缺少空间地址(首次登录必填)。",
|
|
146
|
-
hint="
|
|
147
|
-
"之后裸 `arkclaw login` 即可续登上次的空间。",
|
|
217
|
+
hint="先跑 `arkclaw init` 配置一次,或 `arkclaw login https://<space-url>`。",
|
|
148
218
|
)
|
|
149
|
-
url = prev.url or f"https://{prev.space}"
|
|
150
|
-
emitter.line(f"↻ 重新登录上次的空间: {url}({prev.transport})")
|
|
151
|
-
transport = transport or prev.transport
|
|
152
|
-
endpoint = endpoint or prev.endpoint
|
|
153
|
-
region = region or prev.region
|
|
154
|
-
role_trn = role_trn or prev.role_trn
|
|
155
|
-
provider_trn = provider_trn or prev.provider_trn
|
|
156
|
-
issuer = issuer or prev.issuer
|
|
157
|
-
client_id = client_id or prev.client_id
|
|
158
|
-
clawid = clawid or prev.claw
|
|
159
219
|
base, host = _normalize(url)
|
|
160
220
|
|
|
161
|
-
#
|
|
162
|
-
#
|
|
163
|
-
#
|
|
164
|
-
|
|
165
|
-
disc = discover_cli_config(base) or {}
|
|
221
|
+
# Resolution: explicit flag > env > the space's published
|
|
222
|
+
# /.well-known/arkclaw-cli > `arkclaw init` defaults > derived. The live
|
|
223
|
+
# well-known overrides init defaults; nothing is hardcoded per space.
|
|
224
|
+
disc = {**defaults, **(discover_cli_config(base) or {})}
|
|
166
225
|
transport = transport or str(disc.get("transport") or "") or "openclaw"
|
|
167
226
|
clawid = clawid or disc.get("clawid") # type: ignore[assignment]
|
|
168
227
|
region = (
|
|
@@ -810,11 +869,16 @@ def _ago(ts: object) -> str:
|
|
|
810
869
|
|
|
811
870
|
|
|
812
871
|
def do_agents(emitter: Emitter) -> dict[str, Any]:
|
|
813
|
-
"""``arkclaw agents``: the agents/claws you can chat with
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
872
|
+
"""``arkclaw agents``: the agents/claws you can chat with.
|
|
873
|
+
|
|
874
|
+
openclaw → the claws YOU have used from this machine (kept locally, newest
|
|
875
|
+
first), plus your default claw. This is deliberately NOT a server-side
|
|
876
|
+
enumeration of the space — that API returns every member's claws to anyone
|
|
877
|
+
(no per-user scoping is reachable by the CLI's role), so we don't call it.
|
|
878
|
+
You add a claw to this list by chatting it once: ``arkclaw chat ci-...``.
|
|
879
|
+
|
|
880
|
+
a2a → the agent at the endpoint (its card). Profiles live under ``arkclaw
|
|
881
|
+
profile``, full conversation history under ``arkclaw sessions``."""
|
|
818
882
|
cfg = config_mod.load_config()
|
|
819
883
|
if not cfg or not cfg.space:
|
|
820
884
|
raise NoLoginError("尚未登录。", hint="先运行: arkclaw login <空间地址>")
|
|
@@ -871,73 +935,40 @@ def do_agents(emitter: Emitter) -> dict[str, Any]:
|
|
|
871
935
|
)
|
|
872
936
|
elif cfg.transport == "openclaw":
|
|
873
937
|
emitter.line(f"★ 当前登录(openclaw)· 空间 {cfg.space}")
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
assert isinstance(spaces, dict)
|
|
893
|
-
space_ids = [
|
|
894
|
-
str(s["SpaceId"]) for s in (spaces.get("Spaces") or [])
|
|
895
|
-
if isinstance(s, dict) and s.get("SpaceId")
|
|
896
|
-
]
|
|
897
|
-
if len(space_ids) == 1: # cache it so later calls skip discovery
|
|
898
|
-
config_mod.save_config(dataclasses.replace(cfg, space_id=space_ids[0]))
|
|
899
|
-
except ArkclawError as e:
|
|
900
|
-
current["claws_error"] = {"code": e.code, "message": e.message}
|
|
901
|
-
for sid in space_ids:
|
|
902
|
-
try:
|
|
903
|
-
result = redact_obj(control.call_action(
|
|
904
|
-
"ListClawInstances",
|
|
905
|
-
{"SpaceId": sid, "ProjectName": "default", "MaxResults": 100},
|
|
906
|
-
region=str(cfg.region), creds=creds,
|
|
907
|
-
))
|
|
908
|
-
assert isinstance(result, dict)
|
|
909
|
-
items = result.get("Items") or result.get("ClawInstances") or []
|
|
910
|
-
claws += [it for it in items if isinstance(it, dict)]
|
|
911
|
-
except ArkclawError as e:
|
|
912
|
-
current["claws_error"] = {"code": e.code, "message": e.message}
|
|
938
|
+
# The claws you can chat = the ones YOU have used from this machine,
|
|
939
|
+
# remembered locally — NOT a server enumeration of the space. The
|
|
940
|
+
# space-wide ListClawInstances returns every member's claws to anyone
|
|
941
|
+
# (no per-user scoping reachable by the CLI's role), so we don't call it.
|
|
942
|
+
# New claws enter via `arkclaw chat ci-...`; each chat is recorded
|
|
943
|
+
# (record_session) and shows up here next time.
|
|
944
|
+
seen: dict[str, float] = {}
|
|
945
|
+
for s in config_mod.list_sessions():
|
|
946
|
+
cid = s.get("claw")
|
|
947
|
+
if s.get("space") == cfg.space and cid:
|
|
948
|
+
ts = s.get("last_used")
|
|
949
|
+
seen[str(cid)] = max(seen.get(str(cid), 0.0), float(ts) if isinstance(ts, (int, float)) else 0.0)
|
|
950
|
+
if cfg.claw and str(cfg.claw) not in seen:
|
|
951
|
+
seen[str(cfg.claw)] = 0.0 # the default claw, even if not chatted yet
|
|
952
|
+
claws = [
|
|
953
|
+
{"ClawInstanceId": cid, "last_used": ts}
|
|
954
|
+
for cid, ts in sorted(seen.items(), key=lambda kv: kv[1], reverse=True)
|
|
955
|
+
]
|
|
913
956
|
current["claws"] = claws
|
|
914
|
-
|
|
915
|
-
cid = it.get("ClawInstanceId") or it.get("Id")
|
|
916
|
-
if it.get("Name") and cid:
|
|
917
|
-
config_mod.record_agent(str(it["Name"]), dataclasses.replace(cfg, claw=str(cid)))
|
|
957
|
+
current["source"] = "local-history"
|
|
918
958
|
if claws:
|
|
919
959
|
emitter.table(
|
|
920
|
-
["Claw(可 chat)", "
|
|
960
|
+
["Claw(可 chat)", "上次使用", "默认"],
|
|
921
961
|
[
|
|
922
962
|
[
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
"★" if (it.get("ClawInstanceId") or it.get("Id")) == cfg.claw else "",
|
|
963
|
+
str(c["ClawInstanceId"]),
|
|
964
|
+
_ago(c["last_used"]) if c["last_used"] else "—",
|
|
965
|
+
"★" if c["ClawInstanceId"] == cfg.claw else "",
|
|
927
966
|
]
|
|
928
|
-
for
|
|
967
|
+
for c in claws
|
|
929
968
|
],
|
|
930
969
|
)
|
|
931
970
|
else:
|
|
932
|
-
|
|
933
|
-
emitter.table(
|
|
934
|
-
["Claw(可 chat)", "状态", "说明"],
|
|
935
|
-
[[
|
|
936
|
-
cfg.claw or "(用 --clawid 直连)",
|
|
937
|
-
"默认目标" if cfg.claw else "—",
|
|
938
|
-
f"列表不可用: {err['code']}" if isinstance(err, dict) else "没有可用 claw",
|
|
939
|
-
]],
|
|
940
|
-
)
|
|
971
|
+
emitter.line(" 还没用过任何 claw。用 `arkclaw chat ci-xxxx` 开始,之后这里会记住它。")
|
|
941
972
|
|
|
942
973
|
# `agents` is the list of agents you can chat — nothing else. Saved
|
|
943
974
|
# profiles live under `arkclaw profile`, recent sessions under
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|