mirror of
https://github.com/qwibitai/nanoclaw.git
synced 2026-06-12 18:11:51 +08:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0435736314 | |||
| 384f9c29e3 | |||
| aa8597acf8 | |||
| 3ae4ba18c3 | |||
| de88be8a7a | |||
| b9141218ad | |||
| 341b5950e1 | |||
| 8cb4ed27ef |
@@ -98,13 +98,13 @@ for i in $(seq 1 15); do
|
||||
done
|
||||
```
|
||||
|
||||
If it never becomes healthy, check if the gateway process is running:
|
||||
If it never becomes healthy, check the gateway containers. The gateway is a Docker Compose stack (project `onecli`, compose file at `~/.onecli/docker-compose.yml`), **not** a host process — `ps aux | grep onecli` will not find it, and there is no `onecli start` command (removed in OneCLI 1.4.x).
|
||||
|
||||
```bash
|
||||
ps aux | grep -i onecli | grep -v grep
|
||||
docker ps -a --filter "label=com.docker.compose.project=onecli" --format '{{.Names}}\t{{.Status}}'
|
||||
```
|
||||
|
||||
If it's not running, try starting it manually: `onecli start`. If that fails, show the error and stop — the user needs to debug their OneCLI installation.
|
||||
Both services have `restart: unless-stopped`, so they come back automatically once the Docker daemon is up. If Docker isn't running, start it (`open -a Docker` on macOS) and they'll restart on their own. To bring the stack up manually: `docker compose -f ~/.onecli/docker-compose.yml up -d`. If that fails, show the error and stop — the user needs to debug their OneCLI installation.
|
||||
|
||||
## Phase 3: Migrate existing credentials
|
||||
|
||||
@@ -299,7 +299,7 @@ If an agent uses `git` or `gh`, add to `data/v2-sessions/<agent-group-id>/.claud
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**"OneCLI gateway not reachable" in logs:** The gateway isn't running. Check with `curl -sf ${ONECLI_URL}/health`. Start it with `onecli start` if needed.
|
||||
**"OneCLI gateway not reachable" in logs:** The gateway isn't running. Check with `curl -sf ${ONECLI_URL}/health`. The most common cause is that Docker itself is down (the gateway is a Compose stack) — start Docker (`open -a Docker` on macOS) and the containers restart automatically. To bring them up manually: `docker compose -f ~/.onecli/docker-compose.yml up -d`.
|
||||
|
||||
**Container gets no credentials:** Verify `ONECLI_URL` is set in `.env` and the gateway has an Anthropic secret (`onecli secrets list`).
|
||||
|
||||
|
||||
@@ -153,31 +153,17 @@ Key files: `src/container-restart.ts`, `src/container-runner.ts` (`killContainer
|
||||
|
||||
API keys, OAuth tokens, and auth credentials are managed by the OneCLI gateway. Secrets are injected into per-agent containers at request time — none are passed in env vars or through chat context. The container agent sees this via the `onecli-gateway` container skill (`container/skills/onecli-gateway/SKILL.md`), which teaches it how the proxy works, how to handle auth errors, and to never ask for raw credentials. Host-side wiring: `src/onecli-approvals.ts`, `ensureAgent()` in `container-runner.ts`. Run `onecli --help`.
|
||||
|
||||
### Gotcha: auto-created agents start in `selective` secret mode
|
||||
### Secret modes
|
||||
|
||||
When the host first spawns a session for a new agent group, `container-runner.ts:385` calls `onecli.ensureAgent({ name, identifier })`. The OneCLI `POST /api/agents` endpoint creates the agent in **`selective`** secret mode — meaning **no secrets are assigned to it by default**, even if the secrets exist in the vault and have host patterns that would otherwise match.
|
||||
|
||||
Symptom: container starts, the proxy + CA cert are wired correctly, but the agent gets `401 Unauthorized` (or similar) from APIs whose credentials *are* in the vault. The credential just isn't in this agent's allow-list.
|
||||
|
||||
The SDK does not expose `setSecretMode` — the only fix is the CLI (or the web UI at `http://127.0.0.1:10254`).
|
||||
Auto-created agents default to `all` secret mode — every vault secret whose host pattern matches is injected automatically, so the common case needs no per-agent setup. If an agent is in `selective` mode it gets no secrets until you assign them, which shows up as a `401` from an API whose credential *is* in the vault. The SDK can't change this; use the CLI (or the web UI at `http://127.0.0.1:10254`):
|
||||
|
||||
```bash
|
||||
# Find the agent (identifier is the agent group id)
|
||||
onecli agents list
|
||||
|
||||
# Flip to "all" so every vault secret with a matching host pattern gets injected
|
||||
onecli agents set-secret-mode --id <agent-id> --mode all
|
||||
|
||||
# Or, stay selective and assign specific secrets
|
||||
onecli secrets list # find secret ids
|
||||
onecli agents set-secrets --id <agent-id> --secret-ids <id1>,<id2>
|
||||
|
||||
# Inspect what an agent currently has
|
||||
onecli agents secrets --id <agent-id> # secrets assigned to this agent
|
||||
onecli secrets list # all vault secrets (with host patterns)
|
||||
onecli agents list # check secretMode
|
||||
onecli agents set-secret-mode --id <agent-id> --mode all # inject all matching secrets
|
||||
onecli agents set-secrets --id <agent-id> --secret-ids ... # or stay selective, assign specific ones
|
||||
```
|
||||
|
||||
If you've just enabled `mode all`, no container restart is needed — the gateway looks up secrets per request, so the next API call from the running container will see the new credentials.
|
||||
No container restart needed — the gateway looks up secrets per request.
|
||||
|
||||
### Requiring approval for credential use
|
||||
|
||||
|
||||
@@ -63,34 +63,63 @@ function curl(args: string[], input?: string): { ok: boolean; out: string } {
|
||||
return { ok: r.status === 0, out: (r.stdout ?? '') + (r.stderr ?? '') };
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup instructions for when whoami fails. `body` is the gateway's error
|
||||
* JSON (when the request was proxied through OneCLI). We surface the URL it
|
||||
* hands back — `secret_url` for an unknown host (HF's case), `connect_url`
|
||||
* for an OAuth app, `manage_url` when the secret exists but this agent lacks
|
||||
* access — so the link always points at the right gateway (local or hosted).
|
||||
*/
|
||||
function notSignedInMessage(body: string): string {
|
||||
let setupUrl: string | undefined;
|
||||
try {
|
||||
const e = JSON.parse(body) as { secret_url?: string; connect_url?: string; manage_url?: string };
|
||||
if (e.secret_url) {
|
||||
// The pre-filled `path` defaults to the failing request path
|
||||
// (/api/whoami-v2); broaden it to /* so the secret covers the upload
|
||||
// endpoints too, not just whoami.
|
||||
setupUrl = e.secret_url.replace(/([?&]path=)[^&]*/, '$1%2F*');
|
||||
} else {
|
||||
setupUrl = e.connect_url ?? e.manage_url;
|
||||
}
|
||||
} catch {
|
||||
/* non-JSON body (e.g. HF's own error, or no gateway) — generic fallback */
|
||||
}
|
||||
const lines = [
|
||||
"Can't upload — no Hugging Face token is available to this agent. To set it up:",
|
||||
'',
|
||||
'1. Create a token with WRITE access at https://huggingface.co/settings/tokens',
|
||||
' (New token → type "Write" → copy it).',
|
||||
'',
|
||||
setupUrl
|
||||
? `2. Add it to OneCLI here (host pattern pre-filled): ${setupUrl}`
|
||||
: '2. Add it to the OneCLI vault as a secret with host pattern huggingface.co',
|
||||
'',
|
||||
'Then run /upload-trace again — no restart needed.',
|
||||
];
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
/** Returns a user-facing status line. Never throws. */
|
||||
export function uploadTrace(): string {
|
||||
const file = newestTranscript();
|
||||
if (!file) return 'No transcript to upload for this session yet.';
|
||||
|
||||
const who = curl(['-sf', 'https://huggingface.co/api/whoami-v2']);
|
||||
if (!who.ok) {
|
||||
return [
|
||||
"Can't upload — no Hugging Face token is available to this agent. To set it up:",
|
||||
'',
|
||||
'1. Create a token with WRITE access at https://huggingface.co/settings/tokens',
|
||||
' (New token → type "Write" → copy it).',
|
||||
'',
|
||||
'2. Add it to the OneCLI vault. Open the dashboard — remotely at https://app.onecli.sh/',
|
||||
' or on the host at http://127.0.0.1:10254 — then Secrets → New secret,',
|
||||
' paste the token, and set the host pattern to huggingface.co',
|
||||
'',
|
||||
'3. Assign it to this agent — new agents start with no secrets attached.',
|
||||
' In the same dashboard, open this agent and set its secret mode to "all"; or from the host run:',
|
||||
' onecli agents list # find this agent\'s id',
|
||||
' onecli agents set-secret-mode --id <agent-id> --mode all',
|
||||
'',
|
||||
'Then run /upload-trace again — no restart needed.',
|
||||
].join('\n');
|
||||
// whoami, capturing the body + HTTP status (no -f, so the gateway's error
|
||||
// JSON survives a 401). When no token is available the OneCLI gateway
|
||||
// returns a setup URL pre-filled for *this* gateway — so we never hardcode
|
||||
// local-vs-hosted dashboard links, and never have to know which it is.
|
||||
const who = curl(['-s', '-w', '\n%{http_code}', 'https://huggingface.co/api/whoami-v2']);
|
||||
const nl = who.out.lastIndexOf('\n');
|
||||
const body = nl === -1 ? '' : who.out.slice(0, nl);
|
||||
const status = nl === -1 ? who.out.trim() : who.out.slice(nl + 1).trim();
|
||||
|
||||
if (status !== '200') {
|
||||
return notSignedInMessage(body);
|
||||
}
|
||||
let user: string | undefined;
|
||||
try {
|
||||
user = JSON.parse(who.out)?.name;
|
||||
user = JSON.parse(body)?.name;
|
||||
} catch {
|
||||
/* fall through */
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "nanoclaw",
|
||||
"version": "2.0.71",
|
||||
"version": "2.0.73",
|
||||
"description": "Personal Claude assistant. Lightweight, secure, customizable.",
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@10.33.0",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="20" role="img" aria-label="179k tokens, 89% of context window">
|
||||
<title>179k tokens, 89% of context window</title>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="20" role="img" aria-label="181k tokens, 90% of context window">
|
||||
<title>181k tokens, 90% of context window</title>
|
||||
<linearGradient id="s" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
@@ -15,8 +15,8 @@
|
||||
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">
|
||||
<text aria-hidden="true" x="26" y="15" fill="#010101" fill-opacity=".3">tokens</text>
|
||||
<text x="26" y="14">tokens</text>
|
||||
<text aria-hidden="true" x="71" y="15" fill="#010101" fill-opacity=".3">179k</text>
|
||||
<text x="71" y="14">179k</text>
|
||||
<text aria-hidden="true" x="71" y="15" fill="#010101" fill-opacity=".3">181k</text>
|
||||
<text x="71" y="14">181k</text>
|
||||
</g>
|
||||
</g>
|
||||
</a>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Reference in New Issue
Block a user