agentic-runner 3.29.0__tar.gz → 3.30.1__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.
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/PKG-INFO +2 -2
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/pyproject.toml +1 -1
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/__init__.py +1 -1
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/auth_mode.py +3 -2
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/cli.py +63 -4
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/service.py +25 -8
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/sign_in.py +8 -15
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/skills/agentic-runner-setup/SKILL.md +1 -1
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workstation.py +54 -5
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/.gitignore +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/LICENSE +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/README.md +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/activities.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/attempts.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/build.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/callback.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/child_watcher.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/cli_floor.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/config.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/credentials.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/egress.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/harness_self_test.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/heartbeat_link.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/hooks.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/host_store.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/__init__.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/git/__init__.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/git/contracts.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/git/evidence.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/git/fake_workspace.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/git/workspace.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/github/__init__.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/github/auth.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/github/fake_client.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/github/gh_client.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/oauth/__init__.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/oauth/fake_provider.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/launch.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/lifecycle.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/llm_contracts.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/llm_proxy.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/mcp.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/message_store.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/oauth_connectors.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/organisation_name.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/pool_key.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/private_state.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/py.typed +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/recipient_key_secret.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/registration.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/runner_limit.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/runtime/__init__.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/runtime/verifier_command.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/sealed_box.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/skill.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/testing/__init__.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/testing/__main__.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/testing/control_plane.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/testing/plugin.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/testing/test_conformance.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/testing/workflow.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/tiny_http.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/triage_activities.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/usage_windows.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/user_sources.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/__init__.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/_runtime_support.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/acp_runtime.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/agent_runtime.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/claude_runtime.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/claude_sign_in.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/codex_runtime.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/command_policy.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/contract_isolation.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/fastapi_client.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/harness_logins.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/harness_outcome.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/harness_usage.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/mcp_config.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/settings.py +0 -0
- {agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/skills.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agentic-runner
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.30.1
|
|
4
4
|
Summary: The Agentic OS Runner: an activity-only Temporal executor for workspaces, Agent Runtimes and verb seams
|
|
5
5
|
License-Expression: AGPL-3.0-only
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Requires-Python: >=3.12
|
|
8
|
-
Requires-Dist: agentic-runner-contracts==3.
|
|
8
|
+
Requires-Dist: agentic-runner-contracts==3.30.1
|
|
9
9
|
Requires-Dist: cryptography<51,>=50
|
|
10
10
|
Requires-Dist: httpx<1.0,>=0.27
|
|
11
11
|
Requires-Dist: pydantic-settings<3.0,>=2.0
|
|
@@ -10,7 +10,7 @@ dependencies = [
|
|
|
10
10
|
# Exactly this release's contracts: the two ship as one version, and an unpinned
|
|
11
11
|
# requirement let `uv tool upgrade` keep an older contracts wheel the Runner cannot
|
|
12
12
|
# import from (QTS-1473). `scripts/set-version.py` moves it with the version.
|
|
13
|
-
"agentic-runner-contracts==3.
|
|
13
|
+
"agentic-runner-contracts==3.30.1",
|
|
14
14
|
# Imported directly by `registration.py` (the Ed25519 identity that signs every
|
|
15
15
|
# heartbeat); already present transitively through pyjwt[crypto], declared because
|
|
16
16
|
# we import it.
|
|
@@ -142,7 +142,7 @@ def choose_auth_mode(
|
|
|
142
142
|
raise AuthModeRefusedError(
|
|
143
143
|
SUBSCRIPTION_NEEDS_HARNESS_LOGIN_RULE,
|
|
144
144
|
"no API key is present for this Contract and its harness root on this Runner holds "
|
|
145
|
-
"no login; sign it in on this Runner with `agentic-runner sign-in`",
|
|
145
|
+
"no login; sign it in on this Runner with `agentic-runner sign-in claude` (or `codex`)",
|
|
146
146
|
)
|
|
147
147
|
if AuthMode.SUBSCRIPTION not in runtime_modes:
|
|
148
148
|
raise AuthModeRefusedError(
|
|
@@ -216,7 +216,8 @@ def require_named_access(
|
|
|
216
216
|
raise AuthModeRefusedError(
|
|
217
217
|
SUBSCRIPTION_NOT_SIGNED_IN_RULE,
|
|
218
218
|
f"this Directive was routed to the person's subscription, and its sign-in on "
|
|
219
|
-
f"this Runner is {subscription.value}; run `agentic-runner sign-in`
|
|
219
|
+
f"this Runner is {subscription.value}; run `agentic-runner sign-in claude` (or "
|
|
220
|
+
"`codex`) here",
|
|
220
221
|
)
|
|
221
222
|
return AuthMode.SUBSCRIPTION
|
|
222
223
|
if access.kind == LlmAccessKind.LLM_KEY:
|
|
@@ -11,12 +11,12 @@ namespace and queue this process was assigned. It is idempotent by state file --
|
|
|
11
11
|
restart re-reads what is already there rather than registering a second Runner against
|
|
12
12
|
the Organisation's cap.
|
|
13
13
|
|
|
14
|
-
``install | start | stop | status <name>``, ``credential set`` and ``list`` are the
|
|
14
|
+
``install | start | stop | restart | status <name>``, ``credential set`` and ``list`` are the
|
|
15
15
|
workstation Runner (PRD issue 47, ``workstation.py``): one login agent per Runner, managed
|
|
16
16
|
by the user who hosts it, with no ``sudo`` anywhere. ``<name>`` is the Runner's, the one the
|
|
17
17
|
console lists it under (QTS-1773); a machine with one Runner needs none (QTS-1774).
|
|
18
18
|
|
|
19
|
-
``sign-in
|
|
19
|
+
``sign-in claude|codex`` gives a person's own Runner their LLM access: their subscription,
|
|
20
20
|
through the vendor's own sign-in in this terminal, or an API key stored in the Runner's
|
|
21
21
|
own store (``sign_in.py``, Spec: Runners rev 3). No ids, and no code or token passes
|
|
22
22
|
through the control plane.
|
|
@@ -215,6 +215,17 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
215
215
|
driven.add_argument("name", nargs="?", help=_INSTALLED_NAME_HELP)
|
|
216
216
|
driven.add_argument("--root", default=None, type=Path)
|
|
217
217
|
|
|
218
|
+
restart = subcommands.add_parser(
|
|
219
|
+
"restart",
|
|
220
|
+
help="restart running workstation Runners on the agentic-runner installed now, as "
|
|
221
|
+
"after `uv tool upgrade`",
|
|
222
|
+
)
|
|
223
|
+
restart.add_argument("name", nargs="?", help=_INSTALLED_NAME_HELP)
|
|
224
|
+
which = restart.add_mutually_exclusive_group()
|
|
225
|
+
which.add_argument("--name", dest="name_flag", default=None, help=_INSTALLED_NAME_HELP)
|
|
226
|
+
which.add_argument("--all", action="store_true", help="every Runner running on this machine")
|
|
227
|
+
restart.add_argument("--root", default=None, type=Path)
|
|
228
|
+
|
|
218
229
|
listed = subcommands.add_parser(
|
|
219
230
|
"list", help="the workstation Runners installed on this machine, and which are running"
|
|
220
231
|
)
|
|
@@ -245,12 +256,15 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
245
256
|
sign_in = subcommands.add_parser(
|
|
246
257
|
"sign-in",
|
|
247
258
|
help="give your own Runner your subscription or an API key, for Claude Code or Codex",
|
|
259
|
+
# The runtime is named, never defaulted: a bare `sign-in` read as a Claude sign-in
|
|
260
|
+
# because Claude Code was asked about first (QTS-2159). Usage lists one form each.
|
|
261
|
+
usage="%(prog)s claude [--method {oauth_token,claude_ai}] [--name NAME]\n"
|
|
262
|
+
" %(prog)s codex [--name NAME]",
|
|
248
263
|
)
|
|
249
264
|
sign_in.add_argument(
|
|
250
265
|
"runtime",
|
|
251
|
-
nargs="?",
|
|
252
266
|
choices=tuple(AGENT_RUNTIMES),
|
|
253
|
-
help="
|
|
267
|
+
help="the Agent Runtime to sign in: claude (Claude Code) or codex (Codex)",
|
|
254
268
|
)
|
|
255
269
|
sign_in.add_argument(
|
|
256
270
|
"--method",
|
|
@@ -368,6 +382,8 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
368
382
|
return asyncio.run(service.run(attestation=_image_attestation(), install=_run_install()))
|
|
369
383
|
if arguments.command in {"install", "start", "stop", "credential"}:
|
|
370
384
|
return _workstation(arguments)
|
|
385
|
+
if arguments.command == "restart":
|
|
386
|
+
return _restart(arguments)
|
|
371
387
|
if arguments.command == "list":
|
|
372
388
|
for line in workstation.list_lines(arguments.root or workstation.default_root()):
|
|
373
389
|
print(line)
|
|
@@ -462,6 +478,49 @@ def _workstation(arguments: argparse.Namespace) -> int:
|
|
|
462
478
|
return 1
|
|
463
479
|
|
|
464
480
|
|
|
481
|
+
def _restart(arguments: argparse.Namespace) -> int:
|
|
482
|
+
"""``restart <name> | --name <name> | --all``: exit 2 names the Runners installed here.
|
|
483
|
+
|
|
484
|
+
A stopped Runner is not started: it was stopped on purpose, and ``systemctl restart``
|
|
485
|
+
would start it where ``launchctl kickstart`` refuses to, so the two OSes would differ.
|
|
486
|
+
"""
|
|
487
|
+
|
|
488
|
+
root = arguments.root or workstation.default_root()
|
|
489
|
+
name = arguments.name or arguments.name_flag
|
|
490
|
+
if arguments.all and name:
|
|
491
|
+
print("restart takes a name or --all, not both", file=sys.stderr)
|
|
492
|
+
return 2
|
|
493
|
+
try:
|
|
494
|
+
if arguments.all:
|
|
495
|
+
targets = [
|
|
496
|
+
workstation.RunnerPaths(root=root, name=installed)
|
|
497
|
+
for installed in workstation.installed_names(root)
|
|
498
|
+
]
|
|
499
|
+
if not targets:
|
|
500
|
+
raise ValueError(f"no workstation Runner is installed under {root}")
|
|
501
|
+
elif name:
|
|
502
|
+
targets = [workstation.installed_runner(root, name)]
|
|
503
|
+
else:
|
|
504
|
+
targets = [workstation.only_runner(root, how="name one, or restart --all")]
|
|
505
|
+
except ValueError as error:
|
|
506
|
+
print(error, file=sys.stderr)
|
|
507
|
+
return 2
|
|
508
|
+
failed = False
|
|
509
|
+
for paths in targets:
|
|
510
|
+
if workstation.process_id(paths) is None:
|
|
511
|
+
print(f"{paths.name} is stopped; `agentic-runner start {paths.name}` starts it")
|
|
512
|
+
failed = failed or not arguments.all
|
|
513
|
+
continue
|
|
514
|
+
try:
|
|
515
|
+
pid, version = workstation.restart(paths)
|
|
516
|
+
except (FileNotFoundError, RuntimeError) as error:
|
|
517
|
+
print(f"{paths.name}: restart failed: {error}", file=sys.stderr)
|
|
518
|
+
failed = True
|
|
519
|
+
continue
|
|
520
|
+
print(f"restarted {paths.name}: pid {pid}, agentic-runner {version}")
|
|
521
|
+
return 1 if failed else 0
|
|
522
|
+
|
|
523
|
+
|
|
465
524
|
def _image_attestation() -> HostAttestation:
|
|
466
525
|
"""What a container Runner says about itself, where issue 47 gave the workstation its
|
|
467
526
|
own: the CLIs it found are what the registry serves and routing matches on."""
|
|
@@ -1284,8 +1284,8 @@ def _temporal_worker(client: Client, **kwargs: Any) -> Worker:
|
|
|
1284
1284
|
return Worker(client, **kwargs)
|
|
1285
1285
|
|
|
1286
1286
|
|
|
1287
|
-
async def sign_in(which: str
|
|
1288
|
-
"""``agentic-runner sign-in
|
|
1287
|
+
async def sign_in(which: str, *, store: KeyStore, method: str = OAUTH_TOKEN) -> int:
|
|
1288
|
+
"""``agentic-runner sign-in claude|codex`` (``sign_in.SignIn``), composed here.
|
|
1289
1289
|
|
|
1290
1290
|
Runs with the Runner's environment (``kubectl exec``, ``docker exec``, or a
|
|
1291
1291
|
workstation's own), so a subscription gets the uid and the harness root the Contract's
|
|
@@ -1301,14 +1301,17 @@ async def sign_in(which: str | None, *, store: KeyStore, method: str = OAUTH_TOK
|
|
|
1301
1301
|
can = can_separate_uids()
|
|
1302
1302
|
try:
|
|
1303
1303
|
require_isolation_supported(mode, can_separate_uids=can)
|
|
1304
|
+
# `run` gives the settings this default; a sign-in must build the same ones, or a
|
|
1305
|
+
# workstation, whose environment sets only the control plane, is refused (QTS-2157).
|
|
1306
|
+
_default_callback_url(os.environ.get(CONTROL_PLANE_ENV, "").strip())
|
|
1304
1307
|
settings = get_worker_settings()
|
|
1305
1308
|
except RunnerRegistrationError as error:
|
|
1306
1309
|
print(f"refusing to sign in ({error.reason}): {error}", file=sys.stderr)
|
|
1307
1310
|
return 1
|
|
1308
|
-
except ValidationError:
|
|
1311
|
+
except ValidationError as error:
|
|
1309
1312
|
print(
|
|
1310
|
-
"refusing to sign in: run this with the Runner's
|
|
1311
|
-
"`kubectl exec`, or on the workstation that runs it)",
|
|
1313
|
+
f"refusing to sign in: {_invalid_settings(error)}; run this with the Runner's "
|
|
1314
|
+
"environment (`docker exec`, `kubectl exec`, or on the workstation that runs it)",
|
|
1312
1315
|
file=sys.stderr,
|
|
1313
1316
|
)
|
|
1314
1317
|
return 2
|
|
@@ -1336,6 +1339,22 @@ async def sign_in(which: str | None, *, store: KeyStore, method: str = OAUTH_TOK
|
|
|
1336
1339
|
_SIGN_IN_CLIS: Final[Mapping[str, str]] = {"claude_code": "claude", "codex_cli": "codex"}
|
|
1337
1340
|
|
|
1338
1341
|
|
|
1342
|
+
def _default_callback_url(control_plane: str) -> None:
|
|
1343
|
+
# The control plane the activities call back is the one this process registered
|
|
1344
|
+
# with, unless the operator points them at an internal address explicitly.
|
|
1345
|
+
if control_plane:
|
|
1346
|
+
os.environ.setdefault("INTERNAL_FASTAPI_BASE_URL", control_plane)
|
|
1347
|
+
|
|
1348
|
+
|
|
1349
|
+
def _invalid_settings(error: ValidationError) -> str:
|
|
1350
|
+
"""``INTERNAL_FASTAPI_BASE_URL (Field required)``, one per field, never the value."""
|
|
1351
|
+
|
|
1352
|
+
return ", ".join(
|
|
1353
|
+
f"{'.'.join(str(part) for part in detail['loc'])} ({detail['msg']})"
|
|
1354
|
+
for detail in error.errors(include_input=False, include_url=False)
|
|
1355
|
+
)
|
|
1356
|
+
|
|
1357
|
+
|
|
1339
1358
|
async def run(
|
|
1340
1359
|
*,
|
|
1341
1360
|
http_client: httpx.AsyncClient | None = None,
|
|
@@ -1416,9 +1435,7 @@ async def _run(
|
|
|
1416
1435
|
except (RunnerRegistrationError, UnsafeStateError) as error:
|
|
1417
1436
|
print(f"refusing to start ({error.reason}): {error}", flush=True)
|
|
1418
1437
|
return 1
|
|
1419
|
-
|
|
1420
|
-
# with, unless the operator points them at an internal address explicitly.
|
|
1421
|
-
os.environ.setdefault("INTERNAL_FASTAPI_BASE_URL", control_plane)
|
|
1438
|
+
_default_callback_url(control_plane)
|
|
1422
1439
|
settings = get_worker_settings()
|
|
1423
1440
|
try:
|
|
1424
1441
|
attestation = with_profile_acp_cli_kinds(with_acp_bridges(attestation, settings), settings)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
"""``agentic-runner sign-in
|
|
1
|
+
"""``agentic-runner sign-in claude|codex``: a person gives their own Runner its LLM access.
|
|
2
2
|
|
|
3
3
|
One command for Claude Code and Codex, for a subscription or an API key, on a laptop, in
|
|
4
4
|
``docker exec -it`` or in ``kubectl exec -it`` (Spec: Runners rev 3). It takes no ids: a
|
|
5
5
|
personal Runner serves one Contract, which the last heartbeat ack named
|
|
6
|
-
(``llm_contracts``).
|
|
6
|
+
(``llm_contracts``). It names the one Agent Runtime it signs in: a bare ``sign-in`` read as
|
|
7
|
+
a Claude Code sign-in, so the console script refuses it (QTS-2159).
|
|
7
8
|
|
|
8
9
|
* **An API key** is read without echo and stored in the Runner's own store under the
|
|
9
10
|
provider's key name (``ANTHROPIC_API_KEY``, ``OPENAI_API_KEY``). The running Runner reads
|
|
@@ -66,7 +67,7 @@ class SignIn:
|
|
|
66
67
|
secret: Callable[[str], str]
|
|
67
68
|
say: Callable[[str], None]
|
|
68
69
|
|
|
69
|
-
async def run(self, which: str
|
|
70
|
+
async def run(self, which: str) -> int:
|
|
70
71
|
if self.host_party != "user":
|
|
71
72
|
hosted = f"hosted by {self.host_party!r}" if self.host_party else "not registered"
|
|
72
73
|
self.say(
|
|
@@ -74,22 +75,14 @@ class SignIn:
|
|
|
74
75
|
"Runner their access; a pool Runner uses only its pool's keys"
|
|
75
76
|
)
|
|
76
77
|
return 1
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if missing:
|
|
78
|
+
kind = AGENT_RUNTIMES[which]
|
|
79
|
+
if kind not in self.served:
|
|
80
80
|
self.say(
|
|
81
|
-
f"refusing to sign in: this Runner does not serve {_LABELS[
|
|
81
|
+
f"refusing to sign in: this Runner does not serve {_LABELS[kind]}; "
|
|
82
82
|
"install its CLI on this Runner's PATH and restart it"
|
|
83
83
|
)
|
|
84
84
|
return 1
|
|
85
|
-
|
|
86
|
-
self.say("this Runner serves no Agent Runtime: install `claude` or `codex` first")
|
|
87
|
-
return 1
|
|
88
|
-
for kind in kinds:
|
|
89
|
-
outcome = await self._one(kind)
|
|
90
|
-
if outcome != 0:
|
|
91
|
-
return outcome
|
|
92
|
-
return 0
|
|
85
|
+
return await self._one(kind)
|
|
93
86
|
|
|
94
87
|
async def _one(self, cli_kind: str) -> int:
|
|
95
88
|
label = _LABELS[cli_kind]
|
|
@@ -115,7 +115,7 @@ own terminal, the one command for their way of installing. On this machine:
|
|
|
115
115
|
agentic-runner sign-in claude
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
-
`codex` in place of `claude` for Codex;
|
|
118
|
+
`codex` in place of `claude` for Codex; `sign-in` alone is refused. Add
|
|
119
119
|
`--name <name>` only if this machine runs more than one Runner. On Docker it is
|
|
120
120
|
`docker exec -it <container> agentic-runner sign-in claude`, on Kubernetes
|
|
121
121
|
`kubectl exec -it <pod> -c runner -- agentic-runner sign-in claude`.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""The workstation Runner: one login agent per Runner (PRD issue 47, map ticket 23).
|
|
2
2
|
|
|
3
|
-
``agentic-runner install | start | stop | status <name>`` and ``list``. Everything
|
|
4
|
-
precedent does on a developer machine and nothing a daemon would need:
|
|
3
|
+
``agentic-runner install | start | stop | restart | status <name>`` and ``list``. Everything
|
|
4
|
+
a CI-runner precedent does on a developer machine and nothing a daemon would need:
|
|
5
5
|
|
|
6
6
|
* **One process per Runner** (23 item 3), each with its own state directory, identity,
|
|
7
7
|
namespace, Runner Token, hooks, credential store and Workspaces under ``<root>/<name>/``.
|
|
@@ -37,11 +37,12 @@ import shutil
|
|
|
37
37
|
import subprocess
|
|
38
38
|
import sys
|
|
39
39
|
import tempfile
|
|
40
|
+
import time
|
|
40
41
|
from collections.abc import Callable, Mapping, Sequence
|
|
41
42
|
from dataclasses import dataclass
|
|
42
43
|
from datetime import UTC, datetime
|
|
43
44
|
from pathlib import Path
|
|
44
|
-
from typing import Any, Final
|
|
45
|
+
from typing import Any, Final, Literal
|
|
45
46
|
from xml.sax.saxutils import escape
|
|
46
47
|
|
|
47
48
|
import httpx
|
|
@@ -91,11 +92,13 @@ __all__ = [
|
|
|
91
92
|
"install",
|
|
92
93
|
"install_channel",
|
|
93
94
|
"installed_names",
|
|
95
|
+
"installed_runner",
|
|
94
96
|
"list_lines",
|
|
95
97
|
"locate_clis",
|
|
96
98
|
"only_runner",
|
|
97
99
|
"parse_acp_cli_kinds",
|
|
98
100
|
"require_any_cli",
|
|
101
|
+
"restart",
|
|
99
102
|
"run_environment",
|
|
100
103
|
"run_process",
|
|
101
104
|
"service_definition",
|
|
@@ -158,6 +161,16 @@ def installed_names(root: Path) -> list[str]:
|
|
|
158
161
|
)
|
|
159
162
|
|
|
160
163
|
|
|
164
|
+
def installed_runner(root: Path, name: str) -> RunnerPaths:
|
|
165
|
+
"""``<name>``, refused with the names installed here when it is not one of them."""
|
|
166
|
+
|
|
167
|
+
names = installed_names(root)
|
|
168
|
+
if name in names:
|
|
169
|
+
return RunnerPaths(root=root, name=name)
|
|
170
|
+
installed = ", ".join(names) if names else "none"
|
|
171
|
+
raise ValueError(f"no Runner named {name!r} is installed under {root}; installed: {installed}")
|
|
172
|
+
|
|
173
|
+
|
|
161
174
|
def only_runner(root: Path, *, how: str = "name one") -> RunnerPaths:
|
|
162
175
|
"""The one Runner installed here, so a command needs no name (QTS-1774)."""
|
|
163
176
|
|
|
@@ -444,6 +457,7 @@ class ServiceDefinition:
|
|
|
444
457
|
install: list[list[str]]
|
|
445
458
|
start: list[list[str]]
|
|
446
459
|
stop: list[list[str]]
|
|
460
|
+
restart: list[list[str]]
|
|
447
461
|
|
|
448
462
|
|
|
449
463
|
def runner_command(name: str, root: Path) -> list[str]:
|
|
@@ -504,6 +518,9 @@ def _launch_agent(
|
|
|
504
518
|
install=[],
|
|
505
519
|
start=[["launchctl", "bootstrap", domain, str(plist)]],
|
|
506
520
|
stop=[["launchctl", "bootout", f"{domain}/{name}"]],
|
|
521
|
+
# One call, not bootout then bootstrap: launchd refuses a bootstrap of a label it
|
|
522
|
+
# still holds, and it holds it until the old process has drained.
|
|
523
|
+
restart=[["launchctl", "kickstart", "-k", f"{domain}/{name}"]],
|
|
507
524
|
)
|
|
508
525
|
|
|
509
526
|
|
|
@@ -546,6 +563,7 @@ def _systemd_user_unit(
|
|
|
546
563
|
],
|
|
547
564
|
start=[[*systemctl, "enable", "--now", unit_name]],
|
|
548
565
|
stop=[[*systemctl, "disable", "--now", unit_name]],
|
|
566
|
+
restart=[[*systemctl, "restart", unit_name]],
|
|
549
567
|
)
|
|
550
568
|
|
|
551
569
|
|
|
@@ -599,6 +617,10 @@ def _logon_task(
|
|
|
599
617
|
["schtasks", "/End", "/TN", task_name],
|
|
600
618
|
["schtasks", "/Change", "/TN", task_name, "/DISABLE"],
|
|
601
619
|
],
|
|
620
|
+
restart=[
|
|
621
|
+
["schtasks", "/End", "/TN", task_name],
|
|
622
|
+
["schtasks", "/Run", "/TN", task_name],
|
|
623
|
+
],
|
|
602
624
|
)
|
|
603
625
|
|
|
604
626
|
|
|
@@ -837,9 +859,36 @@ def stop(paths: RunnerPaths, **driver: Any) -> None:
|
|
|
837
859
|
_drive_service(paths, "stop", **driver)
|
|
838
860
|
|
|
839
861
|
|
|
862
|
+
def restart(
|
|
863
|
+
paths: RunnerPaths,
|
|
864
|
+
*,
|
|
865
|
+
timeout: float = 60.0,
|
|
866
|
+
sleep: Callable[[float], None] = time.sleep,
|
|
867
|
+
clock: Callable[[], float] = time.monotonic,
|
|
868
|
+
**driver: Any,
|
|
869
|
+
) -> tuple[int, str]:
|
|
870
|
+
"""Restart a running Runner and wait for its new process: ``(pid, version)``.
|
|
871
|
+
|
|
872
|
+
``uv tool upgrade`` replaces the console script, not the process, so a login agent keeps
|
|
873
|
+
running the old version until this (QTS-2158). The process writes its version before
|
|
874
|
+
its pidfile, so a new pid means the version beside it is the new process's.
|
|
875
|
+
"""
|
|
876
|
+
|
|
877
|
+
previous = process_id(paths)
|
|
878
|
+
_drive_service(paths, "restart", **driver)
|
|
879
|
+
deadline = clock() + timeout
|
|
880
|
+
while (pid := process_id(paths)) is None or pid == previous:
|
|
881
|
+
if clock() >= deadline:
|
|
882
|
+
raise RuntimeError(
|
|
883
|
+
f"no new process wrote {paths.pidfile} within {timeout:.0f}s; see {paths.log}"
|
|
884
|
+
)
|
|
885
|
+
sleep(0.5)
|
|
886
|
+
return pid, paths.started_version.read_text(encoding="utf-8").strip()
|
|
887
|
+
|
|
888
|
+
|
|
840
889
|
def _drive_service(
|
|
841
890
|
paths: RunnerPaths,
|
|
842
|
-
verb:
|
|
891
|
+
verb: Literal["start", "stop", "restart"],
|
|
843
892
|
*,
|
|
844
893
|
platform: str = sys.platform,
|
|
845
894
|
home: Path | None = None,
|
|
@@ -847,7 +896,7 @@ def _drive_service(
|
|
|
847
896
|
) -> None:
|
|
848
897
|
settings = WorkstationSettings.load(paths)
|
|
849
898
|
definition = service_definition(paths, settings, platform=platform, home=home)
|
|
850
|
-
drive(definition
|
|
899
|
+
drive(getattr(definition, verb), run=run)
|
|
851
900
|
|
|
852
901
|
|
|
853
902
|
def run_process(paths: RunnerPaths, *, login_agent: bool) -> int:
|
|
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
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/git/__init__.py
RENAMED
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/git/contracts.py
RENAMED
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/git/evidence.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/git/workspace.py
RENAMED
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/github/__init__.py
RENAMED
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/github/auth.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/github/gh_client.py
RENAMED
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/integrations/oauth/__init__.py
RENAMED
|
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
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/runtime/verifier_command.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/testing/test_conformance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/_runtime_support.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/claude_runtime.py
RENAMED
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/claude_sign_in.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/command_policy.py
RENAMED
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/contract_isolation.py
RENAMED
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/fastapi_client.py
RENAMED
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/harness_logins.py
RENAMED
|
File without changes
|
{agentic_runner-3.29.0 → agentic_runner-3.30.1}/src/agentic_runner/workers/harness_outcome.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|