@agentsdock/server 1.0.7 → 1.0.8-beta.2
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.
- package/README.md +2 -2
- package/package.json +1 -1
- package/server/VERSION +1 -1
- package/server/agent_server.py +392 -60
- package/server/codex_app_server.py +12 -0
- package/server/codex_history_repair.py +79 -14
- package/server/codex_provider.py +159 -76
- package/server/cursor_api_key.py +64 -0
- package/server/install.sh +3 -2
- package/server/opencode_agent_client.py +32 -0
- package/server/provider_connections.py +665 -0
- package/server/pyproject.toml +1 -0
- package/server/title_generation.py +4 -1
- package/server/uv.lock +3 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Cursor-issued keys, not OpenAI-compatible endpoints or a machine login.
|
|
2
|
+
|
|
3
|
+
Cursor exchanges API keys for tokens and normally persists those tokens in the
|
|
4
|
+
user's credential store. Use its in-memory store for every key-bound process;
|
|
5
|
+
an inherited auth token must never take precedence over the selected API key.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
import tempfile
|
|
11
|
+
|
|
12
|
+
from fastapi import HTTPException
|
|
13
|
+
from cursor_agent_client import parse_cursor_models_list
|
|
14
|
+
from side_questions import run_isolated_command, SideQuestionError
|
|
15
|
+
|
|
16
|
+
ENDPOINT = "https://api2.cursor.sh"
|
|
17
|
+
MIN_VERSION = (2026, 9, 26) # First build verified with memory credential isolation.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def overrides(key: str) -> dict[str, str]:
|
|
21
|
+
return {
|
|
22
|
+
"CURSOR_API_KEY": key, "CURSOR_AUTH_TOKEN": "",
|
|
23
|
+
"AGENT_CLI_CREDENTIAL_STORE": "memory",
|
|
24
|
+
"CURSOR_API_ENDPOINT": ENDPOINT, "CURSOR_AGENT_CLI_LOCAL_MODE": "false",
|
|
25
|
+
"DIRENV_DISABLE": "1",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def require_isolation(executable: str | None, env: dict) -> None:
|
|
30
|
+
if not executable:
|
|
31
|
+
raise HTTPException(409, "Install Cursor CLI to use a Cursor API key.")
|
|
32
|
+
try:
|
|
33
|
+
with tempfile.TemporaryDirectory(prefix="agentsdock-cursor-version-") as cwd:
|
|
34
|
+
output = await run_isolated_command([executable, "--version"], prompt="", cwd=cwd,
|
|
35
|
+
env={**env, **overrides("")}, timeout=5)
|
|
36
|
+
match = re.search(r"\b(20\d\d)\.(\d{2})\.(\d{2})\b", output)
|
|
37
|
+
if not match or tuple(map(int, match.groups())) < MIN_VERSION:
|
|
38
|
+
raise ValueError("unsupported build")
|
|
39
|
+
except (SideQuestionError, ValueError):
|
|
40
|
+
raise HTTPException(409, "Update Cursor CLI to 2026.09.26 or later to isolate API keys from CLI login.") from None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
async def catalog(selected: dict, *, executable: str | None, env: dict) -> dict:
|
|
44
|
+
try:
|
|
45
|
+
await require_isolation(executable, env)
|
|
46
|
+
except HTTPException:
|
|
47
|
+
return {"models": [], "discovery_status": "unavailable", "cli_update_required": True}
|
|
48
|
+
try:
|
|
49
|
+
# Read-only authenticated request; no conversation, model inference,
|
|
50
|
+
# project tools, or native credential writes. Never reflect CLI stderr.
|
|
51
|
+
with tempfile.TemporaryDirectory(prefix="agentsdock-cursor-key-") as cwd:
|
|
52
|
+
output = await run_isolated_command([executable, "--list-models"], prompt="", cwd=cwd,
|
|
53
|
+
env={**env, **overrides(selected["api_key"])}, timeout=20)
|
|
54
|
+
models = [{"value": item["id"], "label": item["label"]}
|
|
55
|
+
for item in parse_cursor_models_list(output)
|
|
56
|
+
if re.fullmatch(r"[\x21-\x7e]{1,256}", item["id"])
|
|
57
|
+
and selected["api_key"] not in item["id"] + item["label"]]
|
|
58
|
+
if not models:
|
|
59
|
+
return {"models": [], "discovery_status": "unavailable"}
|
|
60
|
+
return {"models": models[:256], "discovery_status": "ready"}
|
|
61
|
+
except SideQuestionError:
|
|
62
|
+
# Offline and invalid keys can look identical in CLI output. Do not
|
|
63
|
+
# misclassify an outage as a logout or return key-bearing stderr.
|
|
64
|
+
return {"models": [], "discovery_status": "unavailable"}
|
package/server/install.sh
CHANGED
|
@@ -1839,7 +1839,7 @@ if [[ "$TEAM_HUB_MODE" == "host" && "$TEAM_HUB_OPERATION_PENDING" != "true" \
|
|
|
1839
1839
|
fi
|
|
1840
1840
|
fi
|
|
1841
1841
|
|
|
1842
|
-
RELEASE_FILES=(activation_transaction.py execution_activation.py execution_legacy_runner.py execution_preparation.py update_preparation.py update_handoff.py update_recovery.py execution_update_status.py execution_uninstall.py execution_recovery.py execution_recovery_status.py execution_http.py execution_durability.py execution_control.py execution_install.py execution_maintenance.py execution_manage.py execution_ownership.py execution_service.py execution_transport.py agent_server.py local_session_ownership.py cursor_history.py workspace_git.py team_hub_host.py secure_peer_runtime.py team_mail_runtime.py team_mail_websocket.py team_mail_grants.py secure_peer_delivery.py agentsdock_jobs.py agentsdock_chats.py chat_mailbox.py agentsdock_emergency.py agentsdock_publish.py agentsdock_mail.py agentsdock_team.py provider_commands.py provider_usage.py claude_sdk_client.py claude_model_catalog.py claude_goals.py claude_background_reconciliation.py codex_app_server.py codex_auth.py codex_provider.py side_questions.py title_generation.py codex_side_question.py claude_side_question.py cursor_agent_client.py cursor_provider_mcp.py opencode_agent_client.py cursor_process_guard.py claude_history_repair.py claude_history_provenance.py codex_history_repair.py public_chat_shares.py public_chat_transcript.py public_chat_share_routes.py interactive_chat_shares.py interactive_chat_share_routes.py interactive_chat_share_web.py interactive_chat_projection.py interactive_chat_runtime.py interactive_chat_native.py shared_chat_videos.py shared_chat_video_stream.py interactive_chat_controls.py install.sh uninstall.sh instances.sh server_instances.py update_runner.py pyproject.toml uv.lock VERSION release-public-key.pem LICENSE NOTICE)
|
|
1842
|
+
RELEASE_FILES=(activation_transaction.py execution_activation.py execution_legacy_runner.py execution_preparation.py update_preparation.py update_handoff.py update_recovery.py execution_update_status.py execution_uninstall.py execution_recovery.py execution_recovery_status.py execution_http.py execution_durability.py execution_control.py execution_install.py execution_maintenance.py execution_manage.py execution_ownership.py execution_service.py execution_transport.py agent_server.py local_session_ownership.py cursor_history.py workspace_git.py team_hub_host.py secure_peer_runtime.py team_mail_runtime.py team_mail_websocket.py team_mail_grants.py secure_peer_delivery.py agentsdock_jobs.py agentsdock_chats.py chat_mailbox.py agentsdock_emergency.py agentsdock_publish.py agentsdock_mail.py agentsdock_team.py provider_commands.py provider_usage.py claude_sdk_client.py claude_model_catalog.py claude_goals.py claude_background_reconciliation.py codex_app_server.py codex_auth.py codex_provider.py provider_connections.py cursor_api_key.py side_questions.py title_generation.py codex_side_question.py claude_side_question.py cursor_agent_client.py cursor_provider_mcp.py opencode_agent_client.py cursor_process_guard.py claude_history_repair.py claude_history_provenance.py codex_history_repair.py public_chat_shares.py public_chat_transcript.py public_chat_share_routes.py interactive_chat_shares.py interactive_chat_share_routes.py interactive_chat_share_web.py interactive_chat_projection.py interactive_chat_runtime.py interactive_chat_native.py shared_chat_videos.py shared_chat_video_stream.py interactive_chat_controls.py install.sh uninstall.sh instances.sh server_instances.py update_runner.py pyproject.toml uv.lock VERSION release-public-key.pem LICENSE NOTICE)
|
|
1843
1843
|
RELEASE_DIRECTORIES=(agentsdock_team_hub)
|
|
1844
1844
|
TEAM_HUB_RELEASE_FILES=(
|
|
1845
1845
|
__init__.py
|
|
@@ -3640,6 +3640,7 @@ validate_staged_release_runtime() (
|
|
|
3640
3640
|
"$STAGE_DIR/codex_app_server.py" \
|
|
3641
3641
|
"$STAGE_DIR/codex_auth.py" \
|
|
3642
3642
|
"$STAGE_DIR/codex_provider.py" \
|
|
3643
|
+
"$STAGE_DIR/provider_connections.py" "$STAGE_DIR/cursor_api_key.py" \
|
|
3643
3644
|
"$STAGE_DIR/side_questions.py" \
|
|
3644
3645
|
"$STAGE_DIR/workspace_git.py" \
|
|
3645
3646
|
"$STAGE_DIR/codex_side_question.py" \
|
|
@@ -3667,7 +3668,7 @@ validate_staged_release_runtime() (
|
|
|
3667
3668
|
"$STAGE_DIR/update_runner.py"
|
|
3668
3669
|
"$STAGE_DIR/.venv/bin/python" -m py_compile "$STAGE_DIR/server_instances.py"
|
|
3669
3670
|
"$STAGE_DIR/.venv/bin/python" -m compileall -q "$STAGE_DIR/agentsdock_team_hub"
|
|
3670
|
-
PYTHONPATH="$STAGE_DIR" "$STAGE_DIR/.venv/bin/python" -c 'import execution_activation, execution_preparation, update_preparation, update_handoff, update_recovery, execution_update_status, execution_uninstall, execution_recovery, execution_recovery_status, execution_http, execution_durability, execution_control, execution_install, execution_maintenance, execution_manage, execution_ownership, execution_service, execution_transport; import workspace_git, claude_model_catalog, server_instances, local_session_ownership, cursor_history; import codex_auth, codex_provider, side_questions, title_generation, codex_side_question, claude_side_question, agentsdock_team_hub, cursor_agent_client, cursor_provider_mcp, opencode_agent_client, cursor_process_guard, secure_peer_delivery, secure_peer_runtime, team_mail_runtime, team_mail_websocket, team_mail_grants, team_hub_host, agentsdock_mail, agentsdock_team, claude_history_repair, claude_history_provenance, claude_goals, claude_background_reconciliation, codex_history_repair, public_chat_shares, public_chat_transcript, public_chat_share_routes, interactive_chat_shares, interactive_chat_share_routes, interactive_chat_share_web, interactive_chat_projection, interactive_chat_runtime, interactive_chat_native, shared_chat_videos, shared_chat_video_stream, interactive_chat_controls, chat_mailbox, provider_commands, provider_usage; from agentsdock_team_hub import secure_peer, secure_peer_hub' >/dev/null
|
|
3671
|
+
PYTHONPATH="$STAGE_DIR" "$STAGE_DIR/.venv/bin/python" -c 'import execution_activation, execution_preparation, update_preparation, update_handoff, update_recovery, execution_update_status, execution_uninstall, execution_recovery, execution_recovery_status, execution_http, execution_durability, execution_control, execution_install, execution_maintenance, execution_manage, execution_ownership, execution_service, execution_transport; import workspace_git, claude_model_catalog, server_instances, local_session_ownership, cursor_history; import codex_auth, codex_provider, provider_connections, cursor_api_key, side_questions, title_generation, codex_side_question, claude_side_question, agentsdock_team_hub, cursor_agent_client, cursor_provider_mcp, opencode_agent_client, cursor_process_guard, secure_peer_delivery, secure_peer_runtime, team_mail_runtime, team_mail_websocket, team_mail_grants, team_hub_host, agentsdock_mail, agentsdock_team, claude_history_repair, claude_history_provenance, claude_goals, claude_background_reconciliation, codex_history_repair, public_chat_shares, public_chat_transcript, public_chat_share_routes, interactive_chat_shares, interactive_chat_share_routes, interactive_chat_share_web, interactive_chat_projection, interactive_chat_runtime, interactive_chat_native, shared_chat_videos, shared_chat_video_stream, interactive_chat_controls, chat_mailbox, provider_commands, provider_usage; from agentsdock_team_hub import secure_peer, secure_peer_hub' >/dev/null
|
|
3671
3672
|
)
|
|
3672
3673
|
|
|
3673
3674
|
abort_unclaimed_team_hub_reactivation() {
|
|
@@ -486,6 +486,38 @@ def build_opencode_env_overrides(
|
|
|
486
486
|
}
|
|
487
487
|
|
|
488
488
|
|
|
489
|
+
def build_opencode_mcp_overrides(
|
|
490
|
+
existing_config: str | None, *, name: str, command: list[str],
|
|
491
|
+
environment: dict[str, str], enforced_agent_name: str | None = None,
|
|
492
|
+
) -> dict[str, str]:
|
|
493
|
+
"""Add one unpredictable, run-bound tool without granting shell/MCP wildcards.
|
|
494
|
+
|
|
495
|
+
Merge after the endpoint and permission configuration. Never persist this
|
|
496
|
+
configuration, credentials, or permission to the user's OpenCode files.
|
|
497
|
+
Default mode keeps the native agent and any higher-priority managed rules.
|
|
498
|
+
"""
|
|
499
|
+
if not re.fullmatch(r"agentsdock_[0-9a-f]{32}", name):
|
|
500
|
+
raise ValueError("invalid OpenCode provider tool name")
|
|
501
|
+
config = json.loads(existing_config) if existing_config else {}
|
|
502
|
+
if not isinstance(config, dict):
|
|
503
|
+
raise ValueError("OpenCode inline config must be an object")
|
|
504
|
+
mcp = config.setdefault("mcp", {})
|
|
505
|
+
if not isinstance(mcp, dict) or name in mcp:
|
|
506
|
+
raise ValueError("OpenCode provider MCP configuration conflicts")
|
|
507
|
+
mcp[name] = {"type": "local", "command": command,
|
|
508
|
+
"environment": environment, "enabled": True}
|
|
509
|
+
permissions = config.get("permission", {})
|
|
510
|
+
if isinstance(permissions, str):
|
|
511
|
+
permissions = {"*": permissions}
|
|
512
|
+
if not isinstance(permissions, dict):
|
|
513
|
+
raise ValueError("OpenCode permissions must be an object or action")
|
|
514
|
+
config["permission"] = {**permissions, name + "_run": "allow"}
|
|
515
|
+
if enforced_agent_name:
|
|
516
|
+
agent = config["agent"][enforced_agent_name]
|
|
517
|
+
agent["permission"][name + "_run"] = "allow"
|
|
518
|
+
return {"OPENCODE_CONFIG_CONTENT": json.dumps(config, separators=(",", ":"))}
|
|
519
|
+
|
|
520
|
+
|
|
489
521
|
def build_opencode_cmd(
|
|
490
522
|
sess: dict[str, Any],
|
|
491
523
|
prompt: str,
|