levain-cli 0.1.11__tar.gz → 0.1.14__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.
Files changed (26) hide show
  1. {levain_cli-0.1.11 → levain_cli-0.1.14}/PKG-INFO +2 -2
  2. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/cli.py +85 -4
  3. {levain_cli-0.1.11 → levain_cli-0.1.14}/pyproject.toml +2 -2
  4. {levain_cli-0.1.11 → levain_cli-0.1.14}/.gitignore +0 -0
  5. {levain_cli-0.1.11 → levain_cli-0.1.14}/LICENSE +0 -0
  6. {levain_cli-0.1.11 → levain_cli-0.1.14}/README.md +0 -0
  7. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/__init__.py +0 -0
  8. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/__main__.py +0 -0
  9. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/_format.py +0 -0
  10. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/_java.py +0 -0
  11. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/auth.py +0 -0
  12. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/codegen.py +0 -0
  13. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/graph.py +0 -0
  14. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/integrations_check.py +0 -0
  15. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/known_recipes.py +0 -0
  16. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/mcp.py +0 -0
  17. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/models.py +0 -0
  18. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/parser.py +0 -0
  19. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/profile.py +0 -0
  20. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/py.typed +0 -0
  21. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/reducers.py +0 -0
  22. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/source_check.py +0 -0
  23. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/tlc.py +0 -0
  24. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/translate.py +0 -0
  25. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/user_input.py +0 -0
  26. {levain_cli-0.1.11 → levain_cli-0.1.14}/levain_recipe/validate.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: levain-cli
3
- Version: 0.1.11
3
+ Version: 0.1.14
4
4
  Summary: Levain recipe toolchain: PlusCal validator, TLC model checking, and GraphState codegen for agent recipes
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -9,7 +9,7 @@ Requires-Dist: httpx==0.28.1
9
9
  Requires-Dist: pydantic==2.13.4
10
10
  Requires-Dist: tree-sitter-tlaplus==1.5.0
11
11
  Requires-Dist: tree-sitter==0.26.0
12
- Requires-Dist: typer==0.27.2
12
+ Requires-Dist: typer==0.27.3
13
13
  Provides-Extra: mcp
14
14
  Requires-Dist: fastmcp==4.0.5; extra == 'mcp'
15
15
  Description-Content-Type: text/markdown
@@ -337,14 +337,28 @@ def git_credential_cmd(
337
337
  raise typer.Exit(1)
338
338
  scope, slug = repo
339
339
 
340
+ # A ``workspace-<slug>`` repo in a workspace org is an agent's
341
+ # durable workspace, minted on a dedicated route. An agent whose
342
+ # own slug carries the prefix shadows it, so the recipe route is
343
+ # tried first and the workspace route is the 404 fallback.
344
+ mint_paths = [f"{api_base()}/api/v1/{scope}/{slug}/git-credential"]
345
+ if scope == "agents" and slug.startswith("workspace-"):
346
+ mint_paths.append(
347
+ f"{api_base()}/api/v1/agents/"
348
+ f"{slug.removeprefix('workspace-')}/workspace/git-credential"
349
+ )
350
+
340
351
  api = api_base()
341
352
  try:
342
353
  with _client() as client:
343
354
  token = access_token(api, client)
344
- resp = client.post(
345
- f"{api}/api/v1/{scope}/{slug}/git-credential",
346
- headers={"Authorization": f"Bearer {token}"},
347
- )
355
+ for mint_path in mint_paths:
356
+ resp = client.post(
357
+ mint_path,
358
+ headers={"Authorization": f"Bearer {token}"},
359
+ )
360
+ if resp.status_code != 404:
361
+ break
348
362
  except AuthError as exc:
349
363
  print(f"levain git-credential: {exc}", file=sys.stderr)
350
364
  raise typer.Exit(1) from None
@@ -378,3 +392,70 @@ def _repo_from_git_path(path: str) -> tuple[str, str] | None:
378
392
  else:
379
393
  return None
380
394
  return scope, repo.removesuffix(".git")
395
+
396
+
397
+ # ── Inbound webhooks ──────────────────────────────────────
398
+
399
+
400
+ webhook_app = typer.Typer(
401
+ name="webhook",
402
+ help="Manage an agent's inbound webhook.",
403
+ add_completion=False,
404
+ )
405
+ app.add_typer(webhook_app)
406
+
407
+
408
+ def _webhook_request(method: str, path: str) -> None:
409
+ from .auth import AuthError, access_token, api_base
410
+
411
+ api = api_base()
412
+ try:
413
+ with _client() as client:
414
+ token = access_token(api, client)
415
+ resp = client.request(
416
+ method,
417
+ f"{api}{path}",
418
+ headers={"Authorization": f"Bearer {token}"},
419
+ )
420
+ except AuthError as exc:
421
+ print(f"Error: {exc}", file=sys.stderr)
422
+ raise typer.Exit(1) from None
423
+ if resp.status_code >= 400:
424
+ try:
425
+ detail = resp.json().get("detail")
426
+ except ValueError:
427
+ detail = None
428
+ suffix = f" — {detail}" if detail else ""
429
+ print(f"Error: HTTP {resp.status_code}{suffix}", file=sys.stderr)
430
+ raise typer.Exit(1)
431
+ print(json.dumps(resp.json(), indent=2))
432
+
433
+
434
+ @webhook_app.command("create")
435
+ def webhook_create_cmd(
436
+ agent: Annotated[str, typer.Argument(help="Agent id or slug.")],
437
+ ) -> None:
438
+ """Create the agent's inbound webhook.
439
+
440
+ Prints the webhook once, including the URL with its embedded
441
+ secret — store it; it cannot be retrieved later. POST a JSON body
442
+ to the URL to dispatch a run of the agent, or include a top-level
443
+ ``session_id`` to take a turn on an existing session.
444
+ """
445
+ _webhook_request("POST", f"/api/v1/agents/{agent}/webhooks")
446
+
447
+
448
+ @webhook_app.command("rotate")
449
+ def webhook_rotate_cmd(
450
+ agent: Annotated[str, typer.Argument(help="Agent id or slug.")],
451
+ ) -> None:
452
+ """Rotate the webhook secret and print the fresh URL (shown once)."""
453
+ _webhook_request("POST", f"/api/v1/agents/{agent}/webhooks/rotate")
454
+
455
+
456
+ @webhook_app.command("delete")
457
+ def webhook_delete_cmd(
458
+ agent: Annotated[str, typer.Argument(help="Agent id or slug.")],
459
+ ) -> None:
460
+ """Delete the agent's inbound webhook."""
461
+ _webhook_request("DELETE", f"/api/v1/agents/{agent}/webhooks")
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "levain-cli"
3
- version = "0.1.11"
3
+ version = "0.1.14"
4
4
  description = "Levain recipe toolchain: PlusCal validator, TLC model checking, and GraphState codegen for agent recipes"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -27,7 +27,7 @@ dependencies = [
27
27
  "tree-sitter==0.26.0",
28
28
  "tree-sitter-tlaplus==1.5.0",
29
29
  "pydantic==2.13.4",
30
- "typer==0.27.2",
30
+ "typer==0.27.3",
31
31
  # Sign-in and credential minting. Imported lazily so validation
32
32
  # never pays for it.
33
33
  "httpx==0.28.1",
File without changes
File without changes
File without changes