@ravefox/space 0.2.0 → 0.3.0
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/LICENSE +21 -203
- package/README.md +89 -26
- package/bin/space.js +106 -0
- package/lib/agents.js +119 -0
- package/lib/credentials.js +95 -0
- package/lib/daemon.js +294 -0
- package/lib/harness.js +166 -0
- package/lib/http.js +101 -0
- package/lib/login.js +68 -0
- package/lib/mcp.js +34 -0
- package/lib/runtime.js +241 -0
- package/lib/store.js +96 -0
- package/lib/tools.js +93 -0
- package/package.json +28 -40
- package/NOTICE +0 -44
- package/assets/webhook-sdk/python/bot.py +0 -63
- package/assets/webhook-sdk/python/commonly.py +0 -232
- package/node_modules/@commonlyai/seats/README.md +0 -25
- package/node_modules/@commonlyai/seats/package.json +0 -22
- package/node_modules/@commonlyai/seats/src/adapters/hermes.js +0 -264
- package/node_modules/@commonlyai/seats/src/adapters/stub.js +0 -24
- package/node_modules/@commonlyai/seats/src/contract.js +0 -48
- package/node_modules/@commonlyai/seats/src/failures.js +0 -44
- package/node_modules/@commonlyai/seats/src/harness-plan.js +0 -329
- package/node_modules/@commonlyai/seats/src/index.js +0 -22
- package/node_modules/@commonlyai/seats/src/process-group.js +0 -269
- package/node_modules/@commonlyai/seats/src/record/format.js +0 -737
- package/node_modules/@commonlyai/seats/src/record/otel.js +0 -238
- package/node_modules/@commonlyai/seats/src/runtime-observation.js +0 -29
- package/node_modules/@commonlyai/seats/src/seat-model.js +0 -17
- package/node_modules/@commonlyai/seats/src/supervisor.js +0 -308
- package/node_modules/@commonlyai/seats/src/trace/events.js +0 -46
- package/node_modules/@commonlyai/seats/src/trace/store.js +0 -276
- package/node_modules/@commonlyai/seats/src/turn-processes.js +0 -226
- package/node_modules/@commonlyai/seats/src/upstream-refusal.js +0 -418
- package/node_modules/@commonlyai/seats/src/valet/env.js +0 -201
- package/node_modules/@commonlyai/seats/src/valet/grant.js +0 -123
- package/node_modules/@commonlyai/seats/src/valet/permissions.js +0 -213
- package/node_modules/@commonlyai/seats/src/wire/provider-proxy.js +0 -529
- package/skills/commonly/SKILL.md +0 -233
- package/src/commands/agent.js +0 -6334
- package/src/commands/daemon.js +0 -933
- package/src/commands/dev.js +0 -153
- package/src/commands/doctor.js +0 -141
- package/src/commands/inbox.js +0 -330
- package/src/commands/login.js +0 -194
- package/src/commands/mcp.js +0 -56
- package/src/commands/pod.js +0 -120
- package/src/commands/task.js +0 -279
- package/src/commands/verify.js +0 -48
- package/src/index.js +0 -99
- package/src/lib/adapter-login.js +0 -76
- package/src/lib/adapters/broker-bridge-entry.js +0 -76
- package/src/lib/adapters/claude.js +0 -2207
- package/src/lib/adapters/codex-broker-bridge.mjs +0 -298
- package/src/lib/adapters/codex.js +0 -1898
- package/src/lib/adapters/dsh.js +0 -513
- package/src/lib/adapters/goose.js +0 -510
- package/src/lib/adapters/hermes.js +0 -18
- package/src/lib/adapters/index.js +0 -68
- package/src/lib/adapters/opencode.js +0 -1681
- package/src/lib/adapters/pi-commonly-mcp.mjs +0 -71
- package/src/lib/adapters/pi-mcp-client.mjs +0 -617
- package/src/lib/adapters/pi-shell-env.mjs +0 -41
- package/src/lib/adapters/pi.js +0 -1025
- package/src/lib/adapters/qwen.js +0 -523
- package/src/lib/adapters/seat-model.js +0 -8
- package/src/lib/adapters/stub.js +0 -1
- package/src/lib/adapters/tested-version.js +0 -106
- package/src/lib/api.js +0 -205
- package/src/lib/claim-outcome.js +0 -128
- package/src/lib/claimed-work-gate.js +0 -73
- package/src/lib/cli-hint.js +0 -33
- package/src/lib/commit-attribution.js +0 -391
- package/src/lib/config.js +0 -155
- package/src/lib/content-refusal.js +0 -94
- package/src/lib/credential-file.js +0 -208
- package/src/lib/daemon-agent-states.js +0 -95
- package/src/lib/daemon-hint.js +0 -18
- package/src/lib/daemon-logs.js +0 -43
- package/src/lib/daemon-service-windows.js +0 -263
- package/src/lib/daemon-service.js +0 -486
- package/src/lib/daemon-state.js +0 -84
- package/src/lib/daemon-store.js +0 -119
- package/src/lib/daemon-supervisor.js +0 -629
- package/src/lib/daemon-watch.js +0 -115
- package/src/lib/declared-mcp-guard.js +0 -284
- package/src/lib/default-environment.js +0 -207
- package/src/lib/device-login.js +0 -126
- package/src/lib/digest-frame.js +0 -151
- package/src/lib/doctor.js +0 -291
- package/src/lib/egress-proxy.js +0 -160
- package/src/lib/enforcement.js +0 -946
- package/src/lib/environment.js +0 -681
- package/src/lib/first-output-watchdog.js +0 -191
- package/src/lib/git-readonly-server.js +0 -138
- package/src/lib/git-readonly.js +0 -463
- package/src/lib/git-remote-commonly.mjs +0 -100
- package/src/lib/grant-broker-guard.js +0 -345
- package/src/lib/hooks-config.js +0 -238
- package/src/lib/host-gh-broker.js +0 -152
- package/src/lib/host-gh-shim.mjs +0 -98
- package/src/lib/inbox.js +0 -483
- package/src/lib/instance-proxy.js +0 -76
- package/src/lib/machine-host.js +0 -29
- package/src/lib/mcp-credential-delivery.js +0 -121
- package/src/lib/mcp-first-run.js +0 -50
- package/src/lib/mcp-home.js +0 -644
- package/src/lib/mcp-ready.js +0 -68
- package/src/lib/mcp-server-version.js +0 -123
- package/src/lib/mcp-token.js +0 -36
- package/src/lib/mcp-warm-child.mjs +0 -412
- package/src/lib/memory-bridge.js +0 -191
- package/src/lib/memory-import.js +0 -156
- package/src/lib/missing-harness.js +0 -31
- package/src/lib/model-unavailable.js +0 -154
- package/src/lib/native-runtime-inventory.js +0 -40
- package/src/lib/outsider-grant-mcp.js +0 -82
- package/src/lib/own-key-launch.js +0 -42
- package/src/lib/pod-catch-up.js +0 -55
- package/src/lib/pod-focus.js +0 -224
- package/src/lib/pod-tail.js +0 -68
- package/src/lib/poll-liveness.js +0 -103
- package/src/lib/poll-retry.js +0 -81
- package/src/lib/poller.js +0 -200
- package/src/lib/provider-proxy.js +0 -42
- package/src/lib/public-seat-mcp-flag.js +0 -36
- package/src/lib/public-tool-error.js +0 -60
- package/src/lib/public-withheld-tools.js +0 -48
- package/src/lib/reply-receipt.js +0 -55
- package/src/lib/resolve-on-path.js +0 -22
- package/src/lib/runtime-inventory.js +0 -56
- package/src/lib/runtime-observation.js +0 -37
- package/src/lib/sandbox/bwrap.js +0 -275
- package/src/lib/sandbox/codex-perimeter.js +0 -16
- package/src/lib/sandbox/host-check.js +0 -268
- package/src/lib/sandbox/mode.js +0 -49
- package/src/lib/sandbox/opencode-loopback.mjs +0 -68
- package/src/lib/sandbox/seatbelt.js +0 -684
- package/src/lib/sandbox/turn-dir.js +0 -56
- package/src/lib/sandbox/windows-unsandboxed.js +0 -183
- package/src/lib/sandbox/workspace-git-config.js +0 -243
- package/src/lib/sandbox/workspace-pin.js +0 -158
- package/src/lib/seat-runtime-controls.js +0 -61
- package/src/lib/seat-stop.js +0 -97
- package/src/lib/server-skills.js +0 -32
- package/src/lib/session-recycle.js +0 -219
- package/src/lib/session-store.js +0 -215
- package/src/lib/skill-prompt.js +0 -74
- package/src/lib/skills-import.js +0 -113
- package/src/lib/spawn-credential.js +0 -402
- package/src/lib/spawn-retry.js +0 -311
- package/src/lib/stable-install.js +0 -176
- package/src/lib/supervised-turn.js +0 -352
- package/src/lib/task.js +0 -235
- package/src/lib/turn-failure-notice.js +0 -177
- package/src/lib/unconfined-seat-guard.js +0 -94
- package/src/lib/update-check.js +0 -192
- package/src/lib/upstream-refusal.js +0 -1
- package/src/lib/webhook-server.js +0 -109
- package/src/lib/windows-command.js +0 -393
- package/src/lib/workspace-git.js +0 -87
- package/src/lib/workspace-trust.js +0 -162
package/package.json
CHANGED
|
@@ -1,56 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ravefox/space",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "Space CLI \u2014 connect agents and run them in the background",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Space CLI — connect and manage AI agents in your workspace",
|
|
6
5
|
"type": "module",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Ravefox",
|
|
9
|
+
"url": "https://t.me/ravefoxlab"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://space.ravefox.dev",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://vote.ravefox.dev/support"
|
|
11
14
|
},
|
|
12
|
-
"files": [
|
|
13
|
-
"src",
|
|
14
|
-
"assets",
|
|
15
|
-
"README.md",
|
|
16
|
-
"skills",
|
|
17
|
-
"LICENSE",
|
|
18
|
-
"NOTICE"
|
|
19
|
-
],
|
|
20
15
|
"repository": {
|
|
21
16
|
"type": "git",
|
|
22
17
|
"url": "git+https://github.com/grootbro/space.git",
|
|
23
18
|
"directory": "cli"
|
|
24
19
|
},
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
"space",
|
|
28
|
-
"ai-agents",
|
|
29
|
-
"agent",
|
|
30
|
-
"cli",
|
|
31
|
-
"mcp"
|
|
32
|
-
],
|
|
33
|
-
"scripts": {
|
|
34
|
-
"start": "node src/index.js",
|
|
35
|
-
"lint": "eslint src --ext .js",
|
|
36
|
-
"lint:fix": "eslint src --ext .js --fix",
|
|
37
|
-
"test": "node --experimental-vm-modules node_modules/.bin/jest",
|
|
38
|
-
"test:temp-cleanup": "node scripts/verify-temp-cleanup.mjs",
|
|
39
|
-
"prepack": "node scripts/require-bundled-seats.mjs && node scripts/bundle-sdk-examples.mjs"
|
|
20
|
+
"bin": {
|
|
21
|
+
"space": "bin/space.js"
|
|
40
22
|
},
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
23
|
+
"exports": {
|
|
24
|
+
"./runtime": "./lib/runtime.js",
|
|
25
|
+
"./store": "./lib/store.js"
|
|
44
26
|
},
|
|
45
|
-
"
|
|
46
|
-
"
|
|
27
|
+
"files": [
|
|
28
|
+
"bin",
|
|
29
|
+
"lib",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE"
|
|
47
32
|
],
|
|
48
|
-
"devDependencies": {
|
|
49
|
-
"eslint": "^8.56.0",
|
|
50
|
-
"jest": "^29.7.0"
|
|
51
|
-
},
|
|
52
33
|
"engines": {
|
|
53
|
-
"node": ">=22
|
|
34
|
+
"node": ">=22"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"test": "node --test test/*.test.js"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@modelcontextprotocol/sdk": "1.32.2",
|
|
41
|
+
"commander": "12.1.0"
|
|
54
42
|
},
|
|
55
43
|
"publishConfig": {
|
|
56
44
|
"access": "public"
|
package/NOTICE
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
Commonly
|
|
2
|
-
Copyright 2026 Commonly (Team-Commonly) and contributors
|
|
3
|
-
|
|
4
|
-
This product is licensed under the Apache License, Version 2.0.
|
|
5
|
-
The Commonly name, logo, and brand assets are trademarks of the
|
|
6
|
-
Commonly project and are not licensed under the Apache License.
|
|
7
|
-
|
|
8
|
-
Third-party notices
|
|
9
|
-
-------------------
|
|
10
|
-
|
|
11
|
-
OpenAI Codex
|
|
12
|
-
Copyright 2025 OpenAI
|
|
13
|
-
|
|
14
|
-
This project includes code derived from [Ratatui](https://github.com/ratatui/ratatui), licensed under the MIT license.
|
|
15
|
-
Copyright (c) 2016-2022 Florian Dehau
|
|
16
|
-
Copyright (c) 2023-2025 The Ratatui Developers
|
|
17
|
-
|
|
18
|
-
Source repository: https://github.com/openai/codex
|
|
19
|
-
Source commit: e476fc16ce810495cbfabe4f815d20947da0ba79
|
|
20
|
-
Source path: codex-rs/sandboxing/src/seatbelt_base_policy.sbpl
|
|
21
|
-
|
|
22
|
-
The Codex-derived Seatbelt policy is used in
|
|
23
|
-
cli/src/lib/sandbox/seatbelt.js.
|
|
24
|
-
|
|
25
|
-
OfficeCLI
|
|
26
|
-
Copyright 2026 OfficeCLI (https://OfficeCLI.AI)
|
|
27
|
-
|
|
28
|
-
Created and maintained by goworm.
|
|
29
|
-
|
|
30
|
-
This product is licensed under the Apache License, Version 2.0.
|
|
31
|
-
You may obtain a copy of the License in the LICENSE file or at
|
|
32
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
33
|
-
|
|
34
|
-
This NOTICE file is part of the required attribution under
|
|
35
|
-
Section 4 of the Apache License, Version 2.0. Redistributions
|
|
36
|
-
of this work, with or without modification, must retain this
|
|
37
|
-
notice.
|
|
38
|
-
|
|
39
|
-
Source repository: https://github.com/iOfficeAI/OfficeCLI
|
|
40
|
-
Source commit: a1498dbd3cb350abde04a1cda1f2eded61ea8a74
|
|
41
|
-
Source path: NOTICE
|
|
42
|
-
|
|
43
|
-
The OfficeCLI skill bundle is vendored under
|
|
44
|
-
backend/commonly-bundled-skills/officecli/.
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Hello-world Commonly agent — emitted by `commonly agent init --language python`.
|
|
3
|
-
|
|
4
|
-
Reads its runtime token from .commonly-env (or COMMONLY_TOKEN) and echoes
|
|
5
|
-
chat events back into the pod. Replace `handle_event()` with your logic.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
# PEP 604 unions (`str | None`) need 3.10+ at runtime; this keeps the bot
|
|
9
|
-
# importable on macOS's stock /usr/bin/python3 (3.9).
|
|
10
|
-
from __future__ import annotations
|
|
11
|
-
|
|
12
|
-
import os
|
|
13
|
-
import sys
|
|
14
|
-
from pathlib import Path
|
|
15
|
-
|
|
16
|
-
# `commonly.py` is dropped in the same dir by the scaffolder.
|
|
17
|
-
sys.path.insert(0, str(Path(__file__).parent))
|
|
18
|
-
from commonly import Commonly # noqa: E402
|
|
19
|
-
|
|
20
|
-
CHAT_EVENT_TYPES = {"chat.mention", "message.posted", "dm.message"}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def load_token() -> str:
|
|
24
|
-
if os.environ.get("COMMONLY_TOKEN"):
|
|
25
|
-
return os.environ["COMMONLY_TOKEN"]
|
|
26
|
-
env_file = Path(__file__).parent / ".commonly-env"
|
|
27
|
-
if env_file.exists():
|
|
28
|
-
# KEY=VALUE format so `source .commonly-env` works too. We only need
|
|
29
|
-
# the token line; ignore comments and other keys.
|
|
30
|
-
for line in env_file.read_text(encoding="utf-8").splitlines():
|
|
31
|
-
line = line.strip()
|
|
32
|
-
if not line or line.startswith("#"):
|
|
33
|
-
continue
|
|
34
|
-
if line.startswith("COMMONLY_TOKEN="):
|
|
35
|
-
return line.split("=", 1)[1].strip().strip("'\"")
|
|
36
|
-
raise SystemExit("No runtime token found. Set COMMONLY_TOKEN or write .commonly-env.")
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def handle_event(evt: dict) -> str | None:
|
|
40
|
-
"""Echo the user's message back. Replace this with your real logic."""
|
|
41
|
-
if evt.get("type") not in CHAT_EVENT_TYPES:
|
|
42
|
-
return None
|
|
43
|
-
payload = evt.get("payload") or {}
|
|
44
|
-
# rawContent is the message exactly as its author typed it. content is what
|
|
45
|
-
# a model should read: the kernel's context frames, a blank line, then the
|
|
46
|
-
# message. Older servers send only content/prompt/text.
|
|
47
|
-
incoming = payload.get("rawContent")
|
|
48
|
-
if incoming is None:
|
|
49
|
-
incoming = payload.get("content") or payload.get("prompt") or payload.get("text")
|
|
50
|
-
if not incoming:
|
|
51
|
-
return None
|
|
52
|
-
return f"echo: {incoming}"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def main() -> None:
|
|
56
|
-
base_url = os.environ.get("COMMONLY_BASE_URL", "https://api.commonly.me")
|
|
57
|
-
bot = Commonly(base_url=base_url, runtime_token=load_token())
|
|
58
|
-
print(f"[hello-world] polling {base_url} (Ctrl+C to stop)", flush=True)
|
|
59
|
-
bot.run(handle_event)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if __name__ == "__main__":
|
|
63
|
-
main()
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Commonly Python SDK — single-file reference implementation of the four
|
|
3
|
-
Commonly Agent Protocol (CAP) verbs. ADR-006 §SDK shape.
|
|
4
|
-
|
|
5
|
-
Stdlib only. No deps. Drop-copy this file into your project and import.
|
|
6
|
-
|
|
7
|
-
Usage (high-level):
|
|
8
|
-
|
|
9
|
-
from commonly import Commonly
|
|
10
|
-
|
|
11
|
-
bot = Commonly(base_url="https://api.commonly.me", runtime_token="cm_agent_…")
|
|
12
|
-
bot.run(lambda evt: f"echo: {evt['payload'].get('rawContent', evt['payload'].get('content', ''))}")
|
|
13
|
-
|
|
14
|
-
Usage (manual loop):
|
|
15
|
-
|
|
16
|
-
bot = Commonly(...)
|
|
17
|
-
while True:
|
|
18
|
-
for evt in bot.poll_events():
|
|
19
|
-
try:
|
|
20
|
-
reply = handle(evt)
|
|
21
|
-
if reply and evt.get("podId"):
|
|
22
|
-
bot.get_pod_context(evt["podId"]) # read first, or the send guard refuses
|
|
23
|
-
bot.post_message(evt["podId"], reply)
|
|
24
|
-
finally:
|
|
25
|
-
bot.ack(evt["_id"], delivery_id=evt.get("payload", {}).get("deliveryId"))
|
|
26
|
-
time.sleep(5)
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
from __future__ import annotations
|
|
30
|
-
|
|
31
|
-
import json
|
|
32
|
-
import time
|
|
33
|
-
import urllib.error
|
|
34
|
-
import urllib.request
|
|
35
|
-
from typing import Any, Callable, Optional
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
# --------------------------------------------------------------------------- #
|
|
39
|
-
# HTTP error type — subclasses propagate `status` so callers can branch on
|
|
40
|
-
# 401/403/404 without parsing the message string.
|
|
41
|
-
# --------------------------------------------------------------------------- #
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class CommonlyError(Exception):
|
|
45
|
-
def __init__(self, message: str, status: Optional[int] = None, body: Any = None):
|
|
46
|
-
super().__init__(message)
|
|
47
|
-
self.status = status
|
|
48
|
-
self.body = body
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def _describe_http_error(status: int, method: str, path: str, payload: Any) -> str:
|
|
52
|
-
"""Name the server's reason on every 4xx. A 409 from the freshness guard
|
|
53
|
-
carries `message` ("N new messages … Read the conversation …"); dropping it
|
|
54
|
-
left the stock bot logging a bare status with no cause."""
|
|
55
|
-
head = f"HTTP {status} {method} {path}"
|
|
56
|
-
if isinstance(payload, dict):
|
|
57
|
-
reason = payload.get("message") or payload.get("error") or payload.get("code")
|
|
58
|
-
if reason:
|
|
59
|
-
return f"{head}: {reason}"
|
|
60
|
-
elif isinstance(payload, str) and payload.strip():
|
|
61
|
-
return f"{head}: {payload.strip()[:200]}"
|
|
62
|
-
return head
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
# --------------------------------------------------------------------------- #
|
|
66
|
-
# Client
|
|
67
|
-
# --------------------------------------------------------------------------- #
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
class Commonly:
|
|
71
|
-
"""Thin client over the four CAP verbs. Sync; one HTTP request per call."""
|
|
72
|
-
|
|
73
|
-
def __init__(self, *, base_url: str, runtime_token: str, timeout_s: float = 30.0):
|
|
74
|
-
self.base_url = base_url.rstrip("/")
|
|
75
|
-
self.runtime_token = runtime_token
|
|
76
|
-
self.timeout_s = timeout_s
|
|
77
|
-
|
|
78
|
-
# ----- internal -------------------------------------------------------- #
|
|
79
|
-
|
|
80
|
-
@staticmethod
|
|
81
|
-
def _path(base_path: str, **params: Any) -> str:
|
|
82
|
-
from urllib.parse import urlencode
|
|
83
|
-
kept = {k: v for k, v in params.items() if v is not None}
|
|
84
|
-
if not kept:
|
|
85
|
-
return base_path
|
|
86
|
-
return f"{base_path}?{urlencode(kept)}"
|
|
87
|
-
|
|
88
|
-
def _request(self, method: str, path: str, body: Optional[dict] = None) -> Any:
|
|
89
|
-
url = f"{self.base_url}{path}"
|
|
90
|
-
data = json.dumps(body).encode("utf-8") if body is not None else None
|
|
91
|
-
req = urllib.request.Request(url, data=data, method=method)
|
|
92
|
-
req.add_header("Authorization", f"Bearer {self.runtime_token}")
|
|
93
|
-
req.add_header("User-Agent", "commonly-sdk/0.1")
|
|
94
|
-
if data is not None:
|
|
95
|
-
req.add_header("Content-Type", "application/json")
|
|
96
|
-
try:
|
|
97
|
-
with urllib.request.urlopen(req, timeout=self.timeout_s) as resp:
|
|
98
|
-
raw = resp.read().decode("utf-8") or "{}"
|
|
99
|
-
return json.loads(raw)
|
|
100
|
-
except urllib.error.HTTPError as e:
|
|
101
|
-
raw = e.read().decode("utf-8", errors="replace") if e.fp else ""
|
|
102
|
-
try:
|
|
103
|
-
payload = json.loads(raw) if raw else None
|
|
104
|
-
except Exception:
|
|
105
|
-
payload = raw
|
|
106
|
-
raise CommonlyError(_describe_http_error(e.code, method, path, payload),
|
|
107
|
-
status=e.code, body=payload) from e
|
|
108
|
-
|
|
109
|
-
# ----- CAP verbs ------------------------------------------------------- #
|
|
110
|
-
|
|
111
|
-
def poll_events(self, *, limit: int = 10) -> list:
|
|
112
|
-
"""CAP verb 1 — pull queued events. Immediate return; no long-poll."""
|
|
113
|
-
body = self._request("GET", self._path("/api/agents/runtime/events", limit=int(limit)))
|
|
114
|
-
return body.get("events", []) if isinstance(body, dict) else []
|
|
115
|
-
|
|
116
|
-
def ack(self, event_id: str, *, delivery_id: Optional[str] = None,
|
|
117
|
-
outcome: str = "acknowledged", content: Optional[str] = None) -> None:
|
|
118
|
-
"""CAP verb 2 — acknowledge an event. Forward the delivery_id from
|
|
119
|
-
event.payload.deliveryId when present; skip on adapter errors so the
|
|
120
|
-
kernel re-delivers (ADR-005 §Spawning semantics)."""
|
|
121
|
-
result = {"outcome": outcome}
|
|
122
|
-
if content is not None:
|
|
123
|
-
result["content"] = content
|
|
124
|
-
body: dict = {"result": result}
|
|
125
|
-
if delivery_id:
|
|
126
|
-
body["deliveryId"] = delivery_id
|
|
127
|
-
self._request("POST", f"/api/agents/runtime/events/{event_id}/ack", body)
|
|
128
|
-
|
|
129
|
-
def post_message(self, pod_id: str, content: str, *,
|
|
130
|
-
reply_to_message_id: Optional[str] = None,
|
|
131
|
-
metadata: Optional[dict] = None) -> dict:
|
|
132
|
-
"""CAP verb 3 — post a chat message into a pod."""
|
|
133
|
-
body: dict = {"content": content}
|
|
134
|
-
if reply_to_message_id is not None:
|
|
135
|
-
body["reply_to_message_id"] = reply_to_message_id
|
|
136
|
-
if metadata is not None:
|
|
137
|
-
body["metadata"] = metadata
|
|
138
|
-
return self._request("POST", f"/api/agents/runtime/pods/{pod_id}/messages", body)
|
|
139
|
-
|
|
140
|
-
def get_pod_context(self, pod_id: str) -> dict:
|
|
141
|
-
"""Read the pod before replying. The server records this read; the
|
|
142
|
-
send guard refuses a post to a pod the agent has not read since its
|
|
143
|
-
newest message (409 unseen_messages). Reading first is the contract."""
|
|
144
|
-
return self._request("GET", f"/api/agents/runtime/pods/{pod_id}/context")
|
|
145
|
-
|
|
146
|
-
def _post_reply(self, pod_id: str, content: str) -> dict:
|
|
147
|
-
"""Read the pod, then post. If a message lands between the read and the
|
|
148
|
-
post, read once more and retry; a second 409 propagates with its reason."""
|
|
149
|
-
self.get_pod_context(pod_id)
|
|
150
|
-
try:
|
|
151
|
-
return self.post_message(pod_id, content)
|
|
152
|
-
except CommonlyError as exc:
|
|
153
|
-
if exc.status != 409:
|
|
154
|
-
raise
|
|
155
|
-
self.get_pod_context(pod_id)
|
|
156
|
-
return self.post_message(pod_id, content)
|
|
157
|
-
|
|
158
|
-
def get_memory(self) -> dict:
|
|
159
|
-
"""CAP verb 4a — read the agent's memory envelope (ADR-003)."""
|
|
160
|
-
return self._request("GET", "/api/agents/runtime/memory")
|
|
161
|
-
|
|
162
|
-
def sync_memory(self, sections: dict, *, mode: str = "patch",
|
|
163
|
-
source_runtime: str = "webhook-sdk-py") -> dict:
|
|
164
|
-
"""CAP verb 4b — patch-or-replace the memory envelope.
|
|
165
|
-
|
|
166
|
-
ADR-003 invariant #9: callers supply `content` + `visibility` only on
|
|
167
|
-
each section. byteSize / updatedAt / schemaVersion are server-stamped
|
|
168
|
-
and silently discarded if sent.
|
|
169
|
-
"""
|
|
170
|
-
if mode not in ("patch", "full"):
|
|
171
|
-
raise ValueError("mode must be 'patch' or 'full'")
|
|
172
|
-
return self._request("POST", "/api/agents/runtime/memory/sync",
|
|
173
|
-
{"mode": mode, "sourceRuntime": source_runtime,
|
|
174
|
-
"sections": sections})
|
|
175
|
-
|
|
176
|
-
# ----- run loop -------------------------------------------------------- #
|
|
177
|
-
|
|
178
|
-
def run(self, on_event: Callable[[dict], Optional[str]], *,
|
|
179
|
-
interval_s: float = 5.0, max_auth_errors: int = 3) -> None:
|
|
180
|
-
"""Convenience loop. Calls `on_event(evt)` for each polled event; if
|
|
181
|
-
the handler returns a non-empty string AND the event has a podId, the
|
|
182
|
-
string is posted back, then the event is acked.
|
|
183
|
-
|
|
184
|
-
On handler exception the ack is SKIPPED so the kernel re-delivers
|
|
185
|
-
(matches ADR-005 §Spawning semantics — at-least-once + driver
|
|
186
|
-
idempotency). Override `run()` if you want custom retry semantics.
|
|
187
|
-
|
|
188
|
-
Exits after `max_auth_errors` consecutive 401/403 poll responses so a
|
|
189
|
-
revoked token doesn't produce an invisible infinite backoff loop. The
|
|
190
|
-
caller should surface the exit to the user (e.g. prompt them to
|
|
191
|
-
re-issue a token)."""
|
|
192
|
-
consecutive_auth_errors = 0
|
|
193
|
-
while True:
|
|
194
|
-
try:
|
|
195
|
-
events = self.poll_events()
|
|
196
|
-
consecutive_auth_errors = 0
|
|
197
|
-
except CommonlyError as exc:
|
|
198
|
-
if exc.status in (401, 403):
|
|
199
|
-
consecutive_auth_errors += 1
|
|
200
|
-
if consecutive_auth_errors >= max_auth_errors:
|
|
201
|
-
print(
|
|
202
|
-
f"[commonly] runtime token rejected {consecutive_auth_errors}x in a row — exiting. "
|
|
203
|
-
"Check COMMONLY_TOKEN: it must hold the bare cm_agent_... value, not the "
|
|
204
|
-
"KEY=VALUE line from .commonly-env. If it is right, re-issue the token or uninstall the agent.",
|
|
205
|
-
flush=True,
|
|
206
|
-
)
|
|
207
|
-
return
|
|
208
|
-
print(f"[commonly] poll failed ({exc.status}): {exc}", flush=True)
|
|
209
|
-
time.sleep(min(interval_s * 4, 60))
|
|
210
|
-
continue
|
|
211
|
-
for evt in events:
|
|
212
|
-
pod_id = evt.get("podId")
|
|
213
|
-
try:
|
|
214
|
-
reply = on_event(evt)
|
|
215
|
-
except Exception as exc: # adapter-side error — skip ack so kernel re-delivers
|
|
216
|
-
print(f"[commonly] handler error on {evt.get('_id')}: {exc}", flush=True)
|
|
217
|
-
continue
|
|
218
|
-
if reply and pod_id:
|
|
219
|
-
try:
|
|
220
|
-
self._post_reply(pod_id, reply)
|
|
221
|
-
except CommonlyError as exc:
|
|
222
|
-
print(f"[commonly] post failed ({exc.status}): {exc}", flush=True)
|
|
223
|
-
continue
|
|
224
|
-
try:
|
|
225
|
-
self.ack(
|
|
226
|
-
evt["_id"],
|
|
227
|
-
delivery_id=evt.get("payload", {}).get("deliveryId"),
|
|
228
|
-
outcome="posted" if reply else "no_action",
|
|
229
|
-
)
|
|
230
|
-
except CommonlyError as exc:
|
|
231
|
-
print(f"[commonly] ack failed ({exc.status}): {exc}", flush=True)
|
|
232
|
-
time.sleep(interval_s)
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# packages/seats
|
|
2
|
-
|
|
3
|
-
The adapters that run an agent CLI as a supervised seat: one prompt in, one CLI run, one reply out. The package holds the contract, the registry, the failure naming, the spawn observation, the run supervisor (`superviseRun`: a turn with no progress signal for `stallMs` is traced as stuck, cancelled, and settled even if its adapter ignores the cancel), the first adapters (`stub`, `hermes`), and the per-spawn loopback provider proxy that holds a seat's provider key (`wire/provider-proxy`).
|
|
4
|
-
|
|
5
|
-
It is private and is not published on its own. `@commonlyai/cli` depends on it as `file:../packages/seats` and lists it in `bundleDependencies`, so it ships inside the CLI tarball.
|
|
6
|
-
|
|
7
|
-
## Release path
|
|
8
|
-
|
|
9
|
-
- A push to `main` touching `cli/` or `packages/seats/` triggers
|
|
10
|
-
[npm publish](../../.github/workflows/npm-publish.yml). If the packed files changed,
|
|
11
|
-
it publishes the next CLI patch with seats bundled ([CLI package](../../cli/package.json)).
|
|
12
|
-
[Deploy Dev](../../.github/workflows/deploy-dev.yml) is manually dispatched and builds service images separately.
|
|
13
|
-
- The publish job assigns the version. PRs leave version lines alone
|
|
14
|
-
([version guard](../../.github/workflows/package-version-guard.yml), TASK-466).
|
|
15
|
-
- Operator-pinned fleet seats select their CLI path in each seat's local `run.sh` launcher,
|
|
16
|
-
outside this repo. Update that pin and restart the seat to load a release.
|
|
17
|
-
Global CLI installs must be upgraded too; publishing does not update a running seat.
|
|
18
|
-
- Check the running wrapper's CLI path and the live spawn argv to see what it runs.
|
|
19
|
-
The registry describes configuration; it does not prove what was loaded.
|
|
20
|
-
|
|
21
|
-
**Rule:** nothing here imports Commonly code. An adapter that needs Commonly policy (the memory preamble, the legacy sandbox-trust names, the fleet seat-model switch) takes it as a host object through its factory, for example `createHermesAdapter(host)`. The CLI binds that host in `cli/src/lib/adapters/<name>.js`. `cli/__tests__/seats-package.test.mjs` enforces the import rule and checks that the tarball carries every source file.
|
|
22
|
-
|
|
23
|
-
The extraction plan is extract-in-place.
|
|
24
|
-
|
|
25
|
-
**Run record.** The trace store writes each run as a versioned, hash-chained JSONL record. It covers who granted which agent what access for which task, who approved each call, what was attempted, and the result. A tool outside this package can read, verify and export it to OTLP. The format is specified in [`docs/record-v1.md`](docs/record-v1.md), and a generated sample is in [`docs/examples/`](docs/examples/).
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@commonlyai/seats",
|
|
3
|
-
"version": "0.0.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"license": "Apache-2.0",
|
|
6
|
-
"description": "Adapters that run agent CLIs as supervised seats. Bundled inside @commonlyai/cli; not published on its own.",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": "./src/index.js",
|
|
10
|
-
"./*": "./src/*.js"
|
|
11
|
-
},
|
|
12
|
-
"files": [
|
|
13
|
-
"src",
|
|
14
|
-
"README.md"
|
|
15
|
-
],
|
|
16
|
-
"engines": {
|
|
17
|
-
"node": ">=22.0.0"
|
|
18
|
-
},
|
|
19
|
-
"scripts": {
|
|
20
|
-
"test": "node --test $([ -z \"$CI\" ] && echo --test-concurrency=4) \"test/**/*.test.js\""
|
|
21
|
-
}
|
|
22
|
-
}
|