fix: delete key generation commands and fixing session ID
This commit is contained in:
parent
8b32aa4622
commit
457c972a0f
|
|
@ -101,12 +101,6 @@ honcho # show banner + command list
|
|||
| `honcho conclusion create` | Create a conclusion |
|
||||
| `honcho conclusion delete <id>` | Delete a conclusion |
|
||||
|
||||
### Keys
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `honcho key generate` | Generate a scoped JWT (workspace/peer/session) |
|
||||
|
||||
### Config
|
||||
|
||||
| Command | Description |
|
||||
|
|
|
|||
|
|
@ -1,119 +0,0 @@
|
|||
"""Key management commands: generate scoped JWTs."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Optional
|
||||
|
||||
import typer
|
||||
|
||||
from honcho_cli.output import print_error, print_result, status
|
||||
|
||||
from honcho_cli.common import add_common_options
|
||||
|
||||
app = typer.Typer(help="JWT key management.")
|
||||
add_common_options(app)
|
||||
|
||||
|
||||
def _parse_duration(duration: str) -> datetime:
|
||||
"""Parse duration string like '30d', '24h', '90d' into expiry datetime."""
|
||||
match = re.match(r"^(\d+)([dhm])$", duration)
|
||||
if not match:
|
||||
print_error(
|
||||
"INVALID_DURATION",
|
||||
f"Invalid duration format: '{duration}'. Use format like '30d', '24h', '60m'.",
|
||||
{"duration": duration},
|
||||
)
|
||||
raise typer.Exit(1)
|
||||
|
||||
value = int(match.group(1))
|
||||
unit = match.group(2)
|
||||
|
||||
delta = {
|
||||
"d": timedelta(days=value),
|
||||
"h": timedelta(hours=value),
|
||||
"m": timedelta(minutes=value),
|
||||
}[unit]
|
||||
|
||||
return datetime.now(timezone.utc) + delta
|
||||
|
||||
|
||||
@app.command()
|
||||
def generate(
|
||||
workspace: Optional[str] = typer.Option(None, "--workspace", help="Scope to workspace"),
|
||||
peer: Optional[str] = typer.Option(None, "--peer", help="Scope to peer"),
|
||||
session: Optional[str] = typer.Option(None, "--session", help="Scope to session"),
|
||||
admin: bool = typer.Option(False, "--admin", help="Generate admin key"),
|
||||
expires: str = typer.Option("90d", "--expires", help="Expiry duration (e.g., 30d, 24h)"),
|
||||
no_expire: bool = typer.Option(False, "--no-expire", help="No expiration (use with caution)"),
|
||||
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
|
||||
) -> None:
|
||||
"""Generate a scoped JWT key.
|
||||
|
||||
Requires an admin JWT in config. Generated keys are scoped down from admin.
|
||||
"""
|
||||
from honcho_cli.common import handle_cmd_flags
|
||||
from honcho_cli.main import get_resolved_config
|
||||
|
||||
handle_cmd_flags(json_output=json_output)
|
||||
|
||||
config = get_resolved_config()
|
||||
|
||||
if not config.api_key:
|
||||
print_error("NO_API_KEY", "Admin API key required in config. Run `honcho config set api_key <key>`.")
|
||||
raise typer.Exit(3)
|
||||
|
||||
if not admin and not workspace and not peer and not session:
|
||||
# Default to current workspace
|
||||
workspace = config.workspace_id
|
||||
if not workspace:
|
||||
print_error(
|
||||
"NO_SCOPE",
|
||||
"Must specify at least one scope: --workspace, --peer, --session, or --admin",
|
||||
)
|
||||
raise typer.Exit(1)
|
||||
|
||||
# Build expiry
|
||||
expires_at = None
|
||||
if not no_expire:
|
||||
expires_at = _parse_duration(expires)
|
||||
|
||||
# Use raw HTTP to call the /keys endpoint
|
||||
import httpx
|
||||
|
||||
base_url = config.base_url.rstrip("/")
|
||||
params: dict = {}
|
||||
if workspace:
|
||||
params["workspace_id"] = workspace
|
||||
if peer:
|
||||
params["peer_id"] = peer
|
||||
if session:
|
||||
params["session_id"] = session
|
||||
if expires_at:
|
||||
params["expires_at"] = expires_at.isoformat()
|
||||
|
||||
if admin:
|
||||
params = {"admin": "true"}
|
||||
if expires_at:
|
||||
params["expires_at"] = expires_at.isoformat()
|
||||
|
||||
try:
|
||||
resp = httpx.post(
|
||||
f"{base_url}/v3/keys",
|
||||
params=params,
|
||||
headers={"Authorization": f"Bearer {config.api_key}"},
|
||||
timeout=30,
|
||||
)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
print_result(data)
|
||||
except httpx.HTTPStatusError as e:
|
||||
if e.response.status_code == 401 or e.response.status_code == 403:
|
||||
print_error("AUTH_ERROR", "Admin authentication required for key generation", {})
|
||||
raise typer.Exit(3)
|
||||
print_error("KEY_ERROR", f"Failed to generate key: {e.response.text}", {})
|
||||
raise typer.Exit(1)
|
||||
except Exception as e:
|
||||
print_error("KEY_ERROR", f"Failed to generate key: {e}", {})
|
||||
raise typer.Exit(1)
|
||||
|
|
@ -123,11 +123,10 @@ def card(
|
|||
@app.command()
|
||||
def chat(
|
||||
query: str = typer.Argument(help="Question to ask about the peer"),
|
||||
peer_id: Optional[str] = typer.Option(None, help="Peer ID (uses default if omitted)"),
|
||||
target: Optional[str] = typer.Option(None, help="Target peer for perspective"),
|
||||
session: Optional[str] = typer.Option(None, help="Session context"),
|
||||
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
|
||||
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
|
||||
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Peer ID (uses default if omitted)"),
|
||||
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
|
||||
) -> None:
|
||||
"""Query the dialectic about a peer."""
|
||||
|
|
@ -135,7 +134,7 @@ def chat(
|
|||
from honcho_cli.main import get_client
|
||||
|
||||
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
|
||||
pid = _get_peer_id(peer_id)
|
||||
pid = _get_peer_id(None)
|
||||
client, config = get_client()
|
||||
p = client.peer(pid)
|
||||
|
||||
|
|
@ -149,10 +148,9 @@ def chat(
|
|||
@app.command()
|
||||
def search(
|
||||
query: str = typer.Argument(help="Search query"),
|
||||
peer_id: Optional[str] = typer.Option(None, help="Peer ID (uses default if omitted)"),
|
||||
limit: int = typer.Option(10, help="Max results"),
|
||||
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
|
||||
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
|
||||
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Peer ID (uses default if omitted)"),
|
||||
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
|
||||
) -> None:
|
||||
"""Search a peer's messages."""
|
||||
|
|
@ -160,7 +158,7 @@ def search(
|
|||
from honcho_cli.main import get_client
|
||||
|
||||
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
|
||||
pid = _get_peer_id(peer_id)
|
||||
pid = _get_peer_id(None)
|
||||
client, config = get_client()
|
||||
p = client.peer(pid)
|
||||
|
||||
|
|
@ -245,10 +243,9 @@ def get_metadata(
|
|||
|
||||
@app.command("set-metadata")
|
||||
def set_metadata(
|
||||
peer_id: Optional[str] = typer.Argument(None, help="Peer ID (uses default if omitted)"),
|
||||
metadata: str = typer.Option(..., "--metadata", "-m", help="JSON metadata to set (e.g. '{\"key\": \"value\"}')"),
|
||||
metadata: str = typer.Argument(help="JSON metadata to set (e.g. '{\"key\": \"value\"}')"),
|
||||
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
|
||||
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
|
||||
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Peer ID (uses default if omitted)"),
|
||||
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
|
||||
) -> None:
|
||||
"""Set metadata for a peer."""
|
||||
|
|
@ -256,7 +253,7 @@ def set_metadata(
|
|||
from honcho_cli.main import get_client
|
||||
|
||||
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
|
||||
pid = _get_peer_id(peer_id)
|
||||
pid = _get_peer_id(None)
|
||||
client, config = get_client()
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -302,10 +302,9 @@ def remove_peers(
|
|||
@app.command()
|
||||
def search(
|
||||
query: str = typer.Argument(help="Search query"),
|
||||
session_id: Optional[str] = typer.Option(None, help="Session ID (uses default if omitted)"),
|
||||
limit: int = typer.Option(10, help="Max results"),
|
||||
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
|
||||
session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
|
||||
session: Optional[str] = typer.Option(None, "--session", "-s", help="Session ID (uses default if omitted)"),
|
||||
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
|
||||
) -> None:
|
||||
"""Search messages in a session."""
|
||||
|
|
@ -313,7 +312,7 @@ def search(
|
|||
from honcho_cli.main import get_client
|
||||
|
||||
handle_cmd_flags(json_output=json_output, workspace=workspace, session=session)
|
||||
sid = _get_session_id(session_id)
|
||||
sid = _get_session_id(None)
|
||||
client, config = get_client()
|
||||
sess = client.session(sid)
|
||||
|
||||
|
|
@ -336,12 +335,11 @@ def search(
|
|||
@app.command()
|
||||
def representation(
|
||||
peer_id: str = typer.Argument(help="Peer ID to get representation for"),
|
||||
session_id: Optional[str] = typer.Option(None, help="Session ID (uses default if omitted)"),
|
||||
target: Optional[str] = typer.Option(None, help="Target peer (what peer_id knows about target)"),
|
||||
search_query: Optional[str] = typer.Option(None, help="Semantic search query to filter conclusions"),
|
||||
max_conclusions: Optional[int] = typer.Option(None, help="Maximum number of conclusions to include"),
|
||||
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
|
||||
session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
|
||||
session: Optional[str] = typer.Option(None, "--session", "-s", help="Session ID (uses default if omitted)"),
|
||||
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
|
||||
) -> None:
|
||||
"""Get the representation of a peer within a session."""
|
||||
|
|
@ -349,7 +347,7 @@ def representation(
|
|||
from honcho_cli.main import get_client
|
||||
|
||||
handle_cmd_flags(json_output=json_output, workspace=workspace, session=session)
|
||||
sid = _get_session_id(session_id)
|
||||
sid = _get_session_id(None)
|
||||
client, config = get_client()
|
||||
sess = client.session(sid)
|
||||
|
||||
|
|
@ -391,9 +389,8 @@ def get_metadata(
|
|||
@app.command("set-metadata")
|
||||
def set_metadata(
|
||||
metadata: str = typer.Argument(help="JSON metadata to set (e.g. '{\"key\": \"value\"}')"),
|
||||
session_id: Optional[str] = typer.Option(None, help="Session ID (uses default if omitted)"),
|
||||
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
|
||||
session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
|
||||
session: Optional[str] = typer.Option(None, "--session", "-s", help="Session ID (uses default if omitted)"),
|
||||
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
|
||||
) -> None:
|
||||
"""Set metadata for a session."""
|
||||
|
|
@ -401,7 +398,7 @@ def set_metadata(
|
|||
from honcho_cli.main import get_client
|
||||
|
||||
handle_cmd_flags(json_output=json_output, workspace=workspace, session=session)
|
||||
sid = _get_session_id(session_id)
|
||||
sid = _get_session_id(None)
|
||||
client, config = get_client()
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ app.command()(doctor)
|
|||
# Register command groups
|
||||
from honcho_cli.commands.config_cmd import app as config_app
|
||||
from honcho_cli.commands.conclusion import app as conclusion_app
|
||||
from honcho_cli.commands.key import app as key_app
|
||||
from honcho_cli.commands.message import app as message_app
|
||||
from honcho_cli.commands.peer import app as peer_app
|
||||
from honcho_cli.commands.session import app as session_app
|
||||
|
|
@ -120,7 +119,6 @@ app.add_typer(peer_app, name="peer")
|
|||
app.add_typer(session_app, name="session")
|
||||
app.add_typer(message_app, name="message")
|
||||
app.add_typer(conclusion_app, name="conclusion")
|
||||
app.add_typer(key_app, name="key")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -41,4 +41,3 @@ honcho config set peer_id my-peer
|
|||
- `honcho session` — Inspect, messages, context, summaries
|
||||
- `honcho message` — List and get messages
|
||||
- `honcho conclusion` — List, search, create, delete conclusions
|
||||
- `honcho key` — Generate scoped JWT keys
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
---
|
||||
name: honcho-cli-admin
|
||||
version: 0.1.0
|
||||
description: Admin operations for Honcho workspaces
|
||||
---
|
||||
|
||||
# Honcho CLI — Admin Skills
|
||||
|
||||
## Rules
|
||||
|
||||
- Always inspect before deleting: `honcho workspace inspect` then `honcho workspace delete`
|
||||
- Use `--dry-run` for destructive operations to preview impact
|
||||
- Generated keys default to 90-day expiry; use `--no-expire` only when intentional
|
||||
- Admin JWT required for key generation and workspace deletion
|
||||
|
||||
## Key Generation
|
||||
|
||||
```bash
|
||||
# Workspace-scoped key (90-day default)
|
||||
honcho key generate --workspace my-ws --json
|
||||
|
||||
# Peer-scoped key with custom expiry
|
||||
honcho key generate --peer <id> --expires 30d --json
|
||||
|
||||
# No-expiry key (use with caution)
|
||||
honcho key generate --workspace my-ws --no-expire --json
|
||||
```
|
||||
|
||||
## Workspace Lifecycle
|
||||
|
||||
```bash
|
||||
# Inspect first
|
||||
honcho workspace inspect <workspace_id> --json
|
||||
|
||||
# Check queue status
|
||||
honcho workspace queue-status --json
|
||||
|
||||
# Delete (requires --yes for non-interactive)
|
||||
honcho workspace delete <workspace_id> --yes
|
||||
```
|
||||
Loading…
Reference in New Issue